summaryrefslogtreecommitdiffstats
path: root/18.py
blob: ddba81d2723ef4af858c71532b216e52376c59b5 (plain)
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
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
    for access in (car, cdr):
        val = try_explode(access(n), depth + 1)
        if val is None: continue
        if depth == 3: access(n, 0)
        if access == car:
            return Pair(val.car, try_add(n, val.cdr, cdr, car, cdr))
        else:
            return Pair(try_add(n, val.car, car, cdr, car), val.cdr)

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)))