From b904e20b104f043de7cf337e59ea2a44b52bccb0 Mon Sep 17 00:00:00 2001 From: Thibaud Gasser Date: Fri, 6 Dec 2019 17:49:34 +0100 Subject: [PATCH 1/3] [WIP] Day 5 challenge- Part 1: OK- Part 2: KO --- day5/day5.py | 123 +++++++++++++++++++++++++++++++++++++++++++++++++ day5/input.txt | 1 + 2 files changed, 124 insertions(+) create mode 100644 day5/day5.py create mode 100644 day5/input.txt diff --git a/day5/day5.py b/day5/day5.py new file mode 100644 index 0000000..5f24de0 --- /dev/null +++ b/day5/day5.py @@ -0,0 +1,123 @@ +#! /usr/bin/env python3 +from collections import namedtuple + + +def decode_opcode(inp): + opcode = inp % 100 # 2-digit opcode + a = (inp // 10000) % 10 # mode of 1st param + b = (inp // 1000) % 10 # mode of 2nd param + c = (inp // 100) % 10 # mode of 3rd param + # ABC DE + # ^^^ ^^ + # MODE OPCODE + return a, b, c, opcode + + +def decode_parameters(input_prog, ip, modes): + """ + Returns the adress of the parameters according to the input modes. + if mode[n] == 1: direct access, else indirect access + """ + param1 = input_prog[ip + 1] if modes[2] == 0 else (ip + 1) + param2 = input_prog[ip + 2] if modes[1] == 0 else (ip + 2) + param3 = input_prog[ip + 3] if modes[0] == 0 else (ip + 3) + return param1, param2, param3 + + + +def interpret_intcode(input_prog, stdin=None): + # Instruction pointer: index of the next element to be executed + ip = 0 + while ip < len(input_prog): + instruction = input_prog[ip] + *modes, opcode = decode_opcode(instruction) + + if opcode == 99: + break + + parameters = decode_parameters(input_prog, ip, modes) + + if opcode == 1: # ADD + print("ADD", " ".join(str(input_prog[p]) for p in parameters)) + input_prog[parameters[2]] = input_prog[parameters[0]] + input_prog[parameters[1]] + ip += 4 + + elif opcode == 2: # MUL + print("MUL", " ".join(str(input_prog[p]) for p in parameters)) + input_prog[parameters[2]] = input_prog[parameters[0]] * input_prog[parameters[1]] + ip += 4 + + elif opcode == 3: # IN + input_prog[parameters[1]] = stdin + ip += 2 + + elif opcode == 4: # OUT + if input_prog[ip + 1] == 99: + print("End of execution") + break + + print("OUT ", input_prog[parameters[1]]) + ip += 2 + + elif opcode == 5: # JMP if true + if input_prog[parameters[0]] != 0: + print("JT", " ".join(str(input_prog[p]) for p in parameters)) + ip = input_prog[parameters[1]] + else: + ip += 3 + + elif opcode == 6: # JMP if false + if input_prog[parameters[0]] == 0: + print("JF", " ".join(str(input_prog[p]) for p in parameters)) + ip = input_prog[parameters[1]] + else: + ip += 3 + + elif opcode == 7: # LT + print("LT", " ".join(str(input_prog[p]) for p in parameters)) + res = int(input_prog[parameters[0]] < input_prog[parameters[1]]) + input_prog[parameters[2]] = res + ip += 4 + + elif opcode == 8: # EQ + print("EQ", " ".join(str(input_prog[p]) for p in parameters)) + res = int(input_prog[parameters[1]] == input_prog[parameters[2]]) + input_prog[parameters[2]] = res + ip += 4 + + +def tests(): + inputs = ( + [1, 0, 0, 0, 99], # ADD 1 + 1 to location 0 + [2, 3, 0, 3, 99], # MUL 2 * 3 to location 3 + [2, 4, 4, 5, 99, 0], + [1, 1, 1, 4, 99, 5, 6, 0, 99], + ) + expected_outputs = ( + [2, 0, 0, 0, 99], # 1 + 1 = 2 + [2, 3, 0, 6, 99], # 3 * 2 = 6 + [2, 4, 4, 5, 99, 9801], # 99 * 99 = 9801 + [30, 1, 1, 4, 2, 5, 6, 0, 99], + ) + for i, inp in enumerate(inputs): + result = inp[:] + interpret_intcode(result) + assert ( + result == expected_outputs[i] + ), f"Expected output for {inp} is {expected_outputs[i]}, but found {result} instead." + print("\nAll tests passed.\n") + + +if __name__ == "__main__": + tests() + # a = [3, 12, 6, 12, 15, 1, 13, 14, 13, 4, 13, 99, -1, 0, 1, 9] + # b = [3, 3, 1105, -1, 9, 1101, 0, 0, 12, 4, 12, 99, 1] + + # interpret_intcode(b, 0) + # interpret_intcode(a, 0) + + with open("input.txt") as inp: + print("Start of input program.") + memory = [int(x) for x in inp.readline().strip().split(",")] + interpret_intcode(memory, 5) + diff --git a/day5/input.txt b/day5/input.txt new file mode 100644 index 0000000..425efbc --- /dev/null +++ b/day5/input.txt @@ -0,0 +1 @@ +3,225,1,225,6,6,1100,1,238,225,104,0,1102,67,92,225,1101,14,84,225,1002,217,69,224,101,-5175,224,224,4,224,102,8,223,223,101,2,224,224,1,224,223,223,1,214,95,224,101,-127,224,224,4,224,102,8,223,223,101,3,224,224,1,223,224,223,1101,8,41,225,2,17,91,224,1001,224,-518,224,4,224,1002,223,8,223,101,2,224,224,1,223,224,223,1101,37,27,225,1101,61,11,225,101,44,66,224,101,-85,224,224,4,224,1002,223,8,223,101,6,224,224,1,224,223,223,1102,7,32,224,101,-224,224,224,4,224,102,8,223,223,1001,224,6,224,1,224,223,223,1001,14,82,224,101,-174,224,224,4,224,102,8,223,223,101,7,224,224,1,223,224,223,102,65,210,224,101,-5525,224,224,4,224,102,8,223,223,101,3,224,224,1,224,223,223,1101,81,9,224,101,-90,224,224,4,224,102,8,223,223,1001,224,3,224,1,224,223,223,1101,71,85,225,1102,61,66,225,1102,75,53,225,4,223,99,0,0,0,677,0,0,0,0,0,0,0,0,0,0,0,1105,0,99999,1105,227,247,1105,1,99999,1005,227,99999,1005,0,256,1105,1,99999,1106,227,99999,1106,0,265,1105,1,99999,1006,0,99999,1006,227,274,1105,1,99999,1105,1,280,1105,1,99999,1,225,225,225,1101,294,0,0,105,1,0,1105,1,99999,1106,0,300,1105,1,99999,1,225,225,225,1101,314,0,0,106,0,0,1105,1,99999,8,226,226,224,102,2,223,223,1005,224,329,1001,223,1,223,1108,677,677,224,1002,223,2,223,1006,224,344,101,1,223,223,1007,226,677,224,102,2,223,223,1005,224,359,101,1,223,223,1007,677,677,224,1002,223,2,223,1006,224,374,101,1,223,223,1108,677,226,224,1002,223,2,223,1005,224,389,1001,223,1,223,108,226,677,224,102,2,223,223,1006,224,404,101,1,223,223,1108,226,677,224,102,2,223,223,1005,224,419,101,1,223,223,1008,677,677,224,102,2,223,223,1005,224,434,101,1,223,223,7,677,226,224,1002,223,2,223,1005,224,449,101,1,223,223,1008,226,226,224,102,2,223,223,1005,224,464,1001,223,1,223,107,226,677,224,1002,223,2,223,1006,224,479,1001,223,1,223,107,677,677,224,102,2,223,223,1005,224,494,1001,223,1,223,1008,226,677,224,102,2,223,223,1006,224,509,1001,223,1,223,1107,677,226,224,102,2,223,223,1005,224,524,101,1,223,223,1007,226,226,224,1002,223,2,223,1006,224,539,1001,223,1,223,107,226,226,224,102,2,223,223,1006,224,554,101,1,223,223,108,677,677,224,1002,223,2,223,1006,224,569,1001,223,1,223,7,226,677,224,102,2,223,223,1006,224,584,1001,223,1,223,8,677,226,224,102,2,223,223,1005,224,599,101,1,223,223,1107,677,677,224,1002,223,2,223,1005,224,614,101,1,223,223,8,226,677,224,102,2,223,223,1005,224,629,1001,223,1,223,7,226,226,224,1002,223,2,223,1006,224,644,1001,223,1,223,108,226,226,224,1002,223,2,223,1006,224,659,101,1,223,223,1107,226,677,224,1002,223,2,223,1006,224,674,101,1,223,223,4,223,99,226 \ No newline at end of file -- 2.45.2 From d441db73e5b97654022487236065285ea4bb5abd Mon Sep 17 00:00:00 2001 From: Thibaud Date: Mon, 16 Dec 2019 00:23:01 +0100 Subject: [PATCH 2/3] wip wip wip --- day5/day5.py | 208 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 124 insertions(+), 84 deletions(-) diff --git a/day5/day5.py b/day5/day5.py index 5f24de0..4b29bca 100644 --- a/day5/day5.py +++ b/day5/day5.py @@ -1,89 +1,122 @@ #! /usr/bin/env python3 from collections import namedtuple +from enum import Enum +import logging + +logging.basicConfig(format="%(levelname)s:%(message)s", level=logging.DEBUG) -def decode_opcode(inp): - opcode = inp % 100 # 2-digit opcode - a = (inp // 10000) % 10 # mode of 1st param - b = (inp // 1000) % 10 # mode of 2nd param - c = (inp // 100) % 10 # mode of 3rd param - # ABC DE - # ^^^ ^^ - # MODE OPCODE - return a, b, c, opcode +def get_nth_digit(n, number): + "Returns the nth digit of the input number" + return number // n ** 10 % 10 -def decode_parameters(input_prog, ip, modes): - """ - Returns the adress of the parameters according to the input modes. - if mode[n] == 1: direct access, else indirect access - """ - param1 = input_prog[ip + 1] if modes[2] == 0 else (ip + 1) - param2 = input_prog[ip + 2] if modes[1] == 0 else (ip + 2) - param3 = input_prog[ip + 3] if modes[0] == 0 else (ip + 3) - return param1, param2, param3 +class Operation(Enum): + ADDITION = 1 + MULTIPLICATION = 2 + INPUT = 3 + OUTPUT = 4 + JMP_IF_TRUE = 5 + JMP_IF_FALSE = 6 + LESS_THAN = 7 + EQUALS = 8 + TERMINATION = 99 +class Mode(Enum): + POSITION = 0 + IMMEDIATE = 1 -def interpret_intcode(input_prog, stdin=None): - # Instruction pointer: index of the next element to be executed + +class Instruction: + def __init__(self, opcode): + self.operation = Operation(opcode % 100) + self.modes = [Mode(get_nth_digit(n, opcode)) for n in range(2, 5)] + self.handler_name = f"handle_{self.operation.name.lower()}" + self.handler = getattr(self, self.handler_name, self.handle_termination) + + def __repr__(self): + return f"Instruction({self.operation}, {self.modes})" + + def handle(self, program, ip): + return self.handler(program, ip) + + def handle_addition(self, program, ip): + first, second = self._get_parameters(program, ip) + logging.debug(f"ADD {first} {second}") + result = first + second + # the last mode should *always* be POSITION + program[program[ip + 3]] = result + ip += 4 + return ip + + def handle_multiplication(self, program, ip): + first, second = self._get_parameters(program, ip) + logging.debug(f"MUL {first} {second}") + result = first * second + # the last mode should *always* be POSITION + program[program[ip + 3]] = result + ip += 4 + return ip + + def handle_input(self, program, ip): + ip += 2 + program[program[ip + 1]] = int(input("Enter ID > ")) + return ip + + def handle_output(self, program, ip): + print("OUT ", program[program[ip + 1]]) + ip += 2 + return ip + + def handle_jmp_if_true(self, program, ip): + first, second = self._get_parameters(program, ip) + logging.debug(f"JMPT {first} {second}") + return second if first != 0 else ip + 3 + + def handle_jmp_if_false(self, program, ip): + first, second = self._get_parameters(program, ip) + logging.debug(f"JMPF {first} {second}") + return second if first == 0 else ip + 3 + + def handle_less_than(self, program, ip): + first, second = self._get_parameters(program, ip) + logging.debug(f"LT {first} {second}") + program[program[ip + 3]] = int(first < second) + ip += 4 + return ip + + def handle_equals(self, program, ip): + first, second = self._get_parameters(program, ip) + logging.debug(f"EQ {first} {second}") + program[program[ip + 3]] = int(first == second) + ip += 4 + return ip + + def handle_termination(self, program, ip): + print("HALT") + return ip + + def _get_parameters(self, program, ip): + first = ( + program[ip + 1] + if self.modes[0] is Mode.IMMEDIATE + else program[program[ip + 1]] + ) + second = ( + program[ip + 2] + if self.modes[1] is Mode.IMMEDIATE + else program[program[ip + 2]] + ) + return first, second + + +def interpret_intcode(program, stdin=None): ip = 0 - while ip < len(input_prog): - instruction = input_prog[ip] - *modes, opcode = decode_opcode(instruction) - - if opcode == 99: - break - - parameters = decode_parameters(input_prog, ip, modes) - - if opcode == 1: # ADD - print("ADD", " ".join(str(input_prog[p]) for p in parameters)) - input_prog[parameters[2]] = input_prog[parameters[0]] + input_prog[parameters[1]] - ip += 4 - - elif opcode == 2: # MUL - print("MUL", " ".join(str(input_prog[p]) for p in parameters)) - input_prog[parameters[2]] = input_prog[parameters[0]] * input_prog[parameters[1]] - ip += 4 - - elif opcode == 3: # IN - input_prog[parameters[1]] = stdin - ip += 2 - - elif opcode == 4: # OUT - if input_prog[ip + 1] == 99: - print("End of execution") - break - - print("OUT ", input_prog[parameters[1]]) - ip += 2 - - elif opcode == 5: # JMP if true - if input_prog[parameters[0]] != 0: - print("JT", " ".join(str(input_prog[p]) for p in parameters)) - ip = input_prog[parameters[1]] - else: - ip += 3 - - elif opcode == 6: # JMP if false - if input_prog[parameters[0]] == 0: - print("JF", " ".join(str(input_prog[p]) for p in parameters)) - ip = input_prog[parameters[1]] - else: - ip += 3 - - elif opcode == 7: # LT - print("LT", " ".join(str(input_prog[p]) for p in parameters)) - res = int(input_prog[parameters[0]] < input_prog[parameters[1]]) - input_prog[parameters[2]] = res - ip += 4 - - elif opcode == 8: # EQ - print("EQ", " ".join(str(input_prog[p]) for p in parameters)) - res = int(input_prog[parameters[1]] == input_prog[parameters[2]]) - input_prog[parameters[2]] = res - ip += 4 + while program[ip] != 99: + opcode = program[ip] + instruction = Instruction(opcode) + ip = instruction.handle(program, ip) def tests(): @@ -92,12 +125,14 @@ def tests(): [2, 3, 0, 3, 99], # MUL 2 * 3 to location 3 [2, 4, 4, 5, 99, 0], [1, 1, 1, 4, 99, 5, 6, 0, 99], + [1101, 1, 1, 0, 99], # ADD 1 + 1 to location 0 (direct access) ) expected_outputs = ( [2, 0, 0, 0, 99], # 1 + 1 = 2 [2, 3, 0, 6, 99], # 3 * 2 = 6 [2, 4, 4, 5, 99, 9801], # 99 * 99 = 9801 [30, 1, 1, 4, 2, 5, 6, 0, 99], + [2, 1, 1, 0, 99], # 1 + 1 = 2 (direct access) ) for i, inp in enumerate(inputs): result = inp[:] @@ -108,16 +143,21 @@ def tests(): print("\nAll tests passed.\n") -if __name__ == "__main__": - tests() - # a = [3, 12, 6, 12, 15, 1, 13, 14, 13, 4, 13, 99, -1, 0, 1, 9] - # b = [3, 3, 1105, -1, 9, 1101, 0, 0, 12, 4, 12, 99, 1] - - # interpret_intcode(b, 0) - # interpret_intcode(a, 0) +def run_input_program(filename): + import os - with open("input.txt") as inp: + SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) + with open(os.path.join(SCRIPT_DIR, filename)) as inp: print("Start of input program.") memory = [int(x) for x in inp.readline().strip().split(",")] interpret_intcode(memory, 5) + +if __name__ == "__main__": + # tests() + # a = [3, 12, 6, 12, 15, 1, 13, 14, 13, 4, 13, 99, -1, 0, 1, 9] + # b = [3, 3, 1105, -1, 9, 1101, 0, 0, 12, 4, 12, 99, 1] + + # interpret_intcode(b, 0) + # interpret_intcode(a, 0) + run_input_program("input.txt") -- 2.45.2 From 953c372998e78ea32fd47504a400ca7cf6618796 Mon Sep 17 00:00:00 2001 From: Thibaud Date: Mon, 16 Dec 2019 22:05:07 +0100 Subject: [PATCH 3/3] wip wip wip --- day5/day5.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/day5/day5.py b/day5/day5.py index 4b29bca..a11854e 100644 --- a/day5/day5.py +++ b/day5/day5.py @@ -8,7 +8,7 @@ logging.basicConfig(format="%(levelname)s:%(message)s", level=logging.DEBUG) def get_nth_digit(n, number): "Returns the nth digit of the input number" - return number // n ** 10 % 10 + return number // 10 ** n % 10 class Operation(Enum): -- 2.45.2