diff --git a/2023/day3/src/main.rs b/2023/day3/src/main.rs index 91fd583..90e7f29 100644 --- a/2023/day3/src/main.rs +++ b/2023/day3/src/main.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + fn main() { let kind = std::env::args().nth(1).unwrap_or("naive".into()); @@ -57,27 +59,81 @@ fn part1(input: &str) -> u64 { } fn part2(input: &str) -> u64 { - 0 + fn contains_gear(s: &str) -> Option { + s.chars().position(|c| !c.is_ascii_digit() && c != '.') + } + + let len = input.lines().next().unwrap().len(); + let empty_border = std::iter::repeat('.').take(len).collect::(); + + let mut prev2 = empty_border.as_str(); + let mut prev1 = input.lines().next().unwrap(); + + let mut gears: HashMap<(usize, usize), Vec> = HashMap::new(); + + for (line_number, next) in input + .lines() + .skip(1) + .chain(Some(empty_border.as_str())) + .enumerate() + { + let mut numbers = prev1.char_indices().peekable(); + while let Some((start, c)) = numbers.next() { + if c.is_ascii_digit() { + let mut end = start; + while let Some((idx, '0'..='9')) = numbers.next() { + end = idx; + } + + let box_bounds = (start.saturating_sub(1))..std::cmp::min(end + 2, len); + let number = prev1[start..=end].parse::().unwrap(); + + if let Some(position) = contains_gear(&prev2[box_bounds.clone()]) { + let key = (line_number - 1, box_bounds.start + position); + gears.entry(key).or_default().push(number); + } + + if let Some(position) = contains_gear(&prev1[box_bounds.clone()]) { + let key = (line_number, box_bounds.start + position); + gears.entry(key).or_default().push(number); + } + + if let Some(position) = contains_gear(&next[box_bounds.clone()]) { + let key = (line_number + 1, box_bounds.start + position); + gears.entry(key).or_default().push(number); + } + } + } + + prev2 = prev1; + prev1 = next; + } + + gears + .values() + .filter(|v| v.len() == 2) + .map(|v| v[0] * v[1]) + .sum() } #[cfg(test)] mod tests { #[test] fn part1_small() { - assert_eq!(super::part1(include_str!("../input_small.txt")), 8); + assert_eq!(super::part1(include_str!("../input_small.txt")), 4361); } #[test] fn part1() { - assert_eq!(super::part1(include_str!("../input.txt")), 1931); + assert_eq!(super::part1(include_str!("../input.txt")), 537832); } #[test] fn part2_small() { - assert_eq!(super::part2(include_str!("../input_small.txt")), 2286); + assert_eq!(super::part2(include_str!("../input_small.txt")), 467835); } #[test] fn part2() { - assert_eq!(super::part2(include_str!("../input.txt")), 83105); + assert_eq!(super::part2(include_str!("../input.txt")), 81939900); } }