-
Notifications
You must be signed in to change notification settings - Fork 0
/
day21.py
161 lines (137 loc) · 4.12 KB
/
day21.py
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# vi: set shiftwidth=4 tabstop=4 expandtab:
import datetime
import os
import operator
import collections
top_dir = os.path.dirname(os.path.abspath(__file__)) + "/../../"
operations = {
"*": operator.mul,
"+": operator.add,
"-": operator.sub,
"/": operator.floordiv,
}
inverses = {
# A + B = x is also A = x - B or B = x - A
operator.add: lambda left, right, expr: (
(expr, operator.sub, right),
(expr, operator.sub, left),
),
# A // B = x is also A = x * B or B = A // x
operator.floordiv: lambda left, right, expr: (
(expr, operator.mul, right),
(left, operator.floordiv, expr),
),
# A - B = x is also A = x + B or B = A - x
operator.sub: lambda left, right, expr: (
(expr, operator.add, right),
(left, operator.sub, expr),
),
# A * B = x is also A = x // B or B = x // A
operator.mul: lambda left, right, expr: (
(expr, operator.floordiv, right),
(expr, operator.floordiv, left),
),
}
def get_monkey_from_line(string):
sep = ": "
name, mid, expr = string.partition(sep)
assert mid == sep
expr = expr.split(" ")
expr = int(expr[0]) if len(expr) == 1 else (expr[0], operations[expr[1]], expr[2])
return name, expr
def get_monkeys_from_lines(string):
return [get_monkey_from_line(l) for l in string.splitlines()]
def get_monkeys_from_file(file_path=top_dir + "resources/year2022_day21_input.txt"):
with open(file_path) as f:
return get_monkeys_from_lines(f.read())
def eval_expr(expr, values):
if isinstance(expr, int):
return expr
left, func, right = expr
if not isinstance(left, int):
left = values.get(left)
if left is None:
return None
if not isinstance(right, int):
right = values.get(right)
if right is None:
return None
return func(left, right)
def get_values(monkey_lst):
monkey_dict = {}
deps = collections.defaultdict(set)
for name, expr in monkey_lst:
monkey_dict.setdefault(name, []).append(expr)
if not isinstance(expr, int):
left, _, right = expr
deps[left].add(name)
deps[right].add(name)
values = {}
queue = collections.deque(monkey_dict.keys())
while queue:
name = queue.popleft()
if name in values:
continue
for expr in monkey_dict[name]:
expr_value = eval_expr(expr, values)
if expr_value is not None:
values[name] = expr_value
queue.extend(deps[name])
break
return values
def get_root_value(monkeys):
return get_values(monkeys)["root"]
def get_hmn_value(monkeys):
lst = []
# Tweak content of list to convey all the relationships between values
for name, expr in monkeys:
if name == "humn":
continue
if isinstance(expr, int):
lst.append((name, expr))
else:
left, func, right = expr
# Express the relation in different ways
if name == "root":
expr = 0
new_left, new_right = (right, operator.add, name), (
left,
operator.add,
name,
)
else:
new_left, new_right = inverses[func](left, right, name)
lst.append((left, new_left))
lst.append((right, new_right))
lst.append((name, expr))
return get_values(lst)["humn"]
def run_tests():
monkeys = get_monkeys_from_lines(
"""root: pppw + sjmn
dbpl: 5
cczh: sllz + lgvd
zczc: 2
ptdq: humn - dvpt
dvpt: 3
lfqf: 4
humn: 5
ljgn: 2
sjmn: drzm * dbpl
sllz: 4
pppw: cczh / lfqf
lgvd: ljgn * ptdq
drzm: hmdt - zczc
hmdt: 32"""
)
assert get_root_value(monkeys) == 152
assert get_hmn_value(monkeys) == 301
def get_solutions():
monkeys = get_monkeys_from_file()
print(get_root_value(monkeys) == 353837700405464)
print(get_hmn_value(monkeys) == 3678125408017)
if __name__ == "__main__":
begin = datetime.datetime.now()
run_tests()
get_solutions()
end = datetime.datetime.now()
print(end - begin)