diff --git a/src/bin/day10.rs b/src/bin/day10.rs new file mode 100644 index 0000000..521d4cb --- /dev/null +++ b/src/bin/day10.rs @@ -0,0 +1,142 @@ +use std::{collections::HashSet, time::Instant}; + +use aoc_2024::{load, print_res}; +use arrayvec::ArrayVec; +use bstr::{BString, ByteSlice}; + +type Parsed = Vec>; + +#[inline(never)] +pub fn parsing(input: &BString) -> color_eyre::Result { + input + .lines() + .map(|line| { + line.iter() + .map(|&x| match x { + b'0'..=b'9' => Ok(x - b'0'), + _ => Err(color_eyre::eyre::eyre!( + "Unknown character: '{}'", + x as char + )), + }) + .collect() + }) + .collect() +} + +fn neighbours(l: usize, c: usize, map: &Parsed) -> ArrayVec<(usize, usize), 4> { + let mut a = ArrayVec::new(); + + if l != 0 { + a.push((l - 1, c)); + } + if c != 0 { + a.push((l, c - 1)); + } + if l + 1 != map.len() { + a.push((l + 1, c)) + } + if c + 1 != map[0].len() { + a.push((l, c + 1)) + } + + a +} + +fn trailhead_score(l: usize, c: usize, map: &Parsed) -> usize { + let mut locations = HashSet::new(); + locations.insert((l, c)); + + for (current, _) in (0..9).enumerate() { + let current = current as u8; + let mut new_locations = HashSet::new(); + + for (l, c) in locations { + assert!(map[l][c] == current); + + for (nl, nc) in neighbours(l, c, map) { + if map[nl][nc] == current + 1 { + new_locations.insert((nl, nc)); + } + } + } + + locations = new_locations; + } + + locations.len() +} + +#[inline(never)] +pub fn part1(input: Parsed) { + let mut score = 0; + + for l in 0..input.len() { + for c in 0..input[0].len() { + if input[l][c] == 0 { + score += trailhead_score(l, c, &input); + } + } + } + + print_res!("Total score: {score}") +} + +fn trailhead_rating(l: usize, c: usize, map: &Parsed) -> usize { + let mut locations = vec![(l, c)]; + + for (current, _) in (0..9).enumerate() { + let current = current as u8; + let mut new_locations = Vec::new(); + + for (l, c) in locations { + assert!(map[l][c] == current); + + for (nl, nc) in neighbours(l, c, map) { + if map[nl][nc] == current + 1 { + new_locations.push((nl, nc)); + } + } + } + + locations = new_locations; + } + + locations.len() +} + +#[inline(never)] +pub fn part2(input: Parsed) { + let mut rating = 0; + + for l in 0..input.len() { + for c in 0..input[0].len() { + if input[l][c] == 0 { + rating += trailhead_rating(l, c, &input); + } + } + } + + print_res!("Total rating: {rating}") +} + +pub fn main() -> color_eyre::Result<()> { + let context = load()?; + + let start = Instant::now(); + let parsed = parsing(&context.input)?; + let elapsed = humantime::format_duration(start.elapsed()); + + let start = Instant::now(); + if context.part == 1 { + part1(parsed); + } else { + part2(parsed); + } + let elapsed_part = humantime::format_duration(start.elapsed()); + + println!(" Parsing: {elapsed}"); + println!(" Solving: {elapsed_part}"); + + Ok(()) +}