Skip to content

Commit 993087f

Browse files
committed
[2024] Solution for Day 17
1 parent d12dabe commit 993087f

File tree

3 files changed

+271
-1
lines changed

3 files changed

+271
-1
lines changed

2024/day17/main.go

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"os"
7+
"strings"
8+
9+
"github.com/kfarnung/advent-of-code/2024/lib"
10+
)
11+
12+
type computer struct {
13+
registers registers
14+
program []int32
15+
output func(value int32) bool
16+
}
17+
18+
type registers struct {
19+
a, b, c int64
20+
ip int
21+
}
22+
23+
func (c computer) LiteralOperand() int64 {
24+
return int64(c.program[c.registers.ip+1])
25+
}
26+
27+
func (c computer) ComboOperand() int64 {
28+
operand := c.LiteralOperand()
29+
switch operand {
30+
case 0:
31+
fallthrough
32+
case 1:
33+
fallthrough
34+
case 2:
35+
fallthrough
36+
case 3:
37+
return operand
38+
case 4:
39+
return c.registers.a
40+
case 5:
41+
return c.registers.b
42+
case 6:
43+
return c.registers.c
44+
}
45+
46+
log.Fatalf("Invalid operand: %d", operand)
47+
return 0
48+
}
49+
50+
func (c computer) Divide() int64 {
51+
return int64(int64(c.registers.a) / intPow(2, int64(c.ComboOperand())))
52+
}
53+
54+
func (c *computer) Run() error {
55+
seenStates := make(map[registers]bool)
56+
57+
for c.registers.ip < len(c.program) {
58+
if seenStates[c.registers] {
59+
return fmt.Errorf("infinite loop detected")
60+
}
61+
62+
seenStates[c.registers] = true
63+
64+
switch c.program[c.registers.ip] {
65+
case 0:
66+
// adv
67+
c.registers.a = c.Divide()
68+
c.registers.ip += 2
69+
70+
case 1:
71+
// bxl
72+
c.registers.b = c.registers.b ^ c.LiteralOperand()
73+
c.registers.ip += 2
74+
75+
case 2:
76+
// bst
77+
c.registers.b = c.ComboOperand() % 8
78+
c.registers.ip += 2
79+
80+
case 3:
81+
// jnz
82+
if c.registers.a != 0 {
83+
c.registers.ip = int(c.LiteralOperand())
84+
} else {
85+
c.registers.ip += 2
86+
}
87+
88+
case 4:
89+
// bxc
90+
c.registers.b = c.registers.b ^ c.registers.c
91+
c.registers.ip += 2
92+
93+
case 5:
94+
// out
95+
if !c.output(int32(c.ComboOperand() % 8)) {
96+
return fmt.Errorf("program aborted")
97+
}
98+
99+
c.registers.ip += 2
100+
101+
case 6:
102+
// bdv
103+
c.registers.b = c.Divide()
104+
c.registers.ip += 2
105+
106+
case 7:
107+
// cdv
108+
c.registers.c = c.Divide()
109+
c.registers.ip += 2
110+
}
111+
}
112+
113+
return nil
114+
}
115+
116+
func (c *computer) Reset() {
117+
c.registers.a = 0
118+
c.registers.b = 0
119+
c.registers.c = 0
120+
c.registers.ip = 0
121+
}
122+
123+
func part1(input string) string {
124+
computer := parseInput(input)
125+
var output []int32
126+
computer.output = func(value int32) bool {
127+
output = append(output, value)
128+
return true
129+
}
130+
131+
err := computer.Run()
132+
if err != nil {
133+
log.Fatal(err)
134+
}
135+
136+
outputStrings := make([]string, len(output))
137+
for i, val := range output {
138+
outputStrings[i] = fmt.Sprintf("%d", val)
139+
}
140+
141+
return strings.Join(outputStrings, ",")
142+
}
143+
144+
func part2(input string) int64 {
145+
computer := parseInput(input)
146+
value, err := solve(0, computer.program)
147+
if err != nil {
148+
log.Fatal(err)
149+
}
150+
151+
return value
152+
}
153+
154+
func solve(current int64, program []int32) (int64, error) {
155+
target := program[len(program)-1]
156+
for i := int64(0); i < 8; i++ {
157+
value := doIteration(current + i)
158+
if value == int64(target) {
159+
if len(program) == 1 {
160+
return current + i, nil
161+
}
162+
163+
next, err := solve((current+i)*8, program[:len(program)-1])
164+
if err == nil {
165+
return next, nil
166+
}
167+
}
168+
}
169+
170+
return 0, fmt.Errorf("no solution found")
171+
}
172+
173+
func doIteration(a int64) int64 {
174+
b, c := int64(0), int64(0)
175+
b = a % 8
176+
b = b ^ 2
177+
c = a / intPow(2, b)
178+
b = b ^ 3
179+
b = b ^ c
180+
return b % 8
181+
}
182+
183+
func intPow(base, exp int64) int64 {
184+
if exp == 0 {
185+
return 1
186+
}
187+
188+
value := base
189+
for i := int64(1); i < exp; i++ {
190+
value *= base
191+
}
192+
193+
return value
194+
}
195+
196+
func parseInput(input string) computer {
197+
lines := lib.SplitLines(input)
198+
199+
a, err := lib.ParseInt[int64](strings.Split(lines[0], ": ")[1])
200+
if err != nil {
201+
log.Fatal(err)
202+
}
203+
204+
b, err := lib.ParseInt[int64](strings.Split(lines[1], ": ")[1])
205+
if err != nil {
206+
log.Fatal(err)
207+
}
208+
209+
c, err := lib.ParseInt[int64](strings.Split(lines[2], ": ")[1])
210+
if err != nil {
211+
log.Fatal(err)
212+
}
213+
214+
programString := strings.Split(lines[4], ": ")[1]
215+
program, err := lib.StringSliceToInt32(strings.Split(programString, ","))
216+
if err != nil {
217+
log.Fatal(err)
218+
}
219+
220+
return computer{registers: registers{a: a, b: b, c: c}, program: program}
221+
}
222+
223+
func main() {
224+
name := os.Args[1]
225+
content, err := lib.LoadFileContent(name)
226+
if err != nil {
227+
log.Fatal(err)
228+
}
229+
230+
fmt.Printf("Part 1: %s\n", part1(content))
231+
fmt.Printf("Part 2: %d\n", part2(content))
232+
}

2024/day17/main_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package main
2+
3+
import (
4+
"strings"
5+
"testing"
6+
7+
"github.com/kfarnung/advent-of-code/2024/lib"
8+
"github.com/stretchr/testify/assert"
9+
)
10+
11+
var input = strings.Join([]string{
12+
"Register A: 729",
13+
"Register B: 0",
14+
"Register C: 0",
15+
"",
16+
"Program: 0,1,5,4,3,0",
17+
"",
18+
}, "\n")
19+
20+
func TestPart1(t *testing.T) {
21+
// assert.Equal(t, "4,6,3,5,6,3,5,2,1,0", part1(input))
22+
23+
inputContent, err := lib.GetInputContent()
24+
if err != nil {
25+
t.Fatal(err)
26+
}
27+
28+
assert.Equal(t, "7,1,5,2,4,0,7,6,1", part1(inputContent))
29+
}
30+
31+
func TestPart2(t *testing.T) {
32+
inputContent, err := lib.GetInputContent()
33+
if err != nil {
34+
t.Fatal(err)
35+
}
36+
37+
assert.Equal(t, int64(37222273957364), part2(inputContent))
38+
}

private

0 commit comments

Comments
 (0)