diff --git a/2024/input/day04.txt b/2024/input/day04.txt new file mode 100644 index 0000000..71bdfcf Binary files /dev/null and b/2024/input/day04.txt differ diff --git a/2024/src/day04.rs b/2024/src/day04.rs new file mode 100644 index 0000000..9ae3e33 --- /dev/null +++ b/2024/src/day04.rs @@ -0,0 +1,139 @@ +const INPUT: &str = include_str!("../../2024/input/day04.txt"); + +pub(crate) fn part1() -> usize { + solve_part1(INPUT) +} + +pub(crate) fn part2() -> usize { + solve_part2(INPUT) +} + +type Grid = Vec>; + +#[allow(clippy::identity_op)] // I consider the + 0 helpful for readability +fn solve_part1(input: &str) -> usize { + let grid = parse(input); + + let mut horizontal_count = 0; + for line in grid.iter() { + for w in line.windows(4) { + if w == ['X', 'M', 'A', 'S'] || w == ['S', 'A', 'M', 'X'] { + horizontal_count += 1; + } + } + } + + let contains_xmas = |c1: &char, c2: &char, c3: &char, c4: &char| -> bool { + c1 == &'X' && c2 == &'M' && c3 == &'A' && c4 == &'S' + || c1 == &'S' && c2 == &'A' && c3 == &'M' && c4 == &'X' + }; + let mut vertical_count = 0; + for x in 0..grid[0].len() { + for y in 0..grid.len() - 3 { + if contains_xmas( + &grid[y + 0][x], + &grid[y + 1][x], + &grid[y + 2][x], + &grid[y + 3][x], + ) { + vertical_count += 1; + } + } + } + + let mut diagonal1_count = 0; + let mut diagonal2_count = 0; + for x in 0..grid[0].len() - 3 { + for y in 0..grid.len() - 3 { + if contains_xmas( + &grid[y + 0][x + 0], + &grid[y + 1][x + 1], + &grid[y + 2][x + 2], + &grid[y + 3][x + 3], + ) { + diagonal1_count += 1; + } + if contains_xmas( + &grid[y + 3][x + 0], + &grid[y + 2][x + 1], + &grid[y + 1][x + 2], + &grid[y + 0][x + 3], + ) { + diagonal2_count += 1; + } + } + } + horizontal_count + vertical_count + diagonal1_count + diagonal2_count +} + +#[allow(clippy::identity_op)] // I consider the + 0 helpful for readability +fn solve_part2(input: &str) -> usize { + let grid = parse(input); + let mut count = 0; + let contains_mas = |c1: &char, c2: &char, c3: &char| -> bool { + c1 == &'M' && c2 == &'A' && c3 == &'S' || c1 == &'S' && c2 == &'A' && c3 == &'M' + }; + for x in 0..grid[0].len() - 2 { + for y in 0..grid.len() - 2 { + if contains_mas( + &grid[y + 0][x + 0], + &grid[y + 1][x + 1], + &grid[y + 2][x + 2], + ) && contains_mas( + &grid[y + 2][x + 0], + &grid[y + 1][x + 1], + &grid[y + 0][x + 2], + ) { + count += 1; + } + } + } + count +} + +fn parse(input: &str) -> Grid { + input + .trim() + .lines() + .map(|line| line.chars().collect()) + .collect() +} + +#[cfg(test)] +mod tests { + use super::*; + + const EXAMPLE: &str = "\ +MMMSXXMASM +MSAMXMSMSA +AMXSXMAAMM +MSAMASMSMX +XMASAMXAMM +XXAMMXXAMA +SMSMSASXSS +SAXAMASAAA +MAMMMXMMMM +MXMXAXMASX +"; + + #[test] + fn test_part1_example() { + assert_eq!(18, solve_part1(EXAMPLE)); + } + + #[test] + fn test_part1() { + assert_eq!(2507, solve_part1(INPUT)); + } + + #[test] + fn test_part2_example() { + assert_eq!(9, solve_part2(EXAMPLE)); + } + + #[test] + fn test_part2() { + // not 1983, that's when also including a straight cross shape 🤦‍♂️ + assert_eq!(1969, solve_part2(INPUT)); + } +} diff --git a/2024/src/main.rs b/2024/src/main.rs index e29b0e6..a72c582 100644 --- a/2024/src/main.rs +++ b/2024/src/main.rs @@ -3,11 +3,13 @@ use std::fmt::Display; mod day01; mod day02; mod day03; +mod day04; fn main() { print_result(1, day01::part1(), day01::part2()); print_result(1, day02::part1(), day02::part2()); print_result(1, day03::part1(), day03::part2()); + print_result(1, day04::part1(), day04::part2()); } fn print_result(day: i32, part1: impl Display, part2: impl Display) {