Advent of Code Day 25
Given how interactive my solution ended up being, I finally abandoned notebooks and decided to just go with a direct python script (which was also somewhat more fun after 24 days of notebooks).
from collections import deque from itertools import combinations debug = False def readfile(path): with open(path) as input_file: return input_file.read().rstrip('\n') def params(program, i, count): modes = str(program[i] // 100)[::-1] result = [] for x in range(count - 1): arg = program[i + 1 + x] if x >= len(modes) or modes[x] == "0": result.append(program[arg]) elif modes[x] == "2": result.append(program[program.offset + arg]) else: result.append(arg) output = program[i + count] if len(modes) >= count and modes[count - 1] == "2": output += program.offset result.append(output) # output return result def add(program, i, **kwargs): assert program[i] % 100 == 1 a, b, out = params(program, i, 3) program[out] = a + b return i + 4, None def mul(program, i, **kwargs): assert program[i] % 100 == 2 a, b, out = params(program, i, 3) program[out] = a * b return i + 4, None def inp(program, i, input_value, **kwargs): assert program[i] % 100 == 3 out, = params(program, i, 1) program[out] = input_value # print(f"> {program[i]} program[{out}] = {program[out]}") return i + 2, None def out(program, i, **kwargs): assert program[i] % 100 == 4 val, _ = params(program, i, 2) # print(f"> {val}") return i + 2, val def jit(program, i, **kwargs): assert program[i] % 100 == 5 test, ip, _ = params(program, i, 3) if test != 0: return ip, None return i + 3, None def jif(program, i, **kwargs): assert program[i] % 100 == 6 test, ip, _ = params(program, i, 3) if test == 0: return ip, None return i + 3, None def lt(program, i, **kwargs): assert program[i] % 100 == 7 a, b, out = params(program, i, 3) program[out] = int(a < b) return i + 4, None def eq(program, i, **kwargs): assert program[i] % 100 == 8 a, b, out = params(program, i, 3) program[out] = int(a == b) return i + 4, None def rbo(program, i, **kwargs): assert program[i] % 100 == 9 delta, _ = params(program, i, 2) program.offset += delta # print (f"> rbo = {program.offset}" ) return i + 2, None operations = { 1: add, 2: mul, 3: inp, 4: out, 5: jit, 6: jif, 7: lt, 8: eq, 9: rbo } class Program(list): def __init__(self, *args, **kwargs): super(Program, self).__init__(args[0]) self.offset = 0 def compute(program): program = Program(program) l = len(program) i = 0 output_value = None last_output = None while i < l: op = program[i] if op == 99: if debug: print(f">> exit {output_value}") return if debug: print(f">> {i}, {list(enumerate(program))[i:i+5]}") if op % 100 == 3: input_value = yield else: input_value = None i, output_value = operations[op % 100](program, i, input_value=input_value) if output_value is not None: yield output_value raise Exception("Didn't terminate properly") def process(listing): lst = list(map(int, listing.split(","))) lst.extend([0] * 100000) return lst def output(bot, endcommand = False): r = "" try: if endcommand: r += chr(bot.send(ord("\n"))) while (o := next(bot)) is not None: r += chr(o) except StopIteration: print(f"Finished: `{r}`") raise return r def send(bot, msg): for ch in msg: assert (r := bot.send(ord(ch))) is None, f"""Received unexpected output {r})""" def checkpoint(bot, d="west"): print(f"> inv") send(bot, "inv") data = output(bot, True) print(data) items = [x.strip("-").strip() for x in data.strip().split("\n")[1:-2]] total_items = len(items) l = total_items cur = combinations(items, l) current_items = items[:] while l > 0: for item in current_items: print(f"> drop {item}") send(bot, f"drop {item}") print(output(bot, True)) combination = [] try: combination = next(cur) except StopIteration: l = l - 1 cur = combinations(items, l) continue print(f">> {combination}") for item in combination: print(f"> take {item}") send(bot, f"take {item}") print(output(bot, True)) current_items = combination print(f"> {d}") send(bot, d) data = output(bot, True) print(data) if data.index("ejected back to the checkpoint") == -1: return def explore(): global debug input25 = readfile("AoC/input25.txt") bot = compute(process(input25)) first_run = True commands = [] playback = deque() while True: data = output(bot, not first_run) first_run = False print(data) if playback: command = playback.popleft() print(f"Playback: {command}") commands.append(command) send(bot, command) continue while True: command = input() if command == "exit": return elif command.startswith("checkpoint"): checkpoint(bot, command.split(" ")[1]) elif command.startswith("playback"): with open(command.split(" ")[1], "r") as input_file: for cmd in input_file.readlines(): playback.append(cmd.strip()) cmd = playback.popleft() print(f"Playback: {cmd}") commands.append(cmd) send(bot, cmd) break elif command.startswith("record"): with open(command.split(" ")[1], "w") as output_file: output_file.write("\n".join(commands)) elif command == "debug on": debug = True elif command == "debug off": debug = False else: commands.append(command) send(bot, command) break print(output(bot, True)) explore()