Skip to content

Commit

Permalink
AoC 2024 Day 17 - cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
pareronia committed Dec 17, 2024
1 parent 1cca3d7 commit 69bd9e3
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 84 deletions.
104 changes: 46 additions & 58 deletions src/main/python/AoC2024_17.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from aoc.vm import Program
from aoc.vm import VirtualMachine

Input = InputData
Input = tuple[int, int, int, list[int]]
Output1 = str
Output2 = int

Expand All @@ -37,9 +37,12 @@

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

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

ins = []
ip_map = dict[int, int]()
ins.append(Instruction.SET("A", lines[0][12:]))
ins.append(Instruction.SET("B", lines[1][12:]))
ins.append(Instruction.SET("C", lines[2][12:]))
ip = 3
ops = list(map(int, lines[4][9:].split(",")))
ip = 0
for i in range(0, len(ops), 2):
ip_map[i] = ip
opcode, operand = ops[i], ops[i + 1]
match opcode:
case 0:
ins.append(Instruction.SET("X", "2"))
ins.append(Instruction.SET("Y", combo(operand)))
ins.append(Instruction.ADD("Y", -1))
ins.append(Instruction.LSH("X", "*Y"))
ins.append(Instruction.DIV("A", "*X"))
ip += 5
ins.append(Instruction.RSH("A", combo(operand)))
ip += 1
case 1:
ins.append(Instruction.XOR("B", str(operand)))
ip += 1
case 2:
ins.append(Instruction.SET("X", str(combo(operand))))
ins.append(Instruction.MOD("X", "8"))
ins.append(Instruction.SET("B", "*X"))
ip += 3
ins.append(Instruction.SET("B", str(combo(operand))))
ins.append(Instruction.AND("B", "7"))
ip += 2
case 3:
ins.append(
Instruction.JN0("*A", "!" + str(ip_map[operand]))
Expand All @@ -85,61 +79,55 @@ def combo(operand: int) -> str:
ip += 1
case 5:
ins.append(Instruction.SET("X", str(combo(operand))))
ins.append(Instruction.MOD("X", "8"))
ins.append(Instruction.AND("X", "7"))
ins.append(Instruction.OUT("*X"))
ip += 3
case 6:
ins.append(Instruction.SET("X", "2"))
ins.append(Instruction.SET("Y", combo(operand)))
ins.append(Instruction.ADD("Y", -1))
ins.append(Instruction.LSH("X", "*Y"))
ins.append(Instruction.SET("B", "*A"))
ins.append(Instruction.DIV("B", "*X"))
ip += 6
ins.append(Instruction.SET("C", "*B"))
ins.append(Instruction.RSH("C", combo(operand)))
ip += 2
case 7:
ins.append(Instruction.SET("X", "2"))
ins.append(Instruction.SET("Y", combo(operand)))
ins.append(Instruction.ADD("Y", -1))
ins.append(Instruction.LSH("X", "*Y"))
ins.append(Instruction.SET("C", "*A"))
ins.append(Instruction.DIV("C", "*X"))
ip += 6
ins.append(Instruction.RSH("C", combo(operand)))
ip += 2
case _:
raise ValueError
return ins

def run_program(
self, ins: list[Instruction], a: int, b: int, c: int
) -> list[str]:
output = []
program = Program(ins, output_consumer=lambda s: output.append(s))
vm = VirtualMachine()
vm.run_program(program)
program.registers["A"] = int(a)
program.registers["B"] = int(b)
program.registers["C"] = int(c)
VirtualMachine().run_program(program)
return output

def part_1(self, input: Input) -> Output1:
lines = list(input)
output = self.run_program(lines)
return ",".join(map(str, output))
a, b, c, ops = input
ins = self.create_instructions(ops)
return ",".join(self.run_program(ins, a, b, c))

def part_2(self, input: Input) -> Output2:
lines = list(input)

def run_with(a: str) -> list[str]:
lines[0] = "Register A: " + a
return self.run_program(lines)

wanted = lines[4][9:].replace(",", "")
_, b, c, ops = input
ins = self.create_instructions(ops)
wanted = list(str(_) for _ in ops)
log(f"{wanted=}")
seen = set(["0"])
q = deque(["0"])
seen = set([0])
q = deque([0])
while q:
a = q.popleft()
if "".join(str(_) for _ in run_with(a)) == wanted:
return int(a)
na = int(a) * 8
cand_a = q.popleft() * 8
for i in range(8):
test = str(na + i)
res = "".join(str(_) for _ in run_with(test))
size = len(res)
if res == wanted[-size:]:
if test not in seen:
seen.add(test)
log(test)
q.append(test)
na = cand_a + i
res = self.run_program(ins, na, b, c)
if res == wanted:
return na
if res == wanted[-len(res) :] and na not in seen: # noqa E203
seen.add(na)
log(na)
q.append(na)
raise RuntimeError("unsolvable")

@aoc_samples(
Expand Down
45 changes: 21 additions & 24 deletions src/main/python/aoc/vm.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,6 @@ def MUL(cls, register: str, value: str) -> Instruction:
def DIV(cls, register: str, value: str) -> Instruction:
return Instruction("DIV", (register, value))

@classmethod
def MOD(cls, register: str, value: str) -> Instruction:
return Instruction("MOD", (register, value))

@classmethod
def MEM(cls, address: int, value: object) -> Instruction:
return Instruction("MEM", (address, value))
Expand All @@ -76,8 +72,12 @@ def OUT(cls, operand: str) -> Instruction:
return Instruction("OUT", (operand,))

@classmethod
def LSH(cls, operand: str, value: str) -> Instruction:
return Instruction("LSH", (operand, value))
def RSH(cls, operand: str, value: str) -> Instruction:
return Instruction("RSH", (operand, value))

@classmethod
def AND(cls, register: str, value: str) -> Instruction:
return Instruction("AND", (register, value))

@classmethod
def XOR(cls, operand: str, value: str) -> Instruction:
Expand Down Expand Up @@ -195,10 +195,10 @@ def __init__(self, debug: bool = False) -> None:
"SUB": self._sub,
"MUL": self._mul,
"DIV": self._div,
"MOD": self._mod,
"MEM": self._mem,
"OUT": self._out,
"LSH": self._lsh,
"RSH": self._rsh,
"AND": self._and,
"XOR": self._xor,
}
self._debug = debug
Expand Down Expand Up @@ -395,24 +395,24 @@ def _div(
program.move_instruction_pointer(1)
self.log(program.registers)

def _mod(
self, program: Program, instruction: Instruction, ip: int
def _mul(
self, program: Program, instruction: Instruction, ip: str
) -> None:
if instruction.operands is None:
raise RuntimeError
self.log(instruction.opcode + str(instruction.operands))
(register, value) = instruction.operands
value = self._value(program, value)
new_value = (
0
value
if register not in program.registers
else program.registers[register] % value
else program.registers[register] * value
)
program.set_register_value(register, new_value)
program.move_instruction_pointer(1)
self.log(program.registers)

def _mul(
def _rsh(
self, program: Program, instruction: Instruction, ip: str
) -> None:
if instruction.operands is None:
Expand All @@ -423,28 +423,25 @@ def _mul(
new_value = (
value
if register not in program.registers
else program.registers[register] * value
else program.registers[register] >> value
)
program.set_register_value(register, new_value)
program.move_instruction_pointer(1)
self.log(program.registers)

def _lsh(
def _and(
self, program: Program, instruction: Instruction, ip: str
) -> None:
if instruction.operands is None:
raise RuntimeError
self.log(instruction.opcode + str(instruction.operands))
(register, value) = instruction.operands
value = self._value(program, value)
if value == -1:
new_value = 1
else:
new_value = (
value
if register not in program.registers
else program.registers[register] << value
)
new_value = (
value
if register not in program.registers
else program.registers[register] & value
)
program.set_register_value(register, new_value)
program.move_instruction_pointer(1)
self.log(program.registers)
Expand Down Expand Up @@ -489,7 +486,7 @@ def _out(
else:
operand = int(operand)
if operand is not None and program.output_consumer is not None:
program.output_consumer(operand)
program.output_consumer(str(operand))
program.move_instruction_pointer(1)

def _value(self, program: Program, op: str) -> int | str:
Expand Down
9 changes: 7 additions & 2 deletions src/test/python/test_vm.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ def test(self) -> None:
Instruction.JMP(2),
Instruction.NOP(),
Instruction.NOP(),
Instruction.SET("F", "64"),
Instruction.RSH("F", "2"),
Instruction.ADD("F", -2),
Instruction.AND("F", "7"),
Instruction.MEM(1, 100),
Instruction.ADD("A", 6),
Instruction.MEM(3, 300),
Expand Down Expand Up @@ -49,8 +53,9 @@ def test(self) -> None:
self.assertEqual(prog.registers["C"], 0)
self.assertTrue("D" not in prog.registers)
self.assertEqual(prog.registers["E"], 3)
self.assertEqual(prog.instruction_pointer, 26)
self.assertEqual(output, [13, 7])
self.assertEqual(prog.registers["F"], 6)
self.assertEqual(prog.instruction_pointer, 30)
self.assertEqual(output, ["13", "7"])

def test_error_on_infinite_loop(self) -> None:
vm = VirtualMachine()
Expand Down

0 comments on commit 69bd9e3

Please sign in to comment.