Skip to content

Commit 69bd9e3

Browse files
committed
AoC 2024 Day 17 - cleanup
1 parent 1cca3d7 commit 69bd9e3

File tree

3 files changed

+74
-84
lines changed

3 files changed

+74
-84
lines changed

src/main/python/AoC2024_17.py

Lines changed: 46 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from aoc.vm import Program
1515
from aoc.vm import VirtualMachine
1616

17-
Input = InputData
17+
Input = tuple[int, int, int, list[int]]
1818
Output1 = str
1919
Output2 = int
2020

@@ -37,9 +37,12 @@
3737

3838
class Solution(SolutionBase[Input, Output1, Output2]):
3939
def parse_input(self, input_data: InputData) -> Input:
40-
return input_data
40+
lines = list(input_data)
41+
a, b, c = map(int, (lines[i][12:] for i in range(3)))
42+
ops = list(map(int, lines[4][9:].split(",")))
43+
return a, b, c, ops
4144

42-
def run_program(self, lines: list[str]) -> list[str]:
45+
def create_instructions(self, ops: list[int]) -> list[Instruction]:
4346
def combo(operand: int) -> str:
4447
match operand:
4548
case 0 | 1 | 2 | 3:
@@ -51,30 +54,21 @@ def combo(operand: int) -> str:
5154

5255
ins = []
5356
ip_map = dict[int, int]()
54-
ins.append(Instruction.SET("A", lines[0][12:]))
55-
ins.append(Instruction.SET("B", lines[1][12:]))
56-
ins.append(Instruction.SET("C", lines[2][12:]))
57-
ip = 3
58-
ops = list(map(int, lines[4][9:].split(",")))
57+
ip = 0
5958
for i in range(0, len(ops), 2):
6059
ip_map[i] = ip
6160
opcode, operand = ops[i], ops[i + 1]
6261
match opcode:
6362
case 0:
64-
ins.append(Instruction.SET("X", "2"))
65-
ins.append(Instruction.SET("Y", combo(operand)))
66-
ins.append(Instruction.ADD("Y", -1))
67-
ins.append(Instruction.LSH("X", "*Y"))
68-
ins.append(Instruction.DIV("A", "*X"))
69-
ip += 5
63+
ins.append(Instruction.RSH("A", combo(operand)))
64+
ip += 1
7065
case 1:
7166
ins.append(Instruction.XOR("B", str(operand)))
7267
ip += 1
7368
case 2:
74-
ins.append(Instruction.SET("X", str(combo(operand))))
75-
ins.append(Instruction.MOD("X", "8"))
76-
ins.append(Instruction.SET("B", "*X"))
77-
ip += 3
69+
ins.append(Instruction.SET("B", str(combo(operand))))
70+
ins.append(Instruction.AND("B", "7"))
71+
ip += 2
7872
case 3:
7973
ins.append(
8074
Instruction.JN0("*A", "!" + str(ip_map[operand]))
@@ -85,61 +79,55 @@ def combo(operand: int) -> str:
8579
ip += 1
8680
case 5:
8781
ins.append(Instruction.SET("X", str(combo(operand))))
88-
ins.append(Instruction.MOD("X", "8"))
82+
ins.append(Instruction.AND("X", "7"))
8983
ins.append(Instruction.OUT("*X"))
9084
ip += 3
9185
case 6:
92-
ins.append(Instruction.SET("X", "2"))
93-
ins.append(Instruction.SET("Y", combo(operand)))
94-
ins.append(Instruction.ADD("Y", -1))
95-
ins.append(Instruction.LSH("X", "*Y"))
96-
ins.append(Instruction.SET("B", "*A"))
97-
ins.append(Instruction.DIV("B", "*X"))
98-
ip += 6
86+
ins.append(Instruction.SET("C", "*B"))
87+
ins.append(Instruction.RSH("C", combo(operand)))
88+
ip += 2
9989
case 7:
100-
ins.append(Instruction.SET("X", "2"))
101-
ins.append(Instruction.SET("Y", combo(operand)))
102-
ins.append(Instruction.ADD("Y", -1))
103-
ins.append(Instruction.LSH("X", "*Y"))
10490
ins.append(Instruction.SET("C", "*A"))
105-
ins.append(Instruction.DIV("C", "*X"))
106-
ip += 6
91+
ins.append(Instruction.RSH("C", combo(operand)))
92+
ip += 2
93+
case _:
94+
raise ValueError
95+
return ins
96+
97+
def run_program(
98+
self, ins: list[Instruction], a: int, b: int, c: int
99+
) -> list[str]:
107100
output = []
108101
program = Program(ins, output_consumer=lambda s: output.append(s))
109-
vm = VirtualMachine()
110-
vm.run_program(program)
102+
program.registers["A"] = int(a)
103+
program.registers["B"] = int(b)
104+
program.registers["C"] = int(c)
105+
VirtualMachine().run_program(program)
111106
return output
112107

113108
def part_1(self, input: Input) -> Output1:
114-
lines = list(input)
115-
output = self.run_program(lines)
116-
return ",".join(map(str, output))
109+
a, b, c, ops = input
110+
ins = self.create_instructions(ops)
111+
return ",".join(self.run_program(ins, a, b, c))
117112

118113
def part_2(self, input: Input) -> Output2:
119-
lines = list(input)
120-
121-
def run_with(a: str) -> list[str]:
122-
lines[0] = "Register A: " + a
123-
return self.run_program(lines)
124-
125-
wanted = lines[4][9:].replace(",", "")
114+
_, b, c, ops = input
115+
ins = self.create_instructions(ops)
116+
wanted = list(str(_) for _ in ops)
126117
log(f"{wanted=}")
127-
seen = set(["0"])
128-
q = deque(["0"])
118+
seen = set([0])
119+
q = deque([0])
129120
while q:
130-
a = q.popleft()
131-
if "".join(str(_) for _ in run_with(a)) == wanted:
132-
return int(a)
133-
na = int(a) * 8
121+
cand_a = q.popleft() * 8
134122
for i in range(8):
135-
test = str(na + i)
136-
res = "".join(str(_) for _ in run_with(test))
137-
size = len(res)
138-
if res == wanted[-size:]:
139-
if test not in seen:
140-
seen.add(test)
141-
log(test)
142-
q.append(test)
123+
na = cand_a + i
124+
res = self.run_program(ins, na, b, c)
125+
if res == wanted:
126+
return na
127+
if res == wanted[-len(res) :] and na not in seen: # noqa E203
128+
seen.add(na)
129+
log(na)
130+
q.append(na)
143131
raise RuntimeError("unsolvable")
144132

145133
@aoc_samples(

src/main/python/aoc/vm.py

Lines changed: 21 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,6 @@ def MUL(cls, register: str, value: str) -> Instruction:
6363
def DIV(cls, register: str, value: str) -> Instruction:
6464
return Instruction("DIV", (register, value))
6565

66-
@classmethod
67-
def MOD(cls, register: str, value: str) -> Instruction:
68-
return Instruction("MOD", (register, value))
69-
7066
@classmethod
7167
def MEM(cls, address: int, value: object) -> Instruction:
7268
return Instruction("MEM", (address, value))
@@ -76,8 +72,12 @@ def OUT(cls, operand: str) -> Instruction:
7672
return Instruction("OUT", (operand,))
7773

7874
@classmethod
79-
def LSH(cls, operand: str, value: str) -> Instruction:
80-
return Instruction("LSH", (operand, value))
75+
def RSH(cls, operand: str, value: str) -> Instruction:
76+
return Instruction("RSH", (operand, value))
77+
78+
@classmethod
79+
def AND(cls, register: str, value: str) -> Instruction:
80+
return Instruction("AND", (register, value))
8181

8282
@classmethod
8383
def XOR(cls, operand: str, value: str) -> Instruction:
@@ -195,10 +195,10 @@ def __init__(self, debug: bool = False) -> None:
195195
"SUB": self._sub,
196196
"MUL": self._mul,
197197
"DIV": self._div,
198-
"MOD": self._mod,
199198
"MEM": self._mem,
200199
"OUT": self._out,
201-
"LSH": self._lsh,
200+
"RSH": self._rsh,
201+
"AND": self._and,
202202
"XOR": self._xor,
203203
}
204204
self._debug = debug
@@ -395,24 +395,24 @@ def _div(
395395
program.move_instruction_pointer(1)
396396
self.log(program.registers)
397397

398-
def _mod(
399-
self, program: Program, instruction: Instruction, ip: int
398+
def _mul(
399+
self, program: Program, instruction: Instruction, ip: str
400400
) -> None:
401401
if instruction.operands is None:
402402
raise RuntimeError
403403
self.log(instruction.opcode + str(instruction.operands))
404404
(register, value) = instruction.operands
405405
value = self._value(program, value)
406406
new_value = (
407-
0
407+
value
408408
if register not in program.registers
409-
else program.registers[register] % value
409+
else program.registers[register] * value
410410
)
411411
program.set_register_value(register, new_value)
412412
program.move_instruction_pointer(1)
413413
self.log(program.registers)
414414

415-
def _mul(
415+
def _rsh(
416416
self, program: Program, instruction: Instruction, ip: str
417417
) -> None:
418418
if instruction.operands is None:
@@ -423,28 +423,25 @@ def _mul(
423423
new_value = (
424424
value
425425
if register not in program.registers
426-
else program.registers[register] * value
426+
else program.registers[register] >> value
427427
)
428428
program.set_register_value(register, new_value)
429429
program.move_instruction_pointer(1)
430430
self.log(program.registers)
431431

432-
def _lsh(
432+
def _and(
433433
self, program: Program, instruction: Instruction, ip: str
434434
) -> None:
435435
if instruction.operands is None:
436436
raise RuntimeError
437437
self.log(instruction.opcode + str(instruction.operands))
438438
(register, value) = instruction.operands
439439
value = self._value(program, value)
440-
if value == -1:
441-
new_value = 1
442-
else:
443-
new_value = (
444-
value
445-
if register not in program.registers
446-
else program.registers[register] << value
447-
)
440+
new_value = (
441+
value
442+
if register not in program.registers
443+
else program.registers[register] & value
444+
)
448445
program.set_register_value(register, new_value)
449446
program.move_instruction_pointer(1)
450447
self.log(program.registers)
@@ -489,7 +486,7 @@ def _out(
489486
else:
490487
operand = int(operand)
491488
if operand is not None and program.output_consumer is not None:
492-
program.output_consumer(operand)
489+
program.output_consumer(str(operand))
493490
program.move_instruction_pointer(1)
494491

495492
def _value(self, program: Program, op: str) -> int | str:

src/test/python/test_vm.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ def test(self) -> None:
1515
Instruction.JMP(2),
1616
Instruction.NOP(),
1717
Instruction.NOP(),
18+
Instruction.SET("F", "64"),
19+
Instruction.RSH("F", "2"),
20+
Instruction.ADD("F", -2),
21+
Instruction.AND("F", "7"),
1822
Instruction.MEM(1, 100),
1923
Instruction.ADD("A", 6),
2024
Instruction.MEM(3, 300),
@@ -49,8 +53,9 @@ def test(self) -> None:
4953
self.assertEqual(prog.registers["C"], 0)
5054
self.assertTrue("D" not in prog.registers)
5155
self.assertEqual(prog.registers["E"], 3)
52-
self.assertEqual(prog.instruction_pointer, 26)
53-
self.assertEqual(output, [13, 7])
56+
self.assertEqual(prog.registers["F"], 6)
57+
self.assertEqual(prog.instruction_pointer, 30)
58+
self.assertEqual(output, ["13", "7"])
5459

5560
def test_error_on_infinite_loop(self) -> None:
5661
vm = VirtualMachine()

0 commit comments

Comments
 (0)