diff --git a/2019-python/output/__init__.py b/2019-python/output/__init__.py index 7877cc4..4483fa4 100644 --- a/2019-python/output/__init__.py +++ b/2019-python/output/__init__.py @@ -33,6 +33,7 @@ def answer(part_index, fmt_string): else: formatted = fmt_string.format(answer) print(f"[part {part_index}] {formatted}") + return answer return wrapper_aoc diff --git a/2019-python/output/day_05.py b/2019-python/output/day_05.py index 2718781..179e8c3 100644 --- a/2019-python/output/day_05.py +++ b/2019-python/output/day_05.py @@ -13,13 +13,13 @@ def parse_input(data): @answer(1, "[intcode-0.2.0] Program diagnostic code, ID 1: {}") def part_1(program): - _code, _state, _cursorpos, stdout = execute(program, stdin=1) + _code, _state, _cursorpos, rb, stdout = execute(program, stdin=[1]) return max(stdout) @answer(2, "[intcode-0.2.1] Program diagnostic code, ID 5: {}") def part_2(program): - _code, _state, _cursorpos, stdout = execute(program, stdin=5) + _code, _state, _cursorpos, rb, stdout = execute(program, stdin=[5]) return stdout[0] diff --git a/2019-python/output/day_07.py b/2019-python/output/day_07.py index b0e63df..2e90e5e 100644 --- a/2019-python/output/day_07.py +++ b/2019-python/output/day_07.py @@ -22,7 +22,7 @@ def part_1(program): for settings in map(list, permutations(range(5))): o = 0 for ps in settings: - _code, _state, _n, so = execute(program, stdin=[ps, o]) + _code, _state, _n, _rb, so = execute(program, stdin=[ps, o]) o = so.pop(0) thruster_signals.append(o) return max(thruster_signals) @@ -43,9 +43,9 @@ def part_2(program): if paused[amp]: program, resume_at = paused[amp] del paused[amp] - code, state, n, so = execute(program, stdin=o, n=resume_at) + code, state, n, _rb, so = execute(program, stdin=o, n=resume_at) else: - code, state, n, so = execute(program, stdin=[ps, *o]) + code, state, n, _rb, so = execute(program, stdin=[ps, *o]) if code == 3: paused[amp] = ( list(state.values()), diff --git a/2019-python/output/day_09.py b/2019-python/output/day_09.py index f564672..5d118dc 100644 --- a/2019-python/output/day_09.py +++ b/2019-python/output/day_09.py @@ -12,13 +12,13 @@ def parse_input(data): @answer(1, "[intcode 0.3.1] BOOST keycode: {}") def part_1(program): - _c, _s, _n, outputs = execute(program, stdin=[1]) + _c, _s, _n, _rb, outputs = execute(program, stdin=[1]) return outputs.pop(0) @answer(2, "[intcode 0.3.1] Distress signal coordinates: {}") def part_2(program): - _c, _s, _n, outputs = execute(program, stdin=[2]) + _c, _s, _n, _rb, outputs = execute(program, stdin=[2]) return outputs.pop(0) @@ -42,7 +42,7 @@ if __name__ == "__main__": 0, 99, ] - )[3] == [ + )[4] == [ 109, 1, 204, @@ -60,20 +60,20 @@ if __name__ == "__main__": 0, 99, ] - assert len(str(execute([1102, 34915192, 34915192, 7, 4, 7, 99, 0])[3][0])) == 16 - assert 1125899906842624 in execute([104, 1125899906842624, 99])[3] - assert execute([109, -1, 4, 1, 99])[3][0] == -1 - assert execute([109, -1, 104, 1, 99])[3][0] == 1 - assert execute([109, -1, 204, 1, 99])[3][0] == 109 - assert execute([109, 1, 9, 2, 204, -6, 99])[3][0] == 204 - assert execute([109, 1, 109, 9, 204, -6, 99])[3][0] == 204 - assert execute([109, 1, 209, -1, 204, -106, 99])[3][0] == 204 - assert execute([109, 1, 3, 3, 204, 2, 99], stdin=[666])[3][0] == 666 - assert execute([109, 1, 203, 2, 204, 2, 99], stdin=[666])[3][0] == 666 - assert execute([109, 6, 21001, 9, 25, 1, 104, 0, 99, 49])[3][0] == 74 + assert len(str(execute([1102, 34915192, 34915192, 7, 4, 7, 99, 0])[4][0])) == 16 + assert 1125899906842624 in execute([104, 1125899906842624, 99])[4] + assert execute([109, -1, 4, 1, 99])[4][0] == -1 + assert execute([109, -1, 104, 1, 99])[4][0] == 1 + assert execute([109, -1, 204, 1, 99])[4][0] == 109 + assert execute([109, 1, 9, 2, 204, -6, 99])[4][0] == 204 + assert execute([109, 1, 109, 9, 204, -6, 99])[4][0] == 204 + assert execute([109, 1, 209, -1, 204, -106, 99])[4][0] == 204 + assert execute([109, 1, 3, 3, 204, 2, 99], stdin=[666])[4][0] == 666 + assert execute([109, 1, 203, 2, 204, 2, 99], stdin=[666])[4][0] == 666 + assert execute([109, 6, 21001, 9, 25, 1, 104, 0, 99, 49])[4][0] == 74 parsed = parse_input() - assert execute(parsed, stdin=[1])[3][0] == 2351176124 + assert execute(parsed, stdin=[1])[4][0] == 2351176124 part_1(parsed) part_2(parsed) diff --git a/2019-python/output/day_11.py b/2019-python/output/day_11.py new file mode 100644 index 0000000..6f4d790 --- /dev/null +++ b/2019-python/output/day_11.py @@ -0,0 +1,82 @@ +from collections import defaultdict +from output.intcode_computer import execute, parse +from output import answer, puzzleinput + +n = 11 +title = "Space Police" + + +@puzzleinput(n) +def parse_input(data): + return parse(data) + + +DIRS = [ + (-1, 0), + (0, -1), + (1, 0), + (0, 1), +] + +COLORS = [".", "#"] + +CL = ["black", "white"] +DL = ["UP", "LEFT", "BOTTOM", "RIGHT"] +TL = ["RIGHT", "LEFT"] + + +@answer(1, "[intcode 0.3.2] Robot paints {} panes at least once") +def part_1(program): + path, pos, d = _paint(program) + return len(path) + + +@answer( + 2, + '[intcode 0.3.2] The hull has registration identifier "JZPJRAGJ" freshly painted, see below: \n\n{}', +) +def part_2(program): + path, pos, d = _paint(program, 1) + return _inspect(path.copy(), pos, d) + + +def _paint(program, initial=0): + pos = (0, 0) + d = 0 + path = defaultdict(int) + path[pos] = initial + n = 0 + rb = 0 + code = 0 + while True: + code, program, n, rb, outputs = execute(program, n=n, rb=rb, stdin=[path[pos]]) + if code == 99: + break + if outputs: + paint, turn_to = outputs + path[pos] = paint + d = (d - 1 if turn_to == 1 else d + 1) % 4 + pos = (pos[0] + DIRS[d][0], pos[1] + DIRS[d][1]) + return path, pos, d + + +def _inspect(path, p, d): + pk = path.keys() + startx = min(map(lambda yx: yx[1], pk)) - 1 + endx = max(map(lambda yx: yx[1], pk)) + 2 + starty = min(map(lambda yx: yx[0], pk)) - 1 + endy = max(map(lambda yx: yx[0], pk)) + 2 + + matrix = [ + [COLORS[path[(y, x)]] for x in range(startx, endx)] for y in range(starty, endy) + ] + y, x = p + matrix[abs(starty) + y][abs(startx) + x] = "^"[d] + + return "\n".join(["".join(line) for line in matrix]) + + +if __name__ == "__main__": + parsed = parse_input() + part_1(parsed) + part_2(parsed) # JZPJRAGJ diff --git a/2019-python/output/intcode_computer.py b/2019-python/output/intcode_computer.py index 95630cf..e587ef2 100644 --- a/2019-python/output/intcode_computer.py +++ b/2019-python/output/intcode_computer.py @@ -7,6 +7,15 @@ intcode computer, AoC 2019 Changelog ========= +0.3.2 +----- + +Patch release (day 9 part 1-2, day 11 part 1-2). + +- Return relative base upon input suspension +- Improve intcode debugger +- Fix errorous state restoration upon resuming upon input + 0.3.1 ----- @@ -71,7 +80,7 @@ Initial version (day 2 part 1). - 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 """ -__version__ = "0.3.1" +__version__ = "0.3.2" def parse(data): @@ -87,61 +96,42 @@ def execute( interactive=False, verbose=False, n=0, + rb=0, ): if verbose: - title = f"intcode computer, version {v}" + title = f"intcode computer, version {__version__}" print("".join("=" for _ in title)) print(title) print("".join("=" for _ in title)) state = defaultdict(int) - for k, v in zip(range(len(program)), program): - state[k] = v + if isinstance(program, list): + for k, v in zip(range(len(program)), program): + state[k] = v + else: + state = program.copy() if noun: state[1] = noun if verb: state[2] = verb - c = 0 - rb = 0 stdout = [] - if not isinstance(stdin, list): - stdin = [stdin] def halt(code): - return code, state, n, stdout - - def values(modes, *parameters): - for i, v in enumerate(parameters): - if modes[i] == "0" and v < 0: - print("================ ERROR =================") - print("Negative index provided to position mode") - if modes[i] == "2" and rb + v < 0: - print("================ ERROR =================") - print("Negative index provided to relative mode") - - def value(i, v): - if modes[i] == "1": - return v - if modes[i] == "2": - return state[v + rb] - return state[v] - - if len(parameters) > 1: - return [value(i, v) for i, v in enumerate(parameters)] - return value(0, parameters[0]) + return code, state, n, rb, stdout + if debug and n > 0: + print(f"@{str(n).zfill(4)} [resuming program, stdin={stdin}]") while True: instruction = state[n] - # if instruction > 200 and instruction < 1000: - # print("") - # spn = 2 if instruction % 100 % 3 == 0 else 4 - # print(list(state.values())[n : n + spn]) opcode = instruction % 100 modes = str(instruction // 100).zfill(3)[::-1] + if opcode not in (1, 2, 3, 4, 5, 6, 7, 8, 9, 99): + print("opcode={opcode} not implemented, halting") + return halt(-2) if opcode == 1: a = state[n + 1] b = state[n + 2] c = n + 3 - x, y = values(modes, a, b) + x, y = _values(state, modes, rb, a, b) p = state[c] if modes[2] == "2": p += rb @@ -153,7 +143,7 @@ def execute( a = state[n + 1] b = state[n + 2] c = n + 3 - x, y = values(modes, a, b) + x, y = _values(state, modes, rb, a, b) p = state[c] if modes[2] == "2": p += rb @@ -174,15 +164,17 @@ def execute( state[p] = stdin.pop(0) else: if interactive: - state[p] = int(input("> ")) + manual = int(input("> ")) + state[p] = manual + print(f"set STDIN to {manual} at pos {p}") else: if debug: - print(f"@{str(n).zfill(4)} [suspended, awaiting input]") + print(f"@{str(n).zfill(4)} [suspended, awaiting input ...]") return halt(3) n += 2 if opcode == 4: a = state[n + 1] - x = values(modes, a) + x = _values(state, modes, rb, a) stdout.append(x) if verbose: print(x) @@ -192,7 +184,7 @@ def execute( if opcode == 5: a = state[n + 1] b = state[n + 2] - x, y = values(modes, a, b) + x, y = _values(state, modes, rb, a, b) if x != 0: if debug: print(f"@{str(n).zfill(4)} {opcode}_JMP-IF-1 | {x} != 0, n={y}") @@ -204,18 +196,20 @@ def execute( if opcode == 6: a = state[n + 1] b = state[n + 2] - x, y = values(modes, a, b) + x, y = _values(state, modes, rb, a, b) if x == 0: + if debug: + print(f"@{str(n).zfill(4)} {opcode}_JMP-IF-0 | {x} == 0, n={y}") n = y else: + if debug: + print(f"@{str(n).zfill(4)} {opcode}_JMP-IF-0 | {x} == 0, ignoring") n += 3 - if debug: - print(f"{n}:{opcode} | {n}") if opcode == 7: a = state[n + 1] b = state[n + 2] c = n + 3 - x, y = values(modes, a, b) + x, y = _values(state, modes, rb, a, b) p = state[c] if modes[2] == "2": p += rb @@ -227,7 +221,7 @@ def execute( a = state[n + 1] b = state[n + 2] c = n + 3 - x, y = values(modes, a, b) + x, y = _values(state, modes, rb, a, b) p = state[c] if modes[2] == "2": p += rb @@ -237,18 +231,35 @@ def execute( n += 4 if opcode == 9: a = state[n + 1] - x = values(modes, a) + x = _values(state, modes, rb, a) if debug: print(f"@{str(n).zfill(4)} {opcode}_RELBASE | {rb} + {x}") rb += x n += 2 if opcode == 99: - break - c += 1 - if debug and c % 1000 == 0: - print(f"{c} instructions done, current pos: {n}") - if c == 33: - break - if verbose: - title = f"intcode computer received SIGTERM" - return halt(99) + if debug: + print(f"@{str(n).zfill(4)} {opcode}_HALT | n={n}") + return halt(99) + return halt(-1) + + +def _values(state, modes, rb, *parameters): + # for i, v in enumerate(parameters): + # if modes[i] == "0" and v < 0: + # print("================ ERROR =================") + # print("Negative index provided to position mode") + # if modes[i] == "2" and rb + v < 0: + # print("================ ERROR =================") + # print("Negative index provided to relative mode") + + if len(parameters) > 1: + return [_value(state, modes, rb, k, v) for k, v in enumerate(parameters)] + return _value(state, modes, rb, 0, parameters[0]) + + +def _value(state, modes, rb, m, s): + if modes[m] == "1": + return s + if modes[m] == "2": + return state[s + rb] + return state[s]