summaryrefslogtreecommitdiffstats
path: root/day8/__init__.py
blob: c6d086ab2b4f3d44b7981c36eee80fdb888f0d3d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
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