Day 12 - Advent of Code 2024
12 December 2024
Working solution for the day 12 puzzles.
Part One and Part Two
""" day_12_01_02.py """
# usage: python3 day_12_01_02.py < input
import sys
from collections import Counter
from collections import deque
from collections import namedtuple
Point = namedtuple('Point2D', 'x, y')
def box_vertices(ul):
""" vertices of box given upperleft """
return {ul, Point(ul.x + 1, ul.y), Point(ul.x + 1, ul.y + 1),
Point(ul.x, ul.y + 1)}
def group(upperlefts):
""" calculate zones of connected boxes from upperlefts """
deltas = [(0, -1), (1, 0), (0, 1), (-1, 0)]
uls = upperlefts[:]
zones = []
while uls:
ul = uls.pop()
zone = [box_vertices(ul)]
queue = deque([ul])
while queue:
ul = queue.popleft()
for dx, dy in deltas:
adjacent = Point(ul.x + dx, ul.y + dy)
if adjacent in uls:
uls.remove(adjacent)
zone.append(box_vertices(adjacent))
queue.append(adjacent)
zones.append(zone)
return zones
def edge(zone):
""" how many times do two boxes touch diagonally only """
vertices = Counter(vertex for box in zone for vertex in box)
count = 0
for i in [vertex for vertex, freq in vertices.items() if freq == 2]:
box = box_vertices(Point(i.x - 1, i.y - 1))
if box in zone:
box = box_vertices(i)
if box in zone:
count += 1
else:
box = box_vertices(Point(i.x, i.y - 1))
if box in zone:
box = box_vertices(Point(i.x - 1, i.y))
if box in zone:
count += 1
return count
def perimeter(zone):
""" calculate perimeter given boxes in zone """
vertices = Counter(vertex for box in zone for vertex in box)
return len([vertex for vertex, freq in vertices.items()
if freq < 4]) + edge(zone)
def sides(zone):
""" calculate sides given boxes in zone """
vertices = Counter(vertex for box in zone for vertex in box)
return len([vertex for vertex, freq in vertices.items()
if freq in [1, 3]]) + 2 * edge(zone)
plots = {}
with sys.stdin as infile:
point = Point(0, 0)
for line in infile:
for plot in line.strip():
sites = plots.get(plot, [])
sites.append(point)
plots[plot] = sites
point = Point(point.x + 1, point.y)
point = Point(0, point.y + 1)
perimeter_cost = 0
sides_cost = 0
for _, sites in plots.items():
for region in group(sites):
region_area = len(region)
perimeter_cost += region_area * perimeter(region)
sides_cost += region_area * sides(region)
print(perimeter_cost)
print(sides_cost)