Skip to content

Commit f6d3efc

Browse files
committed
AOC 2024 Day 04
1 parent e54c363 commit f6d3efc

File tree

4 files changed

+159
-0
lines changed

4 files changed

+159
-0
lines changed

aoc/2024/rust/Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

aoc/2024/rust/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ test_lib = []
2424
# Template dependencies
2525
chrono = { version = "0.4.38", optional = true }
2626
dhat = { version = "0.3.3", optional = true }
27+
glam = "0.29.2"
2728
itertools = "0.13.0"
2829
pico-args = "0.5.0"
2930
regex = "1.11.1"

aoc/2024/rust/data/examples/04.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
MMMSXXMASM
2+
MSAMXMSMSA
3+
AMXSXMAAMM
4+
MSAMASMSMX
5+
XMASAMXAMM
6+
XXAMMXXAMA
7+
SMSMSASXSS
8+
SAXAMASAAA
9+
MAMMMXMMMM
10+
MXMXAXMASX

aoc/2024/rust/src/bin/04.rs

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
advent_of_code::solution!(4);
2+
3+
use glam::{i32, IVec2};
4+
use std::collections::HashMap;
5+
6+
/**
7+
* Display the 9 characters in the matrix centered around the given position.
8+
*/
9+
#[allow(dead_code)]
10+
fn get_graph(matrix: &HashMap<IVec2, char>, pos: &IVec2) -> String {
11+
let mut graph = String::with_capacity(12);
12+
for y in -1..=1 {
13+
for x in -1..=1 {
14+
let offset = IVec2::new(x, y);
15+
if let Some(char) = matrix.get(&(*pos + offset)) {
16+
graph.push(*char);
17+
} else {
18+
graph.push('.');
19+
}
20+
}
21+
graph.push('\n');
22+
}
23+
graph
24+
}
25+
26+
fn parse(input: &str) -> HashMap<IVec2, char> {
27+
let mut grid = HashMap::new();
28+
for (y, line) in input.lines().enumerate() {
29+
for (x, char) in line.chars().enumerate() {
30+
grid.insert(
31+
IVec2 {
32+
x: x as i32,
33+
y: y as i32,
34+
},
35+
char,
36+
);
37+
}
38+
}
39+
grid
40+
}
41+
42+
/* ********************************** PART ONE *******************************/
43+
44+
const DIRECTIONS: [IVec2; 8] = [
45+
IVec2::new(0, -1),
46+
IVec2::new(0, 1),
47+
IVec2::new(-1, 0),
48+
IVec2::new(1, 0),
49+
IVec2::new(-1, -1),
50+
IVec2::new(1, -1),
51+
IVec2::new(-1, 1),
52+
IVec2::new(1, 1),
53+
];
54+
55+
fn find_xmas(matrix: &HashMap<IVec2, char>, pos: IVec2) -> u32 {
56+
let target = "XMAS";
57+
let mut res = 0;
58+
let mut is_match;
59+
for dir in DIRECTIONS {
60+
is_match = true;
61+
for (i, c) in target.chars().enumerate() {
62+
if let Some(cell) = matrix.get(&(pos + dir * i as i32)) {
63+
if *cell != c {
64+
is_match = false;
65+
break;
66+
}
67+
} else {
68+
is_match = false;
69+
break;
70+
}
71+
}
72+
if is_match {
73+
res += 1;
74+
}
75+
}
76+
res
77+
}
78+
79+
pub fn part_one(input: &str) -> Option<u32> {
80+
let matrix = parse(input);
81+
Some(matrix.keys().map(|k| find_xmas(&matrix, *k)).sum::<u32>())
82+
}
83+
84+
/* ********************************** PART TWO *******************************/
85+
86+
const DIRECTIONS_X: [[IVec2; 2]; 2] = [
87+
[IVec2::new(-1, -1), IVec2::new(1, 1)],
88+
[IVec2::new(-1, 1), IVec2::new(1, -1)],
89+
];
90+
91+
/**
92+
* Find if the given coordinates in the matrix are at the center of a X-MAs group of cells, for that
93+
* to be true, both diagonals need to form "MAS" or "SAM", given that main will call this method
94+
* only when the char at pos is 'A', we only need to check that the oppossing corners are either SM
95+
* or MS.
96+
*/
97+
fn is_x_mas_match(matrix: &HashMap<IVec2, char>, pos: &IVec2) -> bool {
98+
let (ms, sm) = ("MS".to_string(), "SM".to_string());
99+
let mut diagonal_chars = String::with_capacity(2);
100+
for diagonal in DIRECTIONS_X {
101+
diagonal_chars.clear();
102+
for offset in diagonal {
103+
if let Some(c) = matrix.get(&(pos + offset)) {
104+
diagonal_chars.push(*c);
105+
} else {
106+
return false;
107+
}
108+
}
109+
if diagonal_chars != ms && diagonal_chars != sm {
110+
return false;
111+
}
112+
}
113+
true
114+
}
115+
116+
pub fn part_two(input: &str) -> Option<u32> {
117+
let matrix = parse(input);
118+
Some(
119+
matrix
120+
.iter()
121+
.filter(|&(pos, val)| *val == 'A' && is_x_mas_match(&matrix, pos))
122+
.count() as u32,
123+
)
124+
}
125+
126+
#[cfg(test)]
127+
mod tests {
128+
use super::*;
129+
130+
#[test]
131+
fn test_part_one() {
132+
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
133+
assert_eq!(result, Some(18));
134+
}
135+
136+
#[test]
137+
fn test_part_two() {
138+
let result = part_two(&advent_of_code::template::read_file("examples", DAY));
139+
assert_eq!(result, Some(9));
140+
}
141+
}

0 commit comments

Comments
 (0)