2021 Day 18
This commit is contained in:
144
2021/18/code.py
Normal file
144
2021/18/code.py
Normal file
@ -0,0 +1,144 @@
|
||||
# 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)}")
|
Reference in New Issue
Block a user