A storekeeper is a game in which the player pushes boxes around in a warehouse trying to get them to target locations.
The game is represented by an m x n
grid of characters grid
where each element is a wall, floor, or box.
Your task is to move the box 'B'
to the target position 'T'
under the following rules:
'S'
represents the player. The player can move up, down, left, right in grid
if it is a floor (empty cell).'.'
represents the floor which means a free cell to walk.'#'
represents the wall which means an obstacle (impossible to walk there).'B'
and one target cell 'T'
in the grid
.Return the minimum number of pushes to move the box to the target. If there is no way to reach the target, return -1
.
Example 1:
Input: grid = [["#","#","#","#","#","#"], ["#","T","#","#","#","#"], ["#",".",".","B",".","#"], ["#",".","#","#",".","#"], ["#",".",".",".","S","#"], ["#","#","#","#","#","#"]] Output: 3 Explanation: We return only the number of times the box is pushed.
Example 2:
Input: grid = [["#","#","#","#","#","#"], ["#","T","#","#","#","#"], ["#",".",".","B",".","#"], ["#","#","#","#",".","#"], ["#",".",".",".","S","#"], ["#","#","#","#","#","#"]] Output: -1
Example 3:
Input: grid = [["#","#","#","#","#","#"], ["#","T",".",".","#","#"], ["#",".","#","B",".","#"], ["#",".",".",".",".","#"], ["#",".",".",".","S","#"], ["#","#","#","#","#","#"]] Output: 5 Explanation: push the box down, left, left, up and up.
Constraints:
m == grid.length
n == grid[i].length
1 <= m, n <= 20
grid
contains only characters '.'
, '#'
, 'S'
, 'T'
, or 'B'
.'S'
, 'B'
, and 'T'
in the grid
.This problem requires finding the minimum number of pushes needed to move a box ('B') to a target location ('T') on a grid, controlled by a player ('S'). The player can move freely on empty cells ('.') but cannot pass through walls ('#') or the box. The solution uses Breadth-First Search (BFS) to explore possible moves.
State Representation: The core idea is to represent the game state using a tuple: (player_x, player_y, box_x, box_y)
. This tuple uniquely identifies the player's and box's positions on the grid. The solution cleverly encodes this 4-tuple into a pair of integers using a hash function f(i,j) = i * n + j
where n
is the width of the grid. This reduces the state space to be represented using a 2D vis
array for efficient checking of visited states.
BFS Exploration: BFS is employed to systematically explore all reachable states. The algorithm starts with the initial state and explores neighboring states level by level, ensuring that the shortest path (minimum pushes) is found first. A double-ended queue (deque
) is used for optimal efficiency, allowing both enqueueing at the head and tail.
Move Generation: For each state, the algorithm generates possible moves. The player can move in four directions (up, down, left, right). Crucially, a move is only valid if it doesn't lead to a wall.
Push Operations: If the player moves to the same cell as the box, a push operation occurs. The box's position is updated accordingly, and the push count is incremented. The new state (after pushing the box) is added to the queue.
Non-Push Operations: If the player moves without pushing the box, the box's position remains unchanged, and the push count is not incremented. The new state is added to the queue.
Visited States: A 2D boolean array vis
tracks visited states to prevent cycles and redundant explorations. The keys of the vis
array are the flattened versions of the player and box positions using the f
function.
Termination: The BFS continues until the box reaches the target location ('T'). At that point, the push count represents the minimum number of pushes required. If the queue becomes empty without finding the target, it implies no solution exists, and -1 is returned.
Time Complexity: O(M² * N²), where M and N are the dimensions of the grid. In the worst case, the algorithm might explore all possible combinations of player and box positions.
Space Complexity: O(M² * N²). This is due to the vis
array, which stores the visited states. In the worst case it stores all possible states. The queue can also grow to this size in the worst case.
The provided code examples demonstrate the implementation of this approach in Python, Java, C++, and TypeScript. Each implementation follows the same core algorithm but differs in syntax and data structures used. The TypeScript version includes a custom Deque
implementation since TypeScript doesn't have a built-in double-ended queue. All solutions incorporate efficient state representation and visited state tracking for optimal performance. The Python code uses the itertools.pairwise
function which may not be readily available in other languages (hence its absence in other implementations). In those cases a manual approach is used to represent the movement directions.