AoC23/src/day03.rs

142 lines
3.8 KiB
Rust

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::<HashSet<_>>();
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<Vec<char>>,
}
impl FromStr for Grid {
type Err = Box<dyn std::error::Error>;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let chars = s
.lines()
.filter(|l| !l.is_empty())
.map(|l| l.trim().chars().collect::<Vec<_>>())
.collect::<Vec<_>>();
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::<String>()
.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::<Vec<_>>();
adjacent[0] * adjacent[1]
}
}