Advent of Code for 2025!
at main 236 lines 6.1 kB view raw
1fn 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 12fn 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 20fn 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 27fn 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 37fn 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 73fn part_1(banks: Vec<Vec<u64>>) -> u64 { 74 (banks.iter()) 75 .map(|bank| find_highest_naive_joltage(bank.clone())) 76 .sum() 77} 78 79fn 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)] 86mod 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}