Skip to content

Commit b24b95c

Browse files
committed
2024 day 12
1 parent 5c054d9 commit b24b95c

File tree

7 files changed

+159
-0
lines changed

7 files changed

+159
-0
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
AAAA
2+
BBCD
3+
BBCC
4+
EEEC
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
OOOOO
2+
OXOXO
3+
OOOOO
4+
OXOXO
5+
OOOOO
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
RRRRIICCFF
2+
RRRRIICCCF
3+
VVRRRCCFFF
4+
VVRCCCJFFF
5+
VVVVCJJCFE
6+
VVIVCCJJEE
7+
VVIIICJJEE
8+
MIIIIIJJEE
9+
MIIISIJEEE
10+
MMMISSJEEE
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
EEEEE
2+
EXXXX
3+
EEEEE
4+
EXXXX
5+
EEEEE
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
AAAAAA
2+
AAABBA
3+
AAABBA
4+
ABBAAA
5+
ABBAAA
6+
AAAAAA

crates/year2024/src/day12.rs

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
use utils::grid;
2+
use utils::prelude::*;
3+
4+
/// Counting area, perimeter and sides of shapes in a grid.
5+
#[derive(Clone, Debug)]
6+
pub struct Day12 {
7+
grid: Vec<u8>,
8+
offsets: [isize; 4],
9+
}
10+
11+
impl Day12 {
12+
pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
13+
let (_, cols, grid) =
14+
grid::from_str_padded(input, 2, 0, |b| b.is_ascii_uppercase().then_some(b))?;
15+
let offsets = [-(cols as isize), 1, cols as isize, -1];
16+
Ok(Self { grid, offsets })
17+
}
18+
19+
#[must_use]
20+
pub fn part1(&self) -> u64 {
21+
let mut visited = vec![false; self.grid.len()];
22+
let mut total = 0;
23+
for i in 0..self.grid.len() {
24+
if self.grid[i] == 0 || visited[i] {
25+
continue;
26+
}
27+
28+
let (area, perimeter) = self.flood_fill(i, &mut visited);
29+
total += area * perimeter;
30+
}
31+
total
32+
}
33+
34+
fn flood_fill(&self, i: usize, visited: &mut [bool]) -> (u64, u64) {
35+
let plant = self.grid[i];
36+
visited[i] = true;
37+
38+
let (mut area, mut perimeter) = (1, 0);
39+
for &offset in &self.offsets {
40+
let next = i.wrapping_add_signed(offset);
41+
if self.grid[next] == plant {
42+
if !visited[next] {
43+
let (a, p) = self.flood_fill(next, visited);
44+
area += a;
45+
perimeter += p;
46+
}
47+
} else {
48+
perimeter += 1;
49+
}
50+
}
51+
52+
(area, perimeter)
53+
}
54+
55+
#[must_use]
56+
pub fn part2(&self) -> u64 {
57+
let mut visited = vec![false; self.grid.len()];
58+
let mut edges = vec![[false; 4]; self.grid.len()];
59+
let mut total = 0;
60+
for i in 0..self.grid.len() {
61+
if self.grid[i] == 0 || visited[i] {
62+
continue;
63+
}
64+
65+
let (area, min_idx, max_idx) = self.edge_fill(i, &mut visited, &mut edges);
66+
67+
let mut sides = 0;
68+
for dir in 0..4 {
69+
for j in min_idx..=max_idx {
70+
if edges[j][dir] {
71+
sides += 1;
72+
self.edge_unset(j, dir, &mut edges);
73+
}
74+
}
75+
}
76+
77+
total += area * sides;
78+
}
79+
total
80+
}
81+
82+
fn edge_fill(
83+
&self,
84+
i: usize,
85+
visited: &mut [bool],
86+
edges: &mut [[bool; 4]],
87+
) -> (u64, usize, usize) {
88+
let plant = self.grid[i];
89+
visited[i] = true;
90+
91+
let (mut area, mut min_idx, mut max_idx) = (1, usize::MAX, 0);
92+
for dir in 0..4 {
93+
let next = i.wrapping_add_signed(self.offsets[dir]);
94+
if self.grid[next] == plant {
95+
if !visited[next] {
96+
let r = self.edge_fill(next, visited, edges);
97+
area += r.0;
98+
min_idx = min_idx.min(r.1);
99+
max_idx = max_idx.max(r.2);
100+
}
101+
} else {
102+
edges[next][dir] = true;
103+
min_idx = min_idx.min(next);
104+
max_idx = max_idx.max(next);
105+
}
106+
}
107+
108+
(area, min_idx, max_idx)
109+
}
110+
111+
fn edge_unset(&self, i: usize, dir: usize, edges: &mut [[bool; 4]]) {
112+
edges[i][dir] = false;
113+
for offset in [self.offsets[(dir + 1) % 4], self.offsets[(dir + 3) % 4]] {
114+
let next = i.wrapping_add_signed(offset);
115+
if edges[next][dir] {
116+
self.edge_unset(next, dir, edges);
117+
}
118+
}
119+
}
120+
}
121+
122+
examples!(Day12 -> (u64, u64) [
123+
{file: "day12_example0.txt", part1: 140, part2: 80},
124+
{file: "day12_example1.txt", part1: 772},
125+
{file: "day12_example2.txt", part1: 1930, part2: 1206},
126+
{file: "day12_example3.txt", part2: 236},
127+
{file: "day12_example4.txt", part2: 368},
128+
]);

crates/year2024/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ utils::year!(2024 => year2024, ${
1313
9 => day09::Day09<'_>,
1414
10 => day10::Day10,
1515
11 => day11::Day11,
16+
12 => day12::Day12,
1617
});

0 commit comments

Comments
 (0)