|  | 
| 11 | 11 |  * @param {number} mouseJump | 
| 12 | 12 |  * @return {boolean} | 
| 13 | 13 |  */ | 
| 14 |  | -function canMouseWin(grid, catJump, mouseJump) { | 
| 15 |  | - const dirs = [ | 
| 16 |  | - [1, 0], | 
| 17 |  | - [-1, 0], | 
| 18 |  | - [0, 1], | 
| 19 |  | - [0, -1], | 
| 20 |  | - ]; | 
| 21 |  | - const m = grid.length; | 
| 22 |  | - const n = grid[0].length; | 
| 23 |  | - let mouse_pos = null; | 
| 24 |  | - let cat_pos = null; | 
| 25 |  | - let available = 0; // available steps for mouse and cat | 
|  | 14 | +var canMouseWin = function (A, maxC, maxM) { | 
|  | 15 | + let n = A.length, | 
|  | 16 | + m = A[0].length, | 
|  | 17 | + seen = {}, | 
|  | 18 | + pos = [ | 
|  | 19 | + [0, 1], | 
|  | 20 | + [0, -1], | 
|  | 21 | + [1, 0], | 
|  | 22 | + [-1, 0], | 
|  | 23 | + ], | 
|  | 24 | + xM, | 
|  | 25 | + yM, | 
|  | 26 | + xC, | 
|  | 27 | + yC, | 
|  | 28 | + xF, | 
|  | 29 | + yF; | 
|  | 30 | + for (let i = 0; i < n; i++) | 
|  | 31 | + for (let j = 0; j < m; j++) | 
|  | 32 | + if (A[i][j] === "M") [xM, yM] = [i, j]; | 
|  | 33 | + else if (A[i][j] === "C") [xC, yC] = [i, j]; | 
|  | 34 | + else if (A[i][j] === "F") [xF, yF] = [i, j]; | 
| 26 | 35 | 
 | 
| 27 |  | - // Search the start pos of mouse and cat | 
| 28 |  | - for (let i = 0; i < m; i++) { | 
| 29 |  | - for (let j = 0; j < n; j++) { | 
| 30 |  | - if (grid[i][j] !== "#") { | 
| 31 |  | - available++; | 
| 32 |  | - } | 
| 33 |  | - if (grid[i][j] === "M") { | 
| 34 |  | - mouse_pos = [i, j]; | 
| 35 |  | - } else if (grid[i][j] === "C") { | 
| 36 |  | - cat_pos = [i, j]; | 
| 37 |  | - } | 
| 38 |  | - } | 
| 39 |  | - } | 
|  | 36 | + let invalid = ([x, y]) => | 
|  | 37 | + x < 0 || x >= n || y < 0 || y >= m ? true : A[x][y] === "#"; | 
| 40 | 38 | 
 | 
| 41 |  | - const dp = function (turn, mouse_pos, cat_pos) { | 
| 42 |  | - // if (turn === m * n * 2) { | 
| 43 |  | - // We already search the whole grid (9372 ms 74.3 MB) | 
| 44 |  | - if (turn === available * 2) { | 
| 45 |  | - // We already search the whole touchable grid (5200 ms 57.5 MB) | 
| 46 |  | - return false; | 
| 47 |  | - } | 
| 48 |  | - if (turn % 2 === 0) { | 
| 49 |  | - // Mouse | 
| 50 |  | - const [i, j] = mouse_pos; | 
| 51 |  | - for (let [di, dj] of dirs) { | 
| 52 |  | - for (let jump = 0; jump <= mouseJump; jump++) { | 
| 53 |  | - // Note that we want to do range(mouseJump + 1) instead of range(1, mouseJump + 1) | 
| 54 |  | - // considering the case that we can stay at the same position for next turn. | 
| 55 |  | - const new_i = i + di * jump; | 
| 56 |  | - const new_j = j + dj * jump; | 
|  | 39 | + //returns whether the current player ( determined by level -odd/even) can win the game while on the current state | 
|  | 40 | + let recursion = ([[xM, yM], [xC, yC]], level) => { | 
|  | 41 | + let whoPlays = Boolean(level % 2), | 
|  | 42 | + state = [ | 
|  | 43 | + [ | 
|  | 44 | + [xM, yM], | 
|  | 45 | + [xC, yC], | 
|  | 46 | + ], | 
|  | 47 | + level, | 
|  | 48 | + ].toString(), | 
|  | 49 | + maxAllowable = whoPlays ? maxC : maxM; | 
|  | 50 | + if (seen[state] === undefined) { | 
|  | 51 | + //memoization | 
|  | 52 | + //end states | 
|  | 53 | + if ((xC === xF && yC === yF) || (xC === xM && yC === yM) || level >= 70) | 
|  | 54 | + // level>=70 makes this pass | 
|  | 55 | + return (seen[state] = whoPlays); | 
|  | 56 | + if (xM === xF && yM === yF) return (seen[state] = !whoPlays); | 
|  | 57 | + //try staying still first | 
|  | 58 | + if ( | 
|  | 59 | + recursion( | 
|  | 60 | + [ | 
|  | 61 | + [xM, yM], | 
|  | 62 | + [xC, yC], | 
|  | 63 | + ], | 
|  | 64 | + level + 1 | 
|  | 65 | + ) === false | 
|  | 66 | + ) | 
|  | 67 | + return (seen[state] = true); | 
|  | 68 | + //then try moving | 
|  | 69 | + for (let [dx, dy] of pos) | 
|  | 70 | + for (let k = 1; k <= maxAllowable; k++) { | 
|  | 71 | + let newState = whoPlays | 
|  | 72 | + ? [xC + dx * k, yC + dy * k] | 
|  | 73 | + : [xM + dx * k, yM + dy * k]; | 
|  | 74 | + if (invalid(newState) || seen[state]) break; | 
| 57 | 75 |  if ( | 
| 58 |  | - 0 <= new_i && | 
| 59 |  | - new_i < m && | 
| 60 |  | - 0 <= new_j && | 
| 61 |  | - new_j < n && | 
| 62 |  | - grid[new_i][new_j] !== "#" | 
| 63 |  | - ) { | 
| 64 |  | - // Valid pos | 
| 65 |  | - if ( | 
| 66 |  | - dp(turn + 1, [new_i, new_j], cat_pos) || | 
| 67 |  | - grid[new_i][new_j] === "F" | 
| 68 |  | - ) { | 
| 69 |  | - return true; | 
| 70 |  | - } | 
| 71 |  | - } else { | 
| 72 |  | - // Stop extending the jump since we cannot go further | 
| 73 |  | - break; | 
| 74 |  | - } | 
|  | 76 | + recursion( | 
|  | 77 | + whoPlays ? [[xM, yM], newState] : [newState, [xC, yC]], | 
|  | 78 | + level + 1 | 
|  | 79 | + ) === false | 
|  | 80 | + ) | 
|  | 81 | + return (seen[state] = true); | 
| 75 | 82 |  } | 
| 76 |  | - } | 
| 77 |  | - return false; | 
| 78 |  | - } else { | 
| 79 |  | - // Cat | 
| 80 |  | - const [i, j] = cat_pos; | 
| 81 |  | - for (let [di, dj] of dirs) { | 
| 82 |  | - for (let jump = 0; jump <= catJump; jump++) { | 
| 83 |  | - const new_i = i + di * jump; | 
| 84 |  | - const new_j = j + dj * jump; | 
| 85 |  | - if ( | 
| 86 |  | - 0 <= new_i && | 
| 87 |  | - new_i < m && | 
| 88 |  | - 0 <= new_j && | 
| 89 |  | - new_j < n && | 
| 90 |  | - grid[new_i][new_j] !== "#" | 
| 91 |  | - ) { | 
| 92 |  | - if ( | 
| 93 |  | - !dp(turn + 1, mouse_pos, [new_i, new_j]) || | 
| 94 |  | - (new_i === mouse_pos[0] && new_j === mouse_pos[1]) || | 
| 95 |  | - grid[new_i][new_j] === "F" | 
| 96 |  | - ) { | 
| 97 |  | - // This condition will also handle the case that the cat cannot jump through the mouse | 
| 98 |  | - return false; | 
| 99 |  | - } | 
| 100 |  | - } else { | 
| 101 |  | - break; | 
| 102 |  | - } | 
| 103 |  | - } | 
| 104 |  | - } | 
| 105 |  | - return true; | 
|  | 83 | + seen[state] = false; | 
| 106 | 84 |  } | 
|  | 85 | + return seen[state]; | 
| 107 | 86 |  }; | 
| 108 |  | - | 
| 109 |  | - return dp(0, mouse_pos, cat_pos); | 
| 110 |  | -} | 
|  | 87 | + return recursion( | 
|  | 88 | + [ | 
|  | 89 | + [xM, yM], | 
|  | 90 | + [xC, yC], | 
|  | 91 | + ], | 
|  | 92 | + 0 | 
|  | 93 | + ); | 
|  | 94 | +}; | 
0 commit comments