From 65265f348944675db55278b7a2ebe95903b33d4b Mon Sep 17 00:00:00 2001 From: Russell Champoux Date: Tue, 2 Dec 2025 20:32:01 +0000 Subject: [PATCH] first two days --- Cargo.lock | 55 ++++++++++++++++++++++++++++++++ Cargo.toml | 2 ++ README.md | 9 ++++++ data/examples/01.txt | 10 ++++++ data/examples/02.txt | 1 + src/bin/01.rs | 56 ++++++++++++++++++++++++++++++++ src/bin/02.rs | 76 ++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 209 insertions(+) create mode 100644 data/examples/01.txt create mode 100644 data/examples/02.txt create mode 100644 src/bin/01.rs create mode 100644 src/bin/02.rs diff --git a/Cargo.lock b/Cargo.lock index 8147679..90137cc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,10 +23,21 @@ version = "0.12.0" dependencies = [ "chrono", "dhat", + "itertools", "pico-args", + "regex", "tinyjson", ] +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + [[package]] name = "android-tzdata" version = "0.1.1" @@ -126,6 +137,12 @@ dependencies = [ "thousands", ] +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + [[package]] name = "gimli" version = "0.28.1" @@ -155,6 +172,15 @@ dependencies = [ "cc", ] +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.9" @@ -303,6 +329,35 @@ dependencies = [ "bitflags", ] +[[package]] +name = "regex" +version = "1.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" + [[package]] name = "rustc-demangle" version = "0.1.23" diff --git a/Cargo.toml b/Cargo.toml index b1943db..4887e4d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,9 @@ test_lib = [] # Template dependencies chrono = { version = "0.4.38", optional = true } dhat = { version = "0.3.3", optional = true } +itertools = "0.14.0" pico-args = "0.5.0" +regex = "1.12.2" tinyjson = "2.5.1" # Solution dependencies diff --git a/README.md b/README.md index e3db903..45d29af 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,15 @@ Solutions for [Advent of Code](https://adventofcode.com/) in [Rust](https://www. + +## Benchmarks + +| Day | Part 1 | Part 2 | +| :---: | :---: | :---: | +| [Day 1](./src/bin/01.rs) | `881.1µs` | `849.0µs` | +| [Day 2](./src/bin/02.rs) | `225.7ms` | `250.4ms` | + +**Total: 477.83ms** --- diff --git a/data/examples/01.txt b/data/examples/01.txt new file mode 100644 index 0000000..53287c7 --- /dev/null +++ b/data/examples/01.txt @@ -0,0 +1,10 @@ +L68 +L30 +R48 +L5 +R60 +L55 +L1 +L99 +R14 +L82 diff --git a/data/examples/02.txt b/data/examples/02.txt new file mode 100644 index 0000000..a3f22ef --- /dev/null +++ b/data/examples/02.txt @@ -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 diff --git a/src/bin/01.rs b/src/bin/01.rs new file mode 100644 index 0000000..fc1e61d --- /dev/null +++ b/src/bin/01.rs @@ -0,0 +1,56 @@ +advent_of_code::solution!(1); +use regex::Regex; + +pub fn part_one(input: &str) -> Option { + let dir_regex = Regex::new(r"(.)(\d+)").unwrap(); + + let (hits, _total) = dir_regex.captures_iter(input).fold((0u32, 50i32), |(hits, total), cap| { + let (_, [direction, value_str]) = cap.extract(); + let value = value_str.parse::().unwrap(); + + let new_total = (total + 100 + if direction == "L" { -1i32 } else { 1 } * value) % 100; + let new_hits = if new_total == 0 { hits + 1 } else { hits }; + + // println!("{} + {} {} = {}", total, direction, value, new_total); + + (new_hits, new_total) + }); + + Some(hits as u64) +} + +pub fn part_two(input: &str) -> Option { + let dir_regex = Regex::new(r"(.)(\d+)").unwrap(); + + let (hits, _total) = dir_regex.captures_iter(input).fold((0u32, 50i32), |(hits, total), cap| { + let (_, [direction, value_str]) = cap.extract(); + let value: i32 = value_str.parse::().unwrap(); + + let new_total_unmod = total + if direction == "L" { -1i32 } else { 1 } * value; + let new_total = (new_total_unmod % 100 + 100) % 100; + let clicks_past_zero = (new_total_unmod / 100).abs() as u32 + if total != 0 && new_total_unmod <= 0 { 1 } else { 0 }; + + // println!("{} + {} {} = {} ({} total clicks, {} new)", total, direction, value, new_total, hits + clicks_past_zero, clicks_past_zero); + + (hits + clicks_past_zero, new_total) + }); + + Some(hits as u64) +} + +#[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(3)); + } + + #[test] + fn test_part_two() { + let result = part_two(&advent_of_code::template::read_file("examples", DAY)); + assert_eq!(result, Some(6)); + } +} diff --git a/src/bin/02.rs b/src/bin/02.rs new file mode 100644 index 0000000..d792128 --- /dev/null +++ b/src/bin/02.rs @@ -0,0 +1,76 @@ +advent_of_code::solution!(2); +use itertools::Itertools; + +pub fn part_one(input: &str) -> Option { + let mut sum: u64 = 0; + for range in input.split(',') { + // println!("Range is: {}", range); + let values = range.split('-').map(|v| v.trim().parse::().unwrap()).collect::>(); + let start = values[0]; + let end = values[1]; + // println!("Range: {}-{}", start, end); + for n in start..=end { + let s = n.to_string(); + if s.chars().collect_vec().len() % 2 != 0 { + continue; + } + let first_half = &s[0..(s.len() / 2)]; + let second_half = &s[(s.len() / 2)..s.len()]; + if first_half == second_half { + // println!(" Number: {} ({} | {})", n, first_half, second_half); + sum += n; + } + } + } + Some(sum) +} + +pub fn part_two(input: &str) -> Option { + let mut sum: u64 = 0; + for range in input.split(',') { + // println!("Range is: {}", range); + let values = range.split('-').map(|v| v.trim().parse::().unwrap()).collect::>(); + let start = values[0]; + let end = values[1]; + // println!("Range: {}-{}", start, end); + for n in start..=end { + let s = n.to_string(); + let total_chars = s.chars().collect_vec().len(); + 'takeBy: for take_by in 1..=(total_chars / 2) { + if total_chars % take_by != 0 { + continue; + } + let mut idx = 0; + while idx + 2 * take_by <= total_chars { + let part = &s[idx..(idx + take_by)]; + let next_part = &s[(idx + take_by)..(idx + 2 * take_by)]; + if part != next_part { + continue 'takeBy; + } + idx += take_by; + } + // println!(" Number: {} (take by {})", n, take_by); + sum += n; + break; // do not continue checking other take_by values + } + } + } + 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)); + } +}