aboutsummaryrefslogtreecommitdiffstats
path: root/src/16/solve.py
blob: f9a11b6a681b82c5d0d1b8698252edde8797dfc8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
import sys
sys.path.append("../common")
import aoc

def parse_log(ls):
    data = list()
    before = [int(v) for v in ls[0].split("[")[1].split("]")[0].split(",")]
    ins = [int(v) for v in ls[1].split(" ")]
    after = [int(v) for v in ls[2].split("[")[1].split("]")[0].split(",")]
    return (before, ins, after)

inslog = []
data = aoc.data.split("\n")
progsec = None
for i in range(0, len(data), 4):
    if data[i] == "\n":
        progsec = i
        break
    inslog.append(parse_log(data[i:i+4]))

register = list()

opmap = dict()
opmap["addr"] = lambda a, b : register[a] + register[b]
opmap["addi"] = lambda a, b : register[a] + b
opmap["mulr"] = lambda a, b : register[a] * register[b]
opmap["muli"] = lambda a, b : register[a] * b
opmap["banr"] = lambda a, b : register[a] & register[b]
opmap["bani"] = lambda a, b : register[a] & b
opmap["borr"] = lambda a, b : register[a] | register[b]
opmap["bori"] = lambda a, b : register[a] | b
opmap["setr"] = lambda a, b : register[a]
opmap["seti"] = lambda a, b : a
opmap["gtir"] = lambda a, b : 1 * (a > register[b])
opmap["gtri"] = lambda a, b : 1 * (register[a] > b)
opmap["gtrr"] = lambda a, b : 1 * (register[a] > register[b])
opmap["eqir"] = lambda a, b : 1 * (a == register[b])
opmap["eqri"] = lambda a, b : 1 * (register[a] == b)
opmap["eqrr"] = lambda a, b : 1 * (register[a] == register[b])

def get_possible(ins):
    global register
    sregister = register[:]
    before = ins[0]
    after = ins[2]
    register = before
    a = ins[1][1]
    b = ins[1][2]
    c = ins[1][3]
    ops = list(opmap.values())
    possibles = list()
    for i in range(len(ops)):
        op = ops[i]
        res = None
        try:
            res = op(a, b)
        except:
            continue
        if res == after[c]:
            possibles.append(i)
    register = sregister
    return possibles

def solve1(args):
    global register
    uncertain = 0
    for ins in inslog:
        if len(get_possible(ins)) >= 3:
            uncertain += 1
    return uncertain

def solve2(args):
    possible = dict()
    for ins in inslog:
        o = ins[1][0]
        if o in possible:
            possible[o] = [op for op in get_possible(ins) if op in possible[o]]
        else:
            possible[o] = get_possible(ins)

    certain = False
    while not certain:
        singles = [p[0] for p in possible.values() if len(p) == 1]
        for p in possible:
            if len(possible[p]) != 1:
                possible[p] = [v for v in possible[p] if v not in singles]

        certain = True
        for p in possible.values():
            if len(p) != 1:
                certain = False
                break

    ntrans = dict()
    for p in possible: # flatten
        ntrans[p] = possible[p][0]

    for i in range(progsec, len(data)): # execute program
        l = data[i]
        if l == "\n": continue
        cmd = [int(v) for v in l.split(" ")]
        while len(register)-1 < cmd[3]:
            register.append(0)

        register[cmd[3]] = list(opmap.values())[ntrans[cmd[0]]](cmd[1], cmd[2])

    return register[0]

aoc.run(solve1, solve2, sols=[531, 649])