diff --git a/day08/main.go b/day08/main.go index 3a9cb66..ec9f332 100644 --- a/day08/main.go +++ b/day08/main.go @@ -5,21 +5,12 @@ import ( "fmt" "os" "strings" + + aoc "go.sour.is/advent-of-code-2023" ) 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) + result, err := aoc.Runner(run) if err != nil { fmt.Println("ERR", err) os.Exit(1) @@ -29,10 +20,14 @@ func main() { } type result struct { - stepsPT1 int + stepsPT1 uint64 stepsPT2 uint64 } +func (r result) String() string { + return fmt.Sprintf("solution 1: %v\nsolution 2: %v\n", r.stepsPT1, r.stepsPT2) +} + func run(scan *bufio.Scanner) (*result, error) { var path []rune m := make(nodeMap) @@ -64,8 +59,8 @@ func run(scan *bufio.Scanner) (*result, error) { return nil, err } - steps1 := SolutionPT1(m, path) - steps2 := SolutionPT2(m, path) + steps1 := m.SolvePT1(path) + steps2 := m.SolvePT2(path) return &result{steps1, steps2}, nil } @@ -96,26 +91,25 @@ func (m nodeMap) mapNodes() error { return nil } -func SolutionPT1(m nodeMap, path []rune) int { - fmt.Println("---- PART 1 BEGIN ----") - position, ok := m["AAA"] +func (m nodeMap) solver(start string, isEnd func(string) bool, path []rune) uint64 { + position, ok := m[start] if !ok { return 0 } var i int - var steps int + var steps uint64 - for steps < 100000 { + for steps < ^uint64(0) { steps++ if path[i] == 'R' { - fmt.Println("step", steps, position.value, "R->", position.rvalue) + // fmt.Println("step", steps, position.value, "R->", position.rvalue) position = position.right } else { - fmt.Println("step", steps, position.value, "L->", position.lvalue) + // fmt.Println("step", steps, position.value, "L->", position.lvalue) position = position.left } - if position.value == "ZZZ" { + if isEnd(position.value) { break } @@ -124,126 +118,33 @@ func SolutionPT1(m nodeMap, path []rune) int { i = 0 } } - fmt.Println("---- PART 1 END ----") return steps } -func SolutionPT2(m nodeMap, path []rune) uint64 { +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 { fmt.Println("---- PART 2 BEGIN ----") + defer fmt.Println("---- PART 2 END ----") - type loop struct { - start, position, end *node - steps uint64 - } - loops := make(map[*node]loop) + var starts []*node - 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{}{} + starts = append(starts, n) } } - 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 - } + 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) } - - fmt.Println("---- PART 2 END ----") - return steps + return aoc.LCM(loops...) } -// 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 -} diff --git a/day08/main_test.go b/day08/main_test.go index 70b0675..4550f4a 100644 --- a/day08/main_test.go +++ b/day08/main_test.go @@ -30,7 +30,7 @@ func TestExample1(t *testing.T) { is.NoErr(err) t.Log(result.stepsPT1) - is.Equal(result.stepsPT1, 2) + is.Equal(result.stepsPT1, uint64(2)) } func TestExample2(t *testing.T) { @@ -41,7 +41,7 @@ func TestExample2(t *testing.T) { is.NoErr(err) t.Log(result.stepsPT1) - is.Equal(result.stepsPT1, 6) + is.Equal(result.stepsPT1, uint64(6)) } func TestExample3(t *testing.T) { @@ -63,10 +63,18 @@ func TestInput(t *testing.T) { is.NoErr(err) t.Log("part1 solution", result.stepsPT1) - is.Equal(result.stepsPT1, 14429) + is.Equal(result.stepsPT1, uint64(14429)) + t.Log("part2 solution", result.stepsPT2) is.Equal(result.stepsPT2, uint64(10921547990923)) } // first: 14429 -// second: 10921547990923 \ No newline at end of file +// second: 10921547990923 + +// BrĂ¼t +// stops 1 steps 13201 +// stops 2 steps 620447 +// stops 3 steps 36606373 +// stops 4 steps 2232988753 +// stops 5 steps 149610246451 diff --git a/tools.go b/tools.go new file mode 100644 index 0000000..115980a --- /dev/null +++ b/tools.go @@ -0,0 +1,50 @@ +package aoc + +import ( + "bufio" + "fmt" + "os" +) + +func Runner[R any, F func(*bufio.Scanner) (R, error)](run F) (R, error) { + if len(os.Args) != 2 { + fmt.Fprintln(os.Stderr, "Usage:", os.Args[0] , "FILE") + } + + input, err := os.Open(os.Args[1]) + if err != nil { + fmt.Fprintln(os.Stderr, err) + } + + scan := bufio.NewScanner(input) + return run(scan) +} + +// 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 +}