diff --git a/rust/main.rs b/rust/main.rs index a11ce7a..96909a8 100644 --- a/rust/main.rs +++ b/rust/main.rs @@ -5,10 +5,17 @@ use puzzles::day1; use puzzles::day2; use puzzles::day3; use puzzles::day4; +use puzzles::day5; use std::fs; use std::time::{Duration, Instant}; -const PUZZLES: [&dyn SomePuzzle; 4] = [&day1::PUZZLE, &day2::PUZZLE, &day3::PUZZLE, &day4::PUZZLE]; +const PUZZLES: [&dyn SomePuzzle; 5] = [ + &day1::PUZZLE, + &day2::PUZZLE, + &day3::PUZZLE, + &day4::PUZZLE, + &day5::PUZZLE, +]; fn main() { [false, true].iter().for_each(|is_real_data| { diff --git a/rust/puzzles/day5.rs b/rust/puzzles/day5.rs new file mode 100644 index 0000000..94d47bc --- /dev/null +++ b/rust/puzzles/day5.rs @@ -0,0 +1,82 @@ +use crate::puzzle::Puzzle; +use nom::{ + Parser, + character::complete::{char, newline, usize}, + combinator::eof, + error::Error, + multi::{separated_list0, separated_list1}, + sequence::{separated_pair, terminated}, +}; + +pub const PUZZLE: Puzzle<(Vec, Vec), 2> = Puzzle { + number: 5, + parser: |input| { + terminated::<_, _, Error<&str>, _, _>( + separated_pair( + separated_list1( + newline, + separated_pair(usize, char('-'), usize) + .map(|(lower, upper)| Range { lower, upper }), + ), + (newline, newline), + separated_list0(newline, usize), + ), + (newline, eof), + ) + .parse(input) + .unwrap() + .1 + }, + parts: [ + |(ranges, vals)| { + vals.iter() + .filter(|v| ranges.iter().any(|r| r.contains(**v))) + .count() + .to_string() + }, + |(ranges, _)| { + let mut sorted_ranges = ranges.clone(); + sorted_ranges.sort_by(|a, b| b.lower.cmp(&a.lower)); + let merged: Vec = sorted_ranges + .into_iter() + .rfold(Vec::new(), |acc, new| add_interval(new, acc)); + merged.iter().map(|r| r.length()).sum::().to_string() + }, + ], +}; + +#[derive(Clone, Debug)] +pub struct Range { + lower: usize, + upper: usize, +} +impl Range { + fn length(&self) -> usize { + self.upper - self.lower + 1 + } + fn contains(&self, n: usize) -> bool { + n >= self.lower && n <= self.upper + } + fn extend(&self, upper: usize) -> Range { + Range { + lower: self.lower, + upper: self.upper.max(upper), + } + } +} + +fn add_interval(new: Range, mut ranges: Vec) -> Vec { + if ranges.is_empty() { + vec![new] + } else { + let first = &ranges[0]; + if first.contains(new.lower) { + ranges[0] = first.extend(new.upper); + ranges + } else { + let mut result = vec![new]; + result.append(&mut ranges); + result + } + } +} diff --git a/rust/puzzles/mod.rs b/rust/puzzles/mod.rs index b6dc6f5..f5551d6 100644 --- a/rust/puzzles/mod.rs +++ b/rust/puzzles/mod.rs @@ -2,3 +2,4 @@ pub(crate) mod day1; pub(crate) mod day2; pub(crate) mod day3; pub(crate) mod day4; +pub(crate) mod day5;