From c51638fac1a6eceaa6f3a8ec1e1df69a2e7acaa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20Ytterstr=C3=B6m?= Date: Thu, 23 Nov 2023 17:08:13 +0100 Subject: [PATCH] Solve 2019:07 "Amplication Circuit" Breaking change for intcode 0.3.0, revisited earlier use of the computer to update code. --- 2019-python/output/day_02.py | 8 +- 2019-python/output/day_05.py | 8 +- 2019-python/output/day_07.py | 65 +++++++++++++ 2019-python/output/intcode_computer.py | 122 +++++++++++++++++-------- 4 files changed, 158 insertions(+), 45 deletions(-) create mode 100644 2019-python/output/day_07.py diff --git a/2019-python/output/day_02.py b/2019-python/output/day_02.py index 6d10097..e9e4465 100644 --- a/2019-python/output/day_02.py +++ b/2019-python/output/day_02.py @@ -1,7 +1,7 @@ from output import answer, puzzleinput from collections import defaultdict -from output.intcode_computer import execute +from output.intcode_computer import execute, parse n = 2 title = "1202 Program Alarm" @@ -9,18 +9,18 @@ title = "1202 Program Alarm" @puzzleinput(n) def parse_input(data): - return list(map(int, data.split(","))) + return parse(data) @answer(1, "[intcode-0.1.0] Value of pos 0 is {} at halt signal") def part_1(program): - state, _ = execute(program, noun=12, verb=2) + _code, state, *_unused = execute(program, noun=12, verb=2) return state[0] @answer(2, "[intcode-0.1.1] 100 * noun + verb = {} for output 19690720") def part_2(program, noun=76, verb=21): - state, _ = execute(program, noun, verb) + _code, state, *_unused = 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 index 83a7781..2718781 100644 --- a/2019-python/output/day_05.py +++ b/2019-python/output/day_05.py @@ -1,6 +1,6 @@ from output import answer, puzzleinput -from output.intcode_computer import execute +from output.intcode_computer import execute, parse n = 5 title = "Sunny with a Chance of Asteroids" @@ -8,18 +8,18 @@ title = "Sunny with a Chance of Asteroids" @puzzleinput(n) def parse_input(data): - return list(map(int, data.split(","))) + return parse(data) @answer(1, "[intcode-0.2.0] Program diagnostic code, ID 1: {}") def part_1(program): - _, stdout = execute(program, stdin=1) + _code, _state, _cursorpos, 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) + _code, _state, _cursorpos, stdout = execute(program, stdin=5) return stdout[0] diff --git a/2019-python/output/day_07.py b/2019-python/output/day_07.py new file mode 100644 index 0000000..69f8341 --- /dev/null +++ b/2019-python/output/day_07.py @@ -0,0 +1,65 @@ +from collections import defaultdict +from itertools import permutations + +from output import answer, puzzleinput +from output.intcode_computer import execute, parse + +n = 7 +title = "Amplification Circuit" + + +@puzzleinput(n) +def parse_input(data): + return parse(data) + + +@answer( + 1, + "[intcode 0.3.0] Given the phase settings [0, 3, 1, 2, 4], the highest achievable signal to the thruster is {}", +) +def part_1(program): + thruster_signals = [] + for settings in map(list, permutations(range(5))): + o = 0 + for ps in settings: + _code, _state, _n, so = execute(program, stdin=[ps, o]) + o = so.pop(0) + thruster_signals.append(o) + return max(thruster_signals) + + +@answer( + 2, + "[intcode 0.3.0] Given the phase settings [7, 8, 5, 9, 6] and creating feedback loop, the highest achievable signal to the thruster is {}", +) +def part_2(program): + thruster_signals = [] + for settings in map(list, permutations(range(5, 10))): + o = [0] + finished = set() + paused = defaultdict(tuple) + while len(finished) < 5: + for amp, ps in enumerate(settings): + if paused[amp]: + program, resume_at = paused[amp] + del paused[amp] + code, state, n, so = execute(program, stdin=o, n=resume_at) + else: + code, state, n, so = execute(program, stdin=[ps, *o]) + if code == 3: + paused[amp] = ( + list(state.values()), + n, + ) + o = so + if code == 99: + finished.add(amp) + o = so + thruster_signals.append(o[-1]) + return max(thruster_signals) + + +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 index 9473d5a..5638e94 100644 --- a/2019-python/output/intcode_computer.py +++ b/2019-python/output/intcode_computer.py @@ -1,61 +1,99 @@ from collections import defaultdict -def execute(program, noun=None, verb=None, stdin=0, debug=False): - """ - intcode computer, AoC 2019 +""" +intcode computer, AoC 2019 - Changelog - ========= +Changelog +========= - 0.2.1 - ----- +0.3.0 +----- - Patch release (day 5 part 2). +Minor release (day 7 part 1-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 +BREAKING CHANGE: execute() now returns 4 values. +- now: exit code, state at halt, instruction position at halt, and captured stdout. +- before: final state, and captured stdout - 0.2.0 - ----- +Changes: - Minor release (day 5 part 1). +- Add support for a sequence of stdins +- Add interactive param to ask for manual (interactive) input on input opcode +- Add verbose param to show more output in interactive input mode +- Will now halt with code 3 (input) when input is required, stdin is empty and interactive input mode is not enabled - - 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.2.1 +----- - 0.1.1 - ----- +Patch release (day 5 part 2). - Patch release (day 2 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 - - 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.2.0 +----- - 0.1.0 - ----- +Minor release (day 5 part 1). - Initial version (day 2 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 - - 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 - """ +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 +""" +v = "0.2.3" + + +def parse(data): + return list(map(int, data.split(","))) + + +def execute( + program, + noun=None, + verb=None, + stdin=[], + debug=False, + interactive=False, + verbose=False, + n=0, +): + if verbose: + title = f"intcode computer, version {v}" + print("".join("=" for _ in title)) + print(title) + print("".join("=" for _ in title)) state = dict(zip(range(len(program)), program)) if noun: state[1] = noun if verb: state[2] = verb - n = 0 c = 0 stdout = [] + if not isinstance(stdin, list): + stdin = [stdin] while True: instruction = state[n] @@ -83,7 +121,13 @@ def execute(program, noun=None, verb=None, stdin=0, debug=False): print(f"{n}:{opcode} | {x} * {y} to {p}") if opcode == 3: p = state[n + 1] - state[p] = stdin + if stdin: + state[p] = stdin.pop(0) + else: + if interactive: + state[p] = int(input("> ")) + else: + return 3, state, n, stdout n += 2 if debug: print(f"{n}:{opcode} | {i} to {p}") @@ -92,6 +136,8 @@ def execute(program, noun=None, verb=None, stdin=0, debug=False): x = a if modes[1] == "1" else state[a] n += 2 stdout.append(x) + if verbose: + print(x) if debug: print(f"{n}:{opcode} | {stdout}") if opcode == 5: @@ -143,4 +189,6 @@ def execute(program, noun=None, verb=None, stdin=0, debug=False): print(f"{c} instructions done, current pos: {n}") # if c == 3: # break - return state, stdout + if verbose: + title = f"intcode computer received SIGTERM" + return 99, state, n, stdout