diff --git a/src/holt59/aoc/2015/day23.py b/src/holt59/aoc/2015/day23.py new file mode 100644 index 0000000..c93c253 --- /dev/null +++ b/src/holt59/aoc/2015/day23.py @@ -0,0 +1,107 @@ +import inspect +from typing import Any, Callable, Final, Iterator, Mapping + +from ..base import BaseSolver + + +class Instruction: + def __init__(self, fn: Callable[..., None]): + self._fn = fn + + args = inspect.getfullargspec(fn) + + self._argtypes = [args.annotations[arg] for arg in args.args[1:]] + + def __call__(self, args: tuple[str, ...]): + self._fn( + *(argtype(arg) for arg, argtype in zip(args, self._argtypes, strict=True)) + ) + + +class Machine: + def __init__( + self, instructions: list[str], registers: dict[str, int] = {"a": 0, "b": 1} + ): + self.instructions: Final = [ + (part[0], tuple(arg.strip() for arg in " ".join(part[1:]).split(","))) + for instruction in instructions + if (part := instruction.split()) + ] + + self._fns = { + name: Instruction(getattr(self, name)) + for name in ("hlf", "tpl", "inc", "jmp", "jie", "jio") + } + + self._registers = registers.copy() + self._ip = 0 + + @property + def registers(self) -> Mapping[str, int]: + return self._registers + + @property + def ip(self) -> int: + return self._ip + + def reset(self, registers: dict[str, int] = {"a": 0, "b": 0}): + self._registers = registers.copy() + self._ip = 0 + + def hlf(self, register: str): + self._registers[register] //= 2 + self._ip += 1 + + def tpl(self, register: str): + self._registers[register] *= 3 + self._ip += 1 + + def inc(self, register: str): + self._registers[register] += 1 + self._ip += 1 + + def jmp(self, offset: int): + self._ip += offset + assert 0 <= self._ip < len(self.instructions) + + def jie(self, register: str, offset: int): + if self._registers[register] % 2 == 0: + self._ip += offset + else: + self._ip += 1 + + def jio(self, register: str, offset: int): + if self._registers[register] == 1: + self._ip += offset + else: + self._ip += 1 + + def _exec(self) -> bool: + # execute next instruction + if self._ip >= len(self.instructions): + return False + + ins, args = self.instructions[self._ip] + + if ins not in self._fns: + return False + + self._fns[ins](args) + return True + + def run(self): + while self._exec(): + ... + return self.registers + + +class Solver(BaseSolver): + def solve(self, input: str) -> Iterator[Any]: + machine = Machine(input.splitlines()) + + registers = machine.run() + yield registers["b"] + + machine.reset({"a": 1, "b": 0}) + registers = machine.run() + yield registers["b"] diff --git a/src/holt59/aoc/inputs/holt59/2015/day23.txt b/src/holt59/aoc/inputs/holt59/2015/day23.txt new file mode 100644 index 0000000..07b1c76 --- /dev/null +++ b/src/holt59/aoc/inputs/holt59/2015/day23.txt @@ -0,0 +1,49 @@ +jio a, +19 +inc a +tpl a +inc a +tpl a +inc a +tpl a +tpl a +inc a +inc a +tpl a +tpl a +inc a +inc a +tpl a +inc a +inc a +tpl a +jmp +23 +tpl a +tpl a +inc a +inc a +tpl a +inc a +inc a +tpl a +inc a +tpl a +inc a +tpl a +inc a +tpl a +inc a +inc a +tpl a +inc a +inc a +tpl a +tpl a +inc a +jio a, +8 +inc b +jie a, +4 +tpl a +inc a +jmp +2 +hlf a +jmp -7 diff --git a/src/holt59/aoc/inputs/tests/2015/day23.txt b/src/holt59/aoc/inputs/tests/2015/day23.txt new file mode 100644 index 0000000..c55ca77 --- /dev/null +++ b/src/holt59/aoc/inputs/tests/2015/day23.txt @@ -0,0 +1,4 @@ +inc a +jio a, +2 +tpl a +inc a