diff --git a/2019/d05/ex1/ex1.py b/2019/d05/ex1/ex1.py new file mode 100755 index 0000000..09dac02 --- /dev/null +++ b/2019/d05/ex1/ex1.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python + +import sys +from copy import deepcopy +from enum import IntEnum +from typing import List, NamedTuple + + +class ParameterMode(IntEnum): + POSITION = 0 # Acts on address + IMMEDIATE = 1 # Acts on the immediate value + + +class Instruction(NamedTuple): + address: int # The address of the instruction, for convenience + op: int # The opcode + p1_mode: ParameterMode # Which mode is the first parameter in + p2_mode: ParameterMode # Which mode is the second parameter in + p3_mode: ParameterMode # Which mode is the third parameter in + + +def lookup_ops(index: int, memory: List[int]) -> Instruction: + digits = list(map(int, str(memory[index]))) + a, b, c, d, e = [0] * (5 - len(digits)) + digits # Pad with default values + return Instruction( + address=index, + op=d * 10 + e, + p1_mode=ParameterMode(c), + p2_mode=ParameterMode(b), + p3_mode=ParameterMode(a), + ) + + +def do_addition(instr: Instruction, memory: List[int]) -> int: + lhs, rhs, dest = memory[instr.address + 1 : instr.address + 4] + if instr.p1_mode == ParameterMode.POSITION: + lhs = memory[lhs] + if instr.p2_mode == ParameterMode.POSITION: + rhs = memory[rhs] + assert instr.p3_mode != ParameterMode.IMMEDIATE # Sanity check + memory[dest] = lhs + rhs + + return 4 # Length of the instruction + + +def do_multiplication(instr: Instruction, memory: List[int]) -> int: + lhs, rhs, dest = memory[instr.address + 1 : instr.address + 4] + if instr.p1_mode == ParameterMode.POSITION: + lhs = memory[lhs] + if instr.p2_mode == ParameterMode.POSITION: + rhs = memory[rhs] + assert instr.p3_mode != ParameterMode.IMMEDIATE # Sanity check + memory[dest] = lhs * rhs + + return 4 # Length of the instruction + + +def do_input(instr: Instruction, memory: List[int]) -> int: + value = int(input()) + param = memory[instr.address + 1] + + assert instr.p1_mode == ParameterMode.POSITION # Sanity check + memory[param] = value + + return 2 # Length of the instruction + + +def do_output(instr: Instruction, memory: List[int]) -> int: + value = memory[instr.address + 1] + if instr.p1_mode == ParameterMode.POSITION: + value = memory[value] + else: + assert instr.p1_mode == ParameterMode.IMMEDIATE # Sanity check + + print(value) + + return 2 # Length of the instruction + + +def run_op(memory: List[int]) -> None: + index = 0 + while True: + instr = lookup_ops(index, memory) + if instr.op == 99: # Halt + return + elif instr.op == 1: # Sum + index += do_addition(instr, memory) + elif instr.op == 2: # Multiplication + index += do_multiplication(instr, memory) + elif instr.op == 3: # Load from input + index += do_input(instr, memory) + elif instr.op == 4: # Store to output + index += do_output(instr, memory) + + +def main() -> None: + with open("input") as mem_f: + memory = [int(n) for n in mem_f.read().split(",")] + run_op(memory) + + +if __name__ == "__main__": + main()