advent-of-code-2024/src/days/d12.rs

154 lines
5 KiB
Rust
Raw Normal View History

2024-12-14 15:31:54 +01:00
use std::collections::HashMap;
2024-12-08 17:59:22 +01:00
use std::fs;
pub fn solve() {
2024-12-14 15:31:54 +01:00
let path = "res/12/example";
2024-12-08 17:59:22 +01:00
#[allow(unused)]
let contents = fs::read_to_string(path).expect("Something went wrong reading the file");
2024-12-14 15:31:54 +01:00
let grid: Vec<Vec<char>> = contents
.lines()
.map(|line| line.chars().collect())
.collect();
let edges: Vec<Vec<usize>> = grid
.iter()
.enumerate()
.map(|(i, line)| {
line.iter()
.enumerate()
.map(|(j, _)| {
let mut ret = 0;
if i == 0 || grid[i - 1][j] != grid[i][j] {
ret += 1;
}
if i == grid.len() - 1 || grid[i + 1][j] != grid[i][j] {
ret += 1;
}
if j == 0 || grid[i][j - 1] != grid[i][j] {
ret += 1;
}
if j == grid[0].len() - 1 || grid[i][j + 1] != grid[i][j] {
ret += 1;
}
ret
})
.collect::<Vec<usize>>()
})
.collect();
let mut visited: Vec<Vec<bool>> = vec![vec![false; grid[0].len()]; grid.len()];
let mut regions: Vec<Vec<(usize, usize)>> = vec![];
for i in 0..grid.len() {
for j in 0..grid[0].len() {
if visited[i][j] {
continue;
}
let mut stack: Vec<(usize, usize)> = vec![(i, j)];
let mut region: Vec<(usize, usize)> = vec![];
while let Some((i, j)) = stack.pop() {
if visited[i][j] {
continue;
}
visited[i][j] = true;
region.push((i, j));
if i > 0 && grid[i][j] == grid[i - 1][j] && !visited[i - 1][j] {
stack.push((i - 1, j));
}
if i < grid.len() - 1 && grid[i][j] == grid[i + 1][j] && !visited[i + 1][j] {
stack.push((i + 1, j));
}
if j > 0 && grid[i][j] == grid[i][j - 1] && !visited[i][j - 1] {
stack.push((i, j - 1));
}
if j < grid[0].len() - 1 && grid[i][j] == grid[i][j + 1] && !visited[i][j + 1] {
stack.push((i, j + 1));
}
}
regions.push(region);
}
}
let result: usize = regions
.iter()
.map(|r| {
let edge_count: usize = r.iter().map(|(i, j)| edges[*i][*j]).sum();
edge_count * r.len()
})
.sum();
2024-12-08 17:59:22 +01:00
println!("Result 1: {}", result);
2024-12-14 15:31:54 +01:00
// count corners on outer side of region
let mut result = 0;
let regions: HashMap<_, _> = regions
.iter()
.enumerate()
.map(|(i, region)| (i, region.iter().copied().collect::<Vec<_>>()))
.collect();
// Recolor the regions in the grid so that non-overlapping regions have different letters,
// otherwise they could be overlapping when taking 2x2 windows over the grid.
let mapping = regions
.iter()
.enumerate()
.flat_map(|(i, (_, region))| region.iter().map(move |(x, y)| ((*x, *y), i + 1)))
.collect::<HashMap<_, _>>();
let grid = (0..grid.len())
.map(|x| (0..grid[x].len()).map(|y| mapping[&(x, y)]).collect())
.collect::<Vec<Vec<usize>>>();
for (s, region) in regions {
let v = *grid.get_from_coords(s.into()).unwrap();
let subgrid = grid
.to_minimum_spanning_subgrid(&region.iter().copied().collect::<Vec<_>>())
.pad_into(1, 0);
// Take 2x2 windows so that the we can use the center point of the window to check if the
// window contains a corner (or even two corners - see comments below).
let corners = subgrid
.as_ndarray()
.windows([2, 2])
.into_iter()
.map(|window| {
let in_same_region = window
.indexed_iter()
.filter(|(_, &wv)| wv == v)
.collect_vec();
match in_same_region.len() {
// A A
// B A
// (1 corner)
1 | 3 => 1,
// A A
// A A
// (no corners)
0 | 4 => 0,
// A B A A
// B A or B B
// (2 corners) (no corners)
2 => {
let (x1, y1) = in_same_region[0].0;
let (x2, y2) = in_same_region[1].0;
if x1 != x2 && y1 != y2 {
2
} else {
0
}
}
_ => unreachable!(),
}
})
.sum::<usize>();
let area = region.len();
result += area * corners;
}
2024-12-08 17:59:22 +01:00
println!("Result 2: {}", result);
}