C# clean & easy to understand
\n    //{\'#\',\'.\',\'.\',\'.\'}\n    //{\'.\',\'.\',\'S\',\'.\'}\n    //{\'.\',\'B\',\'.\',\'T\'}\n    //{\'.\',\'.\',\'.\',\'#\'}\n    //We can start from th
\n    //{\'#\',\'.\',\'.\',\'.\'}\n    //{\'.\',\'.\',\'S\',\'.\'}\n    //{\'.\',\'B\',\'.\',\'T\'}\n    //{\'.\',\'.\',\'.\',\'#\'}\n    //We can start from the current Box location & do a BFS.\n    //We don\'t really need to move player, we can just simulate it.\n    //In each level, we can try to find valid positions for box in all 4 directions around it\n    //& if those positions have a valid position around it for player then,\n    //we can push those positions to Q.\n    //Box & player new positions valid if: valid boundary/no wall/player can move to the pos.\n    //In each step, we can try finding all valid player positions in grid using DFS before hand.\n    //The level where we reach the target will be our minimum moves.\n    //*********Key is\n    //if we want to push box to left, player must stand at right, to simulate this \n    //we can just use box position & current direction & add current direction to box position\n    //& subtract current direction from box position to get new box & player positions. \n    //we can ignore the player position to simulate the player can move to any position\n    //int bxNew = boxPos.X + d[0], byNew = boxPos.Y + d[1];\n    //int pxNew = boxPos.X - d[0], pyNew = boxPos.Y - d[1];\n    //D[1,0] B[1,1] P[0,1] =>move box left[0,-1]\n    //=> D[1,0] B[1,0(1-1)] P[1(1+0),2(1-(-1)]\n    public class MBM2\n    {\n        static readonly List<int[]> Dirs = new List<int[]>\n        {new[] { -1, 0 }, new[] { 1, 0 }, new[] { 0, 1 }, new[] { 0, -1 }};\n        public static int MinPushBox(char[][] grid)\n        {\n            Position box = null, target = null, player = null;\n            #region Get the box, player & target positions\n            int rows = grid.Length, cols = grid[0].Length;\n            for (var row = 0; row < rows; row++)\n            {\n                for (var col = 0; col < cols; col++)\n                {\n                    if (grid[row][col] == \'B\')\n                        box = new Position(row, col);\n                    else if (grid[row][col] == \'T\')\n                        target = new Position(row, col);\n                    else if (grid[row][col] == \'S\')\n                        player = new Position(row, col);\n                }\n            }\n            #endregion\n\n            //Do BFS to find the min moves\n            //we can save block & player encoded positions int the Q\n            var startPos = Position.GetPositionsAsString(box, player);\n            var q = new Queue<string>();\n            q.Enqueue(startPos);\n            //We use a set to record all the positions we have visited\n            //(including the position of the player)\n            var visitedSet = new HashSet<string>();\n            visitedSet.Add(startPos);\n\n            var res = 0;\n            while (q.Any())\n            {\n                res++;\n                var size = q.Count();\n                while (size > 0)\n                {\n                    size--;\n                    var decodedPositions = Position.GetPositions(q.Dequeue());\n                    var boxPos = decodedPositions.Key;\n                    var playerPos = decodedPositions.Value;\n                    var canVisit = new bool[rows, cols];\n\n                    //Use dfs to find all the positions that the player can reach at this moment. \n                    UpdatePlayerReachablePositions(grid, canVisit, playerPos, boxPos);\n\n                    //Move the box and update the set\n                    foreach (var d in Dirs)\n                    {\n                        //if we want to push box to right, storekeeper must stand at left\n                        int bxNew = boxPos.X + d[0], byNew = boxPos.Y + d[1];\n                        int pxNew = boxPos.X - d[0], pyNew = boxPos.Y - d[1];\n                        var newBoxPos = new Position(bxNew, byNew);\n                        var newPlayerPos = new Position(pxNew, pyNew);\n\n                        //Box cannot move to this direction in any of the following condition \n                        if (!newPlayerPos.IsValid(grid) || !newBoxPos.IsValid(grid) ||\n                            newBoxPos.IsWall(grid) || !canVisit[pxNew, pyNew])\n                            continue;\n\n                        //If we have reached destination return\n                        if (newBoxPos.Equals(target))\n                            return res;\n\n                        //Now we can move the box, also record the new position(including the original position of the box)\n                        var newPosition = Position.GetPositionsAsString(newBoxPos, newPlayerPos);\n                        //Make sure the newPosition has never been visited before\n                        if (visitedSet.Add(newPosition))\n                        {\n                            q.Enqueue(newPosition);\n                        }\n                    }\n                }\n            }\n            return -1;\n        }\n        //DFS to find locations the player can reach\n        public static void UpdatePlayerReachablePositions(char[][] grid, bool[,] canVisit, Position playerPosition, Position boxPosition)\n        {\n            if (!playerPosition.IsValid(grid) ||\n                playerPosition.IsWall(grid) ||\n                playerPosition.Equals(boxPosition) ||\n                canVisit[playerPosition.X, playerPosition.Y])\n                return;\n            canVisit[playerPosition.X, playerPosition.Y] = true;\n            foreach (var d in Dirs)\n            {\n                var newPlayerPos = new Position(playerPosition.X + d[0], playerPosition.Y + d[1]);\n                UpdatePlayerReachablePositions(grid, canVisit, newPlayerPos, boxPosition);\n            }\n        }\n\n        public class Position\n        {\n            public int X { get; set; }\n            public int Y { get; set; }\n            public Position(int x, int y)\n            {\n                X = x;\n                Y = y;\n            }\n            public static string GetPositionsAsString(Position box, Position storeKeeper)\n            {\n                return box.ToString() + "|" + storeKeeper.ToString();\n            }\n\n            public static KeyValuePair<Position, Position> GetPositions(string num)\n            {\n                var pos = num.Split(\'|\');\n                var res = new KeyValuePair<Position, Position>(new Position(pos[0], pos[1]), new Position(pos[2], pos[3]));\n                return res;\n            }\n            public Position(string x, string y)\n            {\n                int.TryParse(x, out var x1);\n                X = x1;\n                int.TryParse(y, out var y1);\n                Y = y1;\n            }\n            public override string ToString()\n            {\n                return X + "|" + Y;\n            }\n\n            public bool IsValid(char[][] grid)\n            {\n                return X >= 0 && Y >= 0 && X < grid.Length && Y < grid[0].Length;\n            }\n\n            public bool IsWall(char[][] grid)\n            {\n                return grid[X][Y] == \'#\';\n            }\n\n            public bool Equals(Position position)\n            {\n                return X == position.X && Y == position.Y;\n            }\n        }\n    }
[]