Skip to content

Commit 0b0f5f4

Browse files
committed
AoC 2024 Day 12 - rust
1 parent 7b6b718 commit 0b0f5f4

File tree

4 files changed

+278
-1
lines changed

4 files changed

+278
-1
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
| ---| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
1111
| 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) | [](src/main/python/AoC2024_13.py) | [](src/main/python/AoC2024_14.py) | [](src/main/python/AoC2024_15.py) | [](src/main/python/AoC2024_16.py) | [](src/main/python/AoC2024_17.py) | [](src/main/python/AoC2024_18.py) | [](src/main/python/AoC2024_19.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) | [](src/main/java/AoC2024_12.java) | | [](src/main/java/AoC2024_14.java) | [](src/main/java/AoC2024_15.java) | | | | | | | | | | |
13-
| 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) | | [](src/main/rust/AoC2024_10/src/main.rs) | [](src/main/rust/AoC2024_11/src/main.rs) | | [](src/main/rust/AoC2024_13/src/main.rs) | [](src/main/rust/AoC2024_14/src/main.rs) | [](src/main/rust/AoC2024_15/src/main.rs) | [](src/main/rust/AoC2024_16/src/main.rs) | [](src/main/rust/AoC2024_17/src/main.rs) | [](src/main/rust/AoC2024_18/src/main.rs) | | | | | | | |
13+
| 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) | | [](src/main/rust/AoC2024_10/src/main.rs) | [](src/main/rust/AoC2024_11/src/main.rs) | [](src/main/rust/AoC2024_12/src/main.rs) | [](src/main/rust/AoC2024_13/src/main.rs) | [](src/main/rust/AoC2024_14/src/main.rs) | [](src/main/rust/AoC2024_15/src/main.rs) | [](src/main/rust/AoC2024_16/src/main.rs) | [](src/main/rust/AoC2024_17/src/main.rs) | [](src/main/rust/AoC2024_18/src/main.rs) | | | | | | | |
1414
<!-- @END:ImplementationsTable:2024@ -->
1515

1616
## 2023
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[package]
2+
name = "AoC2024_12"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
aoc = { path = "../aoc" }
8+
itertools = "0.11"
Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
#![allow(non_snake_case)]
2+
3+
use aoc::geometry::Direction;
4+
use aoc::graph::BFS;
5+
use aoc::grid::Cell;
6+
use aoc::Puzzle;
7+
use itertools::Itertools;
8+
use std::collections::{HashMap, HashSet};
9+
10+
#[derive(Clone, Debug)]
11+
struct Regions {
12+
plots_by_plant: HashMap<char, HashSet<Cell>>,
13+
}
14+
15+
impl Regions {
16+
fn new() -> Self {
17+
Self {
18+
plots_by_plant: HashMap::new(),
19+
}
20+
}
21+
22+
fn iter(&self) -> RegionIterator {
23+
RegionIterator {
24+
all_plots_with_plant: self.plots_by_plant.values().collect(),
25+
index: 0,
26+
seen: HashSet::new(),
27+
}
28+
}
29+
}
30+
31+
impl FromIterator<(char, Cell)> for Regions {
32+
fn from_iter<I: IntoIterator<Item = (char, Cell)>>(iter: I) -> Self {
33+
let mut regions = Regions::new();
34+
for (ch, cell) in iter {
35+
regions
36+
.plots_by_plant
37+
.entry(ch)
38+
.and_modify(|s| {
39+
s.insert(cell);
40+
})
41+
.or_insert(HashSet::from([cell]));
42+
}
43+
regions
44+
}
45+
}
46+
47+
struct RegionIterator<'a> {
48+
all_plots_with_plant: Vec<&'a HashSet<Cell>>,
49+
index: usize,
50+
seen: HashSet<Cell>,
51+
}
52+
53+
#[allow(clippy::needless_lifetimes)]
54+
impl<'a> Iterator for RegionIterator<'a> {
55+
type Item = HashSet<Cell>;
56+
57+
fn next(&mut self) -> Option<Self::Item> {
58+
if self.seen.len() == self.all_plots_with_plant[self.index].len() {
59+
if self.index + 1 == self.all_plots_with_plant.len() {
60+
return None;
61+
}
62+
self.index += 1;
63+
self.seen.clear();
64+
}
65+
let region = BFS::flood_fill(
66+
*self.all_plots_with_plant[self.index]
67+
.difference(&self.seen)
68+
.next()
69+
.unwrap(),
70+
|cell| {
71+
cell.capital_neighbours()
72+
.into_iter()
73+
.filter(|n| {
74+
self.all_plots_with_plant[self.index].contains(n)
75+
&& !self.seen.contains(n)
76+
})
77+
.collect()
78+
},
79+
);
80+
region.iter().for_each(|r| {
81+
self.seen.insert(*r);
82+
});
83+
Some(region)
84+
}
85+
}
86+
87+
struct AoC2024_12;
88+
89+
impl AoC2024_12 {
90+
fn solve<F>(&self, input: &[String], count: F) -> usize
91+
where
92+
F: Fn(&Cell, &HashSet<Cell>) -> usize,
93+
{
94+
let regions: Regions = (0..input.len())
95+
.cartesian_product(0..input[0].len())
96+
.map(|(r, c)| (input[r].chars().nth(c).unwrap(), Cell::at(r, c)))
97+
.collect();
98+
regions
99+
.iter()
100+
.map(|r| {
101+
r.iter()
102+
.map(|plot| count(plot, &r) * r.len())
103+
.sum::<usize>()
104+
})
105+
.sum()
106+
}
107+
}
108+
109+
impl aoc::Puzzle for AoC2024_12 {
110+
type Input = Vec<String>;
111+
type Output1 = usize;
112+
type Output2 = usize;
113+
114+
aoc::puzzle_year_day!(2024, 12);
115+
116+
fn parse_input(&self, lines: Vec<String>) -> Self::Input {
117+
lines
118+
}
119+
120+
fn part_1(&self, input: &Self::Input) -> Self::Output1 {
121+
let count_edges = |plot: &Cell, region: &HashSet<Cell>| {
122+
4 - plot
123+
.capital_neighbours()
124+
.iter()
125+
.filter(|n| region.contains(n))
126+
.count()
127+
};
128+
self.solve(input, count_edges)
129+
}
130+
131+
fn part_2(&self, input: &Self::Input) -> Self::Output2 {
132+
let corner_dirs = [
133+
[Direction::LeftAndUp, Direction::Left, Direction::Up],
134+
[Direction::RightAndUp, Direction::Right, Direction::Up],
135+
[Direction::RightAndDown, Direction::Right, Direction::Down],
136+
[Direction::LeftAndDown, Direction::Left, Direction::Down],
137+
];
138+
let matches = [
139+
[false, false, false],
140+
[true, false, false],
141+
[false, true, true],
142+
];
143+
let count_corners = |plot: &Cell, region: &HashSet<Cell>| {
144+
corner_dirs
145+
.iter()
146+
.filter(|d| {
147+
let test = (0..3)
148+
.map(|i| match plot.try_at(d[i]) {
149+
Some(n) => region.contains(&n),
150+
None => false,
151+
})
152+
.collect::<Vec<bool>>();
153+
matches.iter().any(|m| *m == *test)
154+
})
155+
.count()
156+
};
157+
self.solve(input, count_corners)
158+
}
159+
160+
fn samples(&self) {
161+
aoc::puzzle_samples! {
162+
self, part_1, TEST1, 140,
163+
self, part_1, TEST2, 772,
164+
self, part_1, TEST3, 1930,
165+
self, part_2, TEST1, 80,
166+
self, part_2, TEST2, 436,
167+
self, part_2, TEST3, 1206,
168+
self, part_2, TEST4, 236,
169+
self, part_2, TEST5, 368
170+
};
171+
}
172+
}
173+
174+
fn main() {
175+
AoC2024_12 {}.run(std::env::args());
176+
}
177+
178+
const TEST1: &str = "\
179+
AAAA
180+
BBCD
181+
BBCC
182+
EEEC
183+
";
184+
const TEST2: &str = "\
185+
OOOOO
186+
OXOXO
187+
OOOOO
188+
OXOXO
189+
OOOOO
190+
";
191+
const TEST3: &str = "\
192+
RRRRIICCFF
193+
RRRRIICCCF
194+
VVRRRCCFFF
195+
VVRCCCJFFF
196+
VVVVCJJCFE
197+
VVIVCCJJEE
198+
VVIIICJJEE
199+
MIIIIIJJEE
200+
MIIISIJEEE
201+
MMMISSJEEE
202+
";
203+
const TEST4: &str = "\
204+
EEEEE
205+
EXXXX
206+
EEEEE
207+
EXXXX
208+
EEEEE
209+
";
210+
const TEST5: &str = "\
211+
AAAAAA
212+
AAABBA
213+
AAABBA
214+
ABBAAA
215+
ABBAAA
216+
AAAAAA
217+
";
218+
219+
#[cfg(test)]
220+
mod tests {
221+
use super::*;
222+
223+
#[test]
224+
pub fn samples() {
225+
AoC2024_12 {}.samples();
226+
}
227+
228+
#[test]
229+
pub fn regions_iterator() {
230+
// AABBC
231+
// ACCDC
232+
let regions: Regions = [
233+
('A', Cell::at(0, 0)),
234+
('A', Cell::at(0, 1)),
235+
('B', Cell::at(0, 2)),
236+
('B', Cell::at(0, 3)),
237+
('C', Cell::at(0, 4)),
238+
('A', Cell::at(1, 0)),
239+
('C', Cell::at(1, 1)),
240+
('C', Cell::at(1, 2)),
241+
('D', Cell::at(1, 3)),
242+
('C', Cell::at(1, 4)),
243+
]
244+
.into_iter()
245+
.collect();
246+
let ans = regions.iter().collect::<Vec<_>>();
247+
// A
248+
assert!(ans.contains(&HashSet::from([
249+
Cell::at(0, 0),
250+
Cell::at(0, 1),
251+
Cell::at(1, 0),
252+
])));
253+
// B
254+
assert!(ans.contains(&HashSet::from([Cell::at(0, 2), Cell::at(0, 3),])));
255+
// C
256+
assert!(ans.contains(&HashSet::from([Cell::at(0, 4), Cell::at(1, 4),])));
257+
assert!(ans.contains(&HashSet::from([Cell::at(1, 1), Cell::at(1, 2),])));
258+
// D
259+
assert!(ans.contains(&HashSet::from([Cell::at(1, 3)])));
260+
}
261+
}

src/main/rust/Cargo.lock

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)