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> = contents .lines() .map(|line| line.chars().collect()) .collect(); let edges: Vec> = 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::>() }) .collect(); let mut visited: Vec> = vec![vec![false; grid[0].len()]; grid.len()]; let mut regions: Vec> = 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::>())) .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::>(); let grid = (0..grid.len()) .map(|x| (0..grid[x].len()).map(|y| mapping[&(x, y)]).collect()) .collect::>>(); 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::>()) .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::(); let area = region.len(); result += area * corners; } println!("Result 2: {}", result); }