Advent of Code solutions in Rust

feat: day 2 of AoC 2025

+116 -2
+114
aoc_2025/src/day02.rs
··· 1 + use std::ops::RangeInclusive; 2 + 3 + use aoc_companion::prelude::*; 4 + use itertools::Itertools as _; 5 + use num_traits::Euclid as _; 6 + 7 + pub(crate) struct Door { 8 + ranges: Vec<RangeInclusive<u64>>, 9 + } 10 + 11 + impl<'input> Solution<'input> for Door { 12 + fn parse(input: &'input str) -> Result<Door> { 13 + parse_ranges(input).map(|ranges| Door { ranges }) 14 + } 15 + 16 + fn part1(&self) -> u64 { 17 + self.ranges 18 + .iter() 19 + .cloned() 20 + .flatten() 21 + .filter(is_simple_invalid_id) 22 + .sum() 23 + } 24 + 25 + fn part2(&self) -> u64 { 26 + self.ranges 27 + .iter() 28 + .cloned() 29 + .flatten() 30 + .filter(is_invalid_id) 31 + .sum() 32 + } 33 + } 34 + 35 + fn parse_ranges(s: &str) -> Result<Vec<RangeInclusive<u64>>> { 36 + s.split(',') 37 + .map(|r| { 38 + let Some((lhs, rhs)) = r.split_once('-') else { 39 + anyhow::bail!("missing '-' in range {r:?}"); 40 + }; 41 + Ok(lhs.parse()?..=rhs.parse()?) 42 + }) 43 + .try_collect() 44 + } 45 + 46 + fn is_simple_invalid_id(n: &u64) -> bool { 47 + let s = n.to_string(); 48 + let (mid, 0) = s.len().div_rem_euclid(&2) else { 49 + return false; 50 + }; 51 + let (lhs, rhs) = s.split_at(mid); 52 + lhs == rhs 53 + } 54 + 55 + fn is_invalid_id(n: &u64) -> bool { 56 + let s = n.to_string().into_bytes(); 57 + 58 + (1..=s.len() / 2) 59 + .filter(|c| s.len().is_multiple_of(*c)) 60 + .any(|chunk_size| s.chunks(chunk_size).all_equal()) 61 + } 62 + 63 + #[cfg(test)] 64 + mod tests { 65 + use super::*; 66 + 67 + const EXAMPLE_INPUT: &str = "11-22,95-115,998-1012,1188511880-1188511890,222220-222224,1698522-1698528,446443-446449,38593856-38593862,565653-565659,824824821-824824827,2121212118-2121212124"; 68 + 69 + const EXAMPLE_RANGES: &[RangeInclusive<u64>] = &[ 70 + 11..=22, 71 + 95..=115, 72 + 998..=1012, 73 + 1188511880..=1188511890, 74 + 222220..=222224, 75 + 1698522..=1698528, 76 + 446443..=446449, 77 + 38593856..=38593862, 78 + 565653..=565659, 79 + 824824821..=824824827, 80 + 2121212118..=2121212124, 81 + ]; 82 + 83 + #[test] 84 + fn parse_ranges_in_example_input() { 85 + assert_eq!(parse_ranges(EXAMPLE_INPUT).unwrap(), EXAMPLE_RANGES); 86 + } 87 + 88 + #[test] 89 + fn find_simple_invalid_ids_in_ranges() { 90 + itertools::assert_equal( 91 + EXAMPLE_RANGES 92 + .iter() 93 + .cloned() 94 + .flatten() 95 + .filter(is_simple_invalid_id), 96 + [11, 22, 99, 1010, 1188511885, 222222, 446446, 38593859], 97 + ); 98 + } 99 + 100 + #[test] 101 + fn find_all_invalid_ids_in_ranges() { 102 + itertools::assert_equal( 103 + EXAMPLE_RANGES 104 + .iter() 105 + .cloned() 106 + .flatten() 107 + .filter(is_invalid_id), 108 + [ 109 + 11, 22, 99, 111, 999, 1010, 1188511885, 222222, 446446, 38593859, 565656, 110 + 824824824, 2121212121, 111 + ], 112 + ); 113 + } 114 + }
+2 -2
aoc_2025/src/main.rs
··· 1 1 #![allow(refining_impl_trait_internal)] 2 2 3 3 mod day01; 4 - // mod day02; 4 + mod day02; 5 5 // mod day03; 6 6 // mod day04; 7 7 // mod day05; ··· 19 19 async fn main() -> Result<()> { 20 20 aoc_main(&[ 21 21 door!(2025-12-01 ~> day01), 22 - // door!(2025-12-02 ~> day02), 22 + door!(2025-12-02 ~> day02), 23 23 // door!(2025-12-03 ~> day03), 24 24 // door!(2025-12-04 ~> day04), 25 25 // door!(2025-12-05 ~> day05),