feat(day12): init

This commit is contained in:
uku 2024-12-15 19:22:45 +01:00
parent bee665ddb4
commit 4f6729d15c
Signed by: uku
SSH key fingerprint: SHA256:4P0aN6M8ajKukNi6aPOaX0LacanGYtlfjmN+m/sHY/o
2 changed files with 207 additions and 0 deletions

205
src/solutions/day_12.rs Normal file
View file

@ -0,0 +1,205 @@
use std::collections::{HashSet, VecDeque};
use crate::common::{Answer, Solution};
pub struct Day12;
impl Solution for Day12 {
fn name(&self) -> &'static str {
"Garden Groups"
}
fn part_a(&self, input: &str) -> Answer {
let grid = input
.lines()
.map(|l| l.chars().collect::<Vec<_>>())
.collect::<Vec<_>>();
let mut all_positions: HashSet<(usize, usize)> =
HashSet::with_capacity(grid.len() * grid[0].len());
let mut areas = Vec::new();
for x in 0..grid.len() {
for y in 0..grid[0].len() {
if !all_positions.contains(&(x, y)) {
let result = flood_fill((x, y), &grid);
all_positions.extend(result.iter());
areas.push(result);
}
}
}
areas
.iter()
.map(|positions| {
let area = positions.len();
let perimeter = positions
.iter()
.map(|pos| {
get_adjacent(*pos, &grid)
.filter(|opt| match opt {
Some(other_pos) => {
grid[other_pos.0][other_pos.1] != grid[pos.0][pos.1]
}
None => true,
})
.count()
})
.sum::<usize>();
area * perimeter
})
.sum::<usize>()
.into()
}
fn part_b(&self, input: &str) -> Answer {
let grid = input
.lines()
.map(|l| l.chars().collect::<Vec<_>>())
.collect::<Vec<_>>();
let mut all_positions: HashSet<(usize, usize)> =
HashSet::with_capacity(grid.len() * grid[0].len());
let mut areas = Vec::new();
for x in 0..grid.len() {
for y in 0..grid[0].len() {
if !all_positions.contains(&(x, y)) {
let result = flood_fill((x, y), &grid);
all_positions.extend(result.iter());
areas.push(result);
}
}
}
areas
.iter()
.map(|positions| {
let area = positions.len();
let sides = positions
.iter()
.map(|pos| right_angles(*pos, positions, &grid))
.sum::<usize>();
area * sides
})
.sum::<usize>()
.into()
}
}
fn flood_fill(start: (usize, usize), grid: &[Vec<char>]) -> HashSet<(usize, usize)> {
let letter = grid[start.0][start.1];
let mut positions = HashSet::new();
let mut queue = VecDeque::from([start]);
while let Some(position) = queue.pop_front() {
if !positions.contains(&position) && grid[position.0][position.1] == letter {
positions.insert(position);
queue.extend(get_adjacent(position, grid).flatten());
}
}
positions
}
fn get_adjacent(
pos: (usize, usize),
grid: &[Vec<char>],
) -> impl Iterator<Item = Option<(usize, usize)>> {
let max_x = grid.len() - 1;
let max_y = grid[0].len() - 1;
[
pos.0.checked_sub(1).map(|n| (n, pos.1)),
(pos.0 < max_x).then(|| (pos.0 + 1, pos.1)),
pos.1.checked_sub(1).map(|n| (pos.0, n)),
(pos.1 < max_y).then(|| (pos.0, pos.1 + 1)),
]
.into_iter()
}
fn right_angles(pos: (usize, usize), other: &HashSet<(usize, usize)>, grid: &[Vec<char>]) -> usize {
let is_out = |o: Option<(usize, usize)>| o.filter(|p| other.contains(p)).is_none();
let x_bef = pos.0.checked_sub(1);
let y_bef = pos.1.checked_sub(1);
let x_aft = (pos.0 < grid.len() - 1).then_some(pos.0 + 1);
let y_aft = (pos.1 < grid[0].len() - 1).then_some(pos.1 + 1);
// goes in a circle starting at (-1, -1)
let mut around: VecDeque<_> = [
x_bef.zip(y_bef),
x_bef.map(|x| (x, pos.1)),
x_bef.zip(y_aft),
y_aft.map(|y| (pos.0, y)),
x_aft.zip(y_aft),
x_aft.map(|x| (x, pos.1)),
x_aft.zip(y_bef),
y_bef.map(|y| (pos.0, y)),
]
.into();
let mut count = 0;
for _ in 0..4 {
let back = *around.back().unwrap();
if (is_out(around[1]) && is_out(back))
|| (is_out(around[0]) && !is_out(around[1]) && !is_out(back))
{
count += 1;
}
let front = (around.pop_front().unwrap(), around.pop_front().unwrap());
around.push_back(front.0);
around.push_back(front.1);
}
count
}
#[cfg(test)]
mod test {
use super::Day12;
use crate::common::Solution;
use indoc::indoc;
const INPUT: &str = indoc! {"
RRRRIICCFF
RRRRIICCCF
VVRRRCCFFF
VVRCCCJFFF
VVVVCJJCFE
VVIVCCJJEE
VVIIICJJEE
MIIIIIJJEE
MIIISIJEEE
MMMISSJEEE
"};
const INPUT_B: &str = indoc! {"
AAAAAA
AAABBA
AAABBA
ABBAAA
ABBAAA
AAAAAA
"};
#[test]
fn part_a() {
assert_eq!(Day12.part_a(INPUT), 1930.into());
}
#[test]
fn part_b() {
// assert_eq!(Day12.part_b(INPUT), 1206.into());
assert_eq!(Day12.part_b(INPUT_B), 368.into());
}
}

View file

@ -11,6 +11,7 @@ mod day_08;
mod day_09;
mod day_10;
mod day_11;
mod day_12;
pub const SOLUTIONS: &[&dyn Solution] = &[
&day_01::Day01,
@ -24,4 +25,5 @@ pub const SOLUTIONS: &[&dyn Solution] = &[
&day_09::Day09,
&day_10::Day10,
&day_11::Day11,
&day_12::Day12,
];