-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathday18.py
110 lines (88 loc) · 2.61 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
104
105
106
107
108
109
110
import ast
import itertools
import math
from functools import reduce
from helper.utils import *
DAY = 18
@time_function
def prepare_data():
raw_data = parse_file_rows_to_list(DAY)
return [SnailNumber(ast.literal_eval(row)) for row in raw_data]
@time_function
def part_a(data):
return reduce(SnailNumber.__add__, data).magnitude
@time_function
def part_b(data):
return max((a + b).magnitude for a, b in itertools.permutations(data, 2))
class SnailNumber:
"""
LOTS of help from various reddit threads to implement this class
"""
def __init__(self, num):
self.num = num
def __add__(self, b):
x = [self.num, b.num]
while True:
change, _, x, _ = SnailNumber.explode(x)
if change:
continue
change, x = SnailNumber.split(x)
if not change:
break
return SnailNumber(x)
def __str__(self):
return f"{self.num}"
@property
def magnitude(self):
return SnailNumber._calc_magnitude(self.num)
@staticmethod
def _calc_magnitude(num):
if isinstance(num, int):
return num
return 3 * SnailNumber._calc_magnitude(num[0]) + 2 * SnailNumber._calc_magnitude(num[1])
@staticmethod
def explode(num, n=4):
if isinstance(num, int):
return False, None, num, None
if n == 0:
return True, num[0], 0, num[1]
a, b = num
exp, left, a, right = SnailNumber.explode(a, n - 1)
if exp:
return True, left, [a, SnailNumber.add_left(b, right)], None
exp, left, b, right = SnailNumber.explode(b, n - 1)
if exp:
return True, None, [SnailNumber.add_right(a, left), b], right
return False, None, num, None
@staticmethod
def add_left(x, n):
if n is None:
return x
if isinstance(x, int):
return x + n
return [SnailNumber.add_left(x[0], n), x[1]]
@staticmethod
def add_right(x, n):
if n is None:
return x
if isinstance(x, int):
return x + n
return [x[0], SnailNumber.add_right(x[1], n)]
@staticmethod
def split(x):
if isinstance(x, int):
if x >= 10:
return True, [x // 2, math.ceil(x / 2)]
return False, x
a, b = x
change, a = SnailNumber.split(a)
if change:
return True, [a, b]
change, b = SnailNumber.split(b)
return change, [a, b]
def main():
data = prepare_data()
part_a(data)
part_b(data)
if __name__ == '__main__':
main()