chore: simplify day 8
This commit is contained in:
		
							parent
							
								
									8e7fa3c5a8
								
							
						
					
					
						commit
						5106558ce9
					
				
							
								
								
									
										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 {
 | 
					 | 
				
			||||||
			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
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						return aoc.LCM(loops...)
 | 
				
			||||||
	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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user