2
* @param {character[][]} grid5
var minPushBox = function (grid) {8
const n = grid[0].length;15
const add = (a, b) => [a[0] + b[0], a[1] + b[1]];16
const equals = (a, b) => a[0] === b[0] && a[1] === b[1];17
const validate = ([x, y]) => x >= 0 && y >= 0 && x < m && y < n;18
const getKey = ([x, y]) => x * n + y;20
// find all player, ball, target, and free cells25
const freeSet = new Set();26
for (let i = 0; i < m; i++) {27
for (let j = 0; j < n; j++) {28
if (grid[i][j] === ".") {29
freeSet.add(getKey([i, j]));30
} else if (grid[i][j] === "S") {31
freeSet.add(getKey([i, j]));33
} else if (grid[i][j] === "B") {34
freeSet.add(getKey([i, j]));36
} else if (grid[i][j] === "T") {37
freeSet.add(getKey([i, j]));42
return { player, ball, target, freeSet };44
const { player, ball, target, freeSet } = init();45
const targetKey = getKey(target);47
// detect whether two cells are connected48
const connCache = new Map(); // [x,y,x2,y2,x3,y3] => boolean49
const getConnKey = (a, b, ball) => {50
if (a[0] > b[0] || (a[0] === b[0] && a[1] > b[1])) {53
return [a[0], a[1], b[0], b[1], ball[0], ball[1]].join(",");55
const isConnected = (a, b, ball) => {56
if (a[0] === b[0] && a[1] === b[1]) {59
const connKey = getConnKey(a, b, ball);60
if (connCache.has(connKey)) {61
return connCache.get(connKey);63
const bKey = getKey(b);64
const ballKey = getKey(ball);65
const visited = new Array(m * n).fill(false);66
visited[getKey(a)] = true;68
while (queue.length) {69
const cell = queue.shift();70
for (let i = 0; i < dirs.length; i++) {71
const next = add(cell, dirs[i]);73
const nextKey = getKey(next);74
if (nextKey === bKey) {75
connCache.set(connKey, true);79
freeSet.has(nextKey) &&80
nextKey !== ballKey &&83
visited[nextKey] = true;89
connCache.set(connKey, false);94
const getStateKey = ([x, y], [xx, yy]) => [x, y, xx, yy].join(","); // ball, player95
const stateCache = new Set(); // Set<stateKey>96
let queue = [[ball, player]];98
while (queue.length) {100
for (let i = 0; i < queue.length; i++) {101
const [ball, player] = queue[i];102
for (let j = 0; j < dirs.length; j++) {104
const reverseDir = [dir[0] ? -dir[0] : 0, dir[1] ? -dir[1] : 0];105
const nextBall = add(ball, dir);106
const nextPlayer = add(ball, reverseDir);107
const nextBallKey = getKey(nextBall);108
const nextPlayerKey = getKey(nextPlayer);110
validate(nextBall) &&111
validate(nextPlayer) &&112
freeSet.has(nextBallKey) &&113
freeSet.has(nextPlayerKey)115
const nextStateKey = getStateKey(nextBall, nextPlayer);116
if (isConnected(player, nextPlayer, ball)) {117
if (!stateCache.has(nextStateKey)) {118
stateCache.add(nextStateKey);119
if (nextBallKey === targetKey) {122
nextQueue.push([nextBall, nextPlayer]);