This commit is contained in:
Markus Brueckner 2024-12-04 23:16:43 +01:00
parent a8df580730
commit 22168572c8
4 changed files with 152 additions and 0 deletions

7
2024/day4/Cargo.lock generated Normal file
View file

@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "day4"
version = "0.1.0"

6
2024/day4/Cargo.toml Normal file
View file

@ -0,0 +1,6 @@
[package]
name = "day4"
version = "0.1.0"
edition = "2021"
[dependencies]

22
2024/day4/README.md Normal file
View file

@ -0,0 +1,22 @@
# Solution Day 4
Hooray... 2D text analysis... My favorite thing! Not. I am just not good with those. Never was, probably never
really will be.
## Task 1
I might've gone of the wrong track, because this solution is just so complicated. I started out in 2D, i.e.
split the text into lines and tried from there, but got fed up with the complexity and reverted back to a
1D-approach with jump offsets to get from one line to the other. Basically the algorithm goes from the start
point and checks whether it is too close to one of the edges, I remove the jump offsets in this direction from
my jumps array. After that the jumps get executed 4 times, collecting the letters along the way to form the words.
The algorithm collects all words in all directions from each point in the text and then checks, which of those
fit the pattern.
Sounds simply, hoo boy! how many times I overran the array bounds before I got all parts of that right!
## Task 2
Much, much simpler. Either the task was really simpler or my approach just fit way better. Basically I check every position,
whether it's an `A` and, if yes, I check the diagonals for `M.S` and `S.M `. Basically the whole implementation
worked on the first try. I nearly didn't want to continue after task 1, but this was a quick win after all.

117
2024/day4/src/main.rs Normal file
View file

@ -0,0 +1,117 @@
fn get_words_at(
text: &Vec<char>,
line_length: isize,
num_lines: isize,
top: isize,
left: isize,
) -> Vec<String> {
// the jump offsets to get to the next character in any direction. 0 entries are ignored later on, so we use them as markers when getting next to the borders
let mut offsets = vec![
1, // right
line_length + 1, // down-right
line_length, // down
line_length - 1, // down-left
-1, // left
-line_length - 1, // up-left
-line_length, // up
-line_length + 1, // up-right
];
// remove invalid jump directions (because the word wouldn't fit anyway)
if top < 3 {
offsets[5] = 0;
offsets[6] = 0;
offsets[7] = 0;
};
if top > num_lines - 4 {
offsets[1] = 0;
offsets[2] = 0;
offsets[3] = 0;
};
if left < 3 {
offsets[3] = 0;
offsets[4] = 0;
offsets[5] = 0;
};
if left > line_length - 5
/* don't try the final \n */
{
offsets[0] = 0;
offsets[1] = 0;
offsets[7] = 0;
};
let mut result = vec![];
for &offset in offsets.iter() {
if offset != 0 {
let mut string = String::new();
for i in 0..4 {
let index = top * line_length + left + offset * i;
if index >= text.len() as isize {
println!("Offsets: {:#?}, current: {}, index: {}", offsets, offset, i);
panic!(
"Invalid index: {} = {} * {} + {} + {} * {}",
index, top, line_length, left, offset, i
);
}
string.push(text[index as usize]);
}
result.push(string);
}
}
result
}
fn task1() {
let input = std::fs::read_to_string("./input.txt").unwrap();
let text: Vec<char> = input.chars().collect(); // makes the offset jumperoo easier in get_words_at
let line_length = (input.lines().next().unwrap().len() + 1) as isize; // need to include the final \n to get the calculations right
let num_lines = input.len() as isize / line_length;
let mut count = 0;
println!("Text has {} lines of length {}", num_lines, line_length);
for top in 0..num_lines {
for left in 0..line_length {
count += get_words_at(&text, line_length, num_lines, top, left)
.iter()
.fold(0, |existing, word| {
if word == "XMAS" {
existing + 1
} else {
existing
}
});
}
}
println!("Task 1: total: {}", count);
}
fn task2() {
let input = std::fs::read_to_string("./input.txt").unwrap();
let text: Vec<Vec<char>> = input.lines().map(|line| line.chars().collect()).collect();
let mut count = 0;
for row_idx in 1..text.len() - 1 {
for col_idx in 1..text[row_idx].len() - 1 {
if text[row_idx][col_idx] == 'A'
&& (
// top-left-bottom-right word
text[row_idx - 1][col_idx - 1] == 'M' && text[row_idx + 1][col_idx + 1] == 'S'
|| text[row_idx - 1][col_idx - 1] == 'S'
&& text[row_idx + 1][col_idx + 1] == 'M'
)
&& (
// bottom-left-top-right word
text[row_idx + 1][col_idx - 1] == 'M' && text[row_idx - 1][col_idx + 1] == 'S'
|| text[row_idx + 1][col_idx - 1] == 'S'
&& text[row_idx - 1][col_idx + 1] == 'M'
)
{
count += 1;
}
}
}
println!("Task 2: Total: {}", count);
}
fn main() {
task1();
task2();
}