various improvements #6
163
day08/main.go
163
day08/main.go
|
@ -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 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 ----")
|
fmt.Println("---- PART 2 BEGIN ----")
|
||||||
|
defer fmt.Println("---- PART 2 END ----")
|
||||||
|
|
||||||
type loop struct {
|
var starts []*node
|
||||||
start, position, end *node
|
|
||||||
steps uint64
|
|
||||||
}
|
|
||||||
loops := make(map[*node]loop)
|
|
||||||
|
|
||||||
endpoints := make(map[*node]struct{})
|
|
||||||
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 {
|
return aoc.LCM(loops...)
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
50
tools.go
Normal 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
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user