1
0
Files
AdventOfCode2025/src/solvers/gift_shop.rs
T
2026-01-28 20:24:11 +01:00

105 lines
3.4 KiB
Rust

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<R: BufRead>(&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<Self> {
// Parses first and last IDs from range string.
let ids: Vec<u64> = 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)
}
}