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
|
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, depth=0):
def try_add(n, val, access, primary, secondary):
if val == 0: return 0
content = access(n)
if not is_pair(content):
access(n, content + val)
return 0
val = try_add(content, val, primary, primary, secondary)
val = try_add(content, val, secondary, primary, secondary)
return val
if not is_pair(n): return None
if depth == 4: return n
car_val = try_explode(n.car, depth + 1)
if car_val is not None:
if depth == 3: n.car = 0
return Pair(car_val.car, try_add(n, car_val.cdr, cdr, car, cdr))
else:
cdr_val = try_explode(n.cdr, depth + 1)
if cdr_val is not None:
if depth == 3: n.cdr = 0
return Pair(try_add(n, cdr_val.car, car, cdr, car), cdr_val.cdr)
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)))
|