use std::{collections::HashSet, str::FromStr}; pub fn run() { let input = include_str!("../input/day3.txt"); // let input = r#"467..114.. // ...*...... // ..35..633. // ......#... // 617*...... // .....+.58. // ..592..... // ......755. // ...$.*.... // .664.598.."#; let grid: Grid = input.parse().unwrap(); let all_part_numbers = grid .symbol_positions() .into_iter() .flat_map(|pos| grid.adjacent_numbers(pos)) .collect::>(); let parts_sum: u32 = all_part_numbers .into_iter() .map(|pos| grid.parse_number_starting_at(pos)) .sum(); dbg!(parts_sum); let gears_sum: u32 = grid .gear_positions() .into_iter() .map(|pos| grid.gear_ratio(pos)) .sum(); dbg!(gears_sum); } struct Grid { chars: Vec>, } impl FromStr for Grid { type Err = Box; fn from_str(s: &str) -> Result { let chars = s .lines() .filter(|l| !l.is_empty()) .map(|l| l.trim().chars().collect::>()) .collect::>(); let count = chars[0].len(); for line in chars.iter().skip(1) { assert_eq!(count, line.len()); } Ok(Grid { chars }) } } impl Grid { fn symbol_positions(&self) -> Vec<(usize, usize)> { let mut positions = vec![]; for (row, line) in self.chars.iter().enumerate() { for (col, c) in line.iter().enumerate() { if c.is_ascii_digit() { continue; } else if *c == '.' { continue; } else { positions.push((col, row)); } } } positions } fn adjacent_numbers(&self, (col, row): (usize, usize)) -> HashSet<(usize, usize)> { let mut adjacent = HashSet::new(); for dc in -1isize..=1 { for dr in -1isize..=1 { if dc == 0 && dr == 0 { continue; } let c = col as isize + dc; let r = row as isize + dr; if c < 0 || c >= self.chars[0].len() as isize || r < 0 || r >= self.chars.len() as isize { continue; } if let Some(pos) = self.start_position_of_number_containig((c as usize, r as usize)) { adjacent.insert(pos); } } } adjacent } fn start_position_of_number_containig( &self, (col, row): (usize, usize), ) -> Option<(usize, usize)> { let line = &self.chars[row]; if !line[col].is_ascii_digit() { return None; } let mut start = col; while start > 0 && line[start - 1].is_ascii_digit() { start -= 1; } Some((start, row)) } fn parse_number_starting_at(&self, (col, row): (usize, usize)) -> u32 { self.chars[row][col..] .iter() .take_while(|c| c.is_ascii_digit()) .collect::() .parse() .unwrap() } fn gear_positions(&self) -> Vec<(usize, usize)> { self.symbol_positions() .into_iter() .filter(|(col, row)| { self.chars[*row][*col] == '*' && self.adjacent_numbers((*col, *row)).len() == 2 }) .collect() } fn gear_ratio(&self, pos: (usize, usize)) -> u32 { let adjacent = self .adjacent_numbers(pos) .into_iter() .map(|pos| self.parse_number_starting_at(pos)) .collect::>(); adjacent[0] * adjacent[1] } }