Advent of Code Day 7

In [14]:
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
In [64]:
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_values, **kwargs):
    assert program[i] % 100 == 3
    out, = params(program, i, 1)
    program[out] = input_values.pop(0)
    # print(f"> 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

operations = { 1: add, 2: mul, 3: inp, 4: out, 5: jit, 6: jif, 7: lt, 8: eq }
In [146]:
def compute(program, input_values, 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}")
            yield None
        
        if debug:
            print(f">> {i}, {list(enumerate(program))[max(0, i-15):i+15]}")
            
        i, output_value = operations[op % 100](program, i, input_values=input_values)
        
        if output_value is not None:
            yield output_value
        
    raise Exception("Didn't terminate properly")
    
def process(listing):
    return list(map(int, listing.split(",")))
In [147]:
testinput="3,15,3,16,1002,16,10,16,1,16,15,15,4,15,99,0,0"
In [148]:
def check_phases(program):
    max_set = None
    
    for permutation in permutations(list(range(5))):
        inp = 0
        for p in permutation:
            computation = compute(process(program), [p, inp])
            inp = next(computation)
            
        if not max_set or max_set[0] < inp:
            max_set = (inp, permutation)
    
    return max_set
        
check_phases(testinput)
Out[148]:
(43210, [4, 3, 2, 1, 0])
In [149]:
def permutations(x):
    if len(x) == 1:
        yield x
    else:
        cur = x[0]
        rest = x[1:]
        
        for inner in permutations(rest):
            for posn in range(len(inner) + 1):
                option = inner[:]
                option.insert(posn, cur)
                yield option
In [150]:
check_phases("3,23,3,24,1002,24,10,24,1002,23,-1,23,101,5,23,23,1,24,23,23,4,23,99,0,0")
Out[150]:
(54321, [0, 1, 2, 3, 4])
In [151]:
with open("AoC/input7.txt") as inputfile:
    print( check_phases(inputfile.read().strip()))
(262086, [2, 1, 4, 0, 3])
In [152]:
test_input2 = "3,26,1001,26,-4,26,3,27,1002,27,2,27,1,27,26,27,4,27,1001,28,-1,28,1005,28,6,99,0,0,5"
In [153]:
def check_phases2(permutation, program):
    inputs = [[p] for p in permutation]
    inputs[0].append(0)
    amplifiers = [compute(process(program), inp) for inp in inputs]
    last_outputs = [None] * len(permutation)
    
    i = 0
    while True:
        out = next(amplifiers[i])
        if out is None: 
            break
        elif out != "blocked":
            inputs[(i + 1) % 5].append(out)
            last_outputs[i] = out
        
        i = (i + 1) % 5
        
    return last_outputs

def find_max(program):
    max_set = None
    
    for permutation in permutations(list(range(5, 10))):
        result = check_phases2(permutation, program)
        if not max_set or result[4] > max_set[0]:
            max_set = (result[4], permutation)
    
    return max_set
    
find_max(test_input2)
Out[153]:
(139629729, [9, 8, 7, 6, 5])
In [154]:
list(range(9, 4, -1))
Out[154]:
[9, 8, 7, 6, 5]
In [155]:
with open("AoC/input7.txt") as inputfile:
    print( find_max(inputfile.read().strip()))
(5371621, [5, 7, 6, 8, 9])

Rough solution again: received my highest rank till date - ranked 338 on the second star. Python's coroutines make this significantly easier to reason about.