From 5d8cba41895f32fadea6ffb4e63718063dbb17db Mon Sep 17 00:00:00 2001 From: Dylan Thies Date: Sun, 1 Dec 2024 09:41:59 -0500 Subject: [PATCH] 2024 day-1 done --- 2024/Cargo.toml | 33 +++++++++++++++++++++++++ 2024/day-1/Cargo.toml | 23 ++++++++++++++++++ 2024/day-1/src/lib.rs | 4 +++ 2024/day-1/src/main.rs | 31 +++++++++++++++++++++++ 2024/day-1/src/part1.rs | 54 +++++++++++++++++++++++++++++++++++++++++ 2024/day-1/src/part2.rs | 54 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 199 insertions(+) create mode 100644 2024/Cargo.toml create mode 100644 2024/day-1/Cargo.toml create mode 100644 2024/day-1/src/lib.rs create mode 100644 2024/day-1/src/main.rs create mode 100644 2024/day-1/src/part1.rs create mode 100644 2024/day-1/src/part2.rs diff --git a/2024/Cargo.toml b/2024/Cargo.toml new file mode 100644 index 0000000..0bb189b --- /dev/null +++ b/2024/Cargo.toml @@ -0,0 +1,33 @@ +[workspace] +resolver = "2" +members = [ "day-*" ] + +[workspace.package] +version = "2024.0.0" +edition = "2021" +authors = [ "Dylan Thies" ] +repository = "https://github.com/smellyfis/AOC.git" + +[workspace.dependencies] +derive-getters = "0.5.0" +error-stack = "0.5.0" +itertools = "0.13.0" +log = "0.4.22" +nom = "7.1.3" +nom_locate= "4.2.0" +rstest = "0.23.0" +rstest_reuse = "0.7.0" +dhat = "0.3.3" +glam = "0.29.2" +petgraph = "0.6.5" +num = "0.4.3" +num-traits = "0.2.19" +rustworkx-core = "0.15.1" +pathfinding = "4.11.0" +test-log = {version="0.2.16", features=["default", "unstable"]} +thiserror = "2.0.3" + + +[profile.dhat] +inherits = "release" +debug = true diff --git a/2024/day-1/Cargo.toml b/2024/day-1/Cargo.toml new file mode 100644 index 0000000..7b4bb90 --- /dev/null +++ b/2024/day-1/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "day-1" +version.workspace = true +edition.workspace = true +authors.workspace = true +repository.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + + +[dependencies] +nom.workspace = true +itertools.workspace = true +log.workspace = true +error-stack.workspace = true +thiserror.workspace = true +dhat.workspace = true + +[dev-dependencies] +test-log.workspace = true + +[features] +dhat-heap = [] diff --git a/2024/day-1/src/lib.rs b/2024/day-1/src/lib.rs new file mode 100644 index 0000000..8baf233 --- /dev/null +++ b/2024/day-1/src/lib.rs @@ -0,0 +1,4 @@ +pub(crate) mod part1; +pub use crate::part1::*; +pub(crate) mod part2; +pub use crate::part2::*; diff --git a/2024/day-1/src/main.rs b/2024/day-1/src/main.rs new file mode 100644 index 0000000..65822aa --- /dev/null +++ b/2024/day-1/src/main.rs @@ -0,0 +1,31 @@ +#![warn(clippy::all, clippy::pedantic)] + +use day_1::part1; +use day_1::part2; + +use error_stack::{Result, ResultExt}; +use thiserror::Error; + +#[cfg(feature = "dhat-heap")] +#[global_allocator] +static ALLOC: dhat::Alloc = dhat::Alloc; + +#[derive(Debug, Error)] +enum Day1Error { + #[error("Part 1 failed")] + Part1Error, + #[error("Part 2 failed")] + Part2Error, +} + +fn main() -> Result<(), Day1Error> { + #[cfg(feature = "dhat-heap")] + let _profiler = dhat::Profiler::new_heap(); + + let input = include_str!("./input.txt"); + let part1_result = part1(input).change_context(Day1Error::Part1Error)?; + println!("part 1: {part1_result}"); + let part2_result = part2(input).change_context(Day1Error::Part2Error)?; + println!("part 2: {part2_result}"); + Ok(()) +} diff --git a/2024/day-1/src/part1.rs b/2024/day-1/src/part1.rs new file mode 100644 index 0000000..c004fc1 --- /dev/null +++ b/2024/day-1/src/part1.rs @@ -0,0 +1,54 @@ +#![warn(clippy::all, clippy::pedantic)] + +use error_stack::Result; +use nom::{character::complete, multi::separated_list1, sequence::separated_pair, IResult}; +use thiserror::Error; + +// day-1 +#[derive(Debug, Error)] +pub enum Day1Part1Error { + #[error("Problem parsing Day 1")] + ParseError, +} + +pub fn part1(input: &str) -> Result { + let (_, (mut col1, mut col2)) = + parse_input(input).expect("there should always be valid input from aoc"); + col1.sort(); + col2.sort(); + + Ok(col1 + .into_iter() + .zip(col2.iter()) + .map(|(a, b)| u64::max(a, *b) - u64::min(a, *b)) + .sum()) +} + +fn parse_input(input: &str) -> IResult<&str, (Vec, Vec)> { + let (input, combo) = separated_list1( + complete::line_ending, + separated_pair(complete::u64, complete::space1, complete::u64), + )(input)?; + Ok((input, combo.into_iter().unzip())) + +} + +#[cfg(test)] +mod test { + use super::*; + + const INPUT: &str = "3 4 +4 3 +2 5 +1 3 +3 9 +3 3 "; + + #[test_log::test] + #[test_log(default_log_filter = "trace")] + fn part1_works() { + let result = part1(INPUT).unwrap(); + assert_eq!(result, 11); + } +} + diff --git a/2024/day-1/src/part2.rs b/2024/day-1/src/part2.rs new file mode 100644 index 0000000..f8de359 --- /dev/null +++ b/2024/day-1/src/part2.rs @@ -0,0 +1,54 @@ +#![warn(clippy::all, clippy::pedantic)] + +use std::collections::HashMap; + +use error_stack::{Report, Result, ResultExt}; +use nom::{character::complete, multi::separated_list1, sequence::separated_pair, IResult}; +use thiserror::Error; + +// day-1 +#[derive(Debug, Error)] +pub enum Day1Part2Error { + #[error("Problem parsing Day 1")] + ParseError, +} + +pub fn part2(input: &str) -> Result { + let (_, (col1, col2)) = parse_input(input) + .map_err(|x| Report::from(x.to_owned())) + .change_context(Day1Part2Error::ParseError)?; + let col2_bucket: HashMap = col2.into_iter().fold(HashMap::new(), |mut acc, x| { + let val = acc.entry(x).or_insert(0); + *val += 1; + acc + }); + Ok(col1.iter().map(|x| *x * col2_bucket.get(x).or(Some(&0)).unwrap()).sum()) +} + +fn parse_input(input: &str) -> IResult<&str, (Vec, Vec)> { + let (input, combo) = separated_list1( + complete::line_ending, + separated_pair(complete::u64, complete::space1, complete::u64), + )(input)?; + Ok((input, combo.into_iter().unzip())) +} + +#[cfg(test)] +mod test { + use super::*; + + const INPUT: &str = "3 4 +4 3 +2 5 +1 3 +3 9 +3 3 "; + + #[test_log::test] + #[test_log(default_log_filter = "trace")] + fn part2_works() { + let result = part2(INPUT).unwrap(); + assert_eq!(result, 31); + } +} +