-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathday08.py
executable file
·182 lines (143 loc) · 5.81 KB
/
day08.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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
#!/usr/bin/env python3
"""
--- Day 8: Handheld Halting ---
Your flight to the major airline hub reaches cruising altitude without incident.
While you consider checking the in-flight menu for one of those drinks that come
with a little umbrella, you are interrupted by the kid sitting next to you.
Their handheld game console won't turn on! They ask if you can take a look.
You narrow the problem down to a strange infinite loop in the boot code (your
puzzle input) of the device. You should be able to fix it, but first you need to
be able to run the code in isolation.
The boot code is represented as a text file with one instruction per line of
text. Each instruction consists of an operation (acc, jmp, or nop) and an
argument (a signed number like +4 or -20).
- `acc` increases or decreases a single global value called the accumulator by
the value given in the argument. For example, acc +7 would increase the
accumulator by 7. The accumulator starts at 0. After an acc instruction, the
instruction immediately below it is executed next.
- `jmp` jumps to a new instruction relative to itself. The next instruction to
execute is found using the argument as an offset from the jmp instruction;
for example, jmp +2 would skip the next instruction, jmp +1 would continue to
the instruction immediately below it, and jmp -20 would cause the instruction
20 lines above to be executed next.
- `nop` stands for No OPeration - it does nothing. The instruction immediately
below it is executed next.
For example, consider the following program:
nop +0
acc +1
jmp +4
acc +3
jmp -3
acc -99
acc +1
jmp -4
acc +6
These instructions are visited in this order:
nop +0 | 1
acc +1 | 2, 8(!)
jmp +4 | 3
acc +3 | 6
jmp -3 | 7
acc -99 |
acc +1 | 4
jmp -4 | 5
acc +6 |
First, the nop +0 does nothing. Then, the accumulator is increased from 0 to 1
(acc +1) and jmp +4 sets the next instruction to the other acc +1 near the
bottom. After it increases the accumulator from 1 to 2, jmp -4 executes, setting
the next instruction to the only acc +3. It sets the accumulator to 5, and jmp
-3 causes the program to continue back at the first acc +1.
This is an infinite loop: with this sequence of jumps, the program will run
forever. The moment the program tries to run any instruction a second time, you
know it will never terminate.
Immediately before the program would run an instruction a second time, the value
in the accumulator is 5.
Run your copy of the boot code. Immediately before any instruction is executed a
second time, what value is in the accumulator?
--- Part Two ---
After some careful analysis, you believe that exactly one instruction is
corrupted.
Somewhere in the program, either a jmp is supposed to be a nop, or a nop is
supposed to be a jmp. (No acc instructions were harmed in the corruption of this
boot code.)
The program is supposed to terminate by attempting to execute an instruction
immediately after the last instruction in the file. By changing exactly one jmp
or nop, you can repair the boot code and make it terminate correctly.
For example, consider the same program from above:
nop +0
acc +1
jmp +4
acc +3
jmp -3
acc -99
acc +1
jmp -4
acc +6
If you change the first instruction from nop +0 to jmp +0, it would create a
single-instruction infinite loop, never leaving that instruction. If you change
almost any of the jmp instructions, the program will still eventually find
another jmp instruction and loop forever.
However, if you change the second-to-last instruction (from jmp -4 to nop -4),
the program terminates! The instructions are visited in this order:
nop +0 | 1
acc +1 | 2
jmp +4 | 3
acc +3 |
jmp -3 |
acc -99 |
acc +1 | 4
nop -4 | 5
acc +6 | 6
After the last instruction (acc +6), the program terminates by attempting to run
the instruction below the last instruction in the file. With this change, after
the program terminates, the accumulator contains the value 8 (acc +1, acc +1,
acc +6).
Fix the program so that it terminates normally by changing exactly one jmp (to
nop) or nop (to jmp). What is the value of the accumulator after the program
terminates?
"""
import argparse
from typing import List, Tuple
parser = argparse.ArgumentParser(epilog=__doc__,
formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument('input', type=argparse.FileType('rt'))
parser.add_argument('--part-two', action='store_true')
args = parser.parse_args()
def emulate(program: List[str]) -> Tuple[bool, int]:
commands_executed = set()
instruction_pointer = 0
accumulator = 0
while True:
command = program[instruction_pointer]
instruction, arg = command.split(' ')
if instruction == 'acc':
accumulator += int(arg)
instruction_pointer += 1
elif instruction == 'jmp':
instruction_pointer += int(arg)
elif instruction == 'nop':
instruction_pointer += 1
else:
raise Exception(f'Unhandled instruction "{instruction}"')
if instruction_pointer == len(program):
return True, accumulator
elif instruction_pointer in commands_executed:
return False, accumulator
commands_executed.add(instruction_pointer)
program = [line.strip() for line in args.input]
if not args.part_two:
_, accumulator = emulate(program)
print(accumulator)
else:
for line, command in enumerate(program):
instruction, arg = command.split(' ')
if instruction == 'acc':
continue
swap_instruction = {'jmp': 'nop', 'nop': 'jmp'}
new_command = f'{swap_instruction[instruction]} {arg}'
patched_program = program[:]
patched_program[line] = new_command
terminated, accumulator = emulate(patched_program)
if terminated:
print(accumulator)
break