Skip to content

Commit

Permalink
AoC 2024 Day 12 - rust
Browse files Browse the repository at this point in the history
  • Loading branch information
pareronia committed Dec 19, 2024
1 parent 7b6b718 commit 0b0f5f4
Show file tree
Hide file tree
Showing 4 changed files with 278 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
| ---| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 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) | | | | | | |
| 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) | | | | | | | | | | |
| 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) | | | | | | | |
| 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) | | | | | | | |
<!-- @END:ImplementationsTable:2024@ -->

## 2023
Expand Down
8 changes: 8 additions & 0 deletions src/main/rust/AoC2024_12/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[package]
name = "AoC2024_12"
version = "0.1.0"
edition = "2021"

[dependencies]
aoc = { path = "../aoc" }
itertools = "0.11"
261 changes: 261 additions & 0 deletions src/main/rust/AoC2024_12/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
#![allow(non_snake_case)]

use aoc::geometry::Direction;
use aoc::graph::BFS;
use aoc::grid::Cell;
use aoc::Puzzle;
use itertools::Itertools;
use std::collections::{HashMap, HashSet};

#[derive(Clone, Debug)]
struct Regions {
plots_by_plant: HashMap<char, HashSet<Cell>>,
}

impl Regions {
fn new() -> Self {
Self {
plots_by_plant: HashMap::new(),
}
}

fn iter(&self) -> RegionIterator {
RegionIterator {
all_plots_with_plant: self.plots_by_plant.values().collect(),
index: 0,
seen: HashSet::new(),
}
}
}

impl FromIterator<(char, Cell)> for Regions {
fn from_iter<I: IntoIterator<Item = (char, Cell)>>(iter: I) -> Self {
let mut regions = Regions::new();
for (ch, cell) in iter {
regions
.plots_by_plant
.entry(ch)
.and_modify(|s| {
s.insert(cell);
})
.or_insert(HashSet::from([cell]));
}
regions
}
}

struct RegionIterator<'a> {
all_plots_with_plant: Vec<&'a HashSet<Cell>>,
index: usize,
seen: HashSet<Cell>,
}

#[allow(clippy::needless_lifetimes)]
impl<'a> Iterator for RegionIterator<'a> {
type Item = HashSet<Cell>;

fn next(&mut self) -> Option<Self::Item> {
if self.seen.len() == self.all_plots_with_plant[self.index].len() {
if self.index + 1 == self.all_plots_with_plant.len() {
return None;
}
self.index += 1;
self.seen.clear();
}
let region = BFS::flood_fill(
*self.all_plots_with_plant[self.index]
.difference(&self.seen)
.next()
.unwrap(),
|cell| {
cell.capital_neighbours()
.into_iter()
.filter(|n| {
self.all_plots_with_plant[self.index].contains(n)
&& !self.seen.contains(n)
})
.collect()
},
);
region.iter().for_each(|r| {
self.seen.insert(*r);
});
Some(region)
}
}

struct AoC2024_12;

impl AoC2024_12 {
fn solve<F>(&self, input: &[String], count: F) -> usize
where
F: Fn(&Cell, &HashSet<Cell>) -> usize,
{
let regions: Regions = (0..input.len())
.cartesian_product(0..input[0].len())
.map(|(r, c)| (input[r].chars().nth(c).unwrap(), Cell::at(r, c)))
.collect();
regions
.iter()
.map(|r| {
r.iter()
.map(|plot| count(plot, &r) * r.len())
.sum::<usize>()
})
.sum()
}
}

impl aoc::Puzzle for AoC2024_12 {
type Input = Vec<String>;
type Output1 = usize;
type Output2 = usize;

aoc::puzzle_year_day!(2024, 12);

fn parse_input(&self, lines: Vec<String>) -> Self::Input {
lines
}

fn part_1(&self, input: &Self::Input) -> Self::Output1 {
let count_edges = |plot: &Cell, region: &HashSet<Cell>| {
4 - plot
.capital_neighbours()
.iter()
.filter(|n| region.contains(n))
.count()
};
self.solve(input, count_edges)
}

fn part_2(&self, input: &Self::Input) -> Self::Output2 {
let corner_dirs = [
[Direction::LeftAndUp, Direction::Left, Direction::Up],
[Direction::RightAndUp, Direction::Right, Direction::Up],
[Direction::RightAndDown, Direction::Right, Direction::Down],
[Direction::LeftAndDown, Direction::Left, Direction::Down],
];
let matches = [
[false, false, false],
[true, false, false],
[false, true, true],
];
let count_corners = |plot: &Cell, region: &HashSet<Cell>| {
corner_dirs
.iter()
.filter(|d| {
let test = (0..3)
.map(|i| match plot.try_at(d[i]) {
Some(n) => region.contains(&n),
None => false,
})
.collect::<Vec<bool>>();
matches.iter().any(|m| *m == *test)
})
.count()
};
self.solve(input, count_corners)
}

fn samples(&self) {
aoc::puzzle_samples! {
self, part_1, TEST1, 140,
self, part_1, TEST2, 772,
self, part_1, TEST3, 1930,
self, part_2, TEST1, 80,
self, part_2, TEST2, 436,
self, part_2, TEST3, 1206,
self, part_2, TEST4, 236,
self, part_2, TEST5, 368
};
}
}

fn main() {
AoC2024_12 {}.run(std::env::args());
}

const TEST1: &str = "\
AAAA
BBCD
BBCC
EEEC
";
const TEST2: &str = "\
OOOOO
OXOXO
OOOOO
OXOXO
OOOOO
";
const TEST3: &str = "\
RRRRIICCFF
RRRRIICCCF
VVRRRCCFFF
VVRCCCJFFF
VVVVCJJCFE
VVIVCCJJEE
VVIIICJJEE
MIIIIIJJEE
MIIISIJEEE
MMMISSJEEE
";
const TEST4: &str = "\
EEEEE
EXXXX
EEEEE
EXXXX
EEEEE
";
const TEST5: &str = "\
AAAAAA
AAABBA
AAABBA
ABBAAA
ABBAAA
AAAAAA
";

#[cfg(test)]
mod tests {
use super::*;

#[test]
pub fn samples() {
AoC2024_12 {}.samples();
}

#[test]
pub fn regions_iterator() {
// AABBC
// ACCDC
let regions: Regions = [
('A', Cell::at(0, 0)),
('A', Cell::at(0, 1)),
('B', Cell::at(0, 2)),
('B', Cell::at(0, 3)),
('C', Cell::at(0, 4)),
('A', Cell::at(1, 0)),
('C', Cell::at(1, 1)),
('C', Cell::at(1, 2)),
('D', Cell::at(1, 3)),
('C', Cell::at(1, 4)),
]
.into_iter()
.collect();
let ans = regions.iter().collect::<Vec<_>>();
// A
assert!(ans.contains(&HashSet::from([
Cell::at(0, 0),
Cell::at(0, 1),
Cell::at(1, 0),
])));
// B
assert!(ans.contains(&HashSet::from([Cell::at(0, 2), Cell::at(0, 3),])));
// C
assert!(ans.contains(&HashSet::from([Cell::at(0, 4), Cell::at(1, 4),])));
assert!(ans.contains(&HashSet::from([Cell::at(1, 1), Cell::at(1, 2),])));
// D
assert!(ans.contains(&HashSet::from([Cell::at(1, 3)])));
}
}
8 changes: 8 additions & 0 deletions src/main/rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 0b0f5f4

Please sign in to comment.