-
Notifications
You must be signed in to change notification settings - Fork 0
/
day18.py
103 lines (76 loc) · 2.51 KB
/
day18.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
"""
--- Day 18: Snailfish ---
https://adventofcode.com/2021/day/18
"""
import ast
import copy
from functools import reduce
import aocd
class Number:
__slots__ = "num", "left", "right"
def __init__(self, num, left=None, right=None):
assert num is not None or (left and right)
self.num = num
self.left = left
self.right = right
def __str__(self):
if self.is_regular():
return str(self.num)
return "[" + str(self.left) + "," + str(self.right) + "]"
def is_regular(self):
return self.num is not None
def number(a) -> Number:
if isinstance(a, int):
return Number(a)
return Number(None, number(a[0]), number(a[1]))
numbers = [number(ast.literal_eval(line)) for line in aocd.data.splitlines()]
def add_left(n: Number, v: int):
if not n:
return
if n.is_regular():
n.num += v
else:
add_left(n.left, v)
def add_right(n: Number, v: int):
if not n:
return
if n.is_regular():
n.num += v
else:
add_right(n.right, v)
def explode(n: Number, depth, left, right):
if n.is_regular():
return False
# explode the first pair that is nested inside four pairs
if depth == 4 and n.left.is_regular() and n.right.is_regular():
add_right(left, n.left.num) # add to the first regular number to the left
add_left(right, n.right.num) # add to the first regular number to the right
# replace n with the regular number 0
n.num = 0
n.left = n.right = None
return True
return explode(n.left, depth + 1, left, n.right) or explode(n.right, depth + 1, n.left, right)
def split(n: Number):
if not n.is_regular():
return split(n.left) or split(n.right)
# split the first regular number that is 10 or greater
if n.num >= 10:
n.left = Number(n.num // 2)
n.right = Number((n.num + 1) // 2)
n.num = None
return True
return False
def add(a: Number, b: Number) -> Number:
result = Number(None, copy.deepcopy(a), copy.deepcopy(b))
# reduce the result
while explode(result, 0, None, None) or split(result):
pass
return result
def magnitude(n: Number) -> int:
if n.is_regular():
return n.num
return 3 * magnitude(n.left) + 2 * magnitude(n.right)
s = reduce(add, numbers)
print(f"The sum of the {len(numbers)} numbers is: {s}")
print("Part One", magnitude(s))
print("Part Two", max(magnitude(add(n1, n2)) for n1 in numbers for n2 in numbers if n1 != n2))