diff --git a/2025/Cargo.lock b/2025/Cargo.lock index 5932691..f153b5a 100644 --- a/2025/Cargo.lock +++ b/2025/Cargo.lock @@ -175,6 +175,21 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "day-06" +version = "0.1.0" +dependencies = [ + "divan", + "itertools", + "miette", + "nom", + "rstest", + "test-log", + "thiserror", + "tracing", + "tracing-subscriber", +] + [[package]] name = "divan" version = "0.1.21" diff --git a/2025/Cargo.toml b/2025/Cargo.toml index a146cb3..8da7945 100644 --- a/2025/Cargo.toml +++ b/2025/Cargo.toml @@ -6,6 +6,7 @@ members = [ "day-03", "day-04", "day-05", + "day-06", ] default-members = ["day-*"] resolver = "2" diff --git a/2025/day-06/Cargo.toml b/2025/day-06/Cargo.toml new file mode 100644 index 0000000..a67b941 --- /dev/null +++ b/2025/day-06/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "day-06" +version = "0.1.0" +edition = "2024" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +itertools.workspace = true +nom.workspace = true +tracing.workspace = true +tracing-subscriber.workspace = true +miette.workspace = true +thiserror.workspace = true + +[dev-dependencies] +divan.workspace = true +rstest.workspace = true +test-log.workspace = true + +[[bench]] +name = "day-06-bench" +path = "benches/benchmarks.rs" +harness = false + +[lints] +workspace = true diff --git a/2025/day-06/benches/benchmarks.rs b/2025/day-06/benches/benchmarks.rs new file mode 100644 index 0000000..19c60fb --- /dev/null +++ b/2025/day-06/benches/benchmarks.rs @@ -0,0 +1,21 @@ +use day_06::*; + +fn main() { + divan::main(); +} + +#[divan::bench] +fn part1() { + part1::process(divan::black_box(include_str!( + "../input1.txt", + ))) + .unwrap(); +} + +#[divan::bench] +fn part2() { + part2::process(divan::black_box(include_str!( + "../input2.txt", + ))) + .unwrap(); +} diff --git a/2025/day-06/src/bin/part1.rs b/2025/day-06/src/bin/part1.rs new file mode 100644 index 0000000..6ab04e8 --- /dev/null +++ b/2025/day-06/src/bin/part1.rs @@ -0,0 +1,12 @@ +use day_06::part1::process; +use miette::{Context, Result}; + +#[tracing::instrument] +fn main() -> Result<()> { + tracing_subscriber::fmt::init(); + + let file = include_str!("../../input1.txt"); + let result = process(file).context("process part 1")?; + println!("{result}"); + Ok(()) +} diff --git a/2025/day-06/src/bin/part2.rs b/2025/day-06/src/bin/part2.rs new file mode 100644 index 0000000..267d037 --- /dev/null +++ b/2025/day-06/src/bin/part2.rs @@ -0,0 +1,12 @@ +use day_06::part2::process; +use miette::{Context, Result}; + +#[tracing::instrument] +fn main() -> Result<()> { + tracing_subscriber::fmt::init(); + + let file = include_str!("../../input2.txt"); + let result = process(file).context("process part 2")?; + println!("{result}"); + Ok(()) +} diff --git a/2025/day-06/src/lib.rs b/2025/day-06/src/lib.rs new file mode 100644 index 0000000..faaf542 --- /dev/null +++ b/2025/day-06/src/lib.rs @@ -0,0 +1,2 @@ +pub mod part1; +pub mod part2; diff --git a/2025/day-06/src/part1.rs b/2025/day-06/src/part1.rs new file mode 100644 index 0000000..5566250 --- /dev/null +++ b/2025/day-06/src/part1.rs @@ -0,0 +1,102 @@ +use miette::miette; +use std::{fmt::Display, str::FromStr, vec}; + +#[derive(Debug, Clone)] +struct Row(Vec); + +impl FromStr for Row +where + T: FromStr, + T::Err: Display, +{ + type Err = String; + fn from_str(s: &str) -> Result { + let row = s + .split_whitespace() + .map(str::parse::) + .collect::, _>>() + .map_err(|e| e.to_string())?; + Ok(Self(row)) + } +} + +#[derive(Debug, Clone)] +struct Grid(Vec); + +impl Grid> { + fn to_transposed(&self) -> Self { + if self.0.is_empty() { + return Self(Vec::new()); + } + + let num_cols = self.0[0].0.len(); + let mut transposed = vec![Vec::new(); num_cols]; + + for row in &self.0 { + for (col_idx, val) in row.0.iter().enumerate() { + if col_idx < transposed.len() { + transposed[col_idx].push(val.clone()); + } + } + } + + Self(transposed.into_iter().map(Row).collect()) + } +} + +impl FromStr for Grid +where + T: FromStr, + T::Err: Display, +{ + type Err = String; + fn from_str(s: &str) -> Result { + let rows = s + .lines() + .map(T::from_str) + .collect::, _>>() + .map_err(|e| e.to_string())?; + Ok(Self(rows)) + } +} + +#[tracing::instrument] +#[allow(clippy::missing_panics_doc)] +#[allow(clippy::missing_errors_doc)] +pub fn process(input: &str) -> miette::Result { + let (rest, last_line) = match input.trim_end().rsplit_once('\n') { + Some((r, l)) => (r, l), + None => ("", ""), + }; + let digits = rest + .parse::>>() + .map_err(|e| miette!("{e}"))?; + let operators = last_line.parse::>().map_err(|e| miette!("{e}"))?; + let result = operators + .0 + .iter() + .zip(digits.to_transposed().0) + .map(|(operator, row)| match operator { + '+' => row.0.iter().sum(), + '*' => row.0.iter().product::(), + op => panic!("Unknown operator: {op}"), + }) + .sum(); + Ok(result) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_process() -> miette::Result<()> { + let input = "123 328 51 64 + 45 64 387 23 + 6 98 215 314 +* + * + "; + let result = 4_277_556; + assert_eq!(process(input)?, result); + Ok(()) + } +} diff --git a/2025/day-06/src/part2.rs b/2025/day-06/src/part2.rs new file mode 100644 index 0000000..ede34a7 --- /dev/null +++ b/2025/day-06/src/part2.rs @@ -0,0 +1,23 @@ +use miette::Result; + +#[tracing::instrument] +#[allow(clippy::missing_panics_doc)] +#[allow(clippy::missing_errors_doc)] +pub fn process(input: &str) -> Result { + todo!("day xx - part 2"); + Ok(0) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_process() -> Result<()> { + let input = ""; + todo!("haven't built test yet"); + let result = 0; + assert_eq!(process(input)?, result); + Ok(()) + } +}