Advent of Code solutions in Rust

feat: day 7 of AoC 2025

+247 -3
+135
aoc_2025/src/day07.rs
··· 1 + use std::collections::HashMap; 2 + 3 + use aoc_companion::prelude::*; 4 + use aoc_utils::iter::AtMostTwo; 5 + use itertools::Itertools; 6 + 7 + pub(crate) struct Door { 8 + splitters: Vec<Vec<i32>>, 9 + } 10 + 11 + impl<'input> Solution<'input> for Door { 12 + fn parse(input: &'input str) -> Result<Self> { 13 + parse_splitters(input).map(|splitters| Door { splitters }) 14 + } 15 + 16 + fn part1(&self) -> usize { 17 + split_beam(&self.splitters).splits 18 + } 19 + 20 + fn part2(&self) -> usize { 21 + split_beam(&self.splitters).quantum_worlds() 22 + } 23 + } 24 + 25 + fn parse_splitters(input: &str) -> Result<Vec<Vec<i32>>> { 26 + let mut lines = input.lines().step_by(2); 27 + let start_col = lines 28 + .next() 29 + .unwrap() 30 + .bytes() 31 + .position(|b| b == b'S') 32 + .unwrap() as i32; 33 + Ok(lines 34 + .map(|line| { 35 + line.bytes() 36 + .enumerate() 37 + .filter(|&(_, b)| b == b'^') 38 + .map(|(i, _)| i as i32 - start_col) 39 + .collect() 40 + }) 41 + .collect()) 42 + } 43 + 44 + #[derive(Debug, PartialEq, Eq)] 45 + struct State { 46 + beams: HashMap<i32, usize>, 47 + splits: usize, 48 + } 49 + 50 + impl Default for State { 51 + fn default() -> Self { 52 + Self { 53 + beams: HashMap::from([(0, 1)]), 54 + splits: 0, 55 + } 56 + } 57 + } 58 + 59 + impl State { 60 + fn quantum_worlds(&self) -> usize { 61 + self.beams.values().sum() 62 + } 63 + } 64 + 65 + fn split_beam(splitters: &[impl AsRef<[i32]>]) -> State { 66 + splitters.iter().fold( 67 + State::default(), 68 + |State { beams, mut splits }, splitters| { 69 + let beams = beams 70 + .into_iter() 71 + .flat_map(|(b, c)| { 72 + if splitters.as_ref().iter().contains(&b) { 73 + splits += 1; 74 + AtMostTwo::two((b - 1, c), (b + 1, c)) 75 + } else { 76 + AtMostTwo::one((b, c)) 77 + } 78 + }) 79 + .into_group_map() 80 + .into_iter() 81 + .map(|(b, cs)| (b, cs.into_iter().sum())) 82 + .collect(); 83 + 84 + State { beams, splits } 85 + }, 86 + ) 87 + } 88 + 89 + #[cfg(test)] 90 + mod tests { 91 + use super::*; 92 + 93 + const EXAMPLE_INPUT: &str = "\ 94 + .......S....... 95 + ............... 96 + .......^....... 97 + ............... 98 + ......^.^...... 99 + ............... 100 + .....^.^.^..... 101 + ............... 102 + ....^.^...^.... 103 + ............... 104 + ...^.^...^.^... 105 + ............... 106 + ..^...^.....^.. 107 + ............... 108 + .^.^.^.^.^...^. 109 + ..............."; 110 + 111 + const EXAMPLE_SPLITTERS: &[&[i32]] = &[ 112 + &[0], 113 + &[-1, 1], 114 + &[-2, 0, 2], 115 + &[-3, -1, 3], 116 + &[-4, -2, 2, 4], 117 + &[-5, -1, 5], 118 + &[-6, -4, -2, 0, 2, 6], 119 + ]; 120 + 121 + #[test] 122 + fn parse_example_splitters() { 123 + assert_eq!(parse_splitters(EXAMPLE_INPUT).unwrap(), EXAMPLE_SPLITTERS); 124 + } 125 + 126 + #[test] 127 + fn example_beam_split_count() { 128 + assert_eq!(split_beam(EXAMPLE_SPLITTERS).splits, 21); 129 + } 130 + 131 + #[test] 132 + fn example_quantum_worlds() { 133 + assert_eq!(split_beam(EXAMPLE_SPLITTERS).quantum_worlds(), 40); 134 + } 135 + }
+2 -2
aoc_2025/src/main.rs
··· 6 6 mod day04; 7 7 mod day05; 8 8 mod day06; 9 - // mod day07; 9 + mod day07; 10 10 // mod day08; 11 11 // mod day09; 12 12 // mod day10; ··· 24 24 door!(2025-12-04 ~> day04), 25 25 door!(2025-12-05 ~> day05), 26 26 door!(2025-12-06 ~> day06), 27 - // door!(2025-12-07 ~> day07), 27 + door!(2025-12-07 ~> day07), 28 28 // door!(2025-12-08 ~> day08), 29 29 // door!(2025-12-09 ~> day09), 30 30 // door!(2025-12-10 ~> day10),
+110 -1
aoc_utils/src/iter.rs
··· 1 - use std::rc::Rc; 1 + use std::{iter::FusedIterator, rc::Rc}; 2 2 3 3 pub trait IterUtils: Iterator { 4 4 fn try_unzip<A, B, E, FromA, FromB>(mut self) -> Result<(FromA, FromB), E> ··· 73 73 }) 74 74 } 75 75 } 76 + 77 + #[derive(Debug, Clone, Copy, PartialEq)] 78 + pub struct Few<T, const N: usize>([Option<T>; N]); 79 + 80 + pub type AtMostTwo<T> = Few<T, 2>; 81 + 82 + impl<T, const N: usize> Few<T, N> { 83 + pub fn new<const M: usize>(items: [T; M]) -> Self { 84 + assert!(M <= N); 85 + Few(crate::array::from_iter( 86 + items 87 + .into_iter() 88 + .map(Some) 89 + .chain(std::iter::repeat_with(|| None)), 90 + ) 91 + .ok() 92 + .unwrap()) 93 + } 94 + 95 + pub fn none() -> Self { 96 + Few(std::array::from_fn(|_| None)) 97 + } 98 + 99 + pub fn one(item: T) -> Self { 100 + Few::new([item]) 101 + } 102 + 103 + pub fn two(item1: T, item2: T) -> Self { 104 + Few::new([item1, item2]) 105 + } 106 + } 107 + 108 + impl<T, const N: usize> Default for Few<T, N> { 109 + fn default() -> Self { 110 + Few::none() 111 + } 112 + } 113 + 114 + impl<T, const N: usize> Iterator for Few<T, N> { 115 + type Item = T; 116 + 117 + fn next(&mut self) -> Option<Self::Item> { 118 + if N > 0 { 119 + let item = self.0[0].take(); 120 + self.0.rotate_left(1); 121 + item 122 + } else { 123 + None 124 + } 125 + } 126 + } 127 + 128 + impl<T, const N: usize> FusedIterator for Few<T, N> {} 129 + 130 + #[cfg(test)] 131 + mod tests { 132 + use super::*; 133 + 134 + #[test] 135 + #[should_panic] 136 + fn few_insufficient_capacity_one() { 137 + Few::<i32, 0>::one(42); 138 + } 139 + 140 + #[test] 141 + #[should_panic] 142 + fn few_insufficient_capacity_two() { 143 + Few::<i32, 1>::two(42, 17); 144 + } 145 + 146 + #[test] 147 + #[should_panic] 148 + fn few_insufficient_capacity_new() { 149 + Few::<i32, 2>::new([42, 17, -1]); 150 + } 151 + 152 + #[test] 153 + fn few_iterator() { 154 + itertools::assert_equal(Few::<i32, 0>::none(), []); 155 + itertools::assert_equal(Few::<i32, 1>::none(), []); 156 + itertools::assert_equal(Few::<i32, 2>::none(), []); 157 + itertools::assert_equal(Few::<i32, 1>::one(42), [42]); 158 + itertools::assert_equal(Few::<i32, 2>::one(42), [42]); 159 + itertools::assert_equal(Few::<i32, 2>::two(42, 17), [42, 17]); 160 + itertools::assert_equal(Few::<i32, 3>::two(42, 17), [42, 17]); 161 + itertools::assert_equal(Few::<i32, 3>::new([42, 17]), [42, 17]); 162 + itertools::assert_equal(Few::<i32, 3>::new([42, 17, -1]), [42, 17, -1]); 163 + } 164 + 165 + #[test] 166 + fn few_iterator_fused() { 167 + let mut few = AtMostTwo::two(42, 17); 168 + assert!(few.next().is_some()); 169 + assert!(few.next().is_some()); 170 + assert!(few.next().is_none()); 171 + assert!(few.next().is_none()); 172 + assert!(few.next().is_none()); 173 + assert!(few.next().is_none()); 174 + assert!(few.next().is_none()); 175 + } 176 + 177 + #[test] 178 + fn few_default_empty() { 179 + itertools::assert_equal(Few::<i32, 0>::default(), []); 180 + itertools::assert_equal(Few::<i32, 1>::default(), []); 181 + itertools::assert_equal(Few::<i32, 2>::default(), []); 182 + itertools::assert_equal(Few::<i32, 3>::default(), []); 183 + } 184 + }