2021-11-25 22:17:12 +01:00

182 lines
5.9 KiB
Python

from typing import List, Tuple
# --- 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?
with open("files/P8.txt", "r") as f:
lines = [code for code in f.read().strip().split("\n")]
def part_1(lines: List[str], debug: bool = False) -> Tuple[int, bool]:
accu = 0
executed = []
nline = 0
while True:
try:
instr, acc = lines[nline].split(" ")
if nline in executed:
# we are in a for loop :(
break
else:
executed.append(nline)
if instr == "acc":
accu += int(acc)
nline += 1
continue
elif instr == "jmp":
nline = nline + int(acc)
continue
elif instr == "nop":
nline += 1
continue
except IndexError:
if debug:
print("Code has run successfully")
return accu, True
if debug:
print(f"The value of the accumulator was {accu} before exiting")
return accu, False
# --- 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?
def part_2() -> None:
for nline, _ in enumerate(lines):
inst, acc = lines[nline].split(" ")
# change instructions
if inst == "nop":
new_inst = "jmp"
elif inst == "jmp":
new_inst = "nop"
# make a copy of the original code
new_lines = lines.copy()
# change line according to the instructions in the original code
new_lines[nline] = " ".join((new_inst, acc))
accu, has_ended = part_1(new_lines)
if has_ended:
print(f"The value of the accumulator was {accu} after terminates")
break
if __name__ == "__main__":
part_1(lines, debug=True)
part_2()