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
|
from __future__ import annotations
from copy import deepcopy
from dataclasses import dataclass
from functools import reduce
from itertools import combinations
from math import floor, ceil
from utils import open_day
@dataclass
class Pair:
car: int | Pair
cdr: int | Pair
def __repr__(self) -> str:
return f'[{self.car},{self.cdr}]'
def car(p, assign=None):
if assign is not None:
p.car = assign
return p.car
def cdr(p, assign=None):
if assign is not None:
p.cdr = assign
return p.cdr
def is_pair(p): return isinstance(p, Pair)
def parse(s): return eval(s.replace('[', 'Pair(').replace(']', ')'))
with open_day(18) as f:
homework = [parse(l) for l in f]
def try_explode(n):
def try_add(n, val, primary, secondary):
if val == 0: return 0
if is_pair(primary(n)):
val = try_add(primary(n), val, primary, secondary)
else:
primary(n, primary(n) + val)
return 0
if val != 0:
if is_pair(secondary(n)):
val = try_add(secondary(n), val, primary, secondary)
else:
secondary(n, secondary(n) + val)
return 0
return val
def descend(n, depth=0):
if not is_pair(n): return None
if depth == 4: return n
car_val = descend(n.car, depth + 1)
if car_val is not None:
if depth == 3: n.car = 0
if not is_pair(n.cdr):
n.cdr += car_val.cdr
car_val.cdr = 0
return Pair(car_val.car, try_add(n.cdr, car_val.cdr, car, cdr))
else:
cdr_val = descend(n.cdr, depth + 1)
if cdr_val is not None:
if depth == 3: n.cdr = 0
if not is_pair(n.car):
n.car += cdr_val.car
cdr_val.car = 0
return Pair(try_add(n.car, cdr_val.car, cdr, car), cdr_val.cdr)
return descend(n)
tests = [
('[[[[[9,8],1],2],3],4]', '[[[[0,9],2],3],4]'),
('[7,[6,[5,[4,[3,2]]]]]', '[7,[6,[5,[7,0]]]]'),
('[[6,[5,[4,[3,2]]]],1]', '[[6,[5,[7,0]]],3]'),
('[[3,[2,[1,[7,3]]]],[6,[5,[4,[3,2]]]]]', '[[3,[2,[8,0]]],[9,[5,[4,[3,2]]]]]'),
('[[3,[2,[8,0]]],[9,[5,[4,[3,2]]]]]', '[[3,[2,[8,0]]],[9,[5,[7,0]]]]'),
]
for test, expect in tests:
test = parse(test)
try_explode(test)
assert(test == parse(expect))
def try_split(n, access=lambda n: n):
content = access(n)
if is_pair(content):
return try_split(content, car) or try_split(content, cdr)
if content < 10: return False
content /= 2
access(n, Pair(floor(content), ceil(content)))
return True
def reduce_num(n):
n = deepcopy(n)
while True:
if try_explode(n): continue
if try_split(n): continue
break
return n
def magnitude(n):
if is_pair(n):
return 3 * magnitude(n.car) + 2 * magnitude(n.cdr)
return n
print(magnitude(reduce(lambda a, b: reduce_num(Pair(a, b)), homework)))
print(max(max(magnitude(reduce_num(Pair(a, b))), magnitude(reduce_num(Pair(b, a)))) for a, b in combinations(homework, 2)))
|