Files
advent-of-code-2021/day-18/day-18.py
Pascal Lais f137feefe4
All checks were successful
continuous-integration/drone/push Build is passing
Ad function description
2021-12-18 15:36:15 +01:00

174 lines
4.6 KiB
Python

#!/usr/bin/env python3
from pathlib import Path
def parse(line):
"""This function parses an input line to nested tuples of length 2. It works by stripping the
outermost bracket-pair from the string and counting the 'depth' of the current brackets. After
we got back to the initial level 0 the next comma marks the index where the left and right part
are seperated. Those will be parsed by a recursive parse call each. If there is no comma on a
line, we are on the deepest level of this branch and return the value as integer. Else the left
and right return value will be returned in a tuple. The result will bea binary tree of the
snailfish number.
Alternatives for parsing would be eval() - which can be unsafe and is slow - or json.parse().
"""
if ',' not in line:
return int(line)
line = line[1:-1]
level = 0
split_idx = 0
for i, c in enumerate(line):
if '[' == c:
level += 1
elif ']' == c:
level -= 1
elif 0 == level and ',' == c:
split_idx = i
break
left = parse(line[:split_idx])
right = parse(line[split_idx + 1:])
return (left, right)
def explode_to_left(n, l):
(left, right) = n
res = True
if type(right) == int:
right = right + l
else:
res, right = explode_to_left(right, l)
if not res:
if type(left) == int:
left = left + l
res = True
else:
res, left = explode_to_left(left, l)
if not res:
res = False
return res, (left, right)
def explode_to_right(n, r):
(left, right) = n
res = True
if type(left) == int:
left = left + r
else:
res, left = explode_to_right(left, r)
if not res:
if type(right) == int:
right = right + r
res = True
else:
res, right = explode_to_right(right, r)
if not res:
res = False
return res, (left, right)
def explode(n, lvl=0):
if type(n) is int:
return False, n, None, None
(left, right) = n
if lvl == 4 and type(left) is int and type(right) is int:
return True, 0, left, right
else:
res, left, l, r = explode(left, lvl+1)
if not res:
res, right, l, r = explode(right, lvl+1)
if not res:
return False, n, None, None
else:
if l:
if type(left) is int:
left = left + l
l = None
else:
res, left = explode_to_left(left, l)
if res:
l = None
return True, (left, right), l, r
else:
if r:
if type(right) is int:
right = right + r
r = None
else:
res, right = explode_to_right(right, r)
if res:
r = None
return True, (left, right), l, r
def split(n):
if type(n) is int:
if 10 <= n:
return True, (n // 2, (n + 1) // 2)
else:
return False, n
(left, right) = n
res, left = split(left)
if not res:
res, right = split(right)
return res, (left, right)
def magnitude(n):
m = 0
(left, right) = n
if type(left) is int:
m += 3 * left
else:
m += 3 * magnitude(left)
if type(right) is int:
m += 2 * right
else:
m += 2 * magnitude(right)
return m
def part_1(input):
result = 0
n = None
for line in input:
line = line.rstrip()
if not n:
n = parse(line)
continue
n = (n, parse(line))
res = True
while res:
while res:
res, n, _, _ = explode(n)
res, n = split(n)
result = magnitude(n)
print("Part 1 result:", result)
def part_2(input):
result = 0
nbrs = set()
for line in input:
line = line.rstrip()
nbrs.add(parse(line))
for n1 in nbrs:
for n2 in nbrs:
if n1 == n2:
continue
n = (n1, n2)
res = True
while res:
while res:
res, n, _, _ = explode(n)
res, n = split(n)
result = max(result, magnitude(n))
print("Part 2 result:", result)
input = list()
p = Path(__file__).with_name('input.txt')
with open(p) as f:
input = f.readlines()
part_1(input)
part_2(input)