From 4e622ff28c106cf1324bab9f4e0f630532eee9d2 Mon Sep 17 00:00:00 2001 From: rpidanny Date: Mon, 11 Dec 2023 20:46:31 +0100 Subject: [PATCH] feat(2019): day 7 - part 1 --- 2019/Day07/README.md | 3 +++ 2019/Day07/helpers.py | 19 ++++++++++++++ 2019/Day07/input.txt | 1 + 2019/Day07/input_test.txt | 0 2019/Day07/main.py | 16 +++++++++++ 2019/Day07/solutions.py | 17 ++++++++++++ 2019/Day07/test_solutions.py | 51 ++++++++++++++++++++++++++++++++++++ 2019/utils/intcode.py | 44 +++++++++++++++++++++++++++++++ 8 files changed, 151 insertions(+) create mode 100644 2019/Day07/README.md create mode 100644 2019/Day07/helpers.py create mode 100644 2019/Day07/input.txt create mode 100644 2019/Day07/input_test.txt create mode 100644 2019/Day07/main.py create mode 100644 2019/Day07/solutions.py create mode 100644 2019/Day07/test_solutions.py diff --git a/2019/Day07/README.md b/2019/Day07/README.md new file mode 100644 index 0000000..98e93b9 --- /dev/null +++ b/2019/Day07/README.md @@ -0,0 +1,3 @@ +# --- Day 0: Template --- + +To be used as template for the other days. diff --git a/2019/Day07/helpers.py b/2019/Day07/helpers.py new file mode 100644 index 0000000..904250f --- /dev/null +++ b/2019/Day07/helpers.py @@ -0,0 +1,19 @@ +from copy import deepcopy +import sys + +sys.path.append("utils") + +from intcode import get_program, run_program + + +def amplify(inputs: list[str], phases: list[int], input: int) -> int: + program = get_program(inputs) + + output = input + for amp in range(len(phases)): + mem = deepcopy(program) + io_ip = [phases[amp], output] + io_op = [] + run_program(mem, io_ip, io_op) + output = io_op[0] + return output diff --git a/2019/Day07/input.txt b/2019/Day07/input.txt new file mode 100644 index 0000000..f0d4fc7 --- /dev/null +++ b/2019/Day07/input.txt @@ -0,0 +1 @@ +3,8,1001,8,10,8,105,1,0,0,21,42,51,76,101,118,199,280,361,442,99999,3,9,101,5,9,9,102,2,9,9,1001,9,4,9,102,2,9,9,4,9,99,3,9,1002,9,3,9,4,9,99,3,9,1002,9,4,9,1001,9,3,9,1002,9,5,9,101,3,9,9,1002,9,2,9,4,9,99,3,9,101,4,9,9,1002,9,2,9,1001,9,3,9,1002,9,3,9,101,4,9,9,4,9,99,3,9,101,3,9,9,1002,9,3,9,101,2,9,9,4,9,99,3,9,1002,9,2,9,4,9,3,9,1001,9,2,9,4,9,3,9,1002,9,2,9,4,9,3,9,102,2,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,1001,9,2,9,4,9,3,9,101,2,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,1002,9,2,9,4,9,3,9,1001,9,1,9,4,9,99,3,9,1001,9,1,9,4,9,3,9,1002,9,2,9,4,9,3,9,1001,9,1,9,4,9,3,9,101,1,9,9,4,9,3,9,1001,9,1,9,4,9,3,9,101,2,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,1001,9,2,9,4,9,3,9,101,2,9,9,4,9,3,9,1002,9,2,9,4,9,99,3,9,1002,9,2,9,4,9,3,9,101,1,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,102,2,9,9,4,9,3,9,1001,9,1,9,4,9,3,9,101,1,9,9,4,9,3,9,102,2,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,1002,9,2,9,4,9,3,9,101,2,9,9,4,9,99,3,9,1002,9,2,9,4,9,3,9,1001,9,1,9,4,9,3,9,1002,9,2,9,4,9,3,9,1002,9,2,9,4,9,3,9,1002,9,2,9,4,9,3,9,1002,9,2,9,4,9,3,9,102,2,9,9,4,9,3,9,1001,9,2,9,4,9,3,9,101,2,9,9,4,9,3,9,101,1,9,9,4,9,99,3,9,1002,9,2,9,4,9,3,9,101,1,9,9,4,9,3,9,102,2,9,9,4,9,3,9,102,2,9,9,4,9,3,9,102,2,9,9,4,9,3,9,1001,9,1,9,4,9,3,9,1001,9,1,9,4,9,3,9,101,2,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,101,2,9,9,4,9,99 \ No newline at end of file diff --git a/2019/Day07/input_test.txt b/2019/Day07/input_test.txt new file mode 100644 index 0000000..e69de29 diff --git a/2019/Day07/main.py b/2019/Day07/main.py new file mode 100644 index 0000000..b728c56 --- /dev/null +++ b/2019/Day07/main.py @@ -0,0 +1,16 @@ +import os +import sys + +sys.path.append(".") + +from solutions import part1, part2 + +from utils.inputs import get_inputs +from utils.timings import profile_run + +if __name__ == "__main__": + input_path = f"{os.path.dirname(os.path.realpath(__file__))}/input.txt" + inputs = get_inputs(input_path) + + profile_run("Part 1", lambda: part1(inputs)) + profile_run("Part 2", lambda: part2(inputs)) diff --git a/2019/Day07/solutions.py b/2019/Day07/solutions.py new file mode 100644 index 0000000..32250ae --- /dev/null +++ b/2019/Day07/solutions.py @@ -0,0 +1,17 @@ +from itertools import permutations + +from helpers import amplify + + +def part1(inputs: list[str]) -> int: + max_output = 0 + for phases in list(permutations(range(5), 5)): + max_output = max(max_output, amplify(inputs, list(phases), 0)) + return max_output + + +def part2(inputs: list[str]) -> int: + max_output = 0 + for phases in list(permutations(range(9), 5)): + max_output = max(max_output, amplify(inputs, list(phases), 0)) + return max_output diff --git a/2019/Day07/test_solutions.py b/2019/Day07/test_solutions.py new file mode 100644 index 0000000..c71278f --- /dev/null +++ b/2019/Day07/test_solutions.py @@ -0,0 +1,51 @@ +import os + +import pytest +from solutions import part1, part2 + +from utils.inputs import get_inputs + +current_dir = os.path.dirname(os.path.realpath(__file__)) + +input = get_inputs(f"{current_dir}/input.txt") + + +@pytest.mark.skip(reason="completed") +class TestPart1: + def test_with_test_data_1(self): + assert part1(["3,15,3,16,1002,16,10,16,1,16,15,15,4,15,99,0,0"]) == 43210 + assert ( + part1( + [ + "3,23,3,24,1002,24,10,24,1002,23,-1,23,101,5,23,23,1,24,23,23,4,23,99,0,0" + ] + ) + == 54321 + ) + assert ( + part1( + [ + "3,31,3,32,1002,32,10,32,1001,31,-2,31,1007,31,0,33,1002,33,7,33,1,33,31,31,1,32,31,31,4,31,99,0,0,0" + ] + ) + == 65210 + ) + + def test_with_real_data(self): + assert part1(input) == 75228 + + +class TestPart2: + def test_with_test_data(self): + assert ( + part2( + [ + "3,26,1001,26,-4,26,3,27,1002,27,2,27,1,27,26,27,4,27,1001,28,-1,28,1005,28,6,99,0,0,5" + ] + ) + == 139629729 + ) + + @pytest.mark.skip(reason="not implemented") + def test_with_real_data(self): + assert part2(input) == 2 diff --git a/2019/utils/intcode.py b/2019/utils/intcode.py index 4af34b9..4193a46 100644 --- a/2019/utils/intcode.py +++ b/2019/utils/intcode.py @@ -10,3 +10,47 @@ def parse_instruction(instr: int) -> Tuple[int, int, int, int]: op_code = int(instr[-2:]) m_3, m_2, m_1 = map(int, instr[:3]) return m_3, m_2, m_1, op_code + + +def run_program( + mem: list[int], io_ip: list[int] = [], io_op: list[int] = [] +) -> list[int]: + ip = 0 + + def get_val(addr: int, mode: int) -> int: + return mem[addr] if mode else mem[mem[addr]] + + num_params = {1: 3, 2: 3, 3: 1, 4: 1, 5: 2, 6: 2, 7: 3, 8: 3, 99: 0} + + while True: + _, m_2, m_1, op_code = parse_instruction(mem[ip]) + + jump = False + + if op_code == 1: + mem[mem[ip + 3]] = get_val(ip + 1, m_1) + get_val(ip + 2, m_2) + elif op_code == 2: + mem[mem[ip + 3]] = get_val(ip + 1, m_1) * get_val(ip + 2, m_2) + elif op_code == 3: + mem[mem[ip + 1]] = io_ip.pop(0) + elif op_code == 4: + io_op.append(get_val(ip + 1, m_1)) + elif op_code == 5: + if jump := get_val(ip + 1, m_1) > 0: + ip = get_val(ip + 2, m_2) + elif op_code == 6: + if jump := get_val(ip + 1, m_1) == 0: + ip = get_val(ip + 2, m_2) + elif op_code == 7: + mem[mem[ip + 3]] = 1 if get_val(ip + 1, m_1) < get_val(ip + 2, m_2) else 0 + elif op_code == 8: + mem[mem[ip + 3]] = 1 if get_val(ip + 1, m_1) == get_val(ip + 2, m_2) else 0 + elif op_code == 99: + break + else: + raise Exception(f"Unknown opcode: {op_code}") + + if not jump: + ip += num_params[op_code] + 1 + + return mem