2024 day 12.
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Mikael CAPELLE 2024-12-12 14:38:50 +01:00
parent 89306f4a04
commit 291c627c79
2 changed files with 84 additions and 122 deletions

View File

@ -173,7 +173,6 @@ class Solver(BaseSolver):
)
)
# 1242 (not working)
yield sum(
c
for _, c in play(

View File

@ -1,3 +1,5 @@
import itertools as it
from dataclasses import dataclass
from typing import Any, Iterator, TypeAlias
from ..base import BaseSolver
@ -6,133 +8,94 @@ Node: TypeAlias = tuple[int, int]
Edge: TypeAlias = tuple[Node, Node]
@dataclass(frozen=True)
class Region:
value: str
cells: set[tuple[int, int]]
edges: set[Edge]
sides: list[tuple[Edge, ...]]
def extract_region(grid: list[str], cell: tuple[int, int]):
n_rows, n_cols = len(grid), len(grid[0])
row, col = cell
value = grid[row][col]
cells: set[tuple[int, int]] = set()
edges: set[Edge] = set()
sides: list[tuple[Edge, ...]] = []
queue: list[tuple[int, int]] = [(row, col)]
while queue:
row, col = queue.pop(0)
if (row, col) in cells:
continue
cells.add((row, col))
for ur, uc in (
(row - 1, col),
(row, col + 1),
(row + 1, col),
(row, col - 1),
):
if 0 <= ur < n_rows and 0 <= uc < n_cols and grid[ur][uc] == value:
queue.append((ur, uc))
continue
if ((row, col), (ur, uc)) in edges:
continue
if col == uc:
mid, max = col, n_cols
def get(v: int):
return (row, v, ur, v)
else:
mid, max = row, n_rows
def get(v: int):
return (v, col, v, uc)
side: tuple[Edge, ...] = ((((row, col), (ur, uc))),)
for rng in (range(mid - 1, -1, -1), range(mid, max)):
for r2, c2, ur2, uc2 in map(get, rng):
if grid[r2][c2] != value or (
0 <= ur2 < n_rows
and 0 <= uc2 < n_cols
and grid[r2][c2] == grid[ur2][uc2]
):
break
side += ((((r2, c2), (ur2, uc2))),)
sides.append(side)
edges = edges.union(side)
return Region(value=value, cells=cells, edges=edges, sides=sides)
class Solver(BaseSolver):
def solve(self, input: str) -> Iterator[Any]:
grid = input.splitlines()
n_rows, n_cols = len(grid), len(grid[0])
marked = {(row, col): False for row in range(n_rows) for col in range(n_cols)}
regions: list[Region] = []
to_visit: set[tuple[int, int]] = set(
it.product(range(len(grid)), range(len(grid[0])))
)
total_1: int = 0
total_2: int = 0
while not all(marked.values()):
row, col = next(k for k, v in marked.items() if not v)
cells: set[tuple[int, int]] = set()
perimeter: int = 0
value = grid[row][col]
queue: list[tuple[int, int]] = [(row, col)]
edges: set[Edge] = set()
sides: list[tuple[Edge, ...]] = []
while queue:
row, col = queue.pop(0)
if marked[row, col]:
continue
cells.add((row, col))
marked[row, col] = True
if row == 0 or grid[row - 1][col] != value:
perimeter += 1
if ((row, col), (row - 1, col)) not in edges:
side: list[Edge] = [(((row, col), (row - 1, col)))]
for col2 in list(range(col - 1, -1, -1)):
if grid[row][col2] != value or (
row > 0 and grid[row][col2] == grid[row - 1][col2]
):
break
side.append((((row, col2), (row - 1, col2))))
for col2 in range(col + 1, n_cols):
if grid[row][col2] != value or (
row > 0 and grid[row][col2] == grid[row - 1][col2]
):
break
side.append((((row, col2), (row - 1, col2))))
sides.append(tuple(side))
edges = edges.union(side)
else:
queue.append((row - 1, col))
if col == 0 or grid[row][col - 1] != value:
perimeter += 1
if ((row, col), (row, col - 1)) not in edges:
side: list[Edge] = [(((row, col), (row, col - 1)))]
for row2 in range(row - 1, -1, -1):
if grid[row2][col] != value or (
col > 0 and grid[row2][col] == grid[row2][col - 1]
):
break
side.append((((row2, col), (row2, col - 1))))
for row2 in range(row + 1, n_rows):
if grid[row2][col] != value or (
col > 0 and grid[row2][col] == grid[row2][col - 1]
):
break
side.append((((row2, col), (row2, col - 1))))
sides.append(tuple(side))
edges = edges.union(side)
else:
queue.append((row, col - 1))
if row + 1 == n_rows or grid[row + 1][col] != value:
perimeter += 1
if ((row, col), (row + 1, col)) not in edges:
side: list[Edge] = [(((row, col), (row + 1, col)))]
for col2 in list(range(col - 1, -1, -1)):
if grid[row][col2] != value or (
row + 1 < n_rows
and grid[row][col2] == grid[row + 1][col2]
):
break
side.append((((row, col2), (row + 1, col2))))
for col2 in range(col + 1, n_cols):
if grid[row][col2] != value or (
row + 1 < n_rows
and grid[row][col2] == grid[row + 1][col2]
):
break
side.append((((row, col2), (row + 1, col2))))
sides.append(tuple(side))
edges = edges.union(side)
else:
queue.append((row + 1, col))
if col + 1 == n_cols or grid[row][col + 1] != value:
perimeter += 1
if ((row, col), (row, col + 1)) not in edges:
side: list[Edge] = [(((row, col), (row, col + 1)))]
for row2 in range(row - 1, -1, -1):
if grid[row2][col] != value or (
col + 1 < n_cols
and grid[row2][col] == grid[row2][col + 1]
):
break
side.append((((row2, col), (row2, col + 1))))
for row2 in range(row + 1, n_rows):
if grid[row2][col] != value or (
col + 1 < n_cols
and grid[row2][col] == grid[row2][col + 1]
):
break
side.append((((row2, col), (row2, col + 1))))
sides.append(tuple(side))
edges = edges.union(side)
else:
queue.append((row, col + 1))
while to_visit:
region = extract_region(grid, next(iter(to_visit)))
self.logger.info(
f"region with {value}: {len(cells)} * {perimeter} = {len(cells) * perimeter}, {len(cells)} * {len(sides)} = {len(cells) * len(sides)}"
f"region with {region.value}: "
f"{len(region.cells)} * {len(region.edges)} = {len(region.cells) * len(region.edges)}, "
f"{len(region.cells)} * {len(region.sides)} = {len(region.cells) * len(region.sides)}"
)
total_1 += len(cells) * perimeter
total_2 += len(cells) * len(sides)
yield total_1
yield total_2
to_visit.difference_update(region.cells)
regions.append(region)
yield sum(len(region.cells) * len(region.edges) for region in regions)
yield sum(len(region.cells) * len(region.sides) for region in regions)