“Looks like the Chief’s not here. Next!”
Disappointing! We still haven’t found the Chief and it’s already the 4th. Will we find him by Christmas?
But today’s adventure to Ceres monitoring station isn’t all wasted. A local elf needs our help to find XMAS
!
It’s the traditional word search puzzle from magazines. We are given a word puzzle like
and we need to find how many times XMAS is written there, in any direction, backwards or forwards.
Read input
Today we need to do input reading in two steps because our isolated lines reader doesn’t allow neat tricks like these.
When I’m faced with a grid-based puzzle in Advent of Code, my default solution is to populate a dictionary where keys are coordinates and values are the corresponding values.
Dictionaries are better than lists of lists in my experience for a couple of reason:
- We can create sparse grids where not every coordinate exists and still operate as if it was a full grid.
- We don’t have to check for index boundaries: if we go beyond the coordinates, we get
None
back.
Today we don’t have any sparseness to worry about but we get the second benefit.
Instead of a map function to be run per line, I read the data in as a list of strings and then use this helper grid creator to turn them into a dictionary.
Part 1
Today’s puzzle was the first one this year where I didn’t immediately know the right solution. I had to think. My final solution took quite a few rounds of refactoring. I have previously written about my iterative approach in my blog. Basically: first I dig deep to get a working result by any means necessary and then start to climb back improving the code.
It really helps me to know I have a result I can compare to to make sure my refactoring and other changes don’t break the functionality. That’s why in my full code, you can see an assert result == [number]
line at the bottom of each part. If I make a mistake, it will trip the code and raise an AssertionError
.
The goal today is to count how many XMAS strings there are in the grid. They can be in any of the 8 cardinal and intercardinal directions. And they can be written forwards (XMAS
) or backwards (SAMX
).
Initially, I looked through every item in the grid and if it was X
or S
, I checked if it’s part of XMAS. When refactoring, I realised that I don’t have to check for ones starting with S
because I was already checking all directions.
To check if a given index, in a given direction forms the word XMAS
, I walk from starting direction to the desired direction and if I find something along the line that doesn’t fit the word, I return 0. If we get through the 3 remaining letters and they are M
, A
and S
in that order, we return 1:
I then have a function that takes and index, checks if it points to an X
and if it does, checks for the word in every of the 8 directions.
And finally, the wrapping around it for the part 1:
Part 2
For the first time that I remember, the second part was simpler than the original.
This time we needed to check for any diagonal crosses that form two MAS
s:
Here, I loop through all indices like last time but only act on ones that point to A
:
and then for each of those, check if the ones in diagonally adjacent coordinates form a cross as instructed:
I really liked today’s puzzle. A good warmup for the more complex grid puzzles that are surely to come: ones with sparse grids, infinite grids or grids with moving items.
⭐️ 8 out of 8 stars but no sight of the Chief Historian.