2025-12-04 17:34:11 -05:00
|
|
|
use itertools::Itertools;
|
|
|
|
|
|
2025-12-04 17:11:46 -05:00
|
|
|
advent_of_code::solution!(2);
|
|
|
|
|
|
2025-12-04 17:34:11 -05:00
|
|
|
/// Parse a range string like "11-22" into (start, end)
|
|
|
|
|
fn parse_range(s: &str) -> Option<(u64, u64)> {
|
|
|
|
|
s.trim()
|
|
|
|
|
.split('-')
|
|
|
|
|
.map(|x| x.parse::<u64>().ok())
|
|
|
|
|
.collect_tuple()
|
|
|
|
|
.and_then(|(a, b)| Some((a?, b?)))
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-04 17:11:46 -05:00
|
|
|
/// Check if a number is "invalid" - made of a pattern repeated exactly twice
|
|
|
|
|
fn is_invalid_part1(n: u64) -> bool {
|
|
|
|
|
let s = n.to_string();
|
|
|
|
|
let len = s.len();
|
|
|
|
|
// Must have even length for pattern to repeat exactly twice
|
2025-12-04 17:34:11 -05:00
|
|
|
len % 2 == 0 && s[..len / 2] == s[len / 2..]
|
2025-12-04 17:11:46 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Check if a number is "invalid" - made of a pattern repeated at least twice
|
|
|
|
|
fn is_invalid_part2(n: u64) -> bool {
|
|
|
|
|
let s = n.to_string();
|
|
|
|
|
let len = s.len();
|
|
|
|
|
|
|
|
|
|
// Try each possible pattern length (1 to len/2)
|
2025-12-04 17:34:11 -05:00
|
|
|
(1..=len / 2).any(|pattern_len| {
|
2025-12-04 17:11:46 -05:00
|
|
|
// Pattern can only work if it divides evenly
|
2025-12-04 17:34:11 -05:00
|
|
|
len % pattern_len == 0 && {
|
|
|
|
|
let pattern = &s[..pattern_len];
|
|
|
|
|
// Check if all chunks match the pattern
|
|
|
|
|
s.as_bytes()
|
|
|
|
|
.chunks(pattern_len)
|
|
|
|
|
.all(|chunk| chunk == pattern.as_bytes())
|
2025-12-04 17:11:46 -05:00
|
|
|
}
|
2025-12-04 17:34:11 -05:00
|
|
|
})
|
|
|
|
|
}
|
2025-12-04 17:11:46 -05:00
|
|
|
|
2025-12-04 17:34:11 -05:00
|
|
|
pub fn part_one(input: &str) -> Option<u64> {
|
|
|
|
|
let sum = input
|
|
|
|
|
.split(',')
|
|
|
|
|
.filter_map(|part| parse_range(part))
|
|
|
|
|
.flat_map(|(start, end)| start..=end)
|
|
|
|
|
.filter(|&n| is_invalid_part1(n))
|
|
|
|
|
.sum();
|
2025-12-04 17:11:46 -05:00
|
|
|
|
2025-12-04 17:34:11 -05:00
|
|
|
Some(sum)
|
2025-12-04 17:11:46 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn part_two(input: &str) -> Option<u64> {
|
2025-12-04 17:34:11 -05:00
|
|
|
let sum = input
|
|
|
|
|
.split(',')
|
|
|
|
|
.filter_map(|part| parse_range(part))
|
|
|
|
|
.flat_map(|(start, end)| start..=end)
|
|
|
|
|
.filter(|&n| is_invalid_part2(n))
|
|
|
|
|
.sum();
|
2025-12-04 17:11:46 -05:00
|
|
|
|
|
|
|
|
Some(sum)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_part_one() {
|
|
|
|
|
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
|
|
|
|
|
assert_eq!(result, Some(1227775554));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_part_two() {
|
|
|
|
|
let result = part_two(&advent_of_code::template::read_file("examples", DAY));
|
|
|
|
|
assert_eq!(result, Some(4174379265));
|
|
|
|
|
}
|
|
|
|
|
}
|