advent-of-code/day08/main.go
2023-12-08 15:11:24 -07:00

250 lines
4.4 KiB
Go

package main
import (
"bufio"
"fmt"
"os"
"strings"
)
func main() {
if len(os.Args) != 2 {
fmt.Fprintln(os.Stderr, "Usage: day08 FILE")
}
input, err := os.Open(os.Args[1])
if err != nil {
fmt.Fprintln(os.Stderr, err)
}
scan := bufio.NewScanner(input)
result, err := run(scan)
if err != nil {
fmt.Println("ERR", err)
os.Exit(1)
}
fmt.Println("result", result)
}
type result struct {
stepsPT1 int
stepsPT2 uint64
}
func run(scan *bufio.Scanner) (*result, error) {
var path []rune
m := make(nodeMap)
for scan.Scan() {
text := scan.Text()
if len(text) == 0 {
continue
}
if len(path) == 0 {
fmt.Println("path", text)
path = []rune(strings.TrimSpace(text))
continue
}
n := &node{}
i, err := fmt.Sscanf(text, "%s = (%s %s", &n.value, &n.lvalue, &n.rvalue)
if err != nil {
return nil, err
}
n.lvalue = strings.TrimRight(n.lvalue, ",)")
n.rvalue = strings.TrimRight(n.rvalue, ",)")
m[n.value] = n
fmt.Println("value", i, n.value, n.lvalue, n.rvalue)
}
if err := m.mapNodes(); err != nil {
return nil, err
}
steps1 := SolutionPT1(m, path)
steps2 := SolutionPT2(m, path)
return &result{steps1, steps2}, nil
}
type node struct {
value string
lvalue, rvalue string
left, right *node
}
type nodeMap map[string]*node
func (m nodeMap) mapNodes() error {
for k, v := range m {
if ptr, ok := m[v.lvalue]; ok {
v.left = ptr
} else {
return fmt.Errorf("%s L-> %s not found", k, v.lvalue)
}
if ptr, ok := m[v.rvalue]; ok {
v.right = ptr
} else {
return fmt.Errorf("%s R-> %s not found", k, v.rvalue)
}
m[k] = v
}
return nil
}
func SolutionPT1(m nodeMap, path []rune) int {
fmt.Println("---- PART 1 BEGIN ----")
position, ok := m["AAA"]
if !ok {
return 0
}
var i int
var steps int
for steps < 100000 {
steps++
if path[i] == 'R' {
fmt.Println("step", steps, position.value, "R->", position.rvalue)
position = position.right
} else {
fmt.Println("step", steps, position.value, "L->", position.lvalue)
position = position.left
}
if position.value == "ZZZ" {
break
}
i++
if i > len(path)-1 {
i = 0
}
}
fmt.Println("---- PART 1 END ----")
return steps
}
func SolutionPT2(m nodeMap, path []rune) uint64 {
fmt.Println("---- PART 2 BEGIN ----")
type loop struct {
start, position, end *node
steps uint64
}
loops := make(map[*node]loop)
endpoints := make(map[*node]struct{})
for k, n := range m {
if strings.HasSuffix(k, "A") {
fmt.Println("start", k)
loops[n] = loop{start: n, position: n}
}
if strings.HasSuffix(k, "Z") {
fmt.Println("stop", k)
endpoints[n] = struct{}{}
}
}
var i int
var steps uint64
var stops int
maxUint := ^uint64(0)
loopsFound := 0
for steps < maxUint {
steps++
if path[i] == 'R' {
for k, loop := range loops {
// fmt.Println("step", steps, position.value, "R->", position.rvalue)
loop.position = loop.position.right
loops[k] = loop
}
} else {
for k, loop := range loops {
// fmt.Println("step", steps, position.value, "L->", position.lvalue)
loop.position = loop.position.left
loops[k] = loop
}
}
done := true
s := 0
for k, loop := range loops {
if _, ok := endpoints[loop.position]; !ok {
// fmt.Println("no stop", i, position.value)
done = false
// break
} else {
// fmt.Println("stop", i, position.value)
if loop.end == nil {
loop.end = loop.position
loop.steps = steps
fmt.Println("loop found", loop.position.value, "steps", steps)
loops[k] = loop
loopsFound++
}
s++
}
}
if loopsFound == len(loops) {
var values []uint64
for _, loop := range loops {
values = append(values, loop.steps)
}
return LCM(values...)
}
if s > stops {
stops = s
fmt.Println("stops", stops, "steps", steps)
}
if done {
break
}
i++
if i > len(path)-1 {
i = 0
}
}
fmt.Println("---- PART 2 END ----")
return steps
}
// greatest common divisor (GCD) via Euclidean algorithm
func GCD(a, b uint64) uint64 {
for b != 0 {
t := b
b = a % b
a = t
}
return a
}
// find Least Common Multiple (LCM) via GCD
func LCM(integers ...uint64) uint64 {
if len(integers) == 0 {
return 0
}
if len(integers) == 1 {
return integers[0]
}
a, b := integers[0], integers[1]
result := a * b / GCD(a, b)
for _, c := range integers[2:] {
result = LCM(result, c)
}
return result
}