Given a square grid with some blocked positions in which
there is a Sokoban player (S) and a block (B). The
player S has to move the block B to a target square T.
Assume that the grid is at most 20×20.
Here is an example maze
########### S -- You
#T##......# B -- Box
#.#.#..#### # -- Blocked square
#....B....# T -- Target
#.######..# . -- Empty cell
#.....S...#
###########
The only way to move the block is to push it. So, there
are two types of moves for S:
- Walk east/west/north/south without pushing : Represent as e/w/n/s
- Push east/west/north/south: Represent as E/W/N/S
To push, S must be oriented correctly with respect to B.
Thus, to push B west, S must be one square to the east of
B. A push moves both S and B one step in the direction
of the push.
Find a way to move B to T that minimizes the number of
pushes. If two solutions have the same number of pushes,
find the one with the minimum number of walks.
For example, here is a solution for the grid above:
eennwwWWWWeeeeeesswwwwwwwnnNN
Solution
This is clearly a shortest path problem in a graph. What is the graph?
- Vertices are (Sx,Sy,Bx,By)
- For each outgoing vertex, there are four possible
outgoing edges of which at most one is a push. The
rest are walks.
Note that this is a directed graph — if we go
from (Sx,Sy,Bx,By) to (S'x,S'y,B'x,B'y), we may not be
able to reverse this move, in particular if the move
was a push.
Note that all our earlier algorithms like BFS, DFS,
shortest paths work for directed graphs as well, in
essentially the same way as for undirected
graphs.
- Target node is any vertex (Sx,Sy,Bx,By) where
(Bx,By) = (Tx,Ty). If we assume that the initial box
position is not the target, we have only four
possibilities for (Sx,Sy).
- Maintain the cost of a path as a pair (P,W) where P
is the number of push move and W is the number walks.
So, a push move adds cost (1,1) while a walk move adds
(0,1). Costs are ordered lexicographically:
(P,W)<(P',W') if P<P' or P = P' and
W<W'.
We can also map (P,W) to a single integer. Between
each pair of push moves, the Sokoban can make at most
N^2 moves.
So we can map, say, (P,W) to a single integer P*160000
+ W. With each push move, we add 160000 and with each
walk move we add 1 to the cost.
How do we find the shortest path?
-
Do BFS, but maintain unexplored configurations with
cost in a priority queue (heap), based on cost, rather
than in a queue. At each step, the next configuration
to be explored is the minimum cost unexplored
configuration in the priority queue.
-
We can label edges between configurations with the cost
added by this edge. We can then use Dijkstra's
algorithm to calculate shortest path in M log M time,
where M is the number of edges, using a heap to store
burning times of unburnt edges. Note that the number
of edges is small — each vertex has degree at
most 4, so the total number of edges is about 2N.
Notice that if we use a naive implementation of
Dijkstra using an adjacency matrix, we have to store
N*N entries which is too large. However, the edges are
defined implicitly so we don't need to construct an
explicit representation.
As a further optimization, we can use an incremental
version of Dijkstra's algorithm where we maintain edges
in the heap as we visit them.