Advent of Code solutions in Rust

feat: day 10 of AoC 2025

+225 -2
+223
aoc_2025/src/day10.rs
··· 1 + use std::ops::BitXor; 2 + 3 + use anyhow::{Context, anyhow, bail}; 4 + use aoc_companion::prelude::*; 5 + use itertools::Itertools as _; 6 + use rayon::iter::{ParallelBridge as _, ParallelIterator as _}; 7 + 8 + pub(crate) struct Door { 9 + machines: Vec<Machine>, 10 + } 11 + 12 + impl<'input> Solution<'input> for Door { 13 + fn parse(input: &'input str) -> Result<Self> { 14 + input 15 + .lines() 16 + .map(str::parse) 17 + .try_collect() 18 + .map(|machines| Door { machines }) 19 + } 20 + 21 + fn part1(&self) -> Result<usize> { 22 + self.machines 23 + .iter() 24 + .map(|m| { 25 + m.fewest_button_presses_for_lights().ok_or_else(|| { 26 + anyhow!("no combination of button presses achieved desired indicator lights") 27 + }) 28 + }) 29 + .try_fold(0, |acc, b| Ok(acc + b?)) 30 + } 31 + 32 + fn part2(&self) -> Result<usize> { 33 + self.machines 34 + .iter() 35 + .map(|m| { 36 + m.fewest_button_presses_for_joltage().ok_or_else(|| { 37 + anyhow!( 38 + "no combination of button presses achieved desired joltage requirements" 39 + ) 40 + }) 41 + }) 42 + .try_fold(0, |acc, b| Ok(acc + b?)) 43 + } 44 + } 45 + 46 + #[derive(Debug, Clone, PartialEq, Eq)] 47 + struct Machine { 48 + desired_indicator_lights: u32, 49 + buttons: Vec<u32>, 50 + joltage_requirements: Vec<usize>, 51 + } 52 + 53 + impl std::str::FromStr for Machine { 54 + type Err = anyhow::Error; 55 + 56 + fn from_str(s: &str) -> Result<Self> { 57 + let mut words = s.split_ascii_whitespace(); 58 + let Some(light_pattern) = words.next() else { 59 + bail!("expected indicator light pattern"); 60 + }; 61 + let Some(light_pattern) = light_pattern 62 + .strip_prefix('[') 63 + .and_then(|rest| rest.strip_suffix(']')) 64 + else { 65 + bail!("indicator light pattern needs to be surrounded by square brackets"); 66 + }; 67 + let desired_indicator_lights = light_pattern 68 + .bytes() 69 + .rev() 70 + .fold(0, |acc, b| (acc << 1) | (b == b'#') as u32); 71 + let Some(joltage_seq) = words.next_back() else { 72 + bail!("expected joltage requirements"); 73 + }; 74 + let Some(joltage_seq) = joltage_seq 75 + .strip_prefix('{') 76 + .and_then(|rest| rest.strip_suffix('}')) 77 + else { 78 + bail!("joltage requiements need to be surrounded by curly brackets"); 79 + }; 80 + let joltage_requirements = joltage_seq 81 + .split(',') 82 + .map(|s| { 83 + s.parse() 84 + .with_context(|| anyhow!("parsing joltage requirement {s:?} failed")) 85 + }) 86 + .try_collect()?; 87 + let buttons = words 88 + .map(|button_word| { 89 + let Some(button_word) = button_word 90 + .strip_prefix('(') 91 + .and_then(|rest| rest.strip_suffix(')')) 92 + else { 93 + bail!("button wiring needs to be surrounded by parentheses"); 94 + }; 95 + button_word 96 + .split(',') 97 + .map(|s| { 98 + s.parse() 99 + .with_context(|| anyhow!("parsing button wiring {s:?} failed")) 100 + }) 101 + .try_fold(0, |acc, i: Result<usize, _>| Ok(acc | (1 << i?))) 102 + }) 103 + .try_collect()?; 104 + 105 + Ok(Machine { 106 + desired_indicator_lights, 107 + buttons, 108 + joltage_requirements, 109 + }) 110 + } 111 + } 112 + 113 + fn button_presses_for_lights( 114 + buttons: &[u32], 115 + desired_indicator_lights: u32, 116 + ) -> impl Iterator<Item = Vec<&u32>> { 117 + (0..=buttons.len()) 118 + .flat_map(|k| buttons.iter().combinations(k)) 119 + .filter(move |combo| { 120 + combo.iter().copied().fold(0, BitXor::bitxor) == desired_indicator_lights 121 + }) 122 + } 123 + 124 + impl Machine { 125 + fn fewest_button_presses_for_lights(&self) -> Option<usize> { 126 + button_presses_for_lights(&self.buttons, self.desired_indicator_lights) 127 + .map(|combo| combo.len()) 128 + .next() 129 + } 130 + 131 + fn fewest_button_presses_for_joltage(&self) -> Option<usize> { 132 + fn inner(buttons: &[u32], required_joltage: &[usize]) -> Option<usize> { 133 + let equiv_lights = required_joltage 134 + .iter() 135 + .rev() 136 + .fold(0, |acc, n| (acc << 1) | (n % 2) as u32); 137 + 138 + button_presses_for_lights(buttons, equiv_lights) 139 + .par_bridge() 140 + .filter_map(|combo| { 141 + let remaining_joltage: Vec<_> = required_joltage 142 + .iter() 143 + .enumerate() 144 + .map(|(i, j)| { 145 + j.checked_sub( 146 + combo.iter().copied().filter(|&b| b & (1 << i) != 0).count(), 147 + ) 148 + .map(|j| j / 2) 149 + .ok_or(()) 150 + }) 151 + .try_collect() 152 + .ok()?; 153 + 154 + if remaining_joltage.iter().all(|&j| j == 0) { 155 + Some(combo.len()) 156 + } else { 157 + Some(combo.len() + 2 * inner(buttons, &remaining_joltage)?) 158 + } 159 + }) 160 + .min() 161 + } 162 + 163 + inner(&self.buttons, &self.joltage_requirements) 164 + } 165 + } 166 + 167 + #[cfg(test)] 168 + mod tests { 169 + use super::*; 170 + 171 + const EXAMPLE_INPUT: &str = "\ 172 + [.##.] (3) (1,3) (2) (2,3) (0,2) (0,1) {3,5,4,7} 173 + [...#.] (0,2,3,4) (2,3) (0,4) (0,1,2) (1,2,3,4) {7,5,12,7,2} 174 + [.###.#] (0,1,2,3,4) (0,3,4) (0,1,2,4,5) (1,2) {10,11,11,5,10,5}"; 175 + 176 + fn example_machines() -> Vec<Machine> { 177 + vec![ 178 + Machine { 179 + desired_indicator_lights: 0b0110, 180 + buttons: vec![0b1000, 0b1010, 0b0100, 0b1100, 0b0101, 0b0011], 181 + joltage_requirements: vec![3, 5, 4, 7], 182 + }, 183 + Machine { 184 + desired_indicator_lights: 0b01000, 185 + buttons: vec![0b11101, 0b01100, 0b10001, 0b00111, 0b11110], 186 + joltage_requirements: vec![7, 5, 12, 7, 2], 187 + }, 188 + Machine { 189 + desired_indicator_lights: 0b101110, 190 + buttons: vec![0b011111, 0b011001, 0b110111, 0b000110], 191 + joltage_requirements: vec![10, 11, 11, 5, 10, 5], 192 + }, 193 + ] 194 + } 195 + 196 + #[test] 197 + fn parse_example_input() { 198 + itertools::assert_equal( 199 + Door::parse(EXAMPLE_INPUT).unwrap().machines, 200 + example_machines(), 201 + ); 202 + } 203 + 204 + #[test] 205 + fn fewest_button_presses_for_lights_in_example() { 206 + itertools::assert_equal( 207 + example_machines() 208 + .iter() 209 + .map(Machine::fewest_button_presses_for_lights), 210 + [Some(2), Some(3), Some(2)], 211 + ); 212 + } 213 + 214 + #[test] 215 + fn fewest_button_presses_for_joltage_in_example() { 216 + itertools::assert_equal( 217 + example_machines() 218 + .iter() 219 + .map(Machine::fewest_button_presses_for_joltage), 220 + [Some(10), Some(12), Some(11)], 221 + ); 222 + } 223 + }
+2 -2
aoc_2025/src/main.rs
··· 9 9 mod day07; 10 10 mod day08; 11 11 mod day09; 12 - // mod day10; 12 + mod day10; 13 13 // mod day11; 14 14 // mod day12; 15 15 ··· 27 27 door!(2025-12-07 ~> day07), 28 28 door!(2025-12-08 ~> day08), 29 29 door!(2025-12-09 ~> day09), 30 - // door!(2025-12-10 ~> day10), 30 + door!(2025-12-10 ~> day10), 31 31 // door!(2025-12-11 ~> day11), 32 32 // door!(2025-12-12 ~> day12), 33 33 ])