Skip to content

Commit

Permalink
feat(2024): add day 17
Browse files Browse the repository at this point in the history
  • Loading branch information
believer committed Dec 17, 2024
1 parent 976497a commit 74d3d26
Show file tree
Hide file tree
Showing 7 changed files with 242 additions and 18 deletions.
38 changes: 20 additions & 18 deletions go/2024/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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

Expand Down
151 changes: 151 additions & 0 deletions go/2024/puzzles/day17/main.go
Original file line number Diff line number Diff line change
@@ -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
}
51 changes: 51 additions & 0 deletions go/2024/puzzles/day17/main_test.go
Original file line number Diff line number Diff line change
@@ -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")
}
}
5 changes: 5 additions & 0 deletions go/2024/puzzles/day17/test-input-part2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Register A: 2024
Register B: 0
Register C: 0

Program: 0,3,5,4,3,0
5 changes: 5 additions & 0 deletions go/2024/puzzles/day17/test-input-small-two.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Register A: 2024
Register B: 0
Register C: 0

Program: 0,1,5,4,3,0
5 changes: 5 additions & 0 deletions go/2024/puzzles/day17/test-input-small.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Register A: 10
Register B: 0
Register C: 0

Program: 5,0,5,1,5,4
5 changes: 5 additions & 0 deletions go/2024/puzzles/day17/test-input.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Register A: 729
Register B: 0
Register C: 0

Program: 0,1,5,4,3,0

0 comments on commit 74d3d26

Please sign in to comment.