Advent of Code 2024, Day 4

Day 4 of Advent of Code 2024 brought us a 2d grid and finding shapes on it. It was a fairly straightforward task.

Looking down through a metal grate you find in factories.
Photo by Martin Dörsch on Unsplash

Part of the AoC 2024 series

Today we parse the file contents into a 2d grid, and then do stuff. A usual, spoilers ahead, and the codebase on day 4:

adventofcode2024/day04 at main · javorszky/adventofcode2024
Contribute to javorszky/adventofcode2024 development by creating an account on GitHub.

Spoilers

Part 1

Absolutely nothing special here. I decided to use a HashMap where the keys are Coordinates, which are just (i32, i32), and the value is whatever the character there is.

I have the 8 directions as an enum.

When I parse the file into the grid, I also save the coördinates of all the X characters into a separate Vec<Coordinate>, so I don’t need to needlessly loop over every character that I don’t care about.

Starting with every X, grab the next 3 coördinates in each of the directions, grab the characters at those coördinates from the grid (or . if the key doesn’t exist), add it to a string, and if that string spells out MAS, yay!

I even wrote a bunch of tests!

I do miss Go’s table driven tests. In Go I can write a whole lot of test cases as a slice with inputs and expectations, and run the same test on all of them.

The Rust Way™ seems to be one test for one case.

At least the correct solution happened pretty much instantly. I don’t care to time it at this point.

Part 2

I tried to be clever. See, the task only asks to find MAS in an X shape, but I thought I can also rotate it and find it in a + shape as well! So these two would both be valid:

.M.  M.S
MAS  .A.
.S.  M.S

Turns out they only need the diagonal one.

Implementation itself is fairly straightforward. This time I also took note of the coördinates of all A letters, and then generated relevant pair of coördinates to form the X shape, and then checked whether the letters in those two places are MS or SM for both pairs, and if they are, yay.

I learned that if you have a vec or an array, the difference between getting a reference or a value is:

let coords: Vec<Coordinate> = vec![c1, c2, c3, ...]

for coord in coords {
  // coord is now a value, type is Coordinate
}

for coord in coords.iter() {
  // coord is now a reference, type is &Coordinate
} 

I’m still not entirely sure when I would want to use .enumerate() though, but I’m also too lazy to read the docs at the moment.

I’m also happy about my code organisation and breaking things up into functions, methods, public and not. They’re easily testable, and I could write new tests to find edge cases and have confidence that when something breaks, it’s not in this or that function / method.

Tests are cool!

Should I write the tests first? Sort of. It depends. When I am starting a new project where I don’t necessarily know the shape of the data, then no, I don’t want to start with the test because what am I even testing? I don’t know what it should look like.

When I’m adding new things to or changing something in an existing, well defined system, then test first is absolutely a valid and preferred option!

On to day 5, when it comes out!

Spoiler Photo by ewan bullock on Unsplash