aoc2024/day12.py
2024-12-12 08:10:14 +01:00

116 lines
3.1 KiB
Python

from tools.aoc import AOCDay
from tools.coordinate import Coordinate
from tools.grid import Grid
from typing import Any
SIDE_LOOKUP = {
(0, -1): {
"next": (1, 0),
"turn_look": (-1, 0),
},
(0, 1): {
"next": (-1, 0),
"turn_look": (1, 0),
},
(1, 0): {
"next": (0, 1),
"turn_look": (0, -1),
},
(-1, 0): {
"next": (0, -1),
"turn_look": (0, 1),
},
}
def get_sides(region: set[Coordinate]) -> int:
sides = 0
top_left = cur_pos = min(region)
start_look = next_look = (0, -1)
while cur_pos != top_left or start_look != next_look or sides < 4:
if cur_pos + next_look not in region:
if cur_pos + SIDE_LOOKUP[next_look]["next"] in region:
cur_pos += SIDE_LOOKUP[next_look]["next"]
continue
else:
sides += 1
next_look = SIDE_LOOKUP[next_look]["next"]
else:
sides += 1
cur_pos += next_look
next_look = SIDE_LOOKUP[next_look]["turn_look"]
return sides
class Day(AOCDay):
inputs = [
[
(1930, "input12_test"),
(1424006, "input12"),
],
[
(80, "input12_test2"),
(236, "input12_test3"),
(368, "input12_test4"),
(1206, "input12_test"),
(858684, "input12"),
],
]
def get_regions(self) -> list[set[Coordinate]]:
regions = []
garden = Grid.from_data(self.getInput())
seen = set()
for x in garden.rangeX():
for y in garden.rangeY():
pos = Coordinate(x, y)
if pos in seen:
continue
region = set(garden.getRegion(pos, includeDiagonal=False))
seen |= region
regions.append(region)
return regions
def part1(self) -> Any:
price = 0
for region in self.get_regions():
perimeter = sum(1 for cell in region for n in cell.getNeighbours(includeDiagonal=False) if n not in region)
price += perimeter * len(region)
return price
def part2(self) -> Any:
price = 0
for region in self.get_regions():
region_sides = get_sides(region)
region_plots = len(region)
g = Grid()
for cell in region:
g.set(cell)
g.minX -= 1
g.minY -= 1
g.maxX += 1
g.maxY += 1
region |= set(g.getRegion(Coordinate(g.minX, g.minY), includeDiagonal=True))
for rx in g.rangeX():
for ry in g.rangeY():
r_pos = Coordinate(rx, ry)
if r_pos in region:
continue
inner_region = set(g.getRegion(r_pos, includeDiagonal=False))
region |= inner_region
region_sides += get_sides(inner_region)
price += region_plots * region_sides
return price
if __name__ == "__main__":
day = Day(2024, 12)
day.run(verbose=True)