diff --git a/go/2024/README.md b/go/2024/README.md index 40c9d78..2aee737 100644 --- a/go/2024/README.md +++ b/go/2024/README.md @@ -12,24 +12,25 @@ Collect stars by solving puzzles. Two puzzles will be made available on each day ## Days -| Day | #1 | #1 Answer | #2 | #2 Answer | -| -------------------------------------------------------------------------------------------------------------------- | --- | ------------: | --- | --------------: | -| [Day 1: Historian Hysteria](https://github.com/believer/advent-of-code/blob/master/go/2024/puzzles/day01/main.go) | 🌟 | 1666427 | 🌟 | 24316233 | -| [Day 2: Red-Nosed Reports](https://github.com/believer/advent-of-code/blob/master/go/2024/puzzles/day02/main.go) | 🌟 | 564 | 🌟 | 604 | -| [Day 3: Mull It Over](https://github.com/believer/advent-of-code/blob/master/go/2024/puzzles/day03/main.go) | 🌟 | 161085926 | 🌟 | 82045421 | -| [Day 4: Ceres Search](https://github.com/believer/advent-of-code/blob/master/go/2024/puzzles/day04/main.go) | 🌟 | 2562 | 🌟 | 1902 | -| [Day 5: Print Queue](https://github.com/believer/advent-of-code/blob/master/go/2024/puzzles/day05/main.go) | 🌟 | 3608 | 🌟 | 4922 | -| [Day 6: Guard Gallivant](https://github.com/believer/advent-of-code/blob/master/go/2024/puzzles/day06/main.go) | 🌟 | 4778 | 🌟 | 1618 | -| [Day 7: Bridge Repair](https://github.com/believer/advent-of-code/blob/master/go/2024/puzzles/day07/main.go) | 🌟 | 1399219271639 | 🌟 | 275791737999003 | -| [Day 8: Resonant Collinearity](https://github.com/believer/advent-of-code/blob/master/go/2024/puzzles/day08/main.go) | 🌟 | 220 | 🌟 | 813 | -| [Day 9: Disk Fragmenter](https://github.com/believer/advent-of-code/blob/master/go/2024/puzzles/day09/main.go) | 🌟 | 6384282079460 | 🌟 | 6408966547049 | -| [Day 10: Hoof It](https://github.com/believer/advent-of-code/blob/master/go/2024/puzzles/day10/main.go) | 🌟 | 652 | 🌟 | 1432 | -| [Day 11: Plutonian Pebbles](https://github.com/believer/advent-of-code/blob/master/go/2024/puzzles/day11/main.go) | 🌟 | 187738 | 🌟 | 223767210249237 | -| [Day 12: Garden Groups](https://github.com/believer/advent-of-code/blob/master/go/2024/puzzles/day12/main.go) | 🌟 | 1522850 | 🌟 | 953738 | -| [Day 13: Claw Contraption](https://github.com/believer/advent-of-code/blob/master/go/2024/puzzles/day13/main.go) | 🌟 | 26299 | 🌟 | 107824497933339 | -| [Day 14: Restroom Redoubt](https://github.com/believer/advent-of-code/blob/master/go/2024/puzzles/day14/main.go) | 🌟 | 228690000 | 🌟 | 7093 | -| [Day 15: Warehouse Woes](https://github.com/believer/advent-of-code/blob/master/go/2024/puzzles/day15/main.go) | 🌟 | 1526018 | 🌟 | 1550677 | -| [Day 16: Reindeer Maze](https://github.com/believer/advent-of-code/blob/master/go/2024/puzzles/day16/main.go) | 🌟 | 78428 | 🌟 | 463 | +| Day | #1 | #1 Answer | #2 | #2 Answer | +| ---------------------------------------------------------------------------------------------------------------------- | --- | ----------------: | --- | --------------: | +| [Day 1: Historian Hysteria](https://github.com/believer/advent-of-code/blob/master/go/2024/puzzles/day01/main.go) | 🌟 | 1666427 | 🌟 | 24316233 | +| [Day 2: Red-Nosed Reports](https://github.com/believer/advent-of-code/blob/master/go/2024/puzzles/day02/main.go) | 🌟 | 564 | 🌟 | 604 | +| [Day 3: Mull It Over](https://github.com/believer/advent-of-code/blob/master/go/2024/puzzles/day03/main.go) | 🌟 | 161085926 | 🌟 | 82045421 | +| [Day 4: Ceres Search](https://github.com/believer/advent-of-code/blob/master/go/2024/puzzles/day04/main.go) | 🌟 | 2562 | 🌟 | 1902 | +| [Day 5: Print Queue](https://github.com/believer/advent-of-code/blob/master/go/2024/puzzles/day05/main.go) | 🌟 | 3608 | 🌟 | 4922 | +| [Day 6: Guard Gallivant](https://github.com/believer/advent-of-code/blob/master/go/2024/puzzles/day06/main.go) | 🌟 | 4778 | 🌟 | 1618 | +| [Day 7: Bridge Repair](https://github.com/believer/advent-of-code/blob/master/go/2024/puzzles/day07/main.go) | 🌟 | 1399219271639 | 🌟 | 275791737999003 | +| [Day 8: Resonant Collinearity](https://github.com/believer/advent-of-code/blob/master/go/2024/puzzles/day08/main.go) | 🌟 | 220 | 🌟 | 813 | +| [Day 9: Disk Fragmenter](https://github.com/believer/advent-of-code/blob/master/go/2024/puzzles/day09/main.go) | 🌟 | 6384282079460 | 🌟 | 6408966547049 | +| [Day 10: Hoof It](https://github.com/believer/advent-of-code/blob/master/go/2024/puzzles/day10/main.go) | 🌟 | 652 | 🌟 | 1432 | +| [Day 11: Plutonian Pebbles](https://github.com/believer/advent-of-code/blob/master/go/2024/puzzles/day11/main.go) | 🌟 | 187738 | 🌟 | 223767210249237 | +| [Day 12: Garden Groups](https://github.com/believer/advent-of-code/blob/master/go/2024/puzzles/day12/main.go) | 🌟 | 1522850 | 🌟 | 953738 | +| [Day 13: Claw Contraption](https://github.com/believer/advent-of-code/blob/master/go/2024/puzzles/day13/main.go) | 🌟 | 26299 | 🌟 | 107824497933339 | +| [Day 14: Restroom Redoubt](https://github.com/believer/advent-of-code/blob/master/go/2024/puzzles/day14/main.go) | 🌟 | 228690000 | 🌟 | 7093 | +| [Day 15: Warehouse Woes](https://github.com/believer/advent-of-code/blob/master/go/2024/puzzles/day15/main.go) | 🌟 | 1526018 | 🌟 | 1550677 | +| [Day 16: Reindeer Maze](https://github.com/believer/advent-of-code/blob/master/go/2024/puzzles/day16/main.go) | 🌟 | 78428 | 🌟 | 463 | +| [Day 17: Chronospatial Computer](https://github.com/believer/advent-of-code/blob/master/go/2024/puzzles/day17/main.go) | 🌟 | 2,0,1,3,4,0,2,1,7 | 🌟 | 236580836040301 | ## Benchmarks @@ -53,6 +54,7 @@ Using Go's built-in benchmarking with the [testing](https://pkg.go.dev/testing#h | 14 | 55522 ns/op | 56488050 ns/op | `90.67%` / - | | 15 | 477450 ns/op | 3498110 ns/op | | | 16 | 97868160 ns/op | 99166694 ns/op | | +| 17 | 13964 ns/op | 561424 ns/op | | \* compared to first solution diff --git a/go/2024/puzzles/day17/main.go b/go/2024/puzzles/day17/main.go new file mode 100644 index 0000000..251cfeb --- /dev/null +++ b/go/2024/puzzles/day17/main.go @@ -0,0 +1,151 @@ +package main + +import ( + "fmt" + "regexp" + "slices" + "strconv" + "strings" + + "github.com/believer/aoc-2024/utils" + "github.com/believer/aoc-2024/utils/files" +) + +type Computer struct { + A, B, C int + Iteration int + Instructions []int + Output []string +} + +func (c *Computer) Operand() int { + index := c.Iteration + 1 + + switch c.Instructions[index] { + case 0, 1, 2, 3: + return c.Instructions[index] + case 4: + return c.A + case 5: + return c.B + case 6: + return c.C + default: + panic("This should be unreachable") + } +} + +func (c *Computer) Run() { + for c.Iteration < len(c.Instructions) { + opcode := c.Instructions[c.Iteration] + operand := c.Operand() + literalOperand := c.Instructions[c.Iteration+1] + + switch opcode { + // adv :: A / 2^operand -> A + case 0: + // Found this fancy and performant way of doing the calculation + // using right shift assignment. It's the same as dividing by a + // power of 2 for integers and truncates the value. + // So exactly what the puzzle description hints to. + // For part 1 it doesn't make a difference, but in part 2 it actually + // makes quite a larger difference in performance. + c.A >>= operand + // bxl :: bitwise XOR of B and the _literal_ operand -> B + case 1: + c.B ^= literalOperand + // bst :: operand % 8 -> B + case 2: + // When we calculate modulo of a % b, where b is a power of 2 (like we have), + // we can also do a & (b-1) (Bitwise AND). This should be faster in theory, but + // I tested it and found that it wasn't in this case. Still a nice learning + // to keep around. + c.B = operand % 8 + // jnz :: Nothing if A is zero. Otherwise, jump to _literal_ operand. + case 3: + if c.A != 0 { + c.Iteration = literalOperand + continue + } + // bxc :: bitwise XOR of B and C -> B + case 4: + c.B ^= c.C + // out :: Output value as operand % 8 + case 5: + c.Output = append(c.Output, strconv.Itoa(operand%8)) + // bdv :: A / 2^operand -> B + case 6: + c.B = c.A >> operand + // cdv :: A / 2^operand -> C + case 7: + c.C = c.A >> operand + } + + c.Iteration += 2 + } +} + +func main() { + fmt.Println("Part 1: ", part1("input.txt")) + fmt.Println("Part 2: ", part2("input.txt")) +} + +func part1(name string) string { + A, B, C, instructions := parseInput(name) + + computer := Computer{A: A, B: B, C: C, Instructions: instructions} + computer.Run() + + return strings.Join(computer.Output, ",") +} + +func part2(name string) int { + _, B, C, instructions := parseInput(name) + validPrograms := []int{0} + + // Run through the instructions of the program in reverse since that's + // how they are added to the list + instructionsReversed := append([]int{}, instructions...) + slices.Reverse(instructionsReversed) + + for _, instruction := range instructionsReversed { + next := []int{} + + for _, v := range validPrograms { + for n := range 8 { + // Increase the value three bits, since we're in a 3-bit computer. + // This is effectively v * 2^3. Then insert the value of n in the lower bits. + a := (v << 3) | n + + computer := Computer{A: a, B: B, C: C, Instructions: instructions} + computer.Run() + + // If the instruction matches, add it to the list of valid inputs + if instruction == utils.MustIntFromString(computer.Output[0]) { + next = append(next, a) + } + } + } + + validPrograms = next + } + + return slices.Min(validPrograms) +} + +func parseInput(name string) (int, int, int, []int) { + lines := files.ReadParagraphs(name) + registerRe := regexp.MustCompile(`\d+`) + + A := utils.MustIntFromString(registerRe.FindString(lines[0][0])) + B := utils.MustIntFromString(registerRe.FindString(lines[0][1])) + C := utils.MustIntFromString(registerRe.FindString(lines[0][2])) + instructionList := registerRe.FindAllString(lines[1][0], -1) + instructions := []int{} + + for _, instruction := range instructionList { + instructions = append(instructions, utils.MustIntFromString(instruction)) + } + + return A, B, C, instructions +} diff --git a/go/2024/puzzles/day17/main_test.go b/go/2024/puzzles/day17/main_test.go new file mode 100644 index 0000000..e8d4785 --- /dev/null +++ b/go/2024/puzzles/day17/main_test.go @@ -0,0 +1,51 @@ +package main + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestPart1Small(t *testing.T) { + t.Run("Part 1", func(t *testing.T) { + expected := "0,1,2" + actual := part1("test-input-small.txt") + assert.Equal(t, expected, actual) + }) +} + +func TestPart1SmallTwo(t *testing.T) { + t.Run("Part 1", func(t *testing.T) { + expected := "4,2,5,6,7,7,7,7,3,1,0" + actual := part1("test-input-small-two.txt") + assert.Equal(t, expected, actual) + }) +} + +func TestPart1(t *testing.T) { + t.Run("Part 1", func(t *testing.T) { + expected := "4,6,3,5,6,3,5,2,1,0" + actual := part1("test-input.txt") + assert.Equal(t, expected, actual) + }) +} + +func TestPart2(t *testing.T) { + t.Run("Part 2", func(t *testing.T) { + expected := 117440 + actual := part2("test-input-part2.txt") + assert.Equal(t, expected, actual) + }) +} + +func BenchmarkPart1(b *testing.B) { + for i := 0; i < b.N; i++ { + part1("input.txt") + } +} + +func BenchmarkPart2(b *testing.B) { + for i := 0; i < b.N; i++ { + part2("input.txt") + } +} diff --git a/go/2024/puzzles/day17/test-input-part2.txt b/go/2024/puzzles/day17/test-input-part2.txt new file mode 100644 index 0000000..4a91c26 --- /dev/null +++ b/go/2024/puzzles/day17/test-input-part2.txt @@ -0,0 +1,5 @@ +Register A: 2024 +Register B: 0 +Register C: 0 + +Program: 0,3,5,4,3,0 diff --git a/go/2024/puzzles/day17/test-input-small-two.txt b/go/2024/puzzles/day17/test-input-small-two.txt new file mode 100644 index 0000000..27625b1 --- /dev/null +++ b/go/2024/puzzles/day17/test-input-small-two.txt @@ -0,0 +1,5 @@ +Register A: 2024 +Register B: 0 +Register C: 0 + +Program: 0,1,5,4,3,0 diff --git a/go/2024/puzzles/day17/test-input-small.txt b/go/2024/puzzles/day17/test-input-small.txt new file mode 100644 index 0000000..e1dbc87 --- /dev/null +++ b/go/2024/puzzles/day17/test-input-small.txt @@ -0,0 +1,5 @@ +Register A: 10 +Register B: 0 +Register C: 0 + +Program: 5,0,5,1,5,4 diff --git a/go/2024/puzzles/day17/test-input.txt b/go/2024/puzzles/day17/test-input.txt new file mode 100644 index 0000000..f09839b --- /dev/null +++ b/go/2024/puzzles/day17/test-input.txt @@ -0,0 +1,5 @@ +Register A: 729 +Register B: 0 +Register C: 0 + +Program: 0,1,5,4,3,0