<p>A storekeeper is a game in which the player pushes boxes around in a warehouse trying to get them to target locations.</p>

<p>The game is represented by an <code>m x n</code> grid of characters <code>grid</code> where each element is a wall, floor, or box.</p>

<p>Your task is to move the box <code>&#39;B&#39;</code> to the target position <code>&#39;T&#39;</code> under the following rules:</p>

<ul>
	<li>The character <code>&#39;S&#39;</code> represents the player. The player can move up, down, left, right in <code>grid</code> if it is a floor (empty cell).</li>
	<li>The character <code>&#39;.&#39;</code> represents the floor which means a free cell to walk.</li>
	<li>The character<font face="monospace">&nbsp;</font><code>&#39;#&#39;</code><font face="monospace">&nbsp;</font>represents the wall which means an obstacle (impossible to walk there).</li>
	<li>There is only one box <code>&#39;B&#39;</code> and one target cell <code>&#39;T&#39;</code> in the <code>grid</code>.</li>
	<li>The box can be moved to an adjacent free cell by standing next to the box and then moving in the direction of the box. This is a <strong>push</strong>.</li>
	<li>The player cannot walk through the box.</li>
</ul>

<p>Return <em>the minimum number of <strong>pushes</strong> to move the box to the target</em>. If there is no way to reach the target, return <code>-1</code>.</p>

<p>&nbsp;</p>
<p><strong class="example">Example 1:</strong></p>
<img alt="" src="https://assets.leetcode.com/uploads/2019/11/06/sample_1_1620.png" style="width: 500px; height: 335px;" />
<pre>
<strong>Input:</strong> grid = [[&quot;#&quot;,&quot;#&quot;,&quot;#&quot;,&quot;#&quot;,&quot;#&quot;,&quot;#&quot;],
               [&quot;#&quot;,&quot;T&quot;,&quot;#&quot;,&quot;#&quot;,&quot;#&quot;,&quot;#&quot;],
               [&quot;#&quot;,&quot;.&quot;,&quot;.&quot;,&quot;B&quot;,&quot;.&quot;,&quot;#&quot;],
               [&quot;#&quot;,&quot;.&quot;,&quot;#&quot;,&quot;#&quot;,&quot;.&quot;,&quot;#&quot;],
               [&quot;#&quot;,&quot;.&quot;,&quot;.&quot;,&quot;.&quot;,&quot;S&quot;,&quot;#&quot;],
               [&quot;#&quot;,&quot;#&quot;,&quot;#&quot;,&quot;#&quot;,&quot;#&quot;,&quot;#&quot;]]
<strong>Output:</strong> 3
<strong>Explanation:</strong> We return only the number of times the box is pushed.</pre>

<p><strong class="example">Example 2:</strong></p>

<pre>
<strong>Input:</strong> grid = [[&quot;#&quot;,&quot;#&quot;,&quot;#&quot;,&quot;#&quot;,&quot;#&quot;,&quot;#&quot;],
               [&quot;#&quot;,&quot;T&quot;,&quot;#&quot;,&quot;#&quot;,&quot;#&quot;,&quot;#&quot;],
               [&quot;#&quot;,&quot;.&quot;,&quot;.&quot;,&quot;B&quot;,&quot;.&quot;,&quot;#&quot;],
               [&quot;#&quot;,&quot;#&quot;,&quot;#&quot;,&quot;#&quot;,&quot;.&quot;,&quot;#&quot;],
               [&quot;#&quot;,&quot;.&quot;,&quot;.&quot;,&quot;.&quot;,&quot;S&quot;,&quot;#&quot;],
               [&quot;#&quot;,&quot;#&quot;,&quot;#&quot;,&quot;#&quot;,&quot;#&quot;,&quot;#&quot;]]
<strong>Output:</strong> -1
</pre>

<p><strong class="example">Example 3:</strong></p>

<pre>
<strong>Input:</strong> grid = [[&quot;#&quot;,&quot;#&quot;,&quot;#&quot;,&quot;#&quot;,&quot;#&quot;,&quot;#&quot;],
               [&quot;#&quot;,&quot;T&quot;,&quot;.&quot;,&quot;.&quot;,&quot;#&quot;,&quot;#&quot;],
               [&quot;#&quot;,&quot;.&quot;,&quot;#&quot;,&quot;B&quot;,&quot;.&quot;,&quot;#&quot;],
               [&quot;#&quot;,&quot;.&quot;,&quot;.&quot;,&quot;.&quot;,&quot;.&quot;,&quot;#&quot;],
               [&quot;#&quot;,&quot;.&quot;,&quot;.&quot;,&quot;.&quot;,&quot;S&quot;,&quot;#&quot;],
               [&quot;#&quot;,&quot;#&quot;,&quot;#&quot;,&quot;#&quot;,&quot;#&quot;,&quot;#&quot;]]
<strong>Output:</strong> 5
<strong>Explanation:</strong> push the box down, left, left, up and up.
</pre>

<p>&nbsp;</p>
<p><strong>Constraints:</strong></p>

<ul>
	<li><code>m == grid.length</code></li>
	<li><code>n == grid[i].length</code></li>
	<li><code>1 &lt;= m, n &lt;= 20</code></li>
	<li><code>grid</code> contains only characters <code>&#39;.&#39;</code>, <code>&#39;#&#39;</code>, <code>&#39;S&#39;</code>, <code>&#39;T&#39;</code>, or <code>&#39;B&#39;</code>.</li>
	<li>There is only one character <code>&#39;S&#39;</code>, <code>&#39;B&#39;</code>, and <code>&#39;T&#39;</code> in the <code>grid</code>.</li>
</ul>

# class Solution:
#     def minPushBox(self, grid: List[List[str]]) -> int:
#         height = len(grid)
#         width = len(grid[0])
        
#         walls = set()
#         box = None
#         sprite = None
#         target = None
        
#         for i in range(height):
#             for j in range(width):
#                 if grid[i][j] == "#":
#                     walls.add((i,j))
#                 elif grid[i][j] == "B":
#                     box = (i,j)
#                 elif grid[i][j] == "S":
#                     sprite = (i,j)
#                 elif grid[i][j] == "T":
#                     target = (i,j)
#         # BFS
#         seen = {(sprite,box):0}
#         next_gen = []
#         this_gen = [(0,sprite,box)]
#         ans = -1
#         while this_gen:
#             while this_gen:
#                 step, spt, bx = this_gen.pop(0)
#                 if bx==target:
#                     ans=max(ans,step)
#                     continue
#                 sptx,spty = spt
#                 bxx, bxy = bx
#                 for dx,dy in (1,0),(0,1),(-1,0),(0,-1):
#                     newx, newy = sptx+dx, spty+dy
#                     newpt = (newx,newy)
#                     if newx<0 or newy<0 or newx==height or newy==width:
#                         continue
#                     if (newx,newy) in walls:
#                         continue
#                     newbxx,newbxy = bxx,bxy 
#                     newstep = step
#                     if newpt == bx:
#                         newbxx,newbxy=bxx+dx,bxy+dy
#                         newstep += 1
#                         if newbxx<0 or newbxy<0 or newbxx==height or newbxy==width:
#                             continue
#                         if (newbxx,newbxy) in walls:
#                             continue
#                     if ((newx,newy),(newbxx,newbxy)) in seen and seen[((newx,newy),(newbxx,newbxy))] <= newstep:
#                         continue
#                     seen[((newx,newy),(newbxx,newbxy))] = newstep
#                     next_gen.append((newstep,(newx,newy),(newbxx,newbxy)))
#             this_gen = next_gen
#             next_gen = []
#         return ans
from typing import List, Tuple
from collections import deque

class Solution:
    def minPushBox(self, grid: List[List[str]]) -> int:
        height = len(grid)
        width = len(grid[0])
        
        walls = set()
        box = None
        sprite = None
        target = None
        
        for i in range(height):
            for j in range(width):
                if grid[i][j] == "#":
                    walls.add((i, j))
                elif grid[i][j] == "B":
                    box = (i, j)
                elif grid[i][j] == "S":
                    sprite = (i, j)
                elif grid[i][j] == "T":
                    target = (i, j)
        
        def can_reach(start: Tuple[int, int], box: Tuple[int, int], target: Tuple[int, int]) -> bool:
            queue = deque([start])
            visited = set([start])
            while queue:
                sx, sy = queue.popleft()
                if (sx, sy) == target:
                    return True
                for dx, dy in [(1, 0), (0, 1), (-1, 0), (0, -1)]:
                    nx, ny = sx + dx, sy + dy
                    if 0 <= nx < height and 0 <= ny < width and (nx, ny) not in walls and (nx, ny) != box and (nx, ny) not in visited:
                        visited.add((nx, ny))
                        queue.append((nx, ny))
            return False
        
        queue = deque([(0, sprite, box)])
        seen = set([(sprite, box)])
        
        while queue:
            step, spt, bx = queue.popleft()
            if bx == target:
                return step
            
            sptx, spty = spt
            bxx, bxy = bx
            
            for dx, dy in [(1, 0), (0, 1), (-1, 0), (0, -1)]:
                newbx, newby = bxx + dx, bxy + dy
                newspx, newspy = bxx - dx, bxy - dy
                
                if not (0 <= newbx < height and 0 <= newby < width and (newbx, newby) not in walls):
                    continue
                if not (0 <= newspx < height and 0 <= newspy < width and (newspx, newspy) not in walls and (newspx, newspy) != bx):
                    continue
                if not can_reach(spt, bx, (newspx, newspy)):
                    continue
                
                new_state = ((newspx, newspy), (newbx, newby))
                if new_state in seen:
                    continue
                
                seen.add(new_state)
                queue.append((step + 1, (newspx, newspy), (newbx, newby)))
        
        return -1

168