Python BFS x 2
```py\nclass Solution:\n    \n    offsets = ((1, 0), (-1, 0), (0, 1), (0, -1))\n    \n    def minPushBox(self, grid: List[List[str]]) -> int:\n\n        # check
```py\nclass Solution:\n    \n    offsets = ((1, 0), (-1, 0), (0, 1), (0, -1))\n    \n    def minPushBox(self, grid: List[List[str]]) -> int:\n\n        # check if player can move from start to end given the ball\'s position\n        def can_move(start, end, ball):\n            if not (0 <= end[0] < rows and 0 <= end[1] < cols):\n                return False\n            q = collections.deque([start])\n            seen = {start}\n            while q:\n                row, col = q.popleft()\n                if (row, col) == end:\n                    return True\n                for r_offset, c_offset in self.offsets:\n                    r, c = row + r_offset, col + c_offset\n                    if 0 <= r < rows and 0 <= c < cols and grid[r][c] != \'#\' and (r, c) != ball and (r, c) not in seen:\n                        seen.add((r, c))\n                        q.append((r, c))\n            return False\n\n        rows, cols = len(grid), len(grid[0])\n\n        # find source, target, ball\n        for r in range(rows):\n            for c in range(cols):\n                if grid[r][c] == \'T\':\n                    target = (r, c)\n                elif grid[r][c] == \'S\':\n                    source = (r, c)\n                elif grid[r][c] == \'B\':\n                    ball = (r, c)\n\n        # dfs starting from source, ball\n        seen = {(source, ball)}\n        q = collections.deque([(0, source, ball)])\n\n        while q:\n            dist, source, ball = q.popleft()\n\n            if ball == target:\n                return dist\n\n            for r_offset, c_offset in self.offsets:\n                r, c = ball[0] + r_offset, ball[1] + c_offset\n\n                if 0 <= r < rows and 0 <= c < cols and grid[r][c] != \'#\':\n                    push_pos = (ball[0] - r_offset, ball[1] - c_offset)\n\n                    if (ball, (r, c)) not in seen and can_move(source, push_pos, ball):\n                        seen.add((ball, (r, c)))\n                        q.append((dist + 1, ball, (r, c)))\n\n        return -1\n
[]