diff --git a/2019-python/aoc.py b/2019-python/aoc.py index 9de0c9e..d266e50 100644 --- a/2019-python/aoc.py +++ b/2019-python/aoc.py @@ -76,6 +76,7 @@ https://adventofcode.com/{year}/day/{day_no}/input from output import headline +stars = 0 for i in [str(n).zfill(2) for n in range(1, 26)]: try: day = __import__( @@ -88,8 +89,13 @@ for i in [str(n).zfill(2) for n in range(1, 26)]: headline(day.n, day.title) data = day.parse_input() day.part_1(data, decorate=True) + stars += 1 day.part_2(data, decorate=True) + stars += 1 except IOError: pass except ImportError: pass +print(f"\nStars: {stars}") +print("".join("*" if n <= stars else "•" for n in range(50))) +print("") diff --git a/2019-python/output/day_09.py b/2019-python/output/day_09.py new file mode 100644 index 0000000..f564672 --- /dev/null +++ b/2019-python/output/day_09.py @@ -0,0 +1,79 @@ +from output import answer, puzzleinput +from output.intcode_computer import execute, parse + +n = 9 +title = "Sensor Boost" + + +@puzzleinput(n) +def parse_input(data): + return parse(data) + + +@answer(1, "[intcode 0.3.1] BOOST keycode: {}") +def part_1(program): + _c, _s, _n, 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]) + return outputs.pop(0) + + +if __name__ == "__main__": + assert execute( + [ + 109, + 1, + 204, + -1, + 1001, + 100, + 1, + 100, + 1008, + 100, + 16, + 101, + 1006, + 101, + 0, + 99, + ] + )[3] == [ + 109, + 1, + 204, + -1, + 1001, + 100, + 1, + 100, + 1008, + 100, + 16, + 101, + 1006, + 101, + 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 + + parsed = parse_input() + assert execute(parsed, stdin=[1])[3][0] == 2351176124 + + part_1(parsed) + part_2(parsed) diff --git a/2019-python/output/intcode_computer.py b/2019-python/output/intcode_computer.py index 5638e94..95630cf 100644 --- a/2019-python/output/intcode_computer.py +++ b/2019-python/output/intcode_computer.py @@ -7,13 +7,21 @@ intcode computer, AoC 2019 Changelog ========= +0.3.1 +----- + +Patch release (day 7 part 1-2). + +- Supports relative parameter mode + 0.3.0 ----- Minor release (day 7 part 1-2). BREAKING CHANGE: execute() now returns 4 values. -- now: exit code, state at halt, instruction position at halt, and captured stdout. + +- now: exit code, state at halt, instruction position at halt, and captured stdout - before: final state, and captured stdout Changes: @@ -63,7 +71,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 """ -v = "0.2.3" +__version__ = "0.3.1" def parse(data): @@ -85,77 +93,118 @@ def execute( print("".join("=" for _ in title)) print(title) print("".join("=" for _ in title)) - state = dict(zip(range(len(program)), program)) + state = defaultdict(int) + for k, v in zip(range(len(program)), program): + state[k] = v 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]) + 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 % 100).zfill(2) + modes = str(instruction // 100).zfill(3)[::-1] 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] + c = n + 3 + x, y = values(modes, a, b) + p = state[c] + if modes[2] == "2": + p += rb + if debug: + print(f"@{str(n).zfill(4)} {opcode}_ADDITION | {x} + {y} to {p})") 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] + c = n + 3 + x, y = values(modes, a, b) + p = state[c] + if modes[2] == "2": + p += rb + if debug: + print(f"@{str(n).zfill(4)} {opcode}_MULTIPLY | {x} * {y} to {p}") state[p] = x * y n += 4 - if debug: - print(f"{n}:{opcode} | {x} * {y} to {p}") if opcode == 3: - p = state[n + 1] + a = n + 1 + p = state[a] + if modes[0] == "2": + p += rb + if debug: + print( + f"@{str(n).zfill(4)} {opcode}_INPUT | target={p}, queued={stdin}, interactive={interactive}" + ) if stdin: state[p] = stdin.pop(0) else: if interactive: state[p] = int(input("> ")) else: - return 3, state, n, stdout + if debug: + print(f"@{str(n).zfill(4)} [suspended, awaiting input]") + return halt(3) 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 + x = values(modes, a) stdout.append(x) if verbose: print(x) if debug: - print(f"{n}:{opcode} | {stdout}") + print(f"@{str(n).zfill(4)} {opcode}_OUTPUT | echo {x}") + n += 2 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] + x, y = values(modes, a, b) if x != 0: + if debug: + print(f"@{str(n).zfill(4)} {opcode}_JMP-IF-1 | {x} != 0, n={y}") n = y else: + if debug: + print(f"@{str(n).zfill(4)} {opcode}_JMP-IF-1 | {x} != 0, ignoring") 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] + x, y = values(modes, a, b) if x == 0: n = y else: @@ -165,30 +214,41 @@ def execute( 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] + c = n + 3 + x, y = values(modes, a, b) + p = state[c] + if modes[2] == "2": + p += rb + if debug: + print(f"@{str(n).zfill(4)} {opcode}_LESSTHAN | {x} < {y} to {p}") 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] + c = n + 3 + x, y = values(modes, a, b) + p = state[c] + if modes[2] == "2": + p += rb + if debug: + print(f"@{str(n).zfill(4)} {opcode}_EQUALS | {x} == {y} to {p}") state[p] = int(x == y) n += 4 + if opcode == 9: + a = state[n + 1] + x = values(modes, a) if debug: - print(f"{n}:{opcode} | {x}") + 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 == 3: - # break + if c == 33: + break if verbose: title = f"intcode computer received SIGTERM" - return 99, state, n, stdout + return halt(99)