< expLog

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