Advent of Code for 2025!
at main 182 lines 5.0 kB view raw
1fn main() { 2 aoc2025::run( 3 "Day 1: Secret Entrance", 4 "inputs/day_01.txt", 5 parse_input, 6 part_1, 7 part_2, 8 ) 9 .unwrap(); 10} 11 12/// Parses the input into a list of signed integers. 13fn parse_input(input: &str) -> Vec<i32> { 14 input 15 .split('\n') 16 .filter(|x| !x.is_empty()) 17 .filter_map(|x| parse_line(x)) 18 .collect() 19} 20 21/// Parses the given line into a signed integer 22fn parse_line(line: &str) -> Option<i32> { 23 let line = line.replace("L", "-").replace("R", ""); 24 i32::from_str_radix(line.as_str(), 10).ok() 25} 26 27fn part_1(deltas: Vec<i32>) -> usize { 28 // The input file contains a list of rotations in the form `XYY`, 29 // where `X` is a direction (`L` for left or `R` for right) and 30 // `YY` is a non-zero-padded number of clicks. 31 // 32 // The dial consists of numbers 0-99. Turning left decreases the 33 // number, and turning right increases it. Turning too far will 34 // overflow to the opposite end of the range. 35 // 36 // The dial starts at 50. Run the input file against this dial and 37 // record the number of times it leaves it pointing at 0. 38 39 // Iterate through the list and count the number of times the 40 // accumulated sum equals zero. 41 (deltas.into_iter()) 42 .scan(50, |position, delta| { 43 *position += delta; 44 Some(*position) 45 }) 46 .filter(|position| position % 100 == 0) 47 .count() 48} 49 50fn part_2(deltas: Vec<i32>) -> i32 { 51 (deltas.into_iter()) 52 .scan(50, |prev, delta| { 53 let (next, hits) = step(*prev, delta); 54 *prev = next; 55 Some(hits) 56 }) 57 .sum() 58} 59 60fn step(start: i32, delta: i32) -> (i32, i32) { 61 let mut hits = (delta / 100).abs(); 62 63 let remainder = delta % 100; 64 if start + remainder <= 0 && start != 0 || 99 < start + remainder { 65 hits += 1; 66 } 67 68 ((start + delta).rem_euclid(100), hits) 69} 70 71#[cfg(test)] 72mod tests { 73 use super::*; 74 use proptest::prelude::*; 75 76 prop_compose! { 77 fn line_input()(s in -999_i32..999) -> (i32, String) { 78 (s, format!("{}{}", if s < 0 { "L" } else { "R" }, s.abs())) 79 } 80 } 81 82 mod parse_line { 83 use super::*; 84 85 #[test] 86 fn ignores_gibberish() { 87 assert_eq!(None, parse_line(":")); 88 } 89 90 proptest! { 91 #[test] 92 fn doesnt_crash(s in "\\PC*") { 93 parse_line(&s); 94 } 95 96 #[test] 97 fn parses_lines_correctly((s, line) in line_input()) { 98 prop_assert_eq!(s, parse_line(&line).unwrap()) 99 } 100 } 101 } 102 103 mod parse_input { 104 use super::*; 105 106 proptest! { 107 #[test] 108 fn doesnt_crash(s in "\\PC+") { 109 parse_input(&s); 110 } 111 112 #[test] 113 fn ignores_empty_lines(s in "\\n+") { 114 prop_assert!(parse_input(&s).is_empty()); 115 } 116 117 #[test] 118 fn parses_files(s in prop::collection::vec(line_input(), 1..100)) { 119 let (values, input): (Vec<i32>, Vec<String>) = s.into_iter().unzip(); 120 prop_assert_eq!(values, parse_input(&input.join("\n"))); 121 } 122 } 123 } 124 125 mod part_1 { 126 use super::*; 127 128 proptest! { 129 #[test] 130 fn counts_halfstep_deltas(s in prop::collection::vec(Just(50_i32), 0..100)) { 131 prop_assert_eq!((s.len() + 1) / 2, part_1(s)); 132 } 133 } 134 } 135 136 mod part_2 { 137 use super::*; 138 139 // Slow, naive step implementation to compare fast version against. 140 fn step_reference(start: i32, delta: i32) -> (i32, i32) { 141 let mut pos = start; 142 let dir = if delta >= 0 { 1 } else { -1 }; 143 let mut hits = 0; 144 145 for _ in 0..delta.abs() { 146 pos += dir; 147 if pos.rem_euclid(100) == 0 { 148 hits += 1; 149 } 150 } 151 152 let final_acc = pos.rem_euclid(100); 153 (final_acc, hits) 154 } 155 156 proptest! { 157 #[test] 158 fn counts_perfect_deltas(s in prop::collection::vec(Just(100), 0..100)) { 159 prop_assert_eq!(s.len() as i32, part_2(s)); 160 } 161 162 #[test] 163 fn counts_perfect_multiple_deltas(s in prop::collection::vec(0..9, 0..100)) { 164 prop_assert_eq!( 165 s.clone().into_iter().sum::<i32>(), 166 part_2(s.into_iter().map(|x| x * 100).collect()) 167 ); 168 } 169 170 #[test] 171 fn steps_correctly( 172 start in 0i32..100, 173 delta in -10_000_i32..-1 174 ) { 175 let reference_hits = step_reference(start, delta); 176 let real_hits = step(start, delta); 177 178 prop_assert_eq!(reference_hits, real_hits); 179 } 180 } 181 } 182}