programming puzzle solutions

add 2025 day2 in rust

Changed files
+90
src
+4
Cargo.toml
··· 47 47 name = "2025_day1" 48 48 path = "src/2025/day1/rust/mod.rs" 49 49 50 + [[bin]] 51 + name = "2025_day2" 52 + path = "src/2025/day2/rust/mod.rs" 53 + 50 54 [lints.rust] 51 55 unsafe_code = "forbid" 52 56
+2
README.md
··· 7 7 8 8 Jump straight to my 2025 Day 1 solution's [Dial struct here!](https://tangled.org/quilling.dev/advent-of-code/blob/main/src/2025/day1/rust/common.rs) Modulo arithmetic handles the dial rotation wraparound logic, and for the second part we just determine the magnitude of the rotation using division before applying it. 9 9 10 + For 2025 Day 2 I used some straightforward [pattern checking](https://tangled.org/quilling.dev/advent-of-code/blob/main/src/2025/day2/rust/common.rs) which is easily repeated on substrings in a loop for part two. 11 + 10 12 ![image](https://github.com/Teqed/advent-of-code-2023/assets/5181964/a00fb449-1b3b-4e33-a999-6eef20c773f4)
+28
src/2025/day2/rust/common.rs
··· 1 + /// Parse a range string like "11-22" into (start, end). 2 + pub fn parse_range(range: &str) -> (u64, u64) { 3 + let parts: Vec<&str> = range.split('-').collect(); 4 + (parts[0].parse().expect("valid start"), parts[1].parse().expect("valid end")) 5 + } 6 + 7 + /// Check if a product ID is invalid (digits repeated exactly twice). 8 + pub fn is_invalid_id(num: u64) -> bool { 9 + let s = num.to_string(); let len = s.len(); 10 + if len % 2 != 0 { return false; } // Must have even number of digits to split in half. 11 + let (first_half, second_half) = s.split_at(len / 2); // Split in half and compare. 12 + first_half == second_half // If both halves are equal, it's invalid, return true. 13 + } 14 + 15 + /// Check if a product ID is invalid (digits repeated at least twice) for Part B. 16 + pub fn is_invalid_id_part_b(num: u64) -> bool { 17 + let s = num.to_string(); let len = s.len(); 18 + for pattern_len in 1..=len / 2 { // Try each possible pattern length from 1 to len/2. 19 + if len % pattern_len == 0 { // Pattern length must divide evenly. 20 + let pattern = &s[0..pattern_len]; 21 + let repetitions = len / pattern_len; 22 + if repetitions >= 2 { // Must repeat at least twice. 23 + if pattern.repeat(repetitions) == s { return true; } 24 + } 25 + } 26 + } 27 + false 28 + }
+28
src/2025/day2/rust/mod.rs
··· 1 + mod common; 2 + mod part_a; 3 + mod part_b; 4 + 5 + use part_a::part_a; 6 + use part_b::part_b; 7 + 8 + pub fn main() { 9 + let input = include_str!("../input.txt"); 10 + let example_input = include_str!("../input_example.txt"); 11 + println!("Example Part A: {}", part_a(example_input)); 12 + println!("Part A: {}", part_a(input)); 13 + println!("Example Part B: {}", part_b(example_input)); 14 + println!("Part B: {}", part_b(input)); 15 + } 16 + 17 + #[cfg(test)] 18 + mod tests { 19 + use super::*; 20 + #[test] 21 + fn test_part_a_example() { assert_eq!(part_a(include_str!("../input_example.txt")), 1227775554); } 22 + #[test] 23 + fn test_part_a() { assert_eq!(part_a(include_str!("../input.txt")), 28146997880); } 24 + #[test] 25 + fn test_part_b_example() { assert_eq!(part_b(include_str!("../input_example.txt")), 4174379265); } 26 + #[test] 27 + fn test_part_b() { assert_eq!(part_b(include_str!("../input.txt")), 40028128307); } 28 + }
+14
src/2025/day2/rust/part_a.rs
··· 1 + use super::common::{is_invalid_id, parse_range}; 2 + 3 + /// Sum all invalid product IDs across all ranges. 4 + pub fn part_a(input: &str) -> u64 { 5 + input 6 + .lines() 7 + .flat_map(|line| line.split(',')) // Split by commas. 8 + .filter(|s| !s.trim().is_empty()) // Filter empty strings. 9 + .map(|range| { 10 + let (start, end) = parse_range(range.trim()); 11 + (start..=end).filter(|&n| is_invalid_id(n)).sum::<u64>() 12 + }) 13 + .sum() 14 + }
+14
src/2025/day2/rust/part_b.rs
··· 1 + use super::common::{parse_range, is_invalid_id_part_b}; 2 + 3 + /// Sum all invalid product IDs (repeated at least twice) across all ranges. 4 + pub fn part_b(input: &str) -> u64 { 5 + input 6 + .lines() 7 + .flat_map(|line| line.split(',')) // Split by commas. 8 + .filter(|s| !s.trim().is_empty()) // Filter empty strings. 9 + .map(|range| { 10 + let (start, end) = parse_range(range.trim()); 11 + (start..=end).filter(|&n| is_invalid_id_part_b(n)).sum::<u64>() 12 + }) 13 + .sum() 14 + }