aoc_2024/2025/five/five.go

153 lines
2.8 KiB
Go

package main
import (
"bufio"
"errors"
"fmt"
"os"
"strconv"
"strings"
)
type freshRange struct {
start, end int
}
type freshDB struct {
ranges []freshRange
}
func (r *freshRange) contains(c int) bool {
return c >= r.start && c <= r.end
}
func (r *freshRange) nInRange() int {
total := (r.end - r.start) + 1
return total
}
/* Check for an error, panic if there is */
func check(err error) {
if err != nil {
panic(err)
}
}
func dbFromSlice(ranges []int) (db freshDB, err error) {
if len(ranges)%2 != 0 {
return freshDB{}, errors.New("Input slice not of even length")
}
db.ranges = make([]freshRange, 0, len(ranges)/2)
for i := 0; i < len(ranges); i += 2 {
db.ranges = append(db.ranges, freshRange{ranges[i], ranges[i+1]})
}
return db, nil
}
/* Parse ranges out of a bufio.Scanner */
func parse(scanner *bufio.Scanner) (db freshDB, err error) {
db.ranges = make([]freshRange, 0, 5)
for scanner.Scan() {
text := scanner.Text()
if text == "" {
return db, nil
}
startEnd := strings.Split(text, "-")
if len(startEnd) != 2 {
return db, errors.New("Fresh range does not contain exactly two numbers")
}
start, err := strconv.Atoi(startEnd[0])
if err != nil {
return db, err
}
end, err := strconv.Atoi(startEnd[1])
if err != nil {
return db, err
}
db.ranges = append(db.ranges, freshRange{start, end})
}
return db, errors.New("No empty line in file to delimit fresh ranges")
}
func (db *freshDB) insert(toinsert freshRange) {
for i := range db.ranges {
r := &db.ranges[i]
if r.contains(toinsert.start) {
if toinsert.end > r.end {
r.end = toinsert.end
}
return
}
if r.contains(toinsert.end) {
if toinsert.start < r.start {
r.start = toinsert.start
}
return
}
if toinsert.contains(r.start) && toinsert.contains(r.end) {
r.start = toinsert.start
r.end = toinsert.end
return
}
}
db.ranges = append(db.ranges, toinsert)
}
/* Collapse a list of fresh ingredient ranges to avoid overlaps */
func (db *freshDB) collapse() (before int, after int) {
before = len(db.ranges)
collapsed := freshDB{make([]freshRange, 0, len(db.ranges))}
for _, r := range db.ranges {
collapsed.insert(r)
}
db.ranges = collapsed.ranges
after = len(db.ranges)
return before, after
}
func main() {
file, err := os.Open("five.txt")
check(err)
defer file.Close()
scanner := bufio.NewScanner(file)
db, err := parse(scanner)
check(err)
nRaw := len(db.ranges)
for {
before, after := db.collapse()
fmt.Printf("Collapsed %d to %d ranges\n", before, after)
if before == after {
break
}
}
potentialNFresh := 0
for _, r := range db.ranges {
potentialNFresh += r.nInRange()
}
fmt.Printf(
"N raw %d and consolidated %d. %d potential fresh ingredients.\n",
nRaw,
len(db.ranges),
potentialNFresh,
)
}