153 lines
5 KiB
Rust
153 lines
5 KiB
Rust
use std::collections::HashMap;
|
|
use std::fs;
|
|
|
|
pub fn solve() {
|
|
let path = "res/12/example";
|
|
|
|
#[allow(unused)]
|
|
let contents = fs::read_to_string(path).expect("Something went wrong reading the file");
|
|
|
|
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();
|
|
|
|
println!("Result 1: {}", result);
|
|
|
|
// 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(®ion.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;
|
|
}
|
|
|
|
println!("Result 2: {}", result);
|
|
}
|