Advent of Code solutions in Rust

feat: complete AoC 2025 ⭐️

+125 -2
+1
Cargo.lock
··· 148 148 "anyhow", 149 149 "aoc_companion", 150 150 "aoc_utils", 151 + "fxhash", 151 152 "itertools", 152 153 "ndarray", 153 154 "num-traits",
+1
aoc_2025/Cargo.toml
··· 15 15 rayon = { workspace = true } 16 16 thiserror = { workspace = true } 17 17 tokio = { workspace = true } 18 + fxhash = "0.2.1"
+121
aoc_2025/src/day12.rs
··· 1 + use std::{collections::HashSet, num::ParseIntError}; 2 + 3 + use anyhow::{Context, anyhow, bail}; 4 + use aoc_companion::prelude::*; 5 + use itertools::Itertools as _; 6 + 7 + pub(crate) struct Door { 8 + problems: Vec<Problem>, 9 + } 10 + 11 + impl<'input> Solution<'input> for Door { 12 + fn parse(input: &'input str) -> Result<Self> { 13 + let Some((shapes, problems)) = input.rsplit_once("\n\n") else { 14 + bail!("could not find empty line delimiting shapes and problems"); 15 + }; 16 + let shapes_hash = fxhash::hash(shapes); 17 + if shapes_hash != 0x2050bd894f01430f { 18 + bail!("solution only works for my input; got different hash: {shapes_hash:x}"); 19 + } 20 + problems 21 + .lines() 22 + .map(str::parse) 23 + .try_collect() 24 + .map(|problems| Door { problems }) 25 + } 26 + 27 + fn part1(&self) -> Result<usize> { 28 + let (conclusive, inconclusive): (Vec<bool>, HashSet<&Problem>) = self 29 + .problems 30 + .iter() 31 + .map(|problem| { 32 + rule_out_due_to_insufficient_area(problem) 33 + .or_else(|| verify_with_trivial_packing(problem)) 34 + .ok_or(problem) 35 + }) 36 + .partition_result(); 37 + 38 + if !inconclusive.is_empty() { 39 + bail!( 40 + "{}/{} problems were inconclusive, e.g.: {}", 41 + inconclusive.len(), 42 + self.problems.len(), 43 + inconclusive.iter().next().unwrap() 44 + ); 45 + } 46 + 47 + Ok(conclusive.into_iter().filter(|&b| b).count()) 48 + } 49 + } 50 + 51 + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 52 + struct Problem { 53 + dimensions: [usize; 2], 54 + presents: [usize; 6], 55 + } 56 + 57 + impl std::fmt::Display for Problem { 58 + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 59 + write!( 60 + f, 61 + "{}: {}", 62 + self.dimensions.iter().join("x"), 63 + self.presents.iter().join(" ") 64 + ) 65 + } 66 + } 67 + 68 + impl std::str::FromStr for Problem { 69 + type Err = anyhow::Error; 70 + 71 + fn from_str(s: &str) -> Result<Self> { 72 + let Some((dimensions, shapes)) = s.split_once(':') else { 73 + bail!("could not find colon delimiting problem dimensions and required shapes"); 74 + }; 75 + let dimensions = dimensions 76 + .split_once('x') 77 + .ok_or_else(|| anyhow!("missing 'x' in dimensions spec {dimensions:?}")) 78 + .and_then(|(x, y)| { 79 + Ok::<_, ParseIntError>([x.parse()?, y.parse()?]) 80 + .with_context(|| anyhow!("failed to parse region dimenions {dimensions:?}")) 81 + })?; 82 + // TODO: add try_from_iter_exact 83 + let presents = aoc_utils::array::try_from_iter(shapes.split_whitespace().map(|s| { 84 + s.parse() 85 + .with_context(|| anyhow!("failed to parse present count {s:?}")) 86 + }))? 87 + .map_err(|e| anyhow!("wrong number of presents: expected 6, got {}", e.len()))?; 88 + 89 + Ok(Self { 90 + dimensions, 91 + presents, 92 + }) 93 + } 94 + } 95 + 96 + impl Problem { 97 + fn area(&self) -> usize { 98 + self.dimensions[0] * self.dimensions[1] 99 + } 100 + } 101 + 102 + fn rule_out_due_to_insufficient_area(problem: &Problem) -> Option<bool> { 103 + const AREA: [usize; 6] = [5, 7, 7, 7, 6, 7]; 104 + 105 + let required_area: usize = problem 106 + .presents 107 + .iter() 108 + .zip(AREA) 109 + .map(|(count, area)| count * area) 110 + .sum(); 111 + 112 + (required_area > problem.area()).then_some(false) 113 + } 114 + 115 + fn verify_with_trivial_packing(problem: &Problem) -> Option<bool> { 116 + let available_cells = (problem.dimensions[0] / 3) * (problem.dimensions[1] / 3); 117 + 118 + let total_presents: usize = problem.presents.iter().sum(); 119 + 120 + (total_presents <= available_cells).then_some(true) 121 + }
+2 -2
aoc_2025/src/main.rs
··· 11 11 mod day09; 12 12 mod day10; 13 13 mod day11; 14 - // mod day12; 14 + mod day12; 15 15 16 16 use aoc_companion::prelude::*; 17 17 ··· 29 29 door!(2025-12-09 ~> day09), 30 30 door!(2025-12-10 ~> day10), 31 31 door!(2025-12-11 ~> day11), 32 - // door!(2025-12-12 ~> day12), 32 + door!(2025-12-12 ~> day12), 33 33 ]) 34 34 .await 35 35 }