Advent of Code for 2025!

day 3: Fixing the lobby escalator

+244
+8
examples/day_03_lobby.proptest-regressions
··· 1 + # Seeds for failure cases proptest has generated in the past. It is 2 + # automatically read and these particular cases re-run before any 3 + # novel cases are generated. 4 + # 5 + # It is recommended to check this file in to source control so that 6 + # everyone who runs the test benefits from these saved cases. 7 + cc bb0719e603e9f2a6737bc2edded7acfc9bc57a242925352b86fd427aed246bff # shrinks to v = [1] 8 + cc 71c951b8e7ff83d0d15eb4d275d309b580358257edde61fc52834333377dfd70 # shrinks to s = "0"
+236
examples/day_03_lobby.rs
··· 1 + fn main() { 2 + aoc2025::run( 3 + "Day 3: Lobby", 4 + "inputs/day_03.txt", 5 + parse_input, 6 + part_1, 7 + part_2, 8 + ) 9 + .unwrap(); 10 + } 11 + 12 + fn parse_bank(bank: &str) -> Vec<u64> { 13 + bank.chars() 14 + .filter_map(|ch| ch.to_digit(10)) 15 + .map(|x| x as u64) 16 + .filter(|x| *x != 0) 17 + .collect::<Vec<_>>() 18 + } 19 + 20 + fn parse_input(input: &str) -> Vec<Vec<u64>> { 21 + (input.split("\n")) 22 + .map(|s| parse_bank(s)) 23 + .filter(|v| !v.is_empty()) 24 + .collect::<Vec<_>>() 25 + } 26 + 27 + fn find_highest_naive_joltage(bank: Vec<u64>) -> u64 { 28 + let mut max = 0; 29 + for (i, a) in bank.iter().enumerate() { 30 + for b in &bank[(i + 1)..bank.len()] { 31 + max = max.max(a * 10 + b); 32 + } 33 + } 34 + max 35 + } 36 + 37 + fn find_highest_joltage(bank: Vec<u64>) -> u64 { 38 + let n = bank.len(); 39 + 40 + let keep = 12_usize; 41 + let mut result: Vec<u64> = Vec::with_capacity(keep); 42 + 43 + let mut start = 0_usize; 44 + let mut remaining = keep; 45 + 46 + while remaining > 0 { 47 + let end = n - remaining; 48 + 49 + let mut best_digit = 0_u64; 50 + let mut best_index = start; 51 + 52 + for i in start..=end { 53 + let d = bank[i]; 54 + if d > best_digit { 55 + best_digit = d; 56 + best_index = i; 57 + 58 + if d == 9 { 59 + break; 60 + } 61 + } 62 + } 63 + 64 + result.push(best_digit); 65 + start = best_index + 1; 66 + remaining -= 1; 67 + } 68 + 69 + let s: String = result.iter().map(|d| d.to_string()).collect(); 70 + s.parse::<u64>().unwrap() 71 + } 72 + 73 + fn part_1(banks: Vec<Vec<u64>>) -> u64 { 74 + (banks.iter()) 75 + .map(|bank| find_highest_naive_joltage(bank.clone())) 76 + .sum() 77 + } 78 + 79 + fn part_2(banks: Vec<Vec<u64>>) -> u64 { 80 + (banks.iter()) 81 + .map(|bank| find_highest_joltage(bank.clone())) 82 + .sum() 83 + } 84 + 85 + #[cfg(test)] 86 + mod test { 87 + use super::*; 88 + use proptest::prelude::*; 89 + 90 + fn bank() -> impl Strategy<Value = Vec<u64>> { 91 + prop::collection::vec(1_u64..10, 2..200) 92 + } 93 + 94 + fn rigged_bank(rig_count: usize) -> impl Strategy<Value = (Vec<u64>, Vec<u64>)> { 95 + let min_len = rig_count.max(2); 96 + 97 + prop::collection::vec(1_u64..5, min_len..20).prop_flat_map(move |bank| { 98 + let len = bank.len(); 99 + let all_indices: Vec<usize> = (0..len).collect(); 100 + 101 + let indices_strategy = prop::sample::subsequence(all_indices.clone(), rig_count); 102 + 103 + let values_strategy = 104 + prop::collection::vec(6_u64..10, rig_count).prop_map(|mut vals| { 105 + vals.sort_by(|a, b| b.cmp(a)); 106 + vals 107 + }); 108 + 109 + (indices_strategy, values_strategy).prop_map(move |(indices, rigged_values)| { 110 + let mut bank = bank.clone(); 111 + 112 + for (idx, val) in indices.iter().zip(rigged_values.iter()) { 113 + bank[*idx] = *val; 114 + } 115 + 116 + (bank, rigged_values) 117 + }) 118 + }) 119 + } 120 + 121 + prop_compose! { 122 + fn bank_input()(bank in bank()) -> (Vec<u64>, String) { 123 + let input = bank.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(""); 124 + (bank, input) 125 + } 126 + } 127 + 128 + prop_compose! { 129 + fn input()(banks in prop::collection::vec(bank_input(), 1..200)) -> (Vec<Vec<u64>>, String) { 130 + let (values, input): (Vec<_>, Vec<_>) = banks.into_iter().unzip(); 131 + (values, input.join("\n")) 132 + } 133 + } 134 + 135 + mod parse_bank { 136 + use super::*; 137 + 138 + proptest! { 139 + #[test] 140 + fn doesnt_crash(s in "\\PC+") { 141 + parse_bank(s.as_str()); 142 + } 143 + 144 + #[test] 145 + fn doesnt_parse_nondigits(s in "[^[1-9]]+") { 146 + prop_assert!(parse_bank(s.as_str()).is_empty()); 147 + } 148 + 149 + #[test] 150 + fn correctly_parses_digits((values, input) in bank_input()) { 151 + prop_assert_eq!(values, parse_bank(&input)); 152 + } 153 + } 154 + } 155 + 156 + mod parse_input { 157 + use super::*; 158 + 159 + proptest! { 160 + #[test] 161 + fn doesnt_crash(s in "\\PC+") { 162 + parse_input(s.as_str()); 163 + } 164 + 165 + #[test] 166 + fn ignores_empty_banks(s in "\\n+") { 167 + prop_assert!(parse_input(s.as_str()).is_empty()); 168 + } 169 + 170 + #[test] 171 + fn correctly_parses_banks((values, input) in input()) { 172 + prop_assert_eq!(values, parse_input(input.as_str())); 173 + } 174 + } 175 + } 176 + 177 + mod find_highest_naive_joltage { 178 + use super::*; 179 + 180 + proptest! { 181 + #[test] 182 + fn handles_two_joltages(x in 1_u64..10, y in 1_u64..10) { 183 + prop_assert_eq!(x * 10 + y, find_highest_naive_joltage(vec![x, y])); 184 + } 185 + 186 + #[test] 187 + fn maximizes_bank_joltage((bank, values) in rigged_bank(2)) { 188 + let values = values.iter().map(|x| x.to_string()).collect::<Vec<_>>().join("").parse::<u64>().unwrap(); 189 + prop_assert_eq!(values, find_highest_naive_joltage(bank)); 190 + } 191 + } 192 + } 193 + 194 + mod find_highest_joltage { 195 + use super::*; 196 + 197 + proptest! { 198 + #[test] 199 + fn maximizes_bank_joltage((bank, values) in rigged_bank(12)) { 200 + let values = values.iter().map(|x| x.to_string()).collect::<Vec<_>>().join("").parse::<u64>().unwrap(); 201 + prop_assert_eq!(values, find_highest_joltage(bank)); 202 + } 203 + } 204 + } 205 + 206 + const EXAMPLE_INPUT: &str = r#" 207 + 987654321111111 208 + 811111111111119 209 + 234234234234278 210 + 818181911112111 211 + "#; 212 + 213 + mod part_1 { 214 + use super::*; 215 + 216 + const EXAMPLE_ANSWER: u64 = 357; 217 + 218 + #[test] 219 + fn solves_example() { 220 + let input = parse_input(EXAMPLE_INPUT); 221 + assert_eq!(EXAMPLE_ANSWER, part_1(input)); 222 + } 223 + } 224 + 225 + mod part_2 { 226 + use super::*; 227 + 228 + const EXAMPLE_ANSWER: u64 = 3_121_910_778_619; 229 + 230 + #[test] 231 + fn solves_example() { 232 + let input = parse_input(EXAMPLE_INPUT); 233 + assert_eq!(EXAMPLE_ANSWER, part_2(input)); 234 + } 235 + } 236 + }