C# Readable BFS + BFS with inline comments
\npublic class Solution {\n    private char[][] grid;\n    private Position boxPosition;\n    private int[] directions = new int[] {0, 1, 0, -1, 0};\n    \n    
```\npublic class Solution {\n    private char[][] grid;\n    private Position boxPosition;\n    private int[] directions = new int[] {0, 1, 0, -1, 0};\n    \n    // Represents coordinate with Equals() implemented by default for Queues\n    public struct Position\n    {\n        public int X;\n        public int Y;\n        \n        public Position(int x, int y)\n        {\n            X = x;\n            Y = y;\n        }\n        \n        public override string ToString()\n        {\n            return $"[{Y},{X}]";\n        }\n    }\n    \n    // State, what we will store in BFS for the box\n    public struct BoxAndPlayerPosition\n    {\n        public Position boxPosition;\n        public Position playerPosition;\n        \n        public BoxAndPlayerPosition(Position boxPosition, Position playerPosition)\n        {\n            this.boxPosition = boxPosition;\n            this.playerPosition = playerPosition;\n        }\n        \n        // where the box will be moved along with player. Player will fill in the box spot, box will be moved 1 cell in corresponding direction\n        public BoxAndPlayerPosition NextPosition()\n        {\n            if (boxPosition.X == playerPosition.X)\n            {\n                if (playerPosition.Y < boxPosition.Y)\n                    return new BoxAndPlayerPosition(new Position(boxPosition.X, boxPosition.Y + 1), new Position(playerPosition.X, playerPosition.Y + 1));\n                return new BoxAndPlayerPosition(new Position(boxPosition.X, boxPosition.Y - 1), new Position(playerPosition.X, playerPosition.Y - 1));\n            }\n            \n            if (playerPosition.X < boxPosition.X)\n                return new BoxAndPlayerPosition(new Position(boxPosition.X + 1, boxPosition.Y), new Position(playerPosition.X + 1, playerPosition.Y));\n                return new BoxAndPlayerPosition(new Position(boxPosition.X - 1, boxPosition.Y), new Position(playerPosition.X - 1, playerPosition.Y));\n        }\n        \n        public override string ToString()\n        {\n            return $"b:{boxPosition};p:{playerPosition}";\n        }\n    }\n    \n    // BFS for player, can player move to the position, assuming box is just another \'wall\' for the sake of this method\n    private bool PlayerCanMove(BoxAndPlayerPosition current, Position target)\n    {\n        Queue<Position> queue = new Queue<Position>();\n        boxPosition = current.boxPosition;\n        \n        queue.Enqueue(current.playerPosition);\n        HashSet<Position> visited = new HashSet<Position>();\n        visited.Add(current.playerPosition);\n        \n        while (queue.Count > 0)\n        {\n            Position currPos = queue.Dequeue();\n            if (currPos.Equals(target))\n            {\n                return true;\n            }\n            \n            for (int i = 0; i < 4; i++)\n            {\n                int newX = currPos.X + directions[i];\n                int newY = currPos.Y + directions[i+1];\n                Position newPos = new Position(newX, newY);\n                \n                if (CanMoveTo(newPos, visited))\n                {\n                    visited.Add(newPos);\n                    queue.Enqueue(newPos);\n                }\n            }\n        }\n        \n        return false;\n    }\n    \n    private bool CanMoveTo(Position pos, HashSet<Position> visited)\n    {\n        return (IsValidAndEmpty(pos) && !visited.Contains(pos) &&\n               !pos.Equals(boxPosition));\n    }\n    \n    private bool IsValidAndEmpty(Position pos)\n    {\n        int x = pos.X;\n        int y = pos.Y;\n        \n        return x >= 0 && x < grid[0].Length && y >= 0 && y < grid.Length && grid[y][x] == \'.\';\n    }\n    \n    public int MinPushBox(char[][] grid) {\n        this.grid = grid;\n        \n        Position boxPosition = FindBox(grid);\n        Position playerPosition = FindPlayer(grid);\n        Position targetPosition = FindTarget(grid);\n        \n        // since we captured box, player, and target positions, we change it to \'.\' so it would be easier to check whether we can move in a cell\n        grid[boxPosition.Y][boxPosition.X] = \'.\';\n        grid[playerPosition.Y][playerPosition.X] = \'.\';\n        grid[targetPosition.Y][targetPosition.X] = \'.\';\n        \n        int pushes = 0;\n        Queue<BoxAndPlayerPosition> queue = new Queue<BoxAndPlayerPosition>();\n        HashSet<BoxAndPlayerPosition> visited = new HashSet<BoxAndPlayerPosition>();\n        \n        BoxAndPlayerPosition initial = new BoxAndPlayerPosition(boxPosition, playerPosition);\n        \n        // adding all possible starting points: box and positions to the left, right, top, bottom to the box.\n        for (int i = 0; i < 4; i++)\n        {\n            int newX = boxPosition.X + directions[i];\n            int newY = boxPosition.Y + directions[i+1];\n            Position target = new Position(newX, newY);\n            \n            if (!IsValidAndEmpty(target) || !PlayerCanMove(initial, target))\n                continue;\n            \n            var start = new BoxAndPlayerPosition(boxPosition, target);\n            queue.Enqueue(start);\n            visited.Add(start);\n        }\n        \n        // BFS for player and the box\n        while (queue.Count > 0)\n        {\n            var length = queue.Count;\n            \n            for (int j = 0; j < length; j++)\n            {\n                BoxAndPlayerPosition current = queue.Dequeue();\n                if (current.boxPosition.Equals(targetPosition))\n                    return pushes;\n\n                var next = current.NextPosition();\n                var nextBox = next.boxPosition;\n\n                if (!visited.Contains(next) && IsValidAndEmpty(nextBox)) \n                {\n                    queue.Enqueue(next);\n                    visited.Add(next);\n\n                    // when we add another position \'next\' for the player and the box, we need to try to add all possible positions for the player if the box is fixed. So whenever we move the box, always add all positions for the player as well around the box to the queue.\n                    for (int i = 0; i < 4; i++)\n                    {\n                        int newX = nextBox.X + directions[i];\n                        int newY = nextBox.Y + directions[i+1];\n                        Position newPosition = new Position(newX, newY);\n\n                        if (!next.playerPosition.Equals(newPosition) && IsValidAndEmpty(newPosition) && PlayerCanMove(next, newPosition))\n                        {\n                            var start = new BoxAndPlayerPosition(nextBox, newPosition);\n                            if (!visited.Contains(start))\n                            {\n                                queue.Enqueue(start);\n                                visited.Add(start);\n                            }\n                        }\n                    }\n                }\n            }\n            pushes++;\n        }\n\n        return -1;\n    }\n    \n    private Position FindBox(char[][] grid)\n    {\n        return Find(grid, \'B\');\n    }\n    private Position FindPlayer(char[][] grid)\n    {\n        return Find(grid, \'S\');\n    }\n    private Position FindTarget(char[][] grid)\n    {\n        return Find(grid, \'T\');\n    }\n    \n    private Position Find(char[][] grid, char c)\n    {\n        for (int i = 0; i < grid.Length; i++)\n            for (int j = 0; j < grid[0].Length; j++)\n                if (grid[i][j] == c)\n                    return new Position(j, i);\n        \n        throw new Exception();\n    }   \n}\n```
[]