My Advent of Code solutions in Python. kevinyap.ca/2019/12/going-fast-in-advent-of-code/
advent-of-code python
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add 2021/18

+157
+157
2021/day18.py
··· 1 + import fileinput 2 + from itertools import permutations 3 + 4 + 5 + class Snail: 6 + def __init__(self, val, depth): 7 + self.val = val 8 + self.depth = depth 9 + 10 + def __repr__(self): 11 + return 'S({} @ {})'.format(self.val, self.depth) 12 + 13 + 14 + def process_snailnum(s): 15 + """Turns a written snailfish number into a List[Snail].""" 16 + depth = -1 17 + snails = [] 18 + 19 + for c in s: 20 + if c == '[': 21 + depth += 1 22 + elif c == ']': 23 + depth -= 1 24 + elif '0' <= c <= '9': 25 + snails.append(Snail(int(c), depth)) 26 + 27 + return snails 28 + 29 + 30 + def reduce_num(snails): 31 + """ 32 + Reduces a snailfish number by iteratively going through 33 + it until no more explosions or splits can be performed. 34 + """ 35 + 36 + while True: 37 + did_explode = did_split = False 38 + i = 0 39 + 40 + while i < len(snails): 41 + s = snails[i] 42 + 43 + # Prefer explode 44 + if s.depth >= 4: 45 + # print "ex", 46 + l = snails[i] 47 + n = snails[i+1] 48 + 49 + # Process left explosion 50 + leftmost = False 51 + if i == 0: 52 + snails[i] = Snail(0, s.depth - 1) 53 + leftmost = True 54 + else: 55 + p = snails[i-1] 56 + snails[i-1] = Snail(p.val + l.val, p.depth) 57 + 58 + # Process right explosion 59 + rightmost = False 60 + if i+1 == len(snails) - 1: 61 + snails[i+1] = Snail(0, n.depth - 1) 62 + rightmost = True 63 + else: 64 + nn = snails[i+2] 65 + snails[i+2] = Snail(n.val + nn.val, nn.depth) 66 + 67 + if leftmost: 68 + del snails[1] 69 + elif rightmost: 70 + del snails[-2] 71 + else: 72 + del snails[i+1] 73 + snails[i] = Snail(0, l.depth - 1) 74 + 75 + did_explode = True 76 + break 77 + 78 + i += 1 79 + 80 + if not did_explode: 81 + i = 0 82 + while i < len(snails): 83 + s = snails[i] 84 + if s.val >= 10: 85 + a = s.val // 2 86 + b = (s.val + 1) // 2 87 + assert a + b == s.val 88 + 89 + snails = snails[:i] + [Snail(a, s.depth + 1), Snail(b, s.depth + 1)] + snails[i+1:] 90 + 91 + did_split = True 92 + break 93 + 94 + i += 1 95 + 96 + if not did_explode and not did_split: 97 + break 98 + 99 + return snails 100 + 101 + 102 + def add_snails(la, lb): 103 + """Adds two snailfish numbers together.""" 104 + res = [] 105 + 106 + for x in la: 107 + res.append(Snail(x.val, x.depth + 1)) 108 + for x in lb: 109 + res.append(Snail(x.val, x.depth + 1)) 110 + 111 + res = reduce_num(res) 112 + 113 + return res 114 + 115 + 116 + def magnitude(snails): 117 + """Returns the magnitude of a snailnum (iterative).""" 118 + while True: 119 + max_depth = 0 120 + 121 + i = 0 122 + for i in range(len(snails)): 123 + s = snails[i] 124 + max_depth = max(max_depth, s.depth) 125 + 126 + # Nothing more to reduce 127 + if max_depth == 0: 128 + break 129 + 130 + # Else, search and expand the leftmost pair 131 + expanded = False 132 + for i in range(len(snails)): 133 + s = snails[i] 134 + if s.depth == max_depth: 135 + n = snails[i+1] 136 + assert s.depth == n.depth 137 + ns = Snail(s.val * 3 + n.val * 2, s.depth - 1) 138 + 139 + snails = snails[:i] + [ns] + snails[i+2:] 140 + 141 + expanded = True 142 + break 143 + 144 + return snails[0].val * 3 + snails[1].val * 2 145 + 146 + 147 + # Parse problem input. 148 + SNAILS = [process_snailnum(l.strip()) for l in fileinput.input()] 149 + 150 + # Solve part 1. 151 + part_1_snails = reduce_num(SNAILS[0]) 152 + for s in SNAILS[1:]: 153 + part_1_snails = add_snails(part_1_snails, s) 154 + print "Part 1:", magnitude(part_1_snails) 155 + 156 + # Solve part 2. 157 + print "Part 2:", max(magnitude(add_snails(a, b)) for a, b in permutations(SNAILS, 2))