use std::{ collections::{BTreeSet, HashSet}, fs::File, io::{BufRead, BufReader}, path::Path, }; fn get_around(map: &[Vec], p: (usize, usize)) -> Vec<(char, usize, usize)> { let mut around_us = vec![]; if p.0 > 0 { let (y, x) = (p.0 - 1, p.1); around_us.push((map[y][x], y, x)); } if let Some(row) = map.get(p.0 + 1) { let (y, x) = (p.0 + 1, p.1); around_us.push((row[x], y, x)); } if p.1 > 0 { let (y, x) = (p.0, p.1 - 1); around_us.push((map[y][x], y, x)); } if let Some(f) = map[p.0].get(p.1 + 1) { let (y, x) = (p.0, p.1 + 1); around_us.push((*f, y, x)); } around_us } #[derive(Debug, Eq, Hash, PartialEq)] struct Region { c: char, plots: BTreeSet, } impl Region { fn read_from_map(map: &[Vec], c: &char, p: (usize, usize)) -> Self { let mut region = Region { c: *c, plots: BTreeSet::new(), }; region.plots.insert(Plot { y: p.0, x: p.1, perimiter: (get_around(map, (p.0, p.1)) .iter() .filter(|(f, _, _)| f == c) .collect::>() .len() as i32 - 4) .unsigned_abs() as usize, }); region.read_recurse(map, c, p); region } fn read_recurse(&mut self, map: &[Vec], c: &char, p: (usize, usize)) { for (f, y, x) in get_around(map, p) { if f == *c { let perimiter = (get_around(map, (y, x)) .iter() .filter(|(f, _, _)| f == c) .collect::>() .len() as i32 - 4) .unsigned_abs() as usize; if self.plots.insert(Plot { y, x, perimiter }) { self.read_recurse(map, c, (y, x)); } } } } fn get_fence_cost(&self, map: &[Vec], max_pos: usize) -> usize { // Okay so, instead of perimiters * len, sides * len // form all possible rows and columns of plots // AAAA // BB, B, B, BB // B, B // C, C, CC, C // C, C // D // EEE let mut rows_initial: Vec> = vec![]; for i in 0..max_pos { let mut a: Vec = self.plots.iter().filter(|p| p.y == i).cloned().collect(); if !a.is_empty() { a.sort_by_key(|p| p.x); rows_initial.push(a); } } let mut rows: Vec> = vec![]; for row in &rows_initial { let mut real_rows: Vec> = vec![]; let mut last = None; let mut current_real_row: Vec = vec![]; for p in row { if last.is_none() { current_real_row.push(p.clone()); last = Some(p); continue; } if p.x != last.unwrap().x + 1 { real_rows.push(current_real_row.clone()); current_real_row.clear(); } current_real_row.push(p.clone()); last = Some(p); } real_rows.push(current_real_row.clone()); rows.append(&mut real_rows); } let mut row_sides = 0; for row in rows { // top let mut sides = 0; let mut side_length = 0; for p in &row { let fence = if p.y > 0 { map[p.y - 1][p.x] != self.c } else { true }; if fence { side_length += 1; } else if side_length > 0 { sides += 1; side_length = 0; } } if side_length > 0 { sides += 1; } row_sides += sides; // bottom let mut sides = 0; let mut side_length = 0; for p in &row { let fence = if let Some(below) = map.get(p.y + 1) { below[p.x] != self.c } else { true }; if fence { side_length += 1; } else if side_length > 0 { sides += 1; side_length = 0; } } if side_length > 0 { sides += 1; } row_sides += sides; } let mut cols_initial: Vec> = vec![]; for i in 0..max_pos { let mut a: Vec = self.plots.iter().filter(|p| p.x == i).cloned().collect(); if !a.is_empty() { a.sort_by_key(|p| p.y); cols_initial.push(a); } } let mut cols: Vec> = vec![]; for col in &cols_initial { let mut real_cols: Vec> = vec![]; let mut last = None; let mut current_real_col: Vec = vec![]; for p in col { if last.is_none() { current_real_col.push(p.clone()); last = Some(p); continue; } if p.y != last.unwrap().y + 1 { real_cols.push(current_real_col.clone()); current_real_col.clear(); } current_real_col.push(p.clone()); last = Some(p); } real_cols.push(current_real_col.clone()); cols.append(&mut real_cols); } let mut col_sides = 0; for col in cols { // right let mut sides = 0; let mut side_length = 0; for p in &col { let fence = if p.x > 0 { map[p.y][p.x - 1] != self.c } else { true }; if fence { side_length += 1; } else if side_length > 0 { sides += 1; side_length = 0; } } if side_length > 0 { sides += 1; } col_sides += sides; // left let mut sides = 0; let mut side_length = 0; for p in &col { let fence = if let Some(left) = map[p.y].get(p.x + 1) { *left != self.c } else { true }; if fence { side_length += 1; } else if side_length > 0 { sides += 1; side_length = 0; } } if side_length > 0 { sides += 1; } col_sides += sides; } self.plots.len() * (row_sides + col_sides) } } #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] struct Plot { y: usize, x: usize, perimiter: usize, } pub fn part_two(input: &Path) -> anyhow::Result { let reader = BufReader::new(File::open(input)?); let mut map: Vec> = vec![]; for line in reader.lines() { map.push(line?.chars().collect()); } let mut regions: HashSet = HashSet::new(); for (y, row) in map.iter().enumerate() { for (x, c) in row.iter().enumerate() { regions.insert(Region::read_from_map(&map, c, (y, x))); } } Ok(regions .iter() .map(|r| -> usize { r.get_fence_cost(&map, map.len()) }) .sum()) }