advent-of-code/day09/main.go
2023-12-09 10:56:31 -07:00

142 lines
2.6 KiB
Go

package main
import (
"bufio"
"cmp"
"fmt"
"strconv"
"strings"
aoc "go.sour.is/advent-of-code-2023"
)
func main() { aoc.MustResult(aoc.Runner(run)) }
type result struct {
valuePT1 int
valuePT2 int
}
func (r result) String() string { return fmt.Sprintf("%#v", r) }
var log = aoc.Log
func run(scan *bufio.Scanner) (*result, error) {
var histories [][]int
var values []int
var rvalues []int
for scan.Scan() {
text := scan.Text()
if len(text) == 0 {
continue
}
histories = append(histories, nil)
for _, s := range strings.Fields(text) {
if i, err := strconv.Atoi(s); err == nil {
histories[len(histories)-1] = append(histories[len(histories)-1], i)
}
}
log(last(histories...))
values = append(values, predictNext(last(histories...)))
rvalues = append(rvalues, predictPrev(last(histories...)))
}
log("values", values)
log("rvalues", rvalues)
return &result{valuePT1: sum(values...), valuePT2: sum(rvalues...)}, nil
}
func predictNext(in []int) int {
log(" ---- PREDICT NEXT ----")
defer log(" ----------------------")
history := makeHistory(in)
aoc.Reverse(history)
return predict(history, func(a, b int) int { return a + b })
}
func predictPrev(in []int) int {
log(" ---- PREDICT PREV ----")
defer log(" ----------------------")
history := makeHistory(in)
for i := range history {
aoc.Reverse(history[i])
}
aoc.Reverse(history)
return predict(history, func(a, b int) int { return b - a })
}
func predict(history [][]int, diff func(a, b int) int) int {
log(" ---- PREDICT ----")
defer log(" -----------------")
for i := range history[1:] {
lastHistory, curHistory := last(history[i]...), last(history[i+1]...)
history[i+1] = append(history[i+1], diff(lastHistory, curHistory))
log(lastHistory, curHistory, last(history[i+1]))
}
log("last", last(history...))
return last(last(history...)...)
}
func makeHistory(in []int) [][]int {
var history [][]int
history = append(history, in)
for {
var diffs []int
current := history[len(history)-1]
for i := range current[1:] {
diffs = append(diffs, current[i+1]-current[i])
}
history = append(history, diffs)
log(diffs)
if max(diffs[0], diffs[1:]...) == 0 && min(diffs[0], diffs[1:]...) == 0 {
break
}
}
return history
}
func max[T cmp.Ordered](a T, v ...T) T {
for _, b := range v {
if b > a {
a = b
}
}
return a
}
func min[T cmp.Ordered](a T, v ...T) T {
for _, b := range v {
if b < a {
a = b
}
}
return a
}
func sum[T cmp.Ordered](v ...T) T {
var s T
for _, a := range v {
s += a
}
return s
}
func last[T any](v ...T) T {
return v[len(v)-1]
}