This commit is contained in:
Stefan Harmuth 2024-11-17 23:32:01 +01:00
parent 1a8d7ccf96
commit eaaa845503
4 changed files with 4225 additions and 0 deletions

View File

@ -140,5 +140,17 @@
"wrong": [],
"correct": 20298300
}
},
"16": {
"1": {
"wrong": [
629
],
"correct": 580
},
"2": {
"wrong": [],
"correct": 537
}
}
}

169
day16.py Normal file
View File

@ -0,0 +1,169 @@
from collections import defaultdict
from tools.aoc import AOCDay
from typing import Any
STR_OP_CODES = [
"addr",
"addi",
"mulr",
"muli",
"banr",
"bani",
"borr",
"bori",
"setr",
"seti",
"gtir",
"gtri",
"gtrr",
"eqir",
"eqri",
"eqrr",
]
class WristDevice:
def __init__(self):
self.registers = [0, 0, 0, 0]
self.op_code_learner: dict[int, set[str]] = defaultdict(set)
self.op_code_by_id: dict[int, str] = {}
def set_reg(self, a: int, b: int, c: int, d: int) -> None:
self.registers = [a, b, c, d]
def execute_by_name(self, op_code_name: str, arg0: int, arg1: int, arg2: int):
if op_code_name == "addr":
self.registers[arg2] = self.registers[arg0] + self.registers[arg1]
elif op_code_name == "addi":
self.registers[arg2] = self.registers[arg0] + arg1
elif op_code_name == "mulr":
self.registers[arg2] = self.registers[arg0] * self.registers[arg1]
elif op_code_name == "muli":
self.registers[arg2] = self.registers[arg0] * arg1
elif op_code_name == "banr":
self.registers[arg2] = self.registers[arg0] & self.registers[arg1]
elif op_code_name == "bani":
self.registers[arg2] = self.registers[arg0] & arg1
elif op_code_name == "borr":
self.registers[arg2] = self.registers[arg0] | self.registers[arg1]
elif op_code_name == "bori":
self.registers[arg2] = self.registers[arg0] | arg1
elif op_code_name == "setr":
self.registers[arg2] = self.registers[arg0]
elif op_code_name == "seti":
self.registers[arg2] = arg0
elif op_code_name == "gtir":
self.registers[arg2] = int(arg0 > self.registers[arg1])
elif op_code_name == "gtri":
self.registers[arg2] = int(self.registers[arg0] > arg1)
elif op_code_name == "gtrr":
self.registers[arg2] = int(self.registers[arg0] > self.registers[arg1])
elif op_code_name == "eqir":
self.registers[arg2] = int(arg0 == self.registers[arg1])
elif op_code_name == "eqri":
self.registers[arg2] = int(self.registers[arg0] == arg1)
elif op_code_name == "eqrr":
self.registers[arg2] = int(self.registers[arg0] == self.registers[arg1])
def execute_by_id(self, op_code: int, arg0: int, arg1: int, arg2: int) -> None:
if op_code not in self.op_code_by_id:
raise ValueError(f"Unknown OP Code: {op_code}")
self.execute_by_name(self.op_code_by_id[op_code], arg0, arg1, arg2)
def learn(self, op_code: int, possibles: set[str]) -> None:
if not self.op_code_learner[op_code]:
self.op_code_learner[op_code] = possibles
else:
self.op_code_learner[op_code] &= possibles
def finish_learn(self) -> None:
while len(self.op_code_by_id) < len(self.op_code_learner):
for op_code, possibles in self.op_code_learner.items():
if len(possibles) == 0:
continue
if len(possibles) == 1:
self.op_code_by_id[op_code] = possibles.pop()
for remove_code in self.op_code_learner:
if self.op_code_by_id[op_code] in self.op_code_learner[remove_code]:
self.op_code_learner[remove_code].remove(self.op_code_by_id[op_code])
def test_code(self, op_code: list[int], reg_before: list[int], reg_after: list[int]) -> set[str]:
possible_codes = set()
for test_code in STR_OP_CODES:
self.set_reg(*reg_before)
self.execute_by_name(test_code, *op_code[1:])
if self.registers == reg_after:
possible_codes.add(test_code)
return possible_codes
class Day(AOCDay):
inputs = [
[
(1, "input16_test1"),
(580, "input16"),
],
[
(537, "input16"),
],
]
def parse_input(self) -> (list[(list[int], list[int], list[int])], list[list[int]]):
"""
first return value is a list of (op_code, reg_before, reg_after)
second one is just a list of op_code
"""
lines = self.getInput()
index = 0
tests = []
op_codes = []
while index < len(lines):
if lines[index].startswith("Before:"):
reg_before = list(map(int, lines[index][:-1].split("[")[1].split(", ")))
op_code = list(map(int, lines[index + 1].split()))
reg_after = list(map(int, lines[index + 2][:-1].split("[")[1].split(", ")))
tests.append((op_code, reg_before, reg_after))
index += 4
elif not lines[index]:
index += 1
else:
op_codes.append(list(map(int, lines[index].split())))
index += 1
return tests, op_codes
def part1(self) -> Any:
tests, _ = self.parse_input()
count = 0
device = WristDevice()
for test in tests:
if len(device.test_code(*test)) >= 3:
count += 1
return count
def part2(self) -> Any:
tests, opcodes = self.parse_input()
device = WristDevice()
for test in tests:
device.learn(test[0][0], device.test_code(*test))
device.finish_learn()
device.set_reg(0, 0, 0, 0)
for opcode in opcodes:
device.execute_by_id(*opcode)
return device.registers[0]
if __name__ == "__main__":
day = Day(2018, 16)
day.run(verbose=True)

4041
inputs/input16 Normal file

File diff suppressed because it is too large Load Diff

3
inputs/input16_test1 Normal file
View File

@ -0,0 +1,3 @@
Before: [3, 2, 1, 1]
9 2 1 2
After: [3, 2, 2, 1]