145 lines
4.6 KiB
Python
Raw Normal View History

2021-12-18 21:15:08 +01:00
# SPDX-License-Identifier: MIT
# Copyright (c) 2021 Akumatic
#
# https://adventofcode.com/2021/day/18
import math
class Node:
def __init__(self, left: "Node" = None, right: "Node" = None,
value: int = None, parent: "Node" = None, depth: int = 1):
self.left = left
self.right = right
self.value = value
self.parent = parent
self.depth = depth
def __str__(self) -> str:
return str(self.value) if self.value is not None else f"[{self.left},{self.right}]"
def explode(self) -> bool:
if self.depth < 5:
tmp = False
if self.left.value is None:
tmp = self.left.explode()
if tmp:
return True
if self.right.value is None:
tmp = self.right.explode()
return tmp
elif self.depth == 5:
assert self.left.value is not None and self.right.value is not None
left = self._find_next_node_with_value(left = True)
right = self._find_next_node_with_value(left = False)
if left:
left.value += self.left.value
if right:
right.value += self.right.value
self.left, self.right, self.value = None, None, 0
return True
def split(self) -> bool:
if self.value is None:
tmp = False
tmp = self.left.split()
if tmp:
return True
tmp = self.right.split()
return tmp
if self.value < 10:
return False
val = self.value / 2
self.value = None
self.left = Node(value=math.floor(val), parent=self, depth=self.depth + 1)
self.right = Node(value=math.ceil(val), parent=self, depth=self.depth + 1)
return True
def set_parent(self, parent: "Node") -> None:
self.parent = parent
self.depth = parent.depth + 1
if self.value is None:
self.left.set_parent(self)
self.right.set_parent(self)
def _find_next_node_with_value(self, left: bool) -> "Node":
no_parent = True
node = self
while node.parent is not None:
if left and node.parent.right == node or not left and node.parent.left == node:
node = node.parent.left if left else node.parent.right
no_parent = False
break
else:
node = node.parent
if no_parent:
return None
else:
while left and node.right is not None or not left and node.left is not None:
node = node.right if left else node.left
return node
def read_file(filename: str = "input.txt") -> list:
with open(f"{__file__.rstrip('code.py')}{filename}", "r") as f:
return [eval(line) for line in f.read().strip().split("\n")]
def parse_node(node: list, depth: int = 1) -> Node:
a = Node(value=node[0], depth=depth) if isinstance(node[0], int) else parse_node(node[0], depth = depth + 1)
b = Node(value=node[1], depth=depth) if isinstance(node[1], int) else parse_node(node[1], depth = depth + 1)
node = Node(left=a, right=b, depth=depth)
a.set_parent(node)
b.set_parent(node)
return node
def reduce(node: Node) -> None:
repeat = True
while repeat:
repeat = False
explosion_result = node.explode()
if explosion_result:
repeat = True
continue
repeat = node.split()
def add(a: Node, b: Node) -> Node:
node = Node(left=a, right=b)
a.set_parent(node)
b.set_parent(node)
reduce(node)
return node
def sum_nodes(nodes: list) -> list:
node = parse_node(nodes[0])
for i in range(1, len(nodes)):
node = add(node, parse_node(nodes[i]))
return node
def get_magnitude(node: Node) -> int:
if node.value is not None:
return node.value
return 3 * get_magnitude(node.left) + 2 * get_magnitude(node.right)
def part1(vals: list) -> int:
tree = sum_nodes(vals)
return get_magnitude(tree)
def part2(vals: list) -> int:
max_mag = 0
nodes = [parse_node(node) for node in vals]
for i in range(len(vals)):
for j in range(len(vals)):
if i == j:
continue
a, b = parse_node(vals[i]), parse_node(vals[j])
c, d = parse_node(vals[i]), parse_node(vals[j])
max_mag = max(max_mag, get_magnitude(add(a, b)), get_magnitude(add(c, d)))
return max_mag
if __name__ == "__main__":
vals = read_file()
print(f"Part 1: {part1(vals)}")
print(f"Part 2: {part2(vals)}")