//! Day 3: Gear Ratios use std::collections::{HashMap, HashSet}; use itertools::Itertools; use once_cell::sync::Lazy; use regex::Regex; use crate::util::grid::{NeighborsCfg, Pos}; pub const DAY: u8 = 3; pub const TITLE: &str = "Gear Ratios"; const NCFG: NeighborsCfg = NeighborsCfg { diag: true, tl: Some(Pos::usize(0, 0)), br: None, }; static NUMBERS: Lazy = Lazy::new(|| Regex::new(r"\d+").unwrap()); pub fn part1(input: Vec) -> String { let mut symbol_neighbors = HashSet::new(); for (y, line) in input.iter().enumerate() { for (x, c) in line.chars().enumerate() { if c != '.' && c.is_ascii_punctuation() { let p = Pos::usize(x, y); p.neighbors(&NCFG).for_each(|p| { symbol_neighbors.insert(p); }); } } } let mut sum = 0; for (y, line) in input.iter().enumerate() { for m in NUMBERS.captures_iter(line) { let m = m.get(0).unwrap(); for x in m.range() { let p = Pos::usize(x, y); if symbol_neighbors.contains(&p) { sum += m.as_str().parse::().unwrap(); break; } } } } sum.to_string() } pub fn part2(input: Vec) -> String { let mut numbers = HashMap::new(); for (y, line) in input.iter().enumerate() { for m in NUMBERS.captures_iter(line) { let m = m.get(0).unwrap(); let n = m.as_str().parse::().unwrap(); for x in m.range() { let p = Pos::usize(x, y); numbers.insert(p, n); } } } let mut gears = 0; for (y, line) in input.iter().enumerate() { for (x, c) in line.chars().enumerate() { if c == '*' { let p = Pos::usize(x, y); let nx = p .neighbors(&NCFG) .filter_map(|p| numbers.get(&p)) .dedup() .collect_vec(); if nx.len() == 2 { gears += nx[0] * nx[1]; } } } } gears.to_string() } #[cfg(test)] mod tests { use super::*; #[test] fn t_example1() { assert_eq!(part1(crate::read_example(DAY, 1)), "4361"); } #[test] fn t_part1() { assert_eq!(part1(crate::read_input(DAY)), "520135"); } #[test] fn t_example2() { assert_eq!(part2(crate::read_example(DAY, 2)), "467835"); } #[test] fn t_part2() { assert_eq!(part2(crate::read_input(DAY)), "72514855"); } }