Advent of Code solutions in Rust

feat: day 1 of AoC 2025

+150 -2
+12
Cargo.lock
··· 142 142 ] 143 143 144 144 [[package]] 145 + name = "aoc_2025" 146 + version = "0.1.0" 147 + dependencies = [ 148 + "anyhow", 149 + "aoc_companion", 150 + "aoc_utils", 151 + "itertools", 152 + "num-traits", 153 + "tokio", 154 + ] 155 + 156 + [[package]] 145 157 name = "aoc_companion" 146 158 version = "0.1.0" 147 159 dependencies = [
+3
Cargo.toml
··· 7 7 "aoc_2020", 8 8 "aoc_2022", 9 9 "aoc_2024", 10 + "aoc_2025", 10 11 ] 11 12 12 13 [workspace.dependencies] ··· 17 18 enum-map = "2.7.3" 18 19 itertools = "0.14.0" 19 20 ndarray = "0.17.1" 21 + num-integer = "0.1.46" 22 + num-traits = "0.2.19" 20 23 proptest = { version = "1.9.0", default-features = false, features = ["std"] } 21 24 rayon = "1.11.0" 22 25 regex = "1.12.2"
+1 -1
aoc_2020/Cargo.toml
··· 11 11 anyhow = { workspace = true } 12 12 itertools = { workspace = true } 13 13 ndarray = { workspace = true } 14 + num-integer = { workspace = true } 14 15 regex = { workspace = true } 15 16 thiserror = { workspace = true } 16 17 tokio = { workspace = true } 17 - num-integer = "0.1.46" 18 18
+14
aoc_2025/Cargo.toml
··· 1 + [package] 2 + name = "aoc_2025" 3 + version = "0.1.0" 4 + edition = "2024" 5 + 6 + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 + 8 + [dependencies] 9 + aoc_companion = { workspace = true } 10 + aoc_utils = { workspace = true } 11 + anyhow = { workspace = true } 12 + itertools = { workspace = true } 13 + num-traits = { workspace = true } 14 + tokio = { workspace = true }
+84
aoc_2025/src/day01.rs
··· 1 + use anyhow::Context as _; 2 + use aoc_companion::prelude::*; 3 + use itertools::Itertools as _; 4 + use num_traits::Euclid as _; 5 + 6 + pub(crate) struct Door { 7 + rotations: Vec<i32>, 8 + } 9 + 10 + impl<'input> Solution<'input> for Door { 11 + fn parse(input: &'input str) -> Result<Self> { 12 + Ok(Door { 13 + rotations: input 14 + .lines() 15 + .map(|line| { 16 + let Some((dir, angle)) = line.split_at_checked(1) else { 17 + anyhow::bail!("unexpected empty line"); 18 + }; 19 + let angle: i32 = angle 20 + .parse() 21 + .with_context(|| anyhow::anyhow!("invalid rotation angle"))?; 22 + match dir { 23 + "L" => Ok(-angle), 24 + "R" => Ok(angle), 25 + _ => Err(anyhow::anyhow!("invalid rotation direction {dir:?}")), 26 + } 27 + }) 28 + .try_collect()?, 29 + }) 30 + } 31 + 32 + fn part1(&self) -> usize { 33 + intermediate_angles(self.rotations.iter().copied()) 34 + .filter(|angle| *angle == 0) 35 + .count() 36 + } 37 + 38 + fn part2(&self) -> i32 { 39 + dial_crossings(self.rotations.iter().copied()).sum() 40 + } 41 + } 42 + 43 + fn intermediate_angles(rotations: impl IntoIterator<Item = i32>) -> impl Iterator<Item = i32> { 44 + rotations.into_iter().scan(50, |angle, rot| { 45 + *angle = (*angle + rot).rem_euclid(100); 46 + Some(*angle) 47 + }) 48 + } 49 + 50 + fn dial_crossings(rotations: impl IntoIterator<Item = i32>) -> impl Iterator<Item = i32> { 51 + rotations.into_iter().scan(50, |angle, rot| { 52 + let (div, new_angle) = (*angle + rot).div_rem_euclid(&100); 53 + let correction = if rot < 0 { 54 + (new_angle == 0) as i32 - (*angle == 0) as i32 55 + } else { 56 + 0 57 + }; 58 + *angle = new_angle; 59 + Some(div.abs() + correction) 60 + }) 61 + } 62 + 63 + #[cfg(test)] 64 + mod tests { 65 + use super::*; 66 + 67 + const EXAMPLE_ROTATIONS: &[i32] = &[-68, -30, 48, -5, 60, -55, -1, -99, 14, -82]; 68 + 69 + #[test] 70 + fn intermediate_angles_in_example() { 71 + itertools::assert_equal( 72 + intermediate_angles(EXAMPLE_ROTATIONS.iter().copied()), 73 + [82, 52, 0, 95, 55, 0, 99, 0, 14, 32], 74 + ); 75 + } 76 + 77 + #[test] 78 + fn times_dial_reaches_zero() { 79 + itertools::assert_equal( 80 + dial_crossings(EXAMPLE_ROTATIONS.iter().copied()), 81 + [1, 0, 1, 0, 1, 1, 0, 1, 0, 1], 82 + ); 83 + } 84 + }
+35
aoc_2025/src/main.rs
··· 1 + #![allow(refining_impl_trait_internal)] 2 + 3 + mod day01; 4 + // mod day02; 5 + // mod day03; 6 + // mod day04; 7 + // mod day05; 8 + // mod day06; 9 + // mod day07; 10 + // mod day08; 11 + // mod day09; 12 + // mod day10; 13 + // mod day11; 14 + // mod day12; 15 + 16 + use aoc_companion::prelude::*; 17 + 18 + #[tokio::main(flavor = "current_thread")] 19 + async fn main() -> Result<()> { 20 + aoc_main(&[ 21 + door!(2025-12-01 ~> day01), 22 + // door!(2025-12-02 ~> day02), 23 + // door!(2025-12-03 ~> day03), 24 + // door!(2025-12-04 ~> day04), 25 + // door!(2025-12-05 ~> day05), 26 + // door!(2025-12-06 ~> day06), 27 + // door!(2025-12-07 ~> day07), 28 + // door!(2025-12-08 ~> day08), 29 + // door!(2025-12-09 ~> day09), 30 + // door!(2025-12-10 ~> day10), 31 + // door!(2025-12-11 ~> day11), 32 + // door!(2025-12-12 ~> day12), 33 + ]) 34 + .await 35 + }
+1 -1
aoc_utils/Cargo.toml
··· 9 9 assert_matches = { workspace = true } 10 10 itertools = { workspace = true } 11 11 ndarray = { workspace = true } 12 - num-traits = "0.2.19" 12 + num-traits = { workspace = true } 13 13 paste = "1.0.15" 14 14 thiserror = { workspace = true } 15 15