Skip to content

Commit c8f9d44

Browse files
committed
AoC 2024 Day 12
1 parent bf45143 commit c8f9d44

File tree

2 files changed

+206
-3
lines changed

2 files changed

+206
-3
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22

33
## 2024
44

5-
![](https://img.shields.io/badge/stars%20⭐-22-yellow)
6-
![](https://img.shields.io/badge/days%20completed-11-red)
5+
![](https://img.shields.io/badge/stars%20⭐-24-yellow)
6+
![](https://img.shields.io/badge/days%20completed-12-red)
77

88
<!-- @BEGIN:ImplementationsTable:2024@ -->
99
| | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
1010
| ---| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
11-
| python3 | [](src/main/python/AoC2024_01.py) | [](src/main/python/AoC2024_02.py) | [](src/main/python/AoC2024_03.py) | [](src/main/python/AoC2024_04.py) | [](src/main/python/AoC2024_05.py) | [](src/main/python/AoC2024_06.py) | [](src/main/python/AoC2024_07.py) | [](src/main/python/AoC2024_08.py) | [](src/main/python/AoC2024_09.py) | [](src/main/python/AoC2024_10.py) | [](src/main/python/AoC2024_11.py) | | | | | | | | | | | | | | |
11+
| python3 | [](src/main/python/AoC2024_01.py) | [](src/main/python/AoC2024_02.py) | [](src/main/python/AoC2024_03.py) | [](src/main/python/AoC2024_04.py) | [](src/main/python/AoC2024_05.py) | [](src/main/python/AoC2024_06.py) | [](src/main/python/AoC2024_07.py) | [](src/main/python/AoC2024_08.py) | [](src/main/python/AoC2024_09.py) | [](src/main/python/AoC2024_10.py) | [](src/main/python/AoC2024_11.py) | [](src/main/python/AoC2024_12.py) | | | | | | | | | | | | | |
1212
| java | [](src/main/java/AoC2024_01.java) | [](src/main/java/AoC2024_02.java) | [](src/main/java/AoC2024_03.java) | [](src/main/java/AoC2024_04.java) | [](src/main/java/AoC2024_05.java) | [](src/main/java/AoC2024_06.java) | [](src/main/java/AoC2024_07.java) | [](src/main/java/AoC2024_08.java) | | [](src/main/java/AoC2024_10.java) | [](src/main/java/AoC2024_11.java) | | | | | | | | | | | | | | |
1313
| rust | [](src/main/rust/AoC2024_01/src/main.rs) | [](src/main/rust/AoC2024_02/src/main.rs) | [](src/main/rust/AoC2024_03/src/main.rs) | [](src/main/rust/AoC2024_04/src/main.rs) | [](src/main/rust/AoC2024_05/src/main.rs) | [](src/main/rust/AoC2024_06/src/main.rs) | [](src/main/rust/AoC2024_07/src/main.rs) | [](src/main/rust/AoC2024_08/src/main.rs) | | | | | | | | | | | | | | | | | |
1414
<!-- @END:ImplementationsTable:2024@ -->

src/main/python/AoC2024_12.py

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
#! /usr/bin/env python3
2+
#
3+
# Advent of Code 2024 Day 12
4+
#
5+
6+
import sys
7+
from collections import defaultdict
8+
9+
from aoc.common import InputData
10+
from aoc.common import SolutionBase
11+
from aoc.common import aoc_samples
12+
from aoc.geometry import Direction
13+
from aoc.graph import flood_fill
14+
from aoc.grid import Cell
15+
from aoc.grid import CharGrid
16+
17+
Input = CharGrid
18+
Output1 = int
19+
Output2 = int
20+
21+
22+
TEST1 = """\
23+
AAAA
24+
BBCD
25+
BBCC
26+
EEEC
27+
"""
28+
TEST2 = """\
29+
OOOOO
30+
OXOXO
31+
OOOOO
32+
OXOXO
33+
OOOOO
34+
"""
35+
TEST3 = """\
36+
RRRRIICCFF
37+
RRRRIICCCF
38+
VVRRRCCFFF
39+
VVRCCCJFFF
40+
VVVVCJJCFE
41+
VVIVCCJJEE
42+
VVIIICJJEE
43+
MIIIIIJJEE
44+
MIIISIJEEE
45+
MMMISSJEEE
46+
"""
47+
TEST4 = """\
48+
EEEEE
49+
EXXXX
50+
EEEEE
51+
EXXXX
52+
EEEEE
53+
"""
54+
TEST5 = """\
55+
AAAAAA
56+
AAABBA
57+
AAABBA
58+
ABBAAA
59+
ABBAAA
60+
AAAAAA
61+
"""
62+
63+
64+
class Solution(SolutionBase[Input, Output1, Output2]):
65+
def parse_input(self, input_data: InputData) -> Input:
66+
return CharGrid.from_strings(list(input_data))
67+
68+
def get_regions(self, grid: CharGrid) -> dict[str, list[set[Cell]]]:
69+
p = defaultdict[str, set[Cell]](set)
70+
for plot in grid.get_cells():
71+
p[grid.get_value(plot)].add(plot)
72+
regions = defaultdict[str, list[set[Cell]]](list)
73+
for k, all_p in p.items():
74+
while all_p:
75+
r = flood_fill(
76+
next(iter(all_p)),
77+
lambda cell: (
78+
n
79+
for n in grid.get_capital_neighbours(cell)
80+
if n in all_p
81+
),
82+
)
83+
regions[k].append(r)
84+
all_p -= r
85+
return regions
86+
87+
def part_1(self, grid: Input) -> Output1:
88+
regions = self.get_regions(grid)
89+
d = list[list[int]]()
90+
for plant in regions:
91+
for region in regions[plant]:
92+
dd = list[int]()
93+
for plot in region:
94+
c = 4
95+
for n in grid.get_capital_neighbours(plot):
96+
if grid.get_value(n) == plant:
97+
c -= 1
98+
dd.append(c)
99+
d.append(dd)
100+
return sum(len(dd) * sum(dd) for dd in d)
101+
102+
def part_2(self, grid: Input) -> Output2:
103+
def count_corners(plot: Cell, region: set[Cell]) -> int:
104+
ans = 0
105+
DO = [
106+
[Direction.LEFT, Direction.LEFT_AND_UP, Direction.UP],
107+
[Direction.UP, Direction.RIGHT_AND_UP, Direction.RIGHT],
108+
[Direction.RIGHT, Direction.RIGHT_AND_DOWN, Direction.DOWN],
109+
[Direction.DOWN, Direction.LEFT_AND_DOWN, Direction.LEFT],
110+
]
111+
for d in DO:
112+
o = list(
113+
filter(
114+
lambda n: n in region, map(lambda dd: plot.at(dd), d)
115+
)
116+
)
117+
if len(o) == 0:
118+
ans += 1
119+
if (
120+
plot.at(Direction.LEFT_AND_DOWN) not in region
121+
and plot.at(Direction.LEFT) in region
122+
and plot.at(Direction.DOWN) in region
123+
):
124+
ans += 1
125+
if (
126+
plot.at(Direction.LEFT_AND_UP) not in region
127+
and plot.at(Direction.LEFT) in region
128+
and plot.at(Direction.UP) in region
129+
):
130+
ans += 1
131+
if (
132+
plot.at(Direction.RIGHT_AND_DOWN) not in region
133+
and plot.at(Direction.RIGHT) in region
134+
and plot.at(Direction.DOWN) in region
135+
):
136+
ans += 1
137+
if (
138+
plot.at(Direction.RIGHT_AND_UP) not in region
139+
and plot.at(Direction.RIGHT) in region
140+
and plot.at(Direction.UP) in region
141+
):
142+
ans += 1
143+
if (
144+
plot.at(Direction.LEFT_AND_DOWN) in region
145+
and plot.at(Direction.LEFT) not in region
146+
and plot.at(Direction.DOWN) not in region
147+
):
148+
ans += 1
149+
if (
150+
plot.at(Direction.LEFT_AND_UP) in region
151+
and plot.at(Direction.LEFT) not in region
152+
and plot.at(Direction.UP) not in region
153+
):
154+
ans += 1
155+
if (
156+
plot.at(Direction.RIGHT_AND_DOWN) in region
157+
and plot.at(Direction.RIGHT) not in region
158+
and plot.at(Direction.DOWN) not in region
159+
):
160+
ans += 1
161+
if (
162+
plot.at(Direction.RIGHT_AND_UP) in region
163+
and plot.at(Direction.RIGHT) not in region
164+
and plot.at(Direction.UP) not in region
165+
):
166+
ans += 1
167+
return ans
168+
169+
d = list[list[int]]()
170+
regions = self.get_regions(grid)
171+
for plant in regions:
172+
for region in regions[plant]:
173+
dd = list[int]()
174+
for plot in region:
175+
dd.append(count_corners(plot, region))
176+
d.append(dd)
177+
return sum(len(dd) * sum(dd) for dd in d)
178+
179+
@aoc_samples(
180+
(
181+
("part_1", TEST1, 140),
182+
("part_1", TEST2, 772),
183+
("part_1", TEST3, 1930),
184+
("part_2", TEST1, 80),
185+
("part_2", TEST2, 436),
186+
("part_2", TEST3, 1206),
187+
("part_2", TEST4, 236),
188+
("part_2", TEST5, 368),
189+
)
190+
)
191+
def samples(self) -> None:
192+
pass
193+
194+
195+
solution = Solution(2024, 12)
196+
197+
198+
def main() -> None:
199+
solution.run(sys.argv)
200+
201+
202+
if __name__ == "__main__":
203+
main()

0 commit comments

Comments
 (0)