From fdb74e64398bd031fc753832271d84b25e22ca96 Mon Sep 17 00:00:00 2001 From: Dylan Thies Date: Wed, 4 Dec 2024 08:45:19 -0500 Subject: [PATCH] 2024 day-4 as completed --- 2024/day-4/Cargo.toml | 24 ++++++++++ 2024/day-4/src/lib.rs | 4 ++ 2024/day-4/src/main.rs | 31 +++++++++++++ 2024/day-4/src/part1.rs | 96 ++++++++++++++++++++++++++++++++++++++ 2024/day-4/src/part2.rs | 100 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 255 insertions(+) create mode 100644 2024/day-4/Cargo.toml create mode 100644 2024/day-4/src/lib.rs create mode 100644 2024/day-4/src/main.rs create mode 100644 2024/day-4/src/part1.rs create mode 100644 2024/day-4/src/part2.rs diff --git a/2024/day-4/Cargo.toml b/2024/day-4/Cargo.toml new file mode 100644 index 0000000..918d7a4 --- /dev/null +++ b/2024/day-4/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "day-4" +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 +glam.workspace = true + +[dev-dependencies] +test-log.workspace = true + +[features] +dhat-heap = [] diff --git a/2024/day-4/src/lib.rs b/2024/day-4/src/lib.rs new file mode 100644 index 0000000..3fafe8d --- /dev/null +++ b/2024/day-4/src/lib.rs @@ -0,0 +1,4 @@ +pub mod part1; +pub use crate::part1::*; +pub mod part2; +pub use crate::part2::*; diff --git a/2024/day-4/src/main.rs b/2024/day-4/src/main.rs new file mode 100644 index 0000000..4e06dd5 --- /dev/null +++ b/2024/day-4/src/main.rs @@ -0,0 +1,31 @@ +#![warn(clippy::all, clippy::pedantic)] + +use day_4::part1; +use day_4::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 Day4Error { + #[error("Part 1 failed")] + Part1Error, + #[error("Part 2 failed")] + Part2Error, +} + +fn main() -> Result<(), Day4Error> { + #[cfg(feature = "dhat-heap")] + let _profiler = dhat::Profiler::new_heap(); + + let input = include_str!("./input.txt"); + let part1_result = part1(input).change_context(Day4Error::Part1Error)?; + println!("part 1: {part1_result}"); + let part2_result = part2(input).change_context(Day4Error::Part2Error)?; + println!("part 2: {part2_result}"); + Ok(()) +} diff --git a/2024/day-4/src/part1.rs b/2024/day-4/src/part1.rs new file mode 100644 index 0000000..2123e6f --- /dev/null +++ b/2024/day-4/src/part1.rs @@ -0,0 +1,96 @@ +#![warn(clippy::all, clippy::pedantic)] + +use error_stack::Result; +use glam::IVec2; +use thiserror::Error; + +// day-4 +#[derive(Debug, Error)] +pub enum Day4Part1Error { + #[error("Problem parsing Day 4")] + ParseError, +} + +pub fn part1(input: &str) -> Result { + //read in grid + let grid = input + .lines() + .map(|line| Vec::from(line.as_bytes())) + .collect::>(); + let num_of_rows = grid.len().try_into().unwrap(); + let num_of_cols = grid[0].len().try_into().unwrap(); //because we know it will be rectangular + //window over each letter (skip over not x's + let total: usize = grid + .iter() + .enumerate() + .map(|(row_num, row)| { + row.iter() + .enumerate() + .map(|(col_num, col)| { + if *col == b'X' { + //window over the rest + let point = + IVec2::new(row_num.try_into().unwrap(), col_num.try_into().unwrap()); + [ + IVec2::NEG_X, + IVec2::NEG_ONE, + IVec2::NEG_Y, + IVec2::new(1, -1), + IVec2::X, + IVec2::ONE, + IVec2::Y, + IVec2::new(-1, 1), + ] + .iter() + .filter(|dir| { + let extent = point + (*dir * 3); + if extent.x < 0 + || extent.x >= num_of_rows + || extent.y < 0 + || extent.y >= num_of_cols + { + return false; + } + let m = point + *dir; + let a = point + 2 * *dir; + let s = point + 3 * *dir; + grid[m.x as u32 as usize][m.y as u32 as usize] == b'M' + && grid[a.x as u32 as usize][a.y as u32 as usize] == b'A' + && grid[s.x as u32 as usize][s.y as u32 as usize] == b'S' + }) + .count() + //todo!("at pos {row_num} - {col_num}") + } else { + 0_usize + } + }) + .sum::() + }) + .sum(); + //count + Ok(total.to_string()) +} + +#[cfg(test)] +mod test { + use super::*; + + const INPUT: &str = "MMMSXXMASM +MSAMXMSMSA +AMXSXMAAMM +MSAMASMSMX +XMASAMXAMM +XXAMMXXAMA +SMSMSASXSS +SAXAMASAAA +MAMMMXMMMM +MXMXAXMASX"; + + #[test_log::test] + #[test_log(default_log_filter = "trace")] + fn part1_works() { + let result = part1(INPUT).unwrap(); + assert_eq!(result, "18".to_string()); + } +} + diff --git a/2024/day-4/src/part2.rs b/2024/day-4/src/part2.rs new file mode 100644 index 0000000..ba4c7ef --- /dev/null +++ b/2024/day-4/src/part2.rs @@ -0,0 +1,100 @@ +#![warn(clippy::all, clippy::pedantic)] + +use error_stack::Result; +use glam::IVec2; +use thiserror::Error; + +// day-4 +#[derive(Debug, Error)] +pub enum Day4Part2Error { + #[error("Problem parsing Day 4")] + ParseError, +} + +pub fn part2(input: &str) -> Result { + //read in grid + let grid = input + .lines() + .map(|line| Vec::from(line.as_bytes())) + .collect::>(); + let num_of_rows = grid.len(); + let num_of_cols = grid[0].len(); + //window over each letter (skip over not x's + let total: usize = grid + .iter() + .enumerate() + .skip(1) + .take(num_of_rows - 2) + .map(|(row_num, row)| { + row.iter() + .enumerate() + .skip(1) + .take(num_of_cols - 2) + .map(|(col_num, col)| { + if *col == b'A' { + //window over the rest + let point = + IVec2::new(row_num.try_into().unwrap(), col_num.try_into().unwrap()); + let up_forward = point + IVec2::new(-1, 1); + let up_back = point + IVec2::NEG_ONE; + let down_forward = point + IVec2::ONE; + let down_back = point + IVec2::new(1, -1); + if ((grid[up_back.x as u32 as usize][up_back.y as u32 as usize] == b'M' + && grid[down_forward.x as u32 as usize] + [down_forward.y as u32 as usize] + == b'S') + || (grid[up_back.x as u32 as usize][up_back.y as u32 as usize] == b'S' + && grid[down_forward.x as u32 as usize] + [down_forward.y as u32 as usize] + == b'M')) + && ((grid[down_back.x as u32 as usize][down_back.y as u32 as usize] + == b'M' + && grid[up_forward.x as u32 as usize] + [up_forward.y as u32 as usize] + == b'S') + || (grid[down_back.x as u32 as usize][down_back.y as u32 as usize] + == b'S' + && grid[up_forward.x as u32 as usize] + [up_forward.y as u32 as usize] + == b'M')) + { + //println!(" found at {}-{}", point.x, point.y); + 1 + } else { + 0 + } + // todo!("at pos {row_num} - {col_num}") + } else { + 0_usize + } + }) + .sum::() + }) + .sum(); + //count + Ok(total.to_string()) +} + +#[cfg(test)] +mod test { + use super::*; + + const INPUT: &str = "MMMSXXMASM +MSAMXMSMSA +AMXSXMAAMM +MSAMASMSMX +XMASAMXAMM +XXAMMXXAMA +SMSMSASXSS +SAXAMASAAA +MAMMMXMMMM +MXMXAXMASX"; + + #[test_log::test] + #[test_log(default_log_filter = "trace")] + fn part2_works() { + let result = part2(INPUT).unwrap(); + assert_eq!(result, "9".to_string()); + } +} +