113 lines
3.0 KiB
Python
113 lines
3.0 KiB
Python
|
|
def add(a, b):
|
|
return [a, b]
|
|
|
|
DIGITS = '0123456789'
|
|
|
|
def try_explode(num):
|
|
num_str = str(num).replace(' ', '')
|
|
depth = 0
|
|
for idx, c in enumerate(num_str):
|
|
if c == '[':
|
|
depth += 1
|
|
if depth == 5:
|
|
# Explode!
|
|
end_idx = num_str.find(']', idx)
|
|
a, b = list(map(int, num_str[idx+1:end_idx].split(',')))
|
|
left = num_str[:idx]
|
|
for lidx in range(len(left)-1, -1, -1):
|
|
c = left[lidx]
|
|
if c in DIGITS:
|
|
term = max(i for i in range(lidx)
|
|
if left[i] not in DIGITS)
|
|
val = int(left[term+1:lidx+1])
|
|
val += a
|
|
left = left[:term+1] + str(val) + left[lidx+1:]
|
|
break
|
|
mid = '0'
|
|
right = num_str[end_idx+1:]
|
|
for ridx, c in enumerate(right):
|
|
if c in DIGITS:
|
|
term = min(i for i in range(ridx+1, len(right))
|
|
if right[i] not in DIGITS)
|
|
val = int(right[ridx:term])
|
|
val += b
|
|
right = right[:ridx] + str(val) + right[term:]
|
|
break
|
|
return eval(left + mid + right)
|
|
elif c == ']':
|
|
depth -= 1
|
|
return None
|
|
|
|
def try_split(num):
|
|
if isinstance(num, list):
|
|
a, b = num
|
|
new_a = try_split(a)
|
|
if new_a is not None:
|
|
return [new_a, b]
|
|
new_b = try_split(b)
|
|
if new_b is not None:
|
|
return [a, new_b]
|
|
return None
|
|
if num > 9:
|
|
d, m = divmod(num, 2)
|
|
return [d, d + m]
|
|
return None
|
|
|
|
def reduce(num):
|
|
while True:
|
|
new_num = try_explode(num)
|
|
if new_num is not None:
|
|
num = new_num
|
|
continue
|
|
new_num = try_split(num)
|
|
if new_num is not None:
|
|
num = new_num
|
|
continue
|
|
return num
|
|
|
|
def magnitude(num):
|
|
if isinstance(num, list):
|
|
a, b = num
|
|
a = magnitude(a)
|
|
b = magnitude(b)
|
|
return 3*a + 2*b
|
|
return num
|
|
|
|
def part1(s):
|
|
lines = s.splitlines()
|
|
fake_nums = list(map(eval, lines))
|
|
|
|
num = fake_nums[0]
|
|
|
|
for item in fake_nums[1:]:
|
|
item = reduce(item)
|
|
num = reduce(add(num, item))
|
|
|
|
answer = magnitude(num)
|
|
|
|
print(f'The answer to part one is {answer}')
|
|
|
|
def part2(s):
|
|
lines = s.splitlines()
|
|
fake_nums = list(map(eval, lines))
|
|
|
|
fake_nums = list(map(reduce, fake_nums))
|
|
|
|
best = 0
|
|
for ia, a in enumerate(fake_nums):
|
|
for ib, b in enumerate(fake_nums):
|
|
if ia == ib:
|
|
continue
|
|
|
|
mag = magnitude(reduce(add(a, b)))
|
|
best = max(best, mag)
|
|
|
|
answer = best
|
|
|
|
print(f'The answer to part two is {answer}')
|
|
|
|
with open('./2021/18.input') as input_file:
|
|
INPUT = input_file.read()
|
|
part1(INPUT)
|
|
part2(INPUT) |