from copy import copy from typing import Generator, Iterator, Any, List, Tuple from aoc import BaseAssignment class Assignment(BaseAssignment): @staticmethod def acc(line, accumulated_value, value): return line + 1, accumulated_value + value @staticmethod def jmp(line, accumulated_value, value): return line + value, accumulated_value @staticmethod def nop(line, accumulated_value, value): return line + 1, accumulated_value def read_input(self, example=False) -> List: return list(super().read_input(example)) def parse_item(self, item: str) -> Tuple: instruction, value = item.split(' ') return getattr(self, instruction), int(value) def run(self, input: List) -> int: executed_lines = set() accumulated_value = 0 line = 0 while True: if line in executed_lines: raise Exception( f'Loop detected at line: {line}. ' f'Accumulated value: {accumulated_value}' ) try: instruction, value = input[line] except IndexError: break executed_lines.add(line) line, accumulated_value = instruction( line, accumulated_value, value ) return accumulated_value class AssignmentOne(Assignment): pass class AssignmentTwo(Assignment): def patch_instructions(self, input: List) -> Generator: for index, (instruction, value) in enumerate(input): if instruction is self.acc: continue patched_input = copy(input) patched_input[index] = ( self.nop if instruction is self.jmp else self.jmp, value ) yield patched_input def run(self, input: List) -> int: for patched_input in self.patch_instructions(input): try: return super().run(patched_input) except Exception: continue