Day 4
This commit is contained in:
parent
a8df580730
commit
22168572c8
4 changed files with 152 additions and 0 deletions
7
2024/day4/Cargo.lock
generated
Normal file
7
2024/day4/Cargo.lock
generated
Normal 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
6
2024/day4/Cargo.toml
Normal file
|
@ -0,0 +1,6 @@
|
|||
[package]
|
||||
name = "day4"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
22
2024/day4/README.md
Normal file
22
2024/day4/README.md
Normal 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
117
2024/day4/src/main.rs
Normal 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();
|
||||
}
|
Loading…
Add table
Reference in a new issue