aoc2021/day22.py

118 lines
4.5 KiB
Python

from collections import defaultdict
from tools.aoc import AOCDay
from tools.coordinate import Coordinate
from typing import Any, Generator, List, Union
class Cube:
coord_ulb: Coordinate # min_coord / up left back
coord_urb: Coordinate
coord_ulf: Coordinate
coord_urf: Coordinate
coord_dlb: Coordinate
coord_drb: Coordinate
coord_dlf: Coordinate
coord_drf: Coordinate # max_coord / down right front
def __init__(self, min_coord: Coordinate, max_coord: Coordinate):
assert max_coord >= min_coord, "invalid cube spec: %s < %s" % (min_coord, max_coord)
self.coord_ulb = min_coord
self.coord_drf = max_coord
#self.coord_urb = Coordinate(self.coord_ulb.x, self.coord_drf.y, self.coord_ulb.z)
#self.coord_ulf = Coordinate(self.coord_ulb.x, self.coord_ulb.y, self.coord_drf.z)
#self.coord_urf = Coordinate(self.coord_ulb.x, self.coord_drf.y, self.coord_drf.z)
#self.coord_dlb = Coordinate(self.coord_drf.x, self.coord_ulb.y, self.coord_ulb.z)
#self.coord_drb = Coordinate(self.coord_drf.x, self.coord_drf.y, self.coord_ulb.z)
#self.coord_dlf = Coordinate(self.coord_drf.x, self.coord_ulb.y, self.coord_drf.z)
def getSize(self):
return (
(self.coord_drf.x - self.coord_ulb.x + 1)
* (self.coord_drf.y - self.coord_ulb.y + 1)
* (self.coord_drf.z - self.coord_ulb.z + 1)
)
def intersect(self, other: 'Cube') -> Union['Cube', None]:
intersect_ulr = Coordinate(
#max(self.coord_ulb.x, other.coord_ulb.x),
self.coord_ulb.x if self.coord_ulb.x > other.coord_ulb.x else other.coord_ulb.x,
#max(self.coord_ulb.y, other.coord_ulb.y),
self.coord_ulb.y if self.coord_ulb.y > other.coord_ulb.y else other.coord_ulb.y,
#max(self.coord_ulb.z, other.coord_ulb.z)
self.coord_ulb.z if self.coord_ulb.z > other.coord_ulb.z else other.coord_ulb.z,
)
intersect_drf = Coordinate(
#min(self.coord_drf.x, other.coord_drf.x),
self.coord_drf.x if self.coord_drf.x < other.coord_drf.x else other.coord_drf.x,
#min(self.coord_drf.y, other.coord_drf.y),
self.coord_drf.y if self.coord_drf.y < other.coord_drf.y else other.coord_drf.y,
#min(self.coord_drf.z, other.coord_drf.z),
self.coord_drf.z if self.coord_drf.z < other.coord_drf.z else other.coord_drf.z,
)
if intersect_ulr <= intersect_drf:
return Cube(intersect_ulr, intersect_drf)
def __str__(self):
return "Cube(<%d,%d,%d>;<%d,%d,%d>)" % (
self.coord_ulb.x, self.coord_ulb.y, self.coord_ulb.z,
self.coord_drf.x, self.coord_drf.y, self.coord_drf.z,
)
def __repr__(self):
return self.__str__()
class Day(AOCDay):
inputs = [
[
(39, "test_input22_1_0"),
(590784, "test_input22_1_1"),
(570915, "input22")
],
[
(2758514936282235, "test_input22_2_0"),
(1268313839428137, "input22")
]
]
def getCubeList(self, part1: bool = False):
for line in self.getInput():
state, c = line.split()
co = c.split(",")
_, xco = co[0].split("=")
_, yco = co[1].split("=")
_, zco = co[2].split("=")
minX, maxX = map(int, xco.split(".."))
minY, maxY = map(int, yco.split(".."))
minZ, maxZ = map(int, zco.split(".."))
new_cube = Cube(Coordinate(minX, minY, minZ), Coordinate(maxX, maxY, maxZ))
if not part1 or (new_cube.coord_ulb >= Coordinate(-50, -50, -50) and new_cube.coord_drf <= Coordinate(50, 50, 50)):
yield state == "on", new_cube
def getOnSum(self, part1: bool = False) -> int:
cubes = defaultdict(int)
for switch_state, this_cube in self.getCubeList(part1=part1):
for prior_cube in cubes.copy():
intersect_cube = this_cube.intersect(prior_cube)
if intersect_cube:
cubes[intersect_cube] -= cubes[prior_cube]
if switch_state:
cubes[this_cube] = 1
#print(cubes)
return sum(cube.getSize() * on_off for cube, on_off in cubes.items())
def part1(self) -> Any:
return self.getOnSum(part1=True)
def part2(self) -> Any:
return self.getOnSum()
if __name__ == '__main__':
day = Day(22)
day.run(verbose=True)