chore: simplify day 8

This commit is contained in:
xuu 2023-12-08 16:24:48 -07:00
parent 8e7fa3c5a8
commit 5106558ce9
3 changed files with 94 additions and 135 deletions

View File

@ -5,21 +5,12 @@ import (
"fmt" "fmt"
"os" "os"
"strings" "strings"
aoc "go.sour.is/advent-of-code-2023"
) )
func main() { func main() {
if len(os.Args) != 2 { result, err := aoc.Runner(run)
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 { if err != nil {
fmt.Println("ERR", err) fmt.Println("ERR", err)
os.Exit(1) os.Exit(1)
@ -29,10 +20,14 @@ func main() {
} }
type result struct { type result struct {
stepsPT1 int stepsPT1 uint64
stepsPT2 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) { func run(scan *bufio.Scanner) (*result, error) {
var path []rune var path []rune
m := make(nodeMap) m := make(nodeMap)
@ -64,8 +59,8 @@ func run(scan *bufio.Scanner) (*result, error) {
return nil, err return nil, err
} }
steps1 := SolutionPT1(m, path) steps1 := m.SolvePT1(path)
steps2 := SolutionPT2(m, path) steps2 := m.SolvePT2(path)
return &result{steps1, steps2}, nil return &result{steps1, steps2}, nil
} }
@ -96,26 +91,25 @@ func (m nodeMap) mapNodes() error {
return nil return nil
} }
func SolutionPT1(m nodeMap, path []rune) int { func (m nodeMap) solver(start string, isEnd func(string) bool, path []rune) uint64 {
fmt.Println("---- PART 1 BEGIN ----") position, ok := m[start]
position, ok := m["AAA"]
if !ok { if !ok {
return 0 return 0
} }
var i int var i int
var steps int var steps uint64
for steps < 100000 { for steps < ^uint64(0) {
steps++ steps++
if path[i] == 'R' { 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 position = position.right
} else { } else {
fmt.Println("step", steps, position.value, "L->", position.lvalue) // fmt.Println("step", steps, position.value, "L->", position.lvalue)
position = position.left position = position.left
} }
if position.value == "ZZZ" { if isEnd(position.value) {
break break
} }
@ -124,126 +118,33 @@ func SolutionPT1(m nodeMap, path []rune) int {
i = 0 i = 0
} }
} }
fmt.Println("---- PART 1 END ----")
return steps return steps
} }
func SolutionPT2(m nodeMap, path []rune) uint64 { func (m nodeMap) SolvePT1(path []rune) uint64 {
fmt.Println("---- PART 2 BEGIN ----") fmt.Println("---- PART 1 BEGIN ----")
defer fmt.Println("---- PART 1 END ----")
type loop struct { return m.solver("AAA", func(s string) bool { return s == "ZZZ" }, path)
start, position, end *node
steps uint64
} }
loops := make(map[*node]loop)
endpoints := make(map[*node]struct{}) func (m nodeMap) SolvePT2(path []rune) uint64 {
fmt.Println("---- PART 2 BEGIN ----")
defer fmt.Println("---- PART 2 END ----")
var starts []*node
for k, n := range m { for k, n := range m {
if strings.HasSuffix(k, "A") { if strings.HasSuffix(k, "A") {
fmt.Println("start", k) fmt.Println("start", k)
loops[n] = loop{start: n, position: n} starts = append(starts, n)
}
if strings.HasSuffix(k, "Z") {
fmt.Println("stop", k)
endpoints[n] = struct{}{}
} }
} }
var i int loops := make([]uint64, len(starts))
var steps uint64 for i, n := range starts {
var stops int loops[i] = m.solver(n.value, func(s string) bool { return strings.HasSuffix(s, "Z") }, path)
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
} }
return aoc.LCM(loops...)
} }
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
}

View File

@ -30,7 +30,7 @@ func TestExample1(t *testing.T) {
is.NoErr(err) is.NoErr(err)
t.Log(result.stepsPT1) t.Log(result.stepsPT1)
is.Equal(result.stepsPT1, 2) is.Equal(result.stepsPT1, uint64(2))
} }
func TestExample2(t *testing.T) { func TestExample2(t *testing.T) {
@ -41,7 +41,7 @@ func TestExample2(t *testing.T) {
is.NoErr(err) is.NoErr(err)
t.Log(result.stepsPT1) t.Log(result.stepsPT1)
is.Equal(result.stepsPT1, 6) is.Equal(result.stepsPT1, uint64(6))
} }
func TestExample3(t *testing.T) { func TestExample3(t *testing.T) {
@ -63,10 +63,18 @@ func TestInput(t *testing.T) {
is.NoErr(err) is.NoErr(err)
t.Log("part1 solution", result.stepsPT1) t.Log("part1 solution", result.stepsPT1)
is.Equal(result.stepsPT1, 14429) is.Equal(result.stepsPT1, uint64(14429))
t.Log("part2 solution", result.stepsPT2) t.Log("part2 solution", result.stepsPT2)
is.Equal(result.stepsPT2, uint64(10921547990923)) is.Equal(result.stepsPT2, uint64(10921547990923))
} }
// first: 14429 // first: 14429
// second: 10921547990923 // second: 10921547990923
// Brüt
// stops 1 steps 13201
// stops 2 steps 620447
// stops 3 steps 36606373
// stops 4 steps 2232988753
// stops 5 steps 149610246451

50
tools.go Normal file
View File

@ -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
}