More computer work in day 9, this time adding what is kind of a stack pointer and an opcode to adjust that pointer. Now I can add a relative address mode, getting positions relative to the stack pointer.

## Challenge

The puzzle can be found here. Today I’m to add a new address mode, relative base. I’ll keep a pointer to the current relative base, which starts at 0. Now address mode 2 is to look that many spots from the base pointer. I’ll also need to handle memory space beyond my initial proram.

## Solution

### Part 1

The first thing I did was add the new mode to the computer. Since I’m already working with a get_param function, it was relatively easy. I’ll initialize the relative base at object creation. I did a bit of refactoring so that I could clean up my code calling get_param which included calling with the reg offset vs the value. And I added the new mode:

    def get_param(self, mode, reg):
value = self.program[self.eip + reg]
if mode == "0":
return self.program[value]
elif mode == "1":
return value
elif mode == "2":
return self.program[self.rel_base + value]
else:
print("Error: Invalid Parameter Mode")
sys.exit()


I’ll also handle the new op code:

            elif op == 9:
self.rel_base += self.get_param(mode1, 1)
self.eip += 3


To handle the larger computer memory, with default values of 0, I’ll start using a defaultdict instead of a list. I’ll also move the processing of the string into the __init__ function, so I’m not having to handle it outside the class:

    def __init__(self, program):
self.program = defaultdict(int)
for i, v in enumerate(list(map(int, program.split(",")))):
self.program[i] = v
self.eip = 0
self.rel_base = 0


In day 7, I had to handle two inputs, and I ended up splitting that into a phase that was passed in at computer initialization, and a signal that was passed on each call to compute. Since the BOOST program for today only takes one input, I’ll just pass it to compute. This results in less stuff in __init__ above, and as well as a simplified opcode 3:

            elif op == 3:
self.eip += 2


I spent a lot of time troubleshooting, and eventually figured out that I was mishandling how a result could be written to relatively mode. I had an assert to check that mode3 was 0, but other commands used earlier args to get the address to write to. I eventually added a get_address function:

    def get_address(self, mode, reg):
value = self.program[self.eip + reg]
if mode == "0":
return value
elif mode == "2":
return self.rel_base + value
else:
sys.exit()


Putting it all together, I get a quick answer:

$time python3 ./day9.py 09-puzzle_input.txt Part 1: 3989758265 real 0m0.018s user 0m0.009s sys 0m0.009s  ### Part 2 Nothing changed for part 2, other than the input. So I added a new computer and called compute(2), and ran it, and got the result, albeit slightly more slowly: $ time python3 ./day9.py 09-puzzle_input.txt
Part 1:
3989758265
Part 2:
76791

real    0m0.534s
user    0m0.534s
sys     0m0.000s


## Final Code

#!/usr/bin/env python3

import sys
from collections import defaultdict
from itertools import permutations

class computer:
def __init__(self, program):
self.program = defaultdict(int)
for i, v in enumerate(list(map(int, program.split(",")))):
self.program[i] = v
self.eip = 0
self.rel_base = 0

def get_param(self, mode, reg):
value = self.program[self.eip + reg]
if mode == "0":
return self.program[value]
elif mode == "1":
return value
elif mode == "2":
return self.program[self.rel_base + value]
else:
print("Error: Invalid Parameter Mode")
sys.exit()

value = self.program[self.eip + reg]
if mode == "0":
return value
elif mode == "2":
return self.rel_base + value
else:
sys.exit()

def compute(self, signal):
while True:
inst = self.program[self.eip]
op = inst % 100
mode3, mode2, mode1 = f"{inst // 100:03d}"
if op == 1:
mode1, 1
) + self.get_param(mode2, 2)
self.eip += 4
elif op == 2:
mode1, 1
) * self.get_param(mode2, 2)
self.eip += 4
elif op == 3:
self.eip += 2
elif op == 4:
print(self.get_param(mode1, 1))
self.eip += 2
elif op == 5:
if self.get_param(mode1, 1) != 0:
self.eip = self.get_param(mode2, 2)
else:
self.eip += 3
elif op == 6:
if self.get_param(mode1, 1) == 0:
self.eip = self.get_param(mode2, 2)
else:
self.eip += 3
elif op == 7:
self.get_param(mode1, 1) < self.get_param(mode2, 2)
)
self.eip += 4
elif op == 8:
self.get_param(mode1, 1) == self.get_param(mode2, 2)
)
self.eip += 4
elif op == 9:
self.rel_base += self.get_param(mode1, 1)
self.eip += 2
elif op == 99:
self.done = True
return None
else:
print("Error")
sys.exit()

with open(sys.argv[1], "r") as f:

print("Part 1:")
comp = computer(program_str)
comp.compute(1)

print("Part 2:")
comp = computer(program_str)
comp.compute(2)