diff --git a/intcode.py b/intcode.py new file mode 100644 index 0000000..d2a9c10 --- /dev/null +++ b/intcode.py @@ -0,0 +1,103 @@ +from collections import defaultdict +from opcodes import run_op, CONTINUE, HALT, WAIT + +class StackIO: + def __init__(self): + self.input_stack = [] + self.output_stack = [] + + def input(self): + return self.input_stack.pop(0) + + def output(self, val): + self.output_stack.append(val) + + def wait_for_input(self): + return len(self.input_stack) == 0 + + def pop_out(self): + return self.output_stack.pop(0) + +class Memory: + def __init__(self, program=[]): + self.memory = program[:] + + def write(self, i, val): + self.memory += [0] * (i - len(self.memory) + 1) + self.memory[i] = val + + def read(self, i): + if i >= len(self.memory): + return 0 + return self.memory[i] + + def copy(self): + return Memory(self.memory[:]) + +class Computer(object): + def __init__(self, program, IO, i=0, rel_base=0): + self.memory = Memory(program) + self.IO = IO + self.i = i + self.rel_base = rel_base + + def run(self): + for exe_state in self: + if exe_state != CONTINUE: + break + return exe_state + + def __iter__(self): + return self + + def __next__(self): + return self.run_opcode() + + def copy(self): + return Computer(self.memory.memory[:], self.IO, self.i, self.rel_base) + +class IntComp(Computer): + run_opcode = run_op + + def copy(self): + return IntComp(self.memory.memory[:], self.IO, self.i, self.rel_base) + + +class Display(defaultdict): + def __init__(self, encoding): + super(Display, self).__init__(int) + self.encoding = encoding + + def __str__(self): + xs, ys = zip(*self.keys()) + X, Y = max(xs)+1, max(ys)+1 + return '\n'.join(''.join(self.encoding[self[(x, y)]] + for x in range(X)) + for y in range(Y)) + + + +class DisplayIO(object): + def __init__(self, display): + self.display = display + self.block = tuple() + + def input(self): + return NotImplemented + + def wait_for_input(self): + return NotImplemented + + def output(self, x): + self.block += (x,) + if len(self.block) == 3: + self.display[self.block[:2]] = self.block[2] + self.block = tuple() + +def output(program, input=[], computer=IntComp): + IO = StackIO() + IO.input_stack = input[:] + comp = computer(program, IO) + if comp.run() != HALT: + return False + return IO.output_stack diff --git a/opcodes.py b/opcodes.py new file mode 100644 index 0000000..3a8a90b --- /dev/null +++ b/opcodes.py @@ -0,0 +1,98 @@ +from operator import add, mul, eq, lt +import enum + + +@enum.unique +class State(enum.Enum): + """Execution state class.""" + CONTINUE = 0 + HALT = 1 + WAIT = 2 + + +class Opcode: + def __init__(self, func, parnum): + self.parnum = parnum + self.run = func + + +def operation(operator): + """Make opcode from operator.""" + def opcode(state, p1, p2, p3): + state.memory.write(p3, operator(state.memory.read(p1), state.memory.read(p2))) + state.i += 4 + return State.CONTINUE + return Opcode(opcode, 3) + +def jump(flag): + def opcode(state, p1, p2): + state.i = state.memory.read(p2) if (state.memory.read(p1) > 0)==flag else state.i+3 + return State.CONTINUE + return Opcode(opcode, 2) + +def adjust_base(state, p1): + state.rel_base += state.memory.read(p1) + state.i += 2 + return State.CONTINUE + +def input(state, p1): + if state.IO.wait_for_input(): + return State.WAIT + state.memory.write(p1, state.IO.input()) + state.i += 2 + return State.CONTINUE + +def output(state, p1): + state.IO.output(state.memory.read(p1)) + state.i += 2 + return State.CONTINUE + + +op_add = operation(add) + +op_mul = operation(mul) + +op_lt = operation(lt) + +op_eq = operation(eq) + +op_jump_if_true = jump(True) + +op_jump_if_false = jump(False) + +op_adjust_relative_base = Opcode(adjust_base, 1) + +op_input = Opcode(input, 1) + +op_output = Opcode(output, 1) + +op_halt = Opcode(lambda x: State.HALT, 0) + +opcodes = {1 : op_add, + 2 : op_mul, + 3 : op_input, + 4 : op_output, + 5 : op_jump_if_true, + 6 : op_jump_if_false, + 7 : op_lt, + 8 : op_eq, + 9 : op_adjust_relative_base, + 99 : op_halt} + +def parse_op(op): + op = str(op) + par_modes, op = op[:-2][::-1], int(op[-2:]) + par_modes = par_modes + '0'*(opcodes[op].parnum - len(par_modes)) + return op, map(int, par_modes) + +def run_op(state): + op, par_modes = parse_op(state.memory.read(state.i)) + pars = [] + for pn, mode in enumerate(par_modes, start=1): + p = state.i + pn + if mode == 0: + p = state.memory.read(p) + elif mode == 2: + p = state.rel_base + state.memory.read(p) + pars.append(p) + return opcodes[op].run(state, *pars)