Find my full solution repo here

Debugging this puzzle was a complete nightmare for me. As is the case with most debugging stories, it was completely my fault all along.

The Problem

First, a while back, I thought that the instructions said “a parameter that you write to will always be in immediate mode.” The instructions actually say the exact opposite of that. So, when processing the parameter modes, I would hardcode the parameter mode to “immediate” for arguments that were supposed to be written to. This had some terrible side effects.

The most bad side effect was that this mistake actually led me to get the right answer, because I was handling the writing section of the opcode wrong.

For example, my argument parsing code looked like this:

if immediate mode:
    return program[pc + offset]
else:
    return program[program[pc + offset]]

My write code: program[arg] = value

And my “read” code just said print(arg) since the argument parsing code was supposed to get the right value.

And since I hardcoded “write” parameters to be immediate mode, I was inadvertently doubling up the address calls and getting the right answer in the process.

The Solution

I eventually read every word of the instructions and realized that I had it backwards. The main thing to come out of this issue was that I added a write_arg() method instead of trying to have a single method do both. The two functions now look like this (with the included relative base):

    def get_arg(self, mode, pc_offset):
        if mode == 0:
            location = self.program[self.pc+pc_offset]
        elif mode == 1:
            location = self.pc+pc_offset
        elif mode == 2:
            location = self.program[self.pc+pc_offset] + self.relative_base
        return self.program.setdefault(location, 0)

    def write_arg(self, mode, pc_offset, value):
        if mode == 0:
            location = self.program[self.pc+pc_offset]
        elif mode == 2:
            location = self.program[self.pc+pc_offset] + self.relative_base
        self.program[location] = value

It’s also worth noting that I’m now using a dictionary implementation of memory instead of a list due to the extended memory requirement. With this big misstep out of the way, the rest of the puzzle came along quickly. I’m simply tracking the relative base at the instance level and opcode 9 adjusts it as necessary. The code above shows how the relative base affects reading/writing parameters.

The Takeaway

Time and time again, I learn the same lesson when debugging programming issues. That lesson is best described by a quote from Moira, a character in Overwatch:

“When faced with a setback, we must challenge our assumptions.”

Duly noted.

Find my full solution repo here