Advent of Code: Day #08 - Handheld Halting

8th of December 2020 |

5 - 6 minutes

Introduction

Today we had to implement a very small instruction set with just three instructions, nop, acc, jmp.

Task One

For task one we're given a set of instructions that are stuck in an infinite loop and tasked to return the value in the accumulator before any instruction was ran for a second time.

Reading in the data

I read the data into an array of sub-arrays where each sub array was in the form ["instruction", number] where instruction is nop, acc or jmp and number is a positive or negative integer.

Python Copy
def read_data(path):
dataset = []
with open(path, "r") as file:
for line in file.readlines():
dataset.append(line.strip().split(" "))
dataset[-1][1] = int(dataset[-1][1])

return dataset

Computer Class

I have a feeling we may be asked to expand upon this instruction set in the coming days and weeks so I decided to make a class called Computer to run the instructions and contain all the logic.

Python Copy
class Computer:
def __init__(self, instructions: list):
self.running = False
self.accumulator = 0
self.current_operation = 0
self.instructions = instructions
self.ran_instructions = []

def reset(self, instructions: list):
self.__init__(instructions)

def run(self):
self.running = True

switch = {"nop": self.nop,
"acc": self.acc,
"jmp": self.jmp}

while self.running:
instruction, number = self.instructions[self.current_operation]
self.ran_instructions.append(self.current_operation)
# Call the function
switch[instruction](number)

if self.current_operation in self.ran_instructions:
return False, self.accumulator
if self.current_operation >= len(self.instructions):
return True, self.accumulator

def nop(self, num: int):
self.current_operation += 1
return

def acc(self, num: int):
self.accumulator += num
self.current_operation += 1
return

def jmp(self, num: int):
num -= 1
self.current_operation += num
self.current_operation += 1
return

The Solution

In the computer class I appended the current index to a list called ran_instructions. Every time the current_operation variable changes we check if it is in the ran_instructions list, if it is we stop running and return False to signal it was an incomplete execution and the current value of the accumulator.

Python Copy
def task_one(data):
handheld = Computer(data)
return handheld.run()[1]

Task Two

The Solution

For task two we are asked to fix the program. We are told that somewhere in the program a jmp and nop operation got swapped. Once we have fixed the program we are asked to return the value in the accumulator. My approach to this was brute force, go through all the nop and jmp instructions, swap them, and run the code to see if it works.

Python Copy
def task_two(data):
new_data = deepcopy(data)
handheld = Computer(data)
last_index = 0
# For all the index's of jmp or nop instructions
for index in [index for index in range(len(data))
if data[index][0] == "jmp"
or data[index][0] == "nop"]:

if last_index != 0: # Reset the last change
new_data[last_index][0] = "jmp"
if new_data[last_index][0] == "nop"
else "nop"

# Change this instruction
new_data[index][0] = "jmp" if new_data[index][0] == "nop"
else "nop"

# Rest and re-run
handheld.reset(new_data)
success = handheld.run()
last_index = index

if success[0]:
return success[1]

Leaderboard Code: 353270-1fc6ef28

Github Repository A link to the github repo containing all the days.

main.py A direct download of the main.py script

2.5Kb