diff --git a/2024/gareth/day21/day21.go b/2024/gareth/day21/day21.go new file mode 100644 index 0000000..a1e1445 --- /dev/null +++ b/2024/gareth/day21/day21.go @@ -0,0 +1,125 @@ +package day21 + +import ( + "strconv" + "strings" +) + +type coordinate struct { + row, col int +} + +var numberPad = map[string]coordinate{ + "7": {0, 0}, "8": {0, 1}, "9": {0, 2}, + "4": {1, 0}, "5": {1, 1}, "6": {1, 2}, + "1": {2, 0}, "2": {2, 1}, "3": {2, 2}, + "0": {3, 1}, "A": {3, 2}, +} + +var robotPad = map[string]coordinate{ + "^": {0, 1}, "A": {0, 2}, + "<": {1, 0}, "v": {1, 1}, ">": {1, 2}, +} + +type cacheKey struct { + sequence string + iterations int +} + +var memo = make(map[cacheKey]int) + +func Part1(input string) int { + codes := parseInput(input) + + numpadGraph := createGraph(numberPad, coordinate{3, 0}) + dirpadGraph := createGraph(robotPad, coordinate{0, 0}) + + total := 0 + for _, code := range codes { + codeInt, _ := strconv.Atoi(code[:len(code)-1]) + total += codeInt * getLength(code, 3, true, numpadGraph, dirpadGraph) + } + return total +} + +func Part2(input string) int { + codes := parseInput(input) + + numpadGraph := createGraph(numberPad, coordinate{3, 0}) + dirpadGraph := createGraph(robotPad, coordinate{0, 0}) + + total := 0 + for _, code := range codes { + codeInt, _ := strconv.Atoi(code[:len(code)-1]) + total += codeInt * getLength(code, 26, true, numpadGraph, dirpadGraph) + } + return total +} + +func parseInput(input string) []string { + parts := strings.Split(input, "\n") + codes := []string{} + codes = append(codes, parts...) + return codes +} + +func createGraph(keypad map[string]coordinate, invalidCoords coordinate) map[string]string { + graph := make(map[string]string) + + for a, coordA := range keypad { + for b, coordB := range keypad { + x1, y1 := coordA.row, coordA.col + x2, y2 := coordB.row, coordB.col + + path := strings.Repeat("<", max(0, y1-y2)) + + strings.Repeat("v", max(0, x2-x1)) + + strings.Repeat("^", max(0, x1-x2)) + + strings.Repeat(">", max(0, y2-y1)) + + if invalidCoords == (coordinate{x1, y2}) || invalidCoords == (coordinate{x2, y1}) { + path = reverseString(path) + } + graph[a+"->"+b] = path + "A" + } + } + + return graph +} + +func reverseString(s string) string { + runes := []rune(s) + for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 { + runes[i], runes[j] = runes[j], runes[i] + } + return string(runes) +} + +func getLength(sequence string, iterations int, firstIter bool, numpadGraph map[string]string, dirpadGraph map[string]string) int { + + if iterations == 0 { + return len(sequence) + } + + key := cacheKey{sequence: sequence, iterations: iterations} + if cachedResult, exists := memo[key]; exists { + return cachedResult + } + + graph := numpadGraph + if !firstIter { + graph = dirpadGraph + } + + totalLength := 0 + prev := "A" + for _, char := range sequence { + graphKey := prev + "->" + string(char) + if subSequence, exists := graph[graphKey]; exists { + totalLength += getLength(subSequence, iterations-1, false, numpadGraph, dirpadGraph) + } + prev = string(char) + } + + memo[key] = totalLength + return totalLength +} diff --git a/2024/gareth/day21/day21_test.go b/2024/gareth/day21/day21_test.go new file mode 100644 index 0000000..61f7efe --- /dev/null +++ b/2024/gareth/day21/day21_test.go @@ -0,0 +1,25 @@ +package day21 + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestPart1(t *testing.T) { + r := Part1(`029A +980A +179A +456A +379A`) + assert.Equal(t, 126384, r) +} + +func TestPart2(t *testing.T) { + r := Part2(`029A +980A +179A +456A +379A`) + assert.Equal(t, 126384, r) +} diff --git a/2024/gareth/day21/input.txt b/2024/gareth/day21/input.txt new file mode 100644 index 0000000..8b06a98 --- /dev/null +++ b/2024/gareth/day21/input.txt @@ -0,0 +1,5 @@ +985A +540A +463A +671A +382A \ No newline at end of file diff --git a/2024/gareth/main.go b/2024/gareth/main.go index cd9435c..4173aa4 100644 --- a/2024/gareth/main.go +++ b/2024/gareth/main.go @@ -1,7 +1,7 @@ package main import ( - "aoc2024/day20" + "aoc2024/day21" "fmt" "os" "time" @@ -9,9 +9,9 @@ import ( func main() { start := time.Now() - data, _ := os.ReadFile("day20/input.txt") - fmt.Printf("part 1: %d\n", day20.Part1(string(data))) - fmt.Printf("part 2: %d\n", day20.Part2(string(data))) + data, _ := os.ReadFile("day21/input.txt") + fmt.Printf("part 1: %d\n", day21.Part1(string(data))) + fmt.Printf("part 2: %d\n", day21.Part2(string(data))) elapsed := time.Since(start) fmt.Printf("Execution time: %s\n", elapsed) }