diff --git a/rs/Cargo.lock b/rs/Cargo.lock index afd09727..11e3f06d 100644 --- a/rs/Cargo.lock +++ b/rs/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "aho-corasick" @@ -28,6 +28,7 @@ name = "aoc2024" version = "0.1.0" dependencies = [ "criterion", + "if_chain", "indoc", "itertools 0.13.0", "pretty_assertions", @@ -204,6 +205,12 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" +[[package]] +name = "if_chain" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" + [[package]] name = "indoc" version = "2.0.5" diff --git a/rs/Cargo.toml b/rs/Cargo.toml index d2bc545c..10b1a8ed 100644 --- a/rs/Cargo.toml +++ b/rs/Cargo.toml @@ -17,6 +17,7 @@ name = "aoc2024" path = "src/main.rs" [dependencies] +if_chain = "1.0.2" itertools = "0.13" [dev-dependencies] diff --git a/rs/benches/criterion.rs b/rs/benches/criterion.rs index 860f3bb5..3a9e9b69 100644 --- a/rs/benches/criterion.rs +++ b/rs/benches/criterion.rs @@ -1,4 +1,4 @@ -use aoc2024::{day1, day2}; +use aoc2024::{day1, day2, day3}; use criterion::{black_box, Criterion}; use std::env; use std::fs; @@ -26,6 +26,12 @@ fn aoc2024_bench(c: &mut Criterion) -> io::Result<()> { g.bench_function("part 2", |b| b.iter(|| day2::part2(black_box(&data)))); g.finish(); + let data = get_day_input(3)?; + let mut g = c.benchmark_group("day 3"); + g.bench_function("part 1", |b| b.iter(|| day3::part1(black_box(&data)))); + g.bench_function("part 2", |b| b.iter(|| day3::part2(black_box(&data)))); + g.finish(); + Ok(()) } diff --git a/rs/src/day3.rs b/rs/src/day3.rs new file mode 100644 index 00000000..e89baa90 --- /dev/null +++ b/rs/src/day3.rs @@ -0,0 +1,60 @@ +use if_chain::if_chain; + +pub fn part1(data: &str) -> i32 { + const PREFIX: &'static str = "mul("; + + let mut data = data; + let mut total = 0; + while let Some(offset) = data.find(PREFIX) { + data = data[offset..].strip_prefix(PREFIX).unwrap(); + if_chain! { + if let Some((a, b)) = data.split_once(','); + if let Ok(a) = a.parse::(); + if let Some((b, c)) = b.split_once(')'); + if let Ok(b) = b.parse::(); + then { + data = c; + total += a * b; + } + } + } + total +} + +pub fn part2(data: &str) -> i32 { + let mut data = data; + let mut total = 0; + while !data.is_empty() { + let end = data.find("don't()").unwrap_or_else(|| data.len()); + total += part1(&data[..end]); + let Some(start) = data[end..].find("do()") else { + break; + }; + data = &data[end + start..]; + } + total +} + +#[cfg(test)] +mod tests { + use super::*; + use indoc::indoc; + use pretty_assertions::assert_eq; + + static EXAMPLE1: &str = indoc! {" + xmul(2,4)%&mul[3,7]!@^do_not_mul(5,5)+mul(32,64]then(mul(11,8)mul(8,5)) + "}; + static EXAMPLE2: &str = indoc! {" + xmul(2,4)&mul[3,7]!^don't()_mul(5,5)+mul(32,64](mul(11,8)undo()?mul(8,5)) + "}; + + #[test] + fn part1_examples() { + assert_eq!(161, part1(EXAMPLE1)); + } + + #[test] + fn part2_examples() { + assert_eq!(48, part2(EXAMPLE2)); + } +} diff --git a/rs/src/lib.rs b/rs/src/lib.rs index 4f29302f..4e7d7123 100644 --- a/rs/src/lib.rs +++ b/rs/src/lib.rs @@ -1,2 +1,3 @@ pub mod day1; pub mod day2; +pub mod day3; diff --git a/rs/src/main.rs b/rs/src/main.rs index bbe01821..2253ab6f 100644 --- a/rs/src/main.rs +++ b/rs/src/main.rs @@ -1,4 +1,4 @@ -use aoc2024::{day1, day2}; +use aoc2024::{day1, day2, day3}; use std::collections::HashSet; use std::env; use std::fs; @@ -32,5 +32,13 @@ fn main() -> io::Result<()> { println!(); } + if args.is_empty() || args.contains("3") { + println!("Day 3"); + let data = get_day_input(3)?; + println!("{:?}", day3::part1(&data)); + println!("{:?}", day3::part2(&data)); + println!(); + } + Ok(()) }