# 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

with open(path) as input_file:

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

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

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:
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()
```