2024 day-4 as completed

This commit is contained in:
Dylan Thies
2024-12-04 08:45:19 -05:00
parent 17e5f32800
commit fdb74e6439
5 changed files with 255 additions and 0 deletions

24
2024/day-4/Cargo.toml Normal file
View File

@@ -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 = []

4
2024/day-4/src/lib.rs Normal file
View File

@@ -0,0 +1,4 @@
pub mod part1;
pub use crate::part1::*;
pub mod part2;
pub use crate::part2::*;

31
2024/day-4/src/main.rs Normal file
View File

@@ -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(())
}

96
2024/day-4/src/part1.rs Normal file
View File

@@ -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<String, Day4Part1Error> {
//read in grid
let grid = input
.lines()
.map(|line| Vec::from(line.as_bytes()))
.collect::<Vec<_>>();
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::<usize>()
})
.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());
}
}

100
2024/day-4/src/part2.rs Normal file
View File

@@ -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<String, Day4Part2Error> {
//read in grid
let grid = input
.lines()
.map(|line| Vec::from(line.as_bytes()))
.collect::<Vec<_>>();
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::<usize>()
})
.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());
}
}