From c6e6901dc21197e5d721de7613acd674a2d0e96a Mon Sep 17 00:00:00 2001 From: _tud <98935832+UnderscoreTud@users.noreply.github.com> Date: Wed, 4 Dec 2024 14:07:50 +0300 Subject: [PATCH] Day 4 --- README.md | 3 +- data/examples/04.txt | 10 ++++ src/bin/04.rs | 128 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 data/examples/04.txt create mode 100644 src/bin/04.rs diff --git a/README.md b/README.md index b9ee757..70ce347 100644 --- a/README.md +++ b/README.md @@ -22,8 +22,9 @@ Solutions for [Advent of Code](https://adventofcode.com/) in [Rust](https://www. | [Day 1](./src/bin/01.rs) | `61.4µs` | `106.8µs` | | [Day 2](./src/bin/02.rs) | `220.6µs` | `404.8µs` | | [Day 3](./src/bin/03.rs) | `561.0µs` | `565.5µs` | +| [Day 4](./src/bin/04.rs) | `559.4µs` | `160.7µs` | -**Total: 1.92ms** +**Total: 2.64ms** --- diff --git a/data/examples/04.txt b/data/examples/04.txt new file mode 100644 index 0000000..c41c5ea --- /dev/null +++ b/data/examples/04.txt @@ -0,0 +1,10 @@ +MMMSXXMASM +MSAMXMSMSA +AMXSXMAAMM +MSAMASMSMX +XMASAMXAMM +XXAMMXXAMA +SMSMSASXSS +SAXAMASAAA +MAMMMXMMMM +MXMXAXMASX \ No newline at end of file diff --git a/src/bin/04.rs b/src/bin/04.rs new file mode 100644 index 0000000..dfe6e03 --- /dev/null +++ b/src/bin/04.rs @@ -0,0 +1,128 @@ +use itertools::Itertools; + +advent_of_code::solution!(4); + +#[derive(Debug)] +struct Pos { + x: isize, + y: isize, +} + +impl Pos { + + fn offset(&self, offset: &(isize, isize)) -> Pos { + Pos { x: self.x + offset.0, y: self.y + offset.1 } + } + + fn is_within(&self, dimensions: &(isize, isize)) -> bool { + (0..dimensions.0).contains(&self.x) && (0..dimensions.1).contains(&self.y) + } + +} + +impl From<(usize, usize)> for Pos { + fn from(value: (usize, usize)) -> Self { + Pos { x: value.0 as isize, y: value.1 as isize } + } +} + +const SEQUENCE: &[char; 4] = &['X', 'M', 'A', 'S']; +const DIRECTIONS: [(isize, isize); 8] = [ + (-1, -1), + (-1, 0), + (-1, 1), + (0, -1), + (0, 1), + (1, -1), + (1, 0), + (1, 1), +]; + +fn get_char(matrix: &[Vec], pos: &Pos) -> char { + matrix[pos.y as usize][pos.x as usize] +} + +pub fn part_one(input: &str) -> Option { + Some(search_xmas(input)) +} + +fn search_xmas(input: &str) -> u32 { + let matrix = input.lines().map(|line| line.chars().collect_vec()).collect_vec(); + let dimensions = (matrix[0].len() as isize, matrix.len() as isize); + (0..dimensions.0) + .cartesian_product(0..dimensions.1) + .map(|(x, y)| Pos { x, y }) + .map(|pos| count_xmas_seq(&matrix, &dimensions, &pos, &DIRECTIONS, 0)) + .sum() +} + +fn count_xmas_seq(matrix: &[Vec], dimensions: &(isize, isize), pos: &Pos, directions: &[(isize, isize)], seq_index: usize) -> u32 { + let seq_char = SEQUENCE[seq_index]; + if get_char(matrix, pos) != seq_char { + return 0; + } + if seq_index + 1 >= SEQUENCE.len() { + return 1; + } + let mut matches = 0; + for direction in directions { + let offset_pos = pos.offset(direction); + if !offset_pos.is_within(dimensions) { + continue; + } + matches += count_xmas_seq(matrix, dimensions, &offset_pos, &[*direction], seq_index + 1) + } + matches +} + +pub fn part_two(input: &str) -> Option { + Some(search_cross_mas(input)) +} + +fn search_cross_mas(input: &str) -> u32 { + let matrix = input.lines().map(|line| line.chars().collect_vec()).collect_vec(); + let dimensions = (matrix[0].len() as isize, matrix.len() as isize); + (0..dimensions.0) + .cartesian_product(0..dimensions.1) + .map(|(x, y)| Pos { x, y }) + .filter(|pos| is_cross_mas(&matrix, &dimensions, pos)) + .count() as u32 +} + +fn is_cross_mas(matrix: &[Vec], dimensions: &(isize, isize), pos: &Pos) -> bool { + if !(1..(dimensions.0 - 1)).contains(&pos.x) || !(1..(dimensions.1 - 1)).contains(&pos.y) { + return false + } + let center = get_char(matrix, pos); + if center != 'A' { + return false; + } + + let top_left = get_char(matrix, &pos.offset(&(-1, -1))); + let top_right = get_char(matrix, &pos.offset(&(-1, 1))); + let bottom_left = get_char(matrix, &pos.offset(&(1, -1))); + let bottom_right = get_char(matrix, &pos.offset(&(1, 1))); + + let diagonal1 = [top_left, center, bottom_right]; + let diagonal2 = [bottom_left, center, top_right]; + + (diagonal1 == ['M', 'A', 'S'] || diagonal1 == ['S', 'A', 'M']) + && (diagonal2 == ['M', 'A', 'S'] || diagonal2 == ['S', 'A', 'M']) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_part_one() { + let result = part_one(&advent_of_code::template::read_file("examples", DAY)); + assert_eq!(result, Some(18)); + } + + #[test] + fn test_part_two() { + let result = part_two(&advent_of_code::template::read_file("examples", DAY)); + assert_eq!(result, Some(9)); + } +}