diff --git a/src/main.rs b/src/main.rs index 7495a26..1d3ce08 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,7 @@ use advent_of_code_2025::solvers; fn main() { println!("### Advent of Code 2025 ###\n"); solvers::run(solvers::secret_entrance::SecretEntrance {}); - // GiftShop + solvers::run(solvers::gift_shop::GiftShop {}); // Lobby solvers::run(solvers::printing_department::PrintingDepartment {}); solvers::run(solvers::cafeteria::Cafeteria {}); diff --git a/src/solvers.rs b/src/solvers.rs index 0bf9c0a..44e59dd 100644 --- a/src/solvers.rs +++ b/src/solvers.rs @@ -6,6 +6,7 @@ use std::path; use std::path::Path; pub mod cafeteria; +pub mod gift_shop; pub mod movie_theater; pub mod playground; pub mod printing_department; diff --git a/src/solvers/gift_shop.rs b/src/solvers/gift_shop.rs new file mode 100644 index 0000000..9831c9f --- /dev/null +++ b/src/solvers/gift_shop.rs @@ -0,0 +1,104 @@ +use crate::solvers::Solver; +use std::io::BufRead; + +pub struct GiftShop {} + +impl GiftShop { + const RANGES_SPLIT_CHAR: char = ','; +} + +impl Solver for GiftShop { + const PUZZLE_INDEX: u8 = 2; + const PUZZLE_NAME: &'static str = "Gift Shop"; + + fn process_data(&self, reader: R) -> (u64, u64) { + let mut invalid_ids_sum = 0; + for line in reader.lines().map_while(Result::ok) { + let ranges = line.split(Self::RANGES_SPLIT_CHAR); + for range in ranges { + if let Some(range) = Range::try_from_str(range) { + invalid_ids_sum += range.sum_invalid_ids(); + } + } + } + (invalid_ids_sum, 0) + } +} + +struct Range { + first: u64, + last: u64, + digits: u32, +} + +impl Range { + const SPLIT_CHAR: char = '-'; + + fn try_from_str(s: &str) -> Option { + // Parses first and last IDs from range string. + let ids: Vec = s + .split(Self::SPLIT_CHAR) + .map(|s| { + s.parse() + .expect("could not parse id range, must be integers") + }) + .collect(); + assert_eq!(ids.len(), 2, "could not parse id range, must be two values"); + + // Calculates the logs of first and last IDs and checks the size of the range. + let (a_log, b_log) = (ids[0].ilog10(), ids[1].ilog10()); + assert!(b_log < a_log + 2, "id range is unexpectedly large"); + + if a_log == b_log { + if a_log % 2 == 0 { + // Skips this range if the first and last IDs are both odd. + None + } else { + Some(Range { + first: ids[0], + last: ids[1], + digits: a_log + 1, + }) + } + } else { + if a_log % 2 == 0 { + // First ID becomes the smallest ID with a number of digits equal to 'a_log + 2'. + Some(Range { + first: 10_u64.pow(b_log), + last: ids[1], + digits: b_log + 1, + }) + } else { + // Last ID becomes the largest ID with a number of digits equal to 'a_log + 1'. + Some(Range { + first: ids[0], + last: 10_u64.pow(b_log) - 1, + digits: b_log, + }) + } + } + } + + fn sum_invalid_ids(&self) -> u64 { + let power10 = 10_u64.pow(self.digits / 2); + let first_left = self.first / power10; + let last_left = self.last / power10; + let first_right = self.first % power10; + let last_right = self.last % power10; + let mut sum = 0; + // Checks, for a given range AB-CD, whether invalid ID AA is in the range. + if first_left >= first_right && (first_left < last_left || first_left <= last_right) { + sum += first_left; + } + // Calculates the sum of the in-between invalid IDs directly without loop, but we need to + // explicitly check whether there are any values. + if first_left + 1 <= last_left { + sum += (first_left + last_left) * (last_left - first_left - 1) / 2; + } + // Checks, for a given range AB-CD, whether invalid ID CC is in the range. + if first_left != last_left && last_left <= last_right { + sum += last_left; + } + sum * (power10 + 1) + } +} diff --git a/tests/examples.rs b/tests/examples.rs index 77b337a..328901c 100644 --- a/tests/examples.rs +++ b/tests/examples.rs @@ -16,6 +16,14 @@ fn secret_entrance() { ); } +#[test] +fn gift_shop() { + assert_eq!( + Ok((1227775554, 0)), + solvers::run_solver(solvers::gift_shop::GiftShop {}, &EXAMPLE_PATHS) + ); +} + #[test] fn printing_department() { assert_eq!( diff --git a/tests/full_data.rs b/tests/full_data.rs index 59081b6..fd9339b 100644 --- a/tests/full_data.rs +++ b/tests/full_data.rs @@ -11,6 +11,14 @@ fn secret_entrance() { ); } +#[test] +fn gift_shop() { + assert_eq!( + Ok((38158151648, 0)), + solvers::run_solver(solvers::gift_shop::GiftShop {}, &solvers::DATA_PATHS) + ); +} + #[test] fn printing_department() { assert_eq!(