The top half is a map of the warehouse.
The bottom half is a list of instructions the robot will attempt to perform.
The map is another 2D-space representation:
#: a wall
.: an empty space
O: a box
@: the (single) robot
For the instructions:
^: up
>: right
v: down
<: left
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.
I converted each instruction into a Point too, one with the correct offset for that move.
Data structures
I chose to store the grid in a map again where keys are Point and values are Tile.
Part 1
The robot will attemp to move in the direction of each instruction it gets.
It is very strong and can push many boxes at once, but can not move if a wall blocks the path.
When it is blocked, it doesn’t execute the instruction, and waits for the next one.
A GPS score for a coordinate is: 100 * row_index + col_index.
The question asks for the sum of all GPS scores for boxes after all instructions.
The bulk of the logic is in a helper function again.
It takes in the grid and the list of all instructions.
The broad layout of the function consists of 3 parts:
Determine the position of the robot.
Attempt to do each instruction
Calculate the wanted sum
Zooming in on the part where instructions are executed, because that’s the most interesting part.
It uses a breadth-first-search, adding each affected box to a queue. (and keeps track of each one in a set)
When that search is over it moves each affected box one by one.
During the bfs loop, continue to the next instruction when the next affected position is a wall.
Part 2
Apply the same logic on a second warehouse.
Everything in it except for the robot is twice as big.
You can construct the big warehouse’s map from the small one in the input.
If the tile is #, the new map contains ## instead.
If the tile is O, the new map contains [] instead.
If the tile is ., the new map contains .. instead.
If the tile is @, the new map contains @. instead.
As for GPS coordinates, those are measured from the edge of the map to the closest edge of the box in question.
Because distances start from the top and the left, in practice, that means the [ determines the coordinate.
This changes the structures I used a bit.
And the parsing logic changes a bit too to handle the wideness of the new input.
And the changes to the solving logic.
I swapped my logic for duplicate checking around because that would make inserting into the queue a bit easier.
But both methods are basically identical.
Final code
I had some fun combining both parts today with the goal of not duplicating my data structures.
In hindsight, this combining would have been a lot easier if I worked with the raw characters instead.
But I like using neat datastructures for explanations!
I also had fun with not collecting the bigger map in part 2 into a new string, but that logic didn’t make it into this post.