Last active
January 2, 2020 17:37
-
-
Save bbeck/19f355cb503fbc0c902b08a1b8889e1f to your computer and use it in GitHub Desktop.
Advent of Code 2019 - Multiprocessing based IntCode CPU implementation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import multiprocessing | |
class CPU(multiprocessing.Process): | |
def __init__(self, memory, input=None, output=None): | |
super(CPU, self).__init__() | |
self.memory = memory | |
self.input = input | |
self.output = output | |
self.ip = 0 | |
self.stopped = False | |
def run(self): | |
while not self.stopped: | |
self.step() | |
def read(self, address, mode): | |
if mode == 0: # position mode | |
return self.memory[address] | |
elif mode == 1: # immediate mode | |
return address | |
else: | |
raise Exception(f"unrecognized parameter mode (read): {mode}") | |
def write(self, address, value, mode): | |
if mode == 0: # position mode | |
self.memory[address] = value | |
else: | |
raise Exception(f"unrecognized parameter mode (write): {mode}") | |
def step(self): | |
instruction = self.memory[self.ip] | |
op = instruction % 100 | |
a_mode = (instruction // 100) % 10 | |
b_mode = (instruction // 1000) % 10 | |
c_mode = (instruction // 10000) % 10 | |
if op == 1: # add | |
a = self.memory[self.ip+1] | |
b = self.memory[self.ip+2] | |
c = self.memory[self.ip+3] | |
self.write(c, self.read(a, a_mode) + self.read(b, b_mode), c_mode) | |
self.ip += 4 | |
elif op == 2: # mul | |
a = self.memory[self.ip+1] | |
b = self.memory[self.ip+2] | |
c = self.memory[self.ip+3] | |
self.write(c, self.read(a, a_mode) * self.read(b, b_mode), c_mode) | |
self.ip += 4 | |
elif op == 3: # in | |
a = self.memory[self.ip+1] | |
self.write(a, self.input(), a_mode) | |
self.ip += 2 | |
elif op == 4: # out | |
a = self.memory[self.ip+1] | |
self.output(self.read(a, a_mode)) | |
self.ip += 2 | |
elif op == 5: # jump if true | |
a = self.memory[self.ip+1] | |
b = self.memory[self.ip+2] | |
self.ip = self.read(b, b_mode) if self.read(a, a_mode) != 0 else self.ip+3 | |
elif op == 6: # jump if false | |
a = self.memory[self.ip+1] | |
b = self.memory[self.ip+2] | |
self.ip = self.read(b, b_mode) if self.read(a, a_mode) == 0 else self.ip+3 | |
elif op == 7: # less than | |
a = self.memory[self.ip+1] | |
b = self.memory[self.ip+2] | |
c = self.memory[self.ip+3] | |
if self.read(a, a_mode) < self.read(b, b_mode): | |
self.write(c, 1, c_mode) | |
else: | |
self.write(c, 0, c_mode) | |
self.ip += 4 | |
elif op == 8: # equal | |
a = self.memory[self.ip+1] | |
b = self.memory[self.ip+2] | |
c = self.memory[self.ip+3] | |
if self.read(a, a_mode) == self.read(b, b_mode): | |
self.write(c, 1, c_mode) | |
else: | |
self.write(c, 0, c_mode) | |
self.ip += 4 | |
elif op == 99: # halt | |
self.stopped = True | |
else: | |
raise Exception(f"unrecognized op code: {op}") | |
################################# | |
# Different ways to run the CPU | |
################################# | |
program = [1,0,0,0,99] | |
# To run a program in a blocking fashion... | |
memory = program[:] | |
blocking = CPU(memory) | |
blocking.run() | |
print(f"call blocking: 1 + 1 = {memory[0]}") | |
# To run a program in a non-blocking fashion...be careful here, the memory is | |
# modified in a subprocess so changes to it aren't visible to the calling | |
# process. | |
memory = program[:] | |
nonblocking = CPU(memory) | |
nonblocking.start() | |
nonblocking.join() | |
print(f"call nonblocking (built-in list): 1 + 1 = {memory[0]}") | |
# To make the memory contents visible to the calling process you can instead use | |
# a multiprocessing.Array of integers to hold the memory contents. | |
memory = multiprocessing.Array("i", program) | |
array = CPU(memory) | |
array.start() | |
array.join() | |
print(f"call nonblocking (array): 1 + 1 = {memory[0]}") | |
################################# | |
# Inputting and outputting values | |
################################# | |
program = [3,0,4,0,99] | |
# Values can be passed in/out via a function | |
memory = program[:] | |
function = CPU(memory, lambda: 9876, lambda out: print(f"pass via function: {out}")) | |
function.run() | |
# Values can also be passed in/out via a queue...this will also work either | |
# in process or out of process. | |
q = multiprocessing.Queue() | |
q.put(9876) | |
memory = program[:] | |
queue = CPU(memory, q.get, q.put) | |
queue.start(); queue.join() | |
print(f"pass via queue: {q.get()}") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment