From 1f2dcef32842b2358cbe153a0f675825051bf24d Mon Sep 17 00:00:00 2001 From: Matt Harden Date: Sat, 13 Dec 2025 17:24:29 -0800 Subject: [PATCH] 2025 day 2. --- .zed/settings.json | 5 +++ 2025/01/src/part2/mod.rs | 1 + 2025/02/.gitignore | 1 + 2025/02/Cargo.toml | 10 +++++ 2025/02/data/example1 | 1 + 2025/02/data/input | 1 + 2025/02/src/data.rs | 5 +++ 2025/02/src/invalid_id.rs | 39 ++++++++++++++++++++ 2025/02/src/main.rs | 11 ++++++ 2025/02/src/part1/mod.rs | 41 +++++++++++++++++++++ 2025/02/src/part2/mod.rs | 41 +++++++++++++++++++++ 2025/02/src/puzzle.rs | 32 ++++++++++++++++ Cargo.lock | 77 +++++++++++++++++++++++++++++++++++++++ 13 files changed, 265 insertions(+) create mode 100644 .zed/settings.json create mode 100644 2025/02/.gitignore create mode 100644 2025/02/Cargo.toml create mode 100644 2025/02/data/example1 create mode 100644 2025/02/data/input create mode 100644 2025/02/src/data.rs create mode 100644 2025/02/src/invalid_id.rs create mode 100644 2025/02/src/main.rs create mode 100644 2025/02/src/part1/mod.rs create mode 100644 2025/02/src/part2/mod.rs create mode 100644 2025/02/src/puzzle.rs diff --git a/.zed/settings.json b/.zed/settings.json new file mode 100644 index 0000000..e999390 --- /dev/null +++ b/.zed/settings.json @@ -0,0 +1,5 @@ +{ + "file_types": { + "Plain Text": ["**/data/input", "**/data/example*"], + }, +} diff --git a/2025/01/src/part2/mod.rs b/2025/01/src/part2/mod.rs index 39c7ad8..e3ab99d 100644 --- a/2025/01/src/part2/mod.rs +++ b/2025/01/src/part2/mod.rs @@ -18,6 +18,7 @@ pub fn run(input: &str) -> usize { } count } + #[cfg(test)] mod test { use super::*; diff --git a/2025/02/.gitignore b/2025/02/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/2025/02/.gitignore @@ -0,0 +1 @@ +/target diff --git a/2025/02/Cargo.toml b/2025/02/Cargo.toml new file mode 100644 index 0000000..389f9c4 --- /dev/null +++ b/2025/02/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "y2025d02" +version = "0.1.0" +edition = "2024" + +[dependencies] +quickcheck = "1" +quickcheck_macros = "1" +generic-tests = "0" +lazy-regex = "3" diff --git a/2025/02/data/example1 b/2025/02/data/example1 new file mode 100644 index 0000000..bd04584 --- /dev/null +++ b/2025/02/data/example1 @@ -0,0 +1 @@ +11-22,95-115,998-1012,1188511880-1188511890,222220-222224,1698522-1698528,446443-446449,38593856-38593862,565653-565659,824824821-824824827,2121212118-2121212124 \ No newline at end of file diff --git a/2025/02/data/input b/2025/02/data/input new file mode 100644 index 0000000..5531ea6 --- /dev/null +++ b/2025/02/data/input @@ -0,0 +1 @@ +16100064-16192119,2117697596-2117933551,1-21,9999936269-10000072423,1770-2452,389429-427594,46633-66991,877764826-877930156,880869-991984,18943-26512,7216-9427,825-1162,581490-647864,2736-3909,39327886-39455605,430759-454012,1178-1741,219779-244138,77641-97923,1975994465-1976192503,3486612-3602532,277-378,418-690,74704280-74781349,3915-5717,665312-740273,69386294-69487574,2176846-2268755,26-45,372340114-372408052,7996502103-7996658803,7762107-7787125,48-64,4432420-4462711,130854-178173,87-115,244511-360206,69-86 \ No newline at end of file diff --git a/2025/02/src/data.rs b/2025/02/src/data.rs new file mode 100644 index 0000000..5b7bd47 --- /dev/null +++ b/2025/02/src/data.rs @@ -0,0 +1,5 @@ +#[cfg(test)] +pub const EXAMPLE1: &'static str = include_str!("../data/example1"); + +#[allow(unused)] +pub const INPUT: &'static str = include_str!("../data/input"); diff --git a/2025/02/src/invalid_id.rs b/2025/02/src/invalid_id.rs new file mode 100644 index 0000000..59f2040 --- /dev/null +++ b/2025/02/src/invalid_id.rs @@ -0,0 +1,39 @@ +const DIVISORS_OF: [&'static [u32]; 19] = [ + &[1], + &[1, 2], + &[1, 3], + &[1, 2, 4], + &[1, 5], + &[1, 2, 3, 6], + &[1, 7], + &[1, 2, 4, 8], + &[1, 3, 9], + &[1, 2, 5, 10], + &[1, 11], + &[1, 2, 3, 4, 6, 12], + &[1, 13], + &[1, 2, 7, 14], + &[1, 3, 5, 15], + &[1, 2, 4, 8, 16], + &[1, 17], + &[1, 2, 9, 18], + &[1, 19], +]; + +fn ones(digits: u32) -> u64 { + (10u64.pow(digits - 1) - 1) / 9 * 10 + 1 +} + +pub fn has_repeated_digits(n: u64, repeats: u32) -> bool { + let digits = n.ilog10() + 1; + if !digits.is_multiple_of(repeats) { + return false; + } + n.is_multiple_of(ones(digits) / ones(digits / repeats)) +} + +pub fn is_repeated(n: u64) -> bool { + DIVISORS_OF[n.ilog10() as usize][1..] + .into_iter() + .any(|&repeats| has_repeated_digits(n, repeats)) +} diff --git a/2025/02/src/main.rs b/2025/02/src/main.rs new file mode 100644 index 0000000..31ea51b --- /dev/null +++ b/2025/02/src/main.rs @@ -0,0 +1,11 @@ +mod data; +mod invalid_id; +mod part1; +mod part2; +mod puzzle; + +fn main() { + use data::INPUT; + println!("Part 1: {}", part1::run(INPUT)); + println!("Part 2: {}", part2::run(INPUT)); +} diff --git a/2025/02/src/part1/mod.rs b/2025/02/src/part1/mod.rs new file mode 100644 index 0000000..1d5d6d3 --- /dev/null +++ b/2025/02/src/part1/mod.rs @@ -0,0 +1,41 @@ +use crate::invalid_id::has_repeated_digits; +use crate::puzzle::Puzzle; + +fn sum_of_invalid_ids_in(it: impl Iterator) -> u64 { + it.filter(|&n| has_repeated_digits(n, 2)).sum() +} + +pub fn run(input: &str) -> u64 { + let puzzle: Puzzle = input.parse().expect("parse failed"); + puzzle + .ranges + .iter() + .cloned() + .map(sum_of_invalid_ids_in) + .sum() +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_sum_of_invalid_ids_in_range() { + assert_eq!(sum_of_invalid_ids_in(11..=22), 33); + assert_eq!(sum_of_invalid_ids_in(95..=115), 99); + assert_eq!(sum_of_invalid_ids_in(998..=1012), 1010); + assert_eq!(sum_of_invalid_ids_in(1188511880..=1188511890), 1188511885); + assert_eq!(sum_of_invalid_ids_in(222220..=222224), 222222); + assert_eq!(sum_of_invalid_ids_in(1698522..=1698528), 0); + assert_eq!(sum_of_invalid_ids_in(446443..=446449), 446446); + assert_eq!(sum_of_invalid_ids_in(38593856..=38593862), 38593859); + assert_eq!(sum_of_invalid_ids_in(565653..=565659), 0); + assert_eq!(sum_of_invalid_ids_in(824824821..=824824827), 0); + assert_eq!(sum_of_invalid_ids_in(2121212118..=2121212124), 0); + } + + #[test] + fn test1() { + assert_eq!(run(crate::data::EXAMPLE1), 1227775554); + } +} diff --git a/2025/02/src/part2/mod.rs b/2025/02/src/part2/mod.rs new file mode 100644 index 0000000..3b7aacd --- /dev/null +++ b/2025/02/src/part2/mod.rs @@ -0,0 +1,41 @@ +use crate::invalid_id::is_repeated; +use crate::puzzle::Puzzle; + +fn sum_of_invalid_ids_in(it: impl Iterator) -> u64 { + it.filter(|&n| is_repeated(n)).sum() +} + +pub fn run(input: &str) -> u64 { + let puzzle: Puzzle = input.parse().expect("parse failed"); + puzzle + .ranges + .iter() + .cloned() + .map(sum_of_invalid_ids_in) + .sum() +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_sum_of_invalid_ids_in_range() { + assert_eq!(sum_of_invalid_ids_in(11..=22), 33); + assert_eq!(sum_of_invalid_ids_in(95..=115), 210); + assert_eq!(sum_of_invalid_ids_in(998..=1012), 2009); + assert_eq!(sum_of_invalid_ids_in(1188511880..=1188511890), 1188511885); + assert_eq!(sum_of_invalid_ids_in(222220..=222224), 222222); + assert_eq!(sum_of_invalid_ids_in(1698522..=1698528), 0); + assert_eq!(sum_of_invalid_ids_in(446443..=446449), 446446); + assert_eq!(sum_of_invalid_ids_in(38593856..=38593862), 38593859); + assert_eq!(sum_of_invalid_ids_in(565653..=565659), 565656); + assert_eq!(sum_of_invalid_ids_in(824824821..=824824827), 824824824); + assert_eq!(sum_of_invalid_ids_in(2121212118..=2121212124), 2121212121); + } + + #[test] + fn test1() { + assert_eq!(run(crate::data::EXAMPLE1), 4174379265); + } +} diff --git a/2025/02/src/puzzle.rs b/2025/02/src/puzzle.rs new file mode 100644 index 0000000..a54d7cd --- /dev/null +++ b/2025/02/src/puzzle.rs @@ -0,0 +1,32 @@ +use lazy_regex::regex_captures; + +type Range = std::ops::RangeInclusive; + +#[derive(Debug)] +pub struct Puzzle { + pub ranges: Vec, +} + +#[derive(Debug)] +pub struct ParseError(); + +impl From for ParseError { + fn from(_value: std::num::ParseIntError) -> Self { + Self() + } +} + +fn parse_range(s: &str) -> Result { + let (_, start, end) = regex_captures!(r#"(\d+)-(\d+)"#, s).ok_or(ParseError())?; + Ok((start.parse()?)..=(end.parse()?)) +} + +impl std::str::FromStr for Puzzle { + type Err = ParseError; + + fn from_str(s: &str) -> Result { + Ok(Puzzle { + ranges: s.split(',').map(parse_range).collect::>()?, + }) + } +} diff --git a/Cargo.lock b/Cargo.lock index 475ff75..f0365a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -240,6 +240,16 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "env_logger" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" +dependencies = [ + "log", + "regex", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -286,6 +296,17 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "generic-tests" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9ff6d6584f4f6fa911d5e07856abf1a48dc5599b3734f2eaea130f2c3baa989" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "getrandom" version = "0.2.16" @@ -393,6 +414,12 @@ version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + [[package]] name = "lru" version = "0.7.8" @@ -628,6 +655,28 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "quickcheck" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6" +dependencies = [ + "env_logger", + "log", + "rand", +] + +[[package]] +name = "quickcheck_macros" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f71ee38b42f8459a88d3362be6f9b841ad2d5421844f61eb1c59c11bff3ac14a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "quote" version = "1.0.40" @@ -643,6 +692,24 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + [[package]] name = "regex" version = "1.11.1" @@ -1165,3 +1232,13 @@ version = "0.1.0" [[package]] name = "y2025d01" version = "0.1.0" + +[[package]] +name = "y2025d02" +version = "0.1.0" +dependencies = [ + "generic-tests", + "lazy-regex", + "quickcheck", + "quickcheck_macros", +]