From cb6025af46c745fb1f57d3a3db70a9a52d3f7386 Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Mon, 9 Dec 2019 17:43:02 +0100 Subject: [PATCH] 2019: d07: ex1: add solution --- 2019/d07/ex1/ex1.py | 259 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 259 insertions(+) create mode 100755 2019/d07/ex1/ex1.py diff --git a/2019/d07/ex1/ex1.py b/2019/d07/ex1/ex1.py new file mode 100755 index 0000000..1808ec8 --- /dev/null +++ b/2019/d07/ex1/ex1.py @@ -0,0 +1,259 @@ +#!/usr/bin/env python + +import itertools +import sys +from copy import deepcopy +from dataclasses import dataclass +from enum import IntEnum +from typing import Callable, 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), + ) + + +@dataclass +class Computer: + memory: List[int] # Memory space + rip: int = 0 # Instruction pointer + read_input: Callable[[], str] = input + print_output: Callable[[int], None] = print + + def run(self) -> None: + is_halted = self.run_single() + while not is_halted: + is_halted = self.run_single() + + def run_single(self) -> bool: # Returns True when halted + instr = lookup_ops(self.rip, self.memory) + if instr.op == 99: # Halt + return True # Halted + elif instr.op == 1: # Sum + self.do_addition(instr) + elif instr.op == 2: # Multiplication + self.do_multiplication(instr) + elif instr.op == 3: # Load from input + self.do_input(instr) + elif instr.op == 4: # Store to output + self.do_output(instr) + elif instr.op == 5: # Jump if true + self.do_jump_if_true(instr) + elif instr.op == 6: # Jump if false + self.do_jump_if_false(instr) + elif instr.op == 7: # Less than + self.do_less_than(instr) + elif instr.op == 8: # Equal to + self.do_equal_to(instr) + else: + assert False # Sanity check + return False # Not halted + + def do_addition(self, instr: Instruction) -> None: + lhs, rhs, dest = self.memory[instr.address + 1 : instr.address + 4] + + if instr.p1_mode == ParameterMode.POSITION: + lhs = self.memory[lhs] + else: + assert instr.p1_mode == ParameterMode.IMMEDIATE # Sanity check + + if instr.p2_mode == ParameterMode.POSITION: + rhs = self.memory[rhs] + else: + assert instr.p2_mode == ParameterMode.IMMEDIATE # Sanity check + + assert instr.p3_mode == ParameterMode.POSITION # Sanity check + self.memory[dest] = lhs + rhs + + self.rip += 4 # Length of the instruction + + def do_multiplication(self, instr: Instruction) -> None: + lhs, rhs, dest = self.memory[instr.address + 1 : instr.address + 4] + + if instr.p1_mode == ParameterMode.POSITION: + lhs = self.memory[lhs] + else: + assert instr.p1_mode == ParameterMode.IMMEDIATE # Sanity check + + if instr.p2_mode == ParameterMode.POSITION: + rhs = self.memory[rhs] + else: + assert instr.p2_mode == ParameterMode.IMMEDIATE # Sanity check + + assert instr.p3_mode == ParameterMode.POSITION # Sanity check + self.memory[dest] = lhs * rhs + + self.rip += 4 # Length of the instruction + + def do_input(self, instr: Instruction) -> None: + value = int(self.read_input()) + param = self.memory[instr.address + 1] + + assert instr.p1_mode == ParameterMode.POSITION # Sanity check + self.memory[param] = value + + self.rip += 2 # Length of the instruction + + def do_output(self, instr: Instruction) -> None: + value = self.memory[instr.address + 1] + if instr.p1_mode == ParameterMode.POSITION: + value = self.memory[value] + else: + assert instr.p1_mode == ParameterMode.IMMEDIATE # Sanity check + + self.print_output(value) + + self.rip += 2 # Length of the instruction + + def do_jump_if_true(self, instr: Instruction) -> None: + cond, value = self.memory[instr.address + 1 : instr.address + 3] + + if instr.p1_mode == ParameterMode.POSITION: + cond = self.memory[cond] + else: + assert instr.p1_mode == ParameterMode.IMMEDIATE # Sanity check + + if instr.p2_mode == ParameterMode.POSITION: + value = self.memory[value] + else: + assert instr.p2_mode == ParameterMode.IMMEDIATE # Sanity check + + if cond != 0: + self.rip = value + else: + self.rip += 3 # Length of the instruction + + def do_jump_if_false(self, instr: Instruction) -> None: + cond, value = self.memory[instr.address + 1 : instr.address + 3] + + if instr.p1_mode == ParameterMode.POSITION: + cond = self.memory[cond] + else: + assert instr.p1_mode == ParameterMode.IMMEDIATE # Sanity check + + if instr.p2_mode == ParameterMode.POSITION: + value = self.memory[value] + else: + assert instr.p2_mode == ParameterMode.IMMEDIATE # Sanity check + + if cond == 0: + self.rip = value + else: + self.rip += 3 # Length of the instruction + + def do_less_than(self, instr: Instruction) -> None: + lhs, rhs, dest = self.memory[instr.address + 1 : instr.address + 4] + + if instr.p1_mode == ParameterMode.POSITION: + lhs = self.memory[lhs] + else: + assert instr.p1_mode == ParameterMode.IMMEDIATE # Sanity check + + if instr.p2_mode == ParameterMode.POSITION: + rhs = self.memory[rhs] + else: + assert instr.p2_mode == ParameterMode.IMMEDIATE # Sanity check + + assert instr.p3_mode == ParameterMode.POSITION # Sanity check + self.memory[dest] = 1 if lhs < rhs else 0 + + self.rip += 4 # Length of the instruction + + def do_equal_to(self, instr: Instruction) -> None: + lhs, rhs, dest = self.memory[instr.address + 1 : instr.address + 4] + + if instr.p1_mode == ParameterMode.POSITION: + lhs = self.memory[lhs] + else: + assert instr.p1_mode == ParameterMode.IMMEDIATE # Sanity check + + if instr.p2_mode == ParameterMode.POSITION: + rhs = self.memory[rhs] + else: + assert instr.p2_mode == ParameterMode.IMMEDIATE # Sanity check + + assert instr.p3_mode == ParameterMode.POSITION # Sanity check + self.memory[dest] = 1 if lhs == rhs else 0 + + self.rip += 4 # Length of the instruction + + +def main() -> None: + def get_input_fun(phase: int, last_output: List[int]) -> Callable[[], str]: + has_been_called = False + + def _input() -> str: + nonlocal has_been_called + if has_been_called: + return str(last_output.pop(0)) + has_been_called = True + return str(phase) + + return _input + + memory = [int(n) for n in sys.stdin.read().split(",")] + max = 0 + ans = tuple(-1 for __ in range(5)) + for a, b, c, d, e in itertools.permutations(range(5)): + amp1 = Computer(deepcopy(memory)) + amp1_output: List[int] = [] + amp1.read_input = get_input_fun(a, [0]) + amp1.print_output = lambda x: amp1_output.append(int(x)) + + amp2 = Computer(deepcopy(memory)) + amp2_output: List[int] = [] + amp2.read_input = get_input_fun(b, amp1_output) + amp2.print_output = lambda x: amp2_output.append(int(x)) + + amp3 = Computer(deepcopy(memory)) + amp3_output: List[int] = [] + amp3.read_input = get_input_fun(c, amp2_output) + amp3.print_output = lambda x: amp3_output.append(int(x)) + + amp4 = Computer(deepcopy(memory)) + amp4_output: List[int] = [] + amp4.read_input = get_input_fun(d, amp3_output) + amp4.print_output = lambda x: amp4_output.append(int(x)) + + amp5 = Computer(deepcopy(memory)) + amp5_output: List[int] = [] + amp5.read_input = get_input_fun(e, amp4_output) + amp5.print_output = lambda x: amp5_output.append(int(x)) + + amp1.run() + amp2.run() + amp3.run() + amp4.run() + amp5.run() + + res = amp5_output.pop(0) + if res > max: + max = res + ans = (a, b, c, d, e) + print(f"Max: {max}, res: {ans}") + + print(f"Final one: {max}, with {ans}") + + +if __name__ == "__main__": + main()