From 9eccd731b49bc10b69e3297b01a44cec7d9f1f0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20Ytterstr=C3=B6m?= Date: Thu, 23 Nov 2023 01:31:57 +0100 Subject: [PATCH] Solve 2019:05 "Sunny with a Chance of Asteroids" Refactored the intcode computer to live in a separate file, since it will come back more times. As a nice touch, output of aoc.py will print the version used of the intcode. As another nice touch, the int code has a CHANGELOG. --- 2019-python/output/day_02.py | 36 +----- 2019-python/output/day_05.py | 29 +++++ 2019-python/output/intcode_computer.py | 146 +++++++++++++++++++++++++ 3 files changed, 181 insertions(+), 30 deletions(-) create mode 100644 2019-python/output/day_05.py create mode 100644 2019-python/output/intcode_computer.py diff --git a/2019-python/output/day_02.py b/2019-python/output/day_02.py index 29bff7f..6d10097 100644 --- a/2019-python/output/day_02.py +++ b/2019-python/output/day_02.py @@ -1,6 +1,8 @@ from output import answer, puzzleinput from collections import defaultdict +from output.intcode_computer import execute + n = 2 title = "1202 Program Alarm" @@ -10,41 +12,15 @@ def parse_input(data): return list(map(int, data.split(","))) -@answer(1, "Value of pos 0 is {} at halt signal") +@answer(1, "[intcode-0.1.0] Value of pos 0 is {} at halt signal") def part_1(program): - state = dict(zip(range(len(program)), program)) - state[1] = 12 - state[2] = 2 - - for i in range(0, len(state), 4): - opcode, *args = list(state.values())[i : i + 4] - if opcode == 1: - a, b, p = args - state[p] = state[a] + state[b] - if opcode == 2: - a, b, p = args - state[p] = state[a] * state[b] - if opcode == 99: - break + state, _ = execute(program, noun=12, verb=2) return state[0] -@answer(2, "100 * noun + verb = {} for output 19690720") +@answer(2, "[intcode-0.1.1] 100 * noun + verb = {} for output 19690720") def part_2(program, noun=76, verb=21): - state = dict(zip(range(len(program)), program)) - state[1] = noun - state[2] = verb - - for i in range(0, len(state), 4): - opcode, *args = list(state.values())[i : i + 4] - if opcode == 1: - a, b, p = args - state[p] = state[a] + state[b] - if opcode == 2: - a, b, p = args - state[p] = state[a] * state[b] - if opcode == 99: - break + state, _ = execute(program, noun, verb) if state[0] == 19690720: return 100 * noun + verb return state[0] diff --git a/2019-python/output/day_05.py b/2019-python/output/day_05.py new file mode 100644 index 0000000..83a7781 --- /dev/null +++ b/2019-python/output/day_05.py @@ -0,0 +1,29 @@ +from output import answer, puzzleinput + +from output.intcode_computer import execute + +n = 5 +title = "Sunny with a Chance of Asteroids" + + +@puzzleinput(n) +def parse_input(data): + return list(map(int, data.split(","))) + + +@answer(1, "[intcode-0.2.0] Program diagnostic code, ID 1: {}") +def part_1(program): + _, stdout = execute(program, stdin=1) + return max(stdout) + + +@answer(2, "[intcode-0.2.1] Program diagnostic code, ID 5: {}") +def part_2(program): + _, stdout = execute(program, stdin=5) + return stdout[0] + + +if __name__ == "__main__": + parsed = parse_input() + part_1(parsed) + part_2(parsed) diff --git a/2019-python/output/intcode_computer.py b/2019-python/output/intcode_computer.py new file mode 100644 index 0000000..9473d5a --- /dev/null +++ b/2019-python/output/intcode_computer.py @@ -0,0 +1,146 @@ +from collections import defaultdict + + +def execute(program, noun=None, verb=None, stdin=0, debug=False): + """ + intcode computer, AoC 2019 + + Changelog + ========= + + 0.2.1 + ----- + + Patch release (day 5 part 2). + + - Add operation 5: set instruction pointer to value at parameter 2 position, based on value at parameter 1 position + - Add operation 6: set instruction pointer to value at parameter 2 position, based on value at parameter 1 position + - Add operation 7: compares values in parameter 1 position and parameter 2 position, stores at parameter 3 position + - Add operation 8: compares values in parameter 1 position and parameter 2 position, stores at parameter 3 position + + 0.2.0 + ----- + + Minor release (day 5 part 1). + + - Support immediate parameter mode + - Add stdin argument + - Make arguments optional: noun, verb + - Capture and return stdout + - Add operation 3: store stdin to parameter 1 position + - Add operation 4: output value at parameter 1 position to stdout + + 0.1.1 + ----- + + Patch release (day 2 part 2). + + - Remove initial modification 1=12, 2=2 + - Add noun argument, stored at pos 1 (default value: 12) + - Add verb argument, stored at pos 2 (default value: 2) + + 0.1.0 + ----- + + Initial version (day 2 part 1). + + - Support positional parameter mode + - Add operation 1: adds parameter 1 to parameter 2, store to parameter 3 position + - Add operation 2: multiply parameter 1 with parameter 2, store to parameter 3 position + """ + state = dict(zip(range(len(program)), program)) + if noun: + state[1] = noun + if verb: + state[2] = verb + n = 0 + c = 0 + stdout = [] + + while True: + instruction = state[n] + opcode = instruction % 100 + modes = str(instruction // 100 % 100).zfill(2) + if opcode == 1: + a = state[n + 1] + b = state[n + 2] + p = state[n + 3] + x = a if modes[1] == "1" else state[a] + y = b if modes[0] == "1" else state[b] + state[p] = x + y + n += 4 + if debug: + print(f"{n}:{opcode} | {x} + {y} to {p}") + if opcode == 2: + a = state[n + 1] + b = state[n + 2] + p = state[n + 3] + x = a if modes[1] == "1" else state[a] + y = b if modes[0] == "1" else state[b] + state[p] = x * y + n += 4 + if debug: + print(f"{n}:{opcode} | {x} * {y} to {p}") + if opcode == 3: + p = state[n + 1] + state[p] = stdin + n += 2 + if debug: + print(f"{n}:{opcode} | {i} to {p}") + if opcode == 4: + a = state[n + 1] + x = a if modes[1] == "1" else state[a] + n += 2 + stdout.append(x) + if debug: + print(f"{n}:{opcode} | {stdout}") + if opcode == 5: + a = state[n + 1] + b = state[n + 2] + x = a if modes[1] == "1" else state[a] + y = b if modes[0] == "1" else state[b] + if x != 0: + n = y + else: + n += 3 + if debug: + print(f"{n}:{opcode} | {n}") + if opcode == 6: + a = state[n + 1] + b = state[n + 2] + x = a if modes[1] == "1" else state[a] + y = b if modes[0] == "1" else state[b] + if x == 0: + n = y + else: + n += 3 + if debug: + print(f"{n}:{opcode} | {n}") + if opcode == 7: + a = state[n + 1] + b = state[n + 2] + p = state[n + 3] + x = a if modes[1] == "1" else state[a] + y = b if modes[0] == "1" else state[b] + state[p] = int(x < y) + n += 4 + if debug: + print(f"{n}:{opcode} | {x}") + if opcode == 8: + a = state[n + 1] + b = state[n + 2] + p = state[n + 3] + x = a if modes[1] == "1" else state[a] + y = b if modes[0] == "1" else state[b] + state[p] = int(x == y) + n += 4 + if debug: + print(f"{n}:{opcode} | {x}") + if opcode == 99: + break + c += 1 + if debug and c % 1000 == 0: + print(f"{c} instructions done, current pos: {n}") + # if c == 3: + # break + return state, stdout