From 996b8bffd67e9fbd1a0fd4359dce10ea5c80c8de Mon Sep 17 00:00:00 2001 From: RPJosh Date: Fri, 8 Dec 2023 13:28:54 +0100 Subject: [PATCH] 2023: day 8 --- internal/2023/day_08/in.go | 119 ++++++++++++++++++++++++++++++++++++- pkg/utils/utils.go | 23 +++++++ 2 files changed, 140 insertions(+), 2 deletions(-) diff --git a/internal/2023/day_08/in.go b/internal/2023/day_08/in.go index ba756a5..db85546 100644 --- a/internal/2023/day_08/in.go +++ b/internal/2023/day_08/in.go @@ -1,11 +1,126 @@ package day_08 +import ( + "fmt" + "regexp" + "strings" + + "rpjosh.de/adventOfCode/pkg/utils" +) + type Day struct{} func (d *Day) Part1(in string) string { - return "" + inSplit := strings.Split(in, "\n") + + // Initialize variables + instructions := inSplit[0] + line := 0 + instructionCounter := 0 + instructionRegex := regexp.MustCompile(`[ \(](?P\w+)[,\)]`) + steps := 0 + + // Build indexed map for better performance + instructionMap := make(map[string]int, 0) + for i, val := range inSplit { + if i > 1 && len(val) > 2 { + v := val[0:3] + instructionMap[v] = i + + // Found start + if v == "AAA" { + line = i + } + } + } + + // Find destination + for { + // Start by first instruction on end + if instructionCounter >= len(instructions) { + instructionCounter = 0 + } + + // Get left or right + instructionIndex := 0 + if instructions[instructionCounter] == 'R' { + instructionIndex = 1 + } + + // Get the next instruction to move on + next := instructionRegex.FindAllStringSubmatch(inSplit[line], -1)[instructionIndex] + line = instructionMap[next[1]] + //logger.Debug("Going to %q on line %d", next[1], line) + + steps++ + instructionCounter++ + + // Did we reach the end? + if strings.HasPrefix(next[1], "ZZZ") { + break + } + } + + return fmt.Sprintf("%d", steps) } func (d *Day) Part2(in string) string { - return "" + inSplit := strings.Split(in, "\n") + + // Initialize variables + instructions := inSplit[0] + starts := make([]int, 0) + stepsByStarts := make([]int, 0) + instructionRegex := regexp.MustCompile(`[ \(](?P\w+)[,\)]`) + + // Build map for better performance + instructionMap := make(map[string]int, 0) + for i, val := range inSplit { + if i > 1 && len(val) > 2 { + v := val[0:3] + instructionMap[v] = i + + // Found start + if v[2] == 'A' { + starts = append(starts, i) + } + } + } + + // Find for each starting point the amount of steps that we need to go to the + // first 'Z'. + // The input data makes sure that the way back from a Z Node to another Z Node + // is always the same as from start to the first Z node. + // How would you find out without looping the first time! + for _, val := range starts { + steps := 0 + line := val + instructionCounter := 0 + for { + // Start by first instruction on end + if instructionCounter >= len(instructions) { + instructionCounter = 0 + } + + // Get left or right + instructionIndex := 0 + if instructions[instructionCounter] == 'R' { + instructionIndex = 1 + } + + next := instructionRegex.FindAllStringSubmatch(inSplit[line], -1)[instructionIndex] + line = instructionMap[next[1]] + //logger.Debug("%d. Going to %q on line %d", i, next[1], line) + + steps++ + instructionCounter++ + + if next[1][2] == 'Z' { + break + } + } + stepsByStarts = append(stepsByStarts, steps) + } + + return fmt.Sprintf("%d", utils.CalculateLCM(stepsByStarts...)) } diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index b15e907..6ed5e5b 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -150,3 +150,26 @@ func SortRunes(value string) string { func ReplaceCharacterInString(str, replace string, index int) string { return str[:index] + replace + str[index+1:] } + +// CalculateGCD (greates common divisor) calculates the biggest +// positive integer that is divisible by a and b +func CalculateGCD(a, b int) int { + for b != 0 { + t := b + b = a % b + a = t + } + return a +} + +// CalculateLCM (least common multiple) calculates the smallest +// positive integer that is divisible by all numbers +func CalculateLCM(ints ...int) int { + if len(ints) == 1 { + return ints[0] + } else if len(ints) == 2 { + return ints[0] * ints[1] / CalculateGCD(ints[0], ints[1]) + } + + return CalculateLCM(ints[0], CalculateLCM(ints[1:]...)) +}