163 lines
6.4 KiB
Python
163 lines
6.4 KiB
Python
from tools.aoc import AOCDay
|
|
from tools.coordinate import Coordinate
|
|
from typing import Any, 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) * (self.coord_drf.y - self.coord_ulb.y) * (self.coord_drf.z - self.coord_ulb.z)
|
|
|
|
def intersect(self, other: 'Cube') -> Union['Cube', None]:
|
|
"""
|
|
if not other.coord_drf >= self.coord_ulb and not other.coord_ulb <= self.coord_drf:
|
|
return None
|
|
|
|
if other.coord_ulb <= self.coord_ulb and other.coord_drf >= self.coord_drf:
|
|
return Cube(self.coord_ulb, self.coord_drf)
|
|
|
|
if other.coord_drf <= self.coord_drf and other.coord_ulb >= self.coord_ulb:
|
|
return Cube(other.coord_ulb, other.coord_drf)
|
|
"""
|
|
|
|
try:
|
|
return Cube(
|
|
Coordinate(
|
|
max(self.coord_ulb.x, other.coord_ulb.x),
|
|
max(self.coord_ulb.y, other.coord_ulb.y),
|
|
max(self.coord_ulb.z, other.coord_ulb.z)
|
|
),
|
|
Coordinate(
|
|
min(self.coord_drf.x, other.coord_drf.x),
|
|
min(self.coord_drf.y, other.coord_drf.y),
|
|
min(self.coord_drf.z, other.coord_drf.z),
|
|
)
|
|
)
|
|
except AssertionError:
|
|
return None
|
|
|
|
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__()
|
|
|
|
|
|
def addCube(cubes: List[Cube], state: bool, new_cube: Cube) -> List[Cube]:
|
|
new_cubes = []
|
|
for old_cube in cubes:
|
|
intersect_cube = old_cube.intersect(new_cube)
|
|
if not intersect_cube:
|
|
new_cubes.append(old_cube)
|
|
else:
|
|
if intersect_cube.coord_ulb > old_cube.coord_ulb and intersect_cube.coord_drf < old_cube.coord_drf:
|
|
# somewhere in the middle
|
|
# => 6 new cubes (2*3x3, 2*1x3, 2*1x1
|
|
print("intersection in the middle of old_cube: %s is within %s (intersect: %s)" % (new_cube, old_cube, intersect_cube))
|
|
new_cubes.append(
|
|
Cube(
|
|
old_cube.coord_ulb,
|
|
Coordinate(old_cube.coord_ulb.x, old_cube.coord_urb.y, intersect_cube.coord_ulb.z - 1)
|
|
)
|
|
)
|
|
new_cubes.append(
|
|
Cube(
|
|
Coordinate(old_cube.coord_ulf.x, old_cube.coord_urf.y, intersect_cube.coord_drf.z + 1),
|
|
old_cube.coord_drf
|
|
)
|
|
)
|
|
new_cubes.append(
|
|
Cube(
|
|
Coordinate(old_cube.coord_ulb.x, old_cube.coord_ulb.y, intersect_cube.coord_ulb.z),
|
|
Coordinate(old_cube.coord_dlf.x, intersect_cube.coord_dlf.y - 1, intersect_cube.coord_drf.z)
|
|
)
|
|
)
|
|
new_cubes.append(
|
|
Cube(
|
|
Coordinate(old_cube.coord_urb.x, intersect_cube.coord_urb.y + 1, intersect_cube.coord_urb.z),
|
|
Coordinate(old_cube.coord_drf.x, old_cube.coord_drf.y, intersect_cube.coord_drf.z)
|
|
)
|
|
)
|
|
new_cubes.append(
|
|
Cube(
|
|
Coordinate(old_cube.coord_ulb.x, intersect_cube.coord_ulb.y, intersect_cube.coord_ulb.z),
|
|
Coordinate(intersect_cube.coord_urf.x - 1, intersect_cube.coord_urf.y, intersect_cube.coord_urf.z)
|
|
)
|
|
)
|
|
new_cubes.append(
|
|
Cube(
|
|
Coordinate(intersect_cube.coord_dlb.x + 1, intersect_cube.coord_dlb.y, intersect_cube.coord_dlb.z),
|
|
Coordinate(intersect_cube.coord_drf.x, intersect_cube.coord_drf.y, old_cube.coord_drf.z)
|
|
)
|
|
)
|
|
else:
|
|
print("%s intersecting with %s => %s" % (old_cube, new_cube, intersect_cube))
|
|
pass
|
|
|
|
if state:
|
|
new_cubes.append(new_cube)
|
|
|
|
return new_cubes
|
|
|
|
|
|
class Day(AOCDay):
|
|
test_solutions_p1 = [590784]
|
|
test_solutions_p2 = [2758514936282235]
|
|
|
|
def getCubeList(self, part1: bool = False) -> List[Cube]:
|
|
cubes = []
|
|
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)):
|
|
print("ADDING", state, new_cube)
|
|
cubes = addCube(cubes, state == "on", new_cube)
|
|
print(cubes)
|
|
|
|
return cubes
|
|
|
|
def part1(self) -> Any:
|
|
on_count = 0
|
|
cubes = self.getCubeList(part1=True)
|
|
for cube in cubes:
|
|
on_count += cube.getSize()
|
|
|
|
return on_count
|
|
|
|
def part2(self) -> Any:
|
|
on_count = 0
|
|
for cube in self.getCubeList():
|
|
on_count += cube.getSize()
|
|
|
|
return on_count
|