You are at the reindeer olympics, where the big even is navigating through a maze in as short a route as possible.
An example input looks like this:
O, look, a 2D-grid!
# is a wall
. is an empty space
S is the starting location
E is the ending location
Parsing
If you’ve been following along, you know what I do when I see a puzzle with coordinates by now, Point!
I added a Tile enum for all the locations on the map.
Data structures
I chose to store the grid in a map again where keys are Point and values are Tile.
Also the Points for start and end are returned.
Part 1
The reindeer have to find the shortest path through a maze.
They start at the starting position facing east (I will represent directions with a Point again).
The maze is completed when they reach the ending direction, which direction they face at that point doesn’t matter.
The reindeer can only move forward or turn 90 degrees:
Each move forward costs 1.
Each 90 degree turns costs 1000.
The reindeer have to find the shortest path you say?
You want the lowest cost you say?
BAM! DIJKSTRA!
I heavily commented my code today.
Helpers
Dijkstra uses a priority queue.
I want whatever item is in the queue that has the lowest cost to be popped from the queue first.
To do this in Rust, I’ll use a BinaryHeap, and define how things in that heap should be ordered.
The things in that heap will be my own Node structs.
Those hold 3 things:
a position
a direction
a cost
The Nodes are ordered by their cost, so a Node in the priority queue with the lowest cost will get popped first.
Now, the rules for movement costs.
At some point in the algorithm, I need to produce all possible next steps, and I decided to make a helper for it.
It takes in the map, the current position and direction, and returns a list of all possible moves.
And 2 possible moves are turns, so I updated the logic for Point to handle that.
A resulting valid move consists of 3 things:
the new position
the new facing direction
the cost for that move
Time to bring it all together and implement Dijkstra’s shortest path algorithm.
This function takes in the map, the start, and the end.
It returns the minimum cost to reach the end given the rules.
Code
Another one where all heavy lifting is in helpers so the driving-code is nice and short.
Part 2
You want to watch the event and want a good place to sit.
Each empty space (., S, and E) is equipped with a seat.
The question asks how many seats there are along a (not the! a!) shortest path.
This requires some changes to our code.
It can no longer stop when it finds a shortest path, it should find all shortest paths.
I need to reconstruct all Points along all shortest paths, the answer is the length of that set of Points.
That first part is done by no longer returning from the function when I find a node at the ending position, but continuing until I see an ending node whose cost is higher than a previous ending node.
To do the second change I’ll keep track of where a Node came from.
For each Node that is created, it refers to the previous Node on its path.
Whenever I find a Node whose position is at the ending, I backtrack through all those steps until I reach the start.
I added a from field to my Node struct that points to another Node.
In rust, this is a bit tricky, so I explicitly made it optional and made the compiler count how many times that Node is referenced while my code executes.
In many other languages, this will “just work™️”
Now in the main algorithm function, I no longer return from the function when I see an ending position.
I check if the cost for the current node is higher than the current global lowest cost.
If it is, that means all shortest paths have been found, and I can break out of the loop.
If it is not, it found a shortest path and I record all positions by backtracking starting at the end and going to the start, one node.from step at a time.
Then I continue looping.
Code
Again, nice and tidy because of helper functions.
Final code
Combining both is as complicated as also returning the global lowest cost along with the set length in the part 2 function.