generated from public/aoc_template
102 lines
3.4 KiB
Python
102 lines
3.4 KiB
Python
from tools.aoc import AOCDay
|
|
from tools.coordinate import Coordinate
|
|
from typing import Any, Iterable
|
|
|
|
|
|
DIRECTIONS = [Coordinate(1, 0), Coordinate(0, 1), Coordinate(-1, 0), Coordinate(0, -1)]
|
|
DIR_TRANS = {'R': 0, 'D': 1, 'L': 2, 'U': 3}
|
|
|
|
|
|
def get_borders(coords: set[tuple[Coordinate, Coordinate]]) -> (int, int, int, int):
|
|
"""min_x, max_x, min_y, max_y"""
|
|
minX = list(sorted(coords))[0][0][0]
|
|
minY = list(sorted(coords, key=lambda d: (d[0][1], d[0][0])))[0][0][1]
|
|
maxX = list(sorted(coords, reverse=True))[0][0][0]
|
|
maxY = list(sorted(coords, key=lambda d: (d[0][1], d[0][0]), reverse=True))[0][0][1]
|
|
|
|
return minX, maxX, minY, maxY
|
|
|
|
|
|
def split_polygon(coords: set[tuple[Coordinate, Coordinate]], cut_idx: int, horizontal: bool) -> (set[Coordinate], set[Coordinate]):
|
|
"""split the polygon described by coords at cut_idx horizontally or vertically and return both resulting polygons"""
|
|
if horizontal:
|
|
vertical_edges = {x for x in coords if x[0].x == x[1].x}
|
|
intersecting_edges = {x for x in vertical_edges if min(x[0].y, x[1].y) <= cut_idx <= max(x[0].y, x[1].y)}
|
|
above = set()
|
|
below = set()
|
|
for c in coords:
|
|
if c in intersecting_edges:
|
|
# split into two
|
|
# c = ((from_x, from_y), (to_x, to_y))
|
|
c_1 = (c[0], Coordinate(c[1].x, cut_idx))
|
|
c_2 = (Coordinate(c[0].x, cut_idx), c[1])
|
|
if c[0].y <= cut_idx:
|
|
above.add(c_1)
|
|
below.add(c_2)
|
|
else:
|
|
above.add(c_2)
|
|
below.add(c_1)
|
|
else:
|
|
if c[0].y <= cut_idx:
|
|
above.add(c)
|
|
if c[0].y >= cut_idx:
|
|
below.add(c)
|
|
return above, below
|
|
|
|
|
|
def get_inside_area(coords: set[tuple[Coordinate, Coordinate]]) -> int:
|
|
min_x, max_x, min_y, max_y = get_borders(coords)
|
|
if len(coords) == 4: # rectangle, hopefully
|
|
return (max_x - min_x - 1) * (max_y - min_y - 1)
|
|
else:
|
|
if max_x - min_x > max_y - min_y:
|
|
half_1, half_2 = split_polygon(coords, (max_x - min_x) // 2, horizontal=False)
|
|
else:
|
|
half_1, half_2 = split_polygon(coords, (max_y - min_y) // 2, horizontal=True)
|
|
return get_inside_area(half_1) + get_inside_area(half_2)
|
|
|
|
class Day(AOCDay):
|
|
inputs = [
|
|
[
|
|
(62, "input18_test"),
|
|
(50603, "input18"),
|
|
],
|
|
[
|
|
(952408144115, "input18_test"),
|
|
(None, "input18"),
|
|
]
|
|
]
|
|
|
|
def parse_input(self, part2: bool = False) -> Iterable[tuple[int, int]]:
|
|
for line in self.getInput():
|
|
d, s, c = line.split()
|
|
if not part2:
|
|
yield DIR_TRANS[d], int(s)
|
|
else:
|
|
yield int(c[-2]), int(c[2:-2], 16)
|
|
|
|
def get_lagoon_area(self, part2: bool = False) -> int:
|
|
start = Coordinate(0, 0)
|
|
edges = set()
|
|
perimeter = 0
|
|
for d, l in self.parse_input(part2):
|
|
perimeter += l
|
|
end = start + DIRECTIONS[d] * l
|
|
edges.add((start, end))
|
|
start = end
|
|
|
|
print(split_polygon(edges, 4, True))
|
|
|
|
#return get_inside_area(edges) + perimeter
|
|
|
|
def part1(self) -> Any:
|
|
return self.get_lagoon_area()
|
|
|
|
def part2(self) -> Any:
|
|
return ""
|
|
|
|
|
|
if __name__ == '__main__':
|
|
day = Day(2023, 18)
|
|
day.run(verbose=True)
|