2023-12-08 15:11:24 -07:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"strings"
|
2023-12-08 16:24:48 -07:00
|
|
|
|
2023-12-15 15:13:24 -07:00
|
|
|
aoc "go.sour.is/advent-of-code"
|
2023-12-08 15:11:24 -07:00
|
|
|
)
|
|
|
|
|
|
|
|
func main() {
|
2023-12-08 16:24:48 -07:00
|
|
|
result, err := aoc.Runner(run)
|
2023-12-08 15:11:24 -07:00
|
|
|
if err != nil {
|
|
|
|
fmt.Println("ERR", err)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Println("result", result)
|
|
|
|
}
|
|
|
|
|
|
|
|
type result struct {
|
2023-12-08 16:24:48 -07:00
|
|
|
stepsPT1 uint64
|
2023-12-08 15:11:24 -07:00
|
|
|
stepsPT2 uint64
|
|
|
|
}
|
|
|
|
|
2023-12-08 16:24:48 -07:00
|
|
|
func (r result) String() string {
|
|
|
|
return fmt.Sprintf("solution 1: %v\nsolution 2: %v\n", r.stepsPT1, r.stepsPT2)
|
|
|
|
}
|
|
|
|
|
2023-12-08 15:11:24 -07:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2023-12-08 16:24:48 -07:00
|
|
|
steps1 := m.SolvePT1(path)
|
|
|
|
steps2 := m.SolvePT2(path)
|
2023-12-08 15:11:24 -07:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2023-12-08 16:24:48 -07:00
|
|
|
func (m nodeMap) solver(start string, isEnd func(string) bool, path []rune) uint64 {
|
|
|
|
position, ok := m[start]
|
2023-12-08 15:11:24 -07:00
|
|
|
if !ok {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
var i int
|
2023-12-08 16:24:48 -07:00
|
|
|
var steps uint64
|
2023-12-08 15:11:24 -07:00
|
|
|
|
2023-12-08 16:24:48 -07:00
|
|
|
for steps < ^uint64(0) {
|
2023-12-08 15:11:24 -07:00
|
|
|
steps++
|
|
|
|
if path[i] == 'R' {
|
2023-12-08 16:24:48 -07:00
|
|
|
// fmt.Println("step", steps, position.value, "R->", position.rvalue)
|
2023-12-08 15:11:24 -07:00
|
|
|
position = position.right
|
|
|
|
} else {
|
2023-12-08 16:24:48 -07:00
|
|
|
// fmt.Println("step", steps, position.value, "L->", position.lvalue)
|
2023-12-08 15:11:24 -07:00
|
|
|
position = position.left
|
|
|
|
}
|
|
|
|
|
2023-12-08 16:24:48 -07:00
|
|
|
if isEnd(position.value) {
|
2023-12-08 15:11:24 -07:00
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
i++
|
|
|
|
if i > len(path)-1 {
|
|
|
|
i = 0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return steps
|
|
|
|
}
|
|
|
|
|
2023-12-08 16:24:48 -07:00
|
|
|
func (m nodeMap) SolvePT1(path []rune) uint64 {
|
|
|
|
fmt.Println("---- PART 1 BEGIN ----")
|
|
|
|
defer fmt.Println("---- PART 1 END ----")
|
|
|
|
|
|
|
|
return m.solver("AAA", func(s string) bool { return s == "ZZZ" }, path)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m nodeMap) SolvePT2(path []rune) uint64 {
|
2023-12-08 15:11:24 -07:00
|
|
|
fmt.Println("---- PART 2 BEGIN ----")
|
2023-12-08 16:24:48 -07:00
|
|
|
defer fmt.Println("---- PART 2 END ----")
|
2023-12-08 15:11:24 -07:00
|
|
|
|
2023-12-08 16:24:48 -07:00
|
|
|
var starts []*node
|
2023-12-08 15:11:24 -07:00
|
|
|
|
|
|
|
for k, n := range m {
|
|
|
|
if strings.HasSuffix(k, "A") {
|
|
|
|
fmt.Println("start", k)
|
2023-12-08 16:24:48 -07:00
|
|
|
starts = append(starts, n)
|
2023-12-08 15:11:24 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-08 16:24:48 -07:00
|
|
|
loops := make([]uint64, len(starts))
|
|
|
|
for i, n := range starts {
|
|
|
|
loops[i] = m.solver(n.value, func(s string) bool { return strings.HasSuffix(s, "Z") }, path)
|
2023-12-08 15:11:24 -07:00
|
|
|
}
|
2023-12-08 16:24:48 -07:00
|
|
|
return aoc.LCM(loops...)
|
2023-12-08 15:11:24 -07:00
|
|
|
}
|