use itertools::Itertools; use std::fmt::Debug; pub fn run() { let input = include_str!("../input/day11.txt"); // let input = r#"...#...... // .......#.. // #......... // .......... // ......#... // .#........ // .........# // .......... // .......#.. // #...#....."#; let image: Image = input.into(); let expansions = image.expand(); dbg!(calculate_distances(&image, &expansions, 2)); dbg!(calculate_distances(&image, &expansions, 1_000_000)); } fn calculate_distances(image: &Image, expansions: &Expansions, factor: usize) -> usize { image .iter_galaxies() .combinations(2) .map(|v| expansions.distance(factor, v[0], v[1])) .sum::() } #[derive(Clone)] struct Image { chars: Vec>, width: usize, height: usize, } impl From<&str> for Image { fn from(value: &str) -> Self { let chars = value .trim() .lines() .map(|l| l.trim().chars().collect::>()) .collect::>(); let width = chars[0].len(); let height = chars.len(); Self { chars, width, height, } } } impl Debug for Image { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let s = self .chars .iter() .map(|row| row.iter().collect::()) .collect::>() .join("\n"); write!(f, "Image(\n{})", s) } } impl Image { fn at(&self, col: usize, row: usize) -> char { if col < self.chars[0].len() && row < self.chars.len() { self.chars[row][col] } else { '.' } } fn expand(&self) -> Expansions { let rows = (0..self.height) .rev() .filter(|&row| (0..self.width).all(|col| self.at(col, row) == '.')) .collect::>(); let cols = (0..self.width) .rev() .filter(|&col| (0..self.height).all(|row| self.at(col, row) == '.')) .collect::>(); Expansions { rows, cols } } fn iter_galaxies<'a>(&'a self) -> impl Iterator + 'a { (0..self.height) .into_iter() .flat_map(|row| (0..self.width).into_iter().map(move |col| (col, row))) .filter(|(col, row)| self.at(*col, *row) == '#') } } #[derive(Debug, Clone)] struct Expansions { rows: Vec, cols: Vec, } impl Expansions { fn distance(&self, factor: usize, (x1, y1): (usize, usize), (x2, y2): (usize, usize)) -> usize { let min_x = x1.min(x2); let max_x = x1.max(x2); let min_y = y1.min(y2); let max_y = y1.max(y2); let expanded_rows = self .rows .iter() .filter(|row| (min_y..=max_y).contains(row)) .count(); let expanded_cols = self .cols .iter() .filter(|col| (min_x..=max_x).contains(col)) .count(); (max_x - min_x) + (max_y - min_y) + (expanded_rows + expanded_cols) * (factor - 1) } }