Advent of Code Day 5

In [164]:
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])
        else:
            result.append(arg)
            
    result.append(program[i + count]) # output
    
    return result
        
assert params([1101, 0, 1], 0, 2) == [0, 1]
assert params([1, 0, 1], 0, 2) == [1, 1]
assert params([101, 0, 1], 0, 2) == [0, 1]
In [240]:
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[{out}] = {input_value}")
    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

operations = { 1: add, 2: mul, 3: inp, 4: out, 5: jit, 6: jif, 7: lt, 8: eq }
In [249]:
def compute(program, input_value, debug=False):
    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 last_output, program
        
        if debug:
            print(f">> {i}, {list(enumerate(program))[max(0, i-15):i+15]}")
        i, output_value = operations[op % 100](program, i, input_value=input_value)
        
        if output_value:
            last_output = output_value
        
    raise Exception("Didn't terminate properly")
In [250]:
listing = None
with open("AoC/input5.txt") as input_file:
    listing = input_file.read()
In [251]:
def process(listing):
    return list(map(int, listing.split(",")))
In [252]:
compute(process("3,0,4,0,99"), 1)
> program[0] = 1
> 1
Out[252]:
(1, [1, 0, 4, 0, 99])
In [253]:
compute(process("3,9,8,9,10,9,4,9,99,-1,8"), 8)
compute(process("3,9,8,9,10,9,4,9,99,-1,8"), 3)
compute(process("3,9,7,9,10,9,4,9,99,-1,8"), 5)
compute(process("3,9,7,9,10,9,4,9,99,-1,8"), 9)
compute(process("3,3,1108,-1,8,3,4,3,99"), 8)
compute(process("3,3,1108,-1,8,3,4,3,99"), 100)
compute(process("3,12,6,12,15,1,13,14,13,4,13,99,-1,0,1,9"), -100)
compute(process("3,12,6,12,15,1,13,14,13,4,13,99,-1,0,1,9"), 0)
> program[9] = 8
> 1
> program[9] = 3
> 0
> program[9] = 5
> 1
> program[9] = 9
> 0
> program[3] = 8
> 1
> program[3] = 100
> 0
> program[12] = -100
> 1
> program[12] = 0
> 0
Out[253]:
(None, [3, 12, 6, 12, 15, 1, 13, 14, 13, 4, 13, 99, 0, 0, 1, 9])
In [258]:
compute(process("3,21,1008,21,8,20,1005,20,22,107,8,21,20,1006,20,31,1106,0,36,98,0,0,1002,21,125,20,4,20,1105,1,46,104,999,1105,1,46,1101,1000,1,20,4,20,1105,1,46,98,99"), 7)[0]
> program[21] = 7
> 999
Out[258]:
999
In [255]:
compute(process(listing), 1)[0]
> program[225] = 1
> 0
> 0
> 0
> 0
> 0
> 0
> 0
> 0
> 0
> 13294380
Out[255]:
13294380
In [257]:
compute(process(listing), 5)[0]
> program[225] = 5
> 11460760
Out[257]:
11460760

This one took me a ridiculous amount of time to complete: around halfway I gave up on hacked together code and decided to clean it up to handle opcodes more neatly and to make debugging slightly easier. I'm definitely struggling a bit against the notebook at this point, particularly around refactoring code across cells simply and quickly.

Having cleaner unit tests is also hard; I almost wish I could split cells vertically too and attach a unit test to the right half of the cell.