#![forbid(elided_lifetimes_in_paths, unsafe_code)] use aoc23::read; use chumsky::{prelude::*, text::int}; use std::collections::HashMap; enum Entity { Void, Symbol(char), Number { num: usize, len: usize } } impl Entity { fn len(&self) -> usize { match self { Self::Void | Self::Symbol(_) => 1, Self::Number { len, .. } => *len } } fn parser() -> impl Parser> { choice(( just('.').map(|_| Self::Void), int(10).map(|num: String| Self::Number { num: num.parse().unwrap(), len: num.len() }), none_of(['\n']).map(Self::Symbol) )) } } struct Line { entities: HashMap } impl Line { fn parser() -> impl Parser> { Entity::parser().repeated().map(|line| { let mut col = 0; let mut entities = HashMap::new(); for ent in line { let len = ent.len(); entities.insert(col, ent); col += len; } Line { entities } }) } } fn parser() -> impl Parser, Error = Simple> { Line::parser() .then_ignore(just("\n")) .repeated() .then_ignore(end()) } fn main() -> anyhow::Result<()> { let grid = read("inputs/day3.txt", parser())?; let mut sum = 0; for (i, line) in grid.iter().enumerate() { 'entities: for (col, ent) in &line.entities { let Entity::Number { num, len } = ent else { continue; }; // see if adjacent for search_row in i.saturating_sub(1) ..= i + 1 { let Some(row) = grid.get(search_row) else { continue; }; for search_col in col.saturating_sub(1) ..= col + len { if let Some(Entity::Symbol(_)) = row.entities.get(&search_col) { sum += num; continue 'entities; } } } } } println!("{sum}"); Ok(()) }