-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path08.py
116 lines (85 loc) · 3.32 KB
/
08.py
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
import runner
from pathlib import Path
def part1(data):
commands = dict(enumerate(data.splitlines()))
acc = 0
current_idx = 0
emergency_brake = 1000
while emergency_brake > 0:
try:
cmd = commands.pop(current_idx)
except KeyError:
print(f'Command {current_idx} already ran.')
break
if cmd.startswith('acc'):
acc += int(cmd.split(' ')[1])
current_idx += 1
elif cmd.startswith('jmp'):
current_idx += int(cmd.split(' ')[1])
else:
current_idx += 1
emergency_brake -= 1
return acc
class TheEnd(Exception):
def __init__(self, acc: int, *args: object) -> None:
super().__init__(*args)
self.acc = acc
def run(commands: dict, current_idx: int, exit_idx: int, primary: bool = True) -> int:
emergency_brake = 1000
acc = 0
while emergency_brake > 0:
try:
cmd = commands.pop(current_idx)
parameter = int(cmd.split(' ')[1])
except KeyError:
raise KeyError(f'You hit an infinite loop.')
if cmd.startswith('acc'):
acc += parameter
current_idx += 1
elif cmd.startswith('jmp'):
if primary:
# allowed to change 1 command
if (current_idx + 1) == exit_idx:
print(f'Changing L{current_idx} "{cmd}" to noop ends the loop.')
raise TheEnd(acc=acc)
# add run path: if this was a noop
try:
print(f'Create run path from L{current_idx}')
acc += run(commands.copy(), current_idx + 1, exit_idx, primary=False)
print(f'Run path completed from L{current_idx}: ACC = {acc}')
except KeyError:
# nope, still an infinite loop
print('nice try')
pass
except TheEnd as end:
# Run path hit the end of instruction. This is good.
print(f'Hit the end in run path L{current_idx}: ACC = {acc} + {end.acc} => {acc+end.acc}')
raise TheEnd(acc=acc + end.acc)
else:
# not allowed to change another cmd
pass
current_idx += parameter # JMP +/- X
else:
# check if we could exit if this was a jmp
if primary and (current_idx + parameter) == exit_idx:
print(f'Found it. Changed L{current_idx} "{cmd}" to "jmp {parameter}".')
return acc
current_idx += 1
if current_idx == exit_idx:
print('Reached the end')
raise TheEnd(acc=acc)
emergency_brake -= 1
if emergency_brake == 0:
raise RuntimeError('Emergency break.')
return acc
def part2(data):
command_list = data.splitlines()
commands = dict(enumerate(command_list))
exit_idx = len(command_list)
try:
acc = run(commands, 0, exit_idx)
except TheEnd as end:
print(end)
acc = end.acc
return acc
runner.run(day=int(Path(__file__).stem))