Back to all solutions
#1728 - Cat and Mouse II
Problem Description
A game is played by a cat and a mouse named Cat and Mouse.
The environment is represented by a grid of size rows x cols, where each element is a wall, floor, player (Cat, Mouse), or food.
- Players are represented by the characters 'C'(Cat),'M'(Mouse).
- Floors are represented by the character '.' and can be walked on.
- Walls are represented by the character '#' and cannot be walked on.
- Food is represented by the character 'F' and can be walked on.
- There is only one of each character 'C', 'M', and 'F' in grid.
Mouse and Cat play according to the following rules:
- Mouse moves first, then they take turns to move.
- During each turn, Cat and Mouse can jump in one of the four directions (left, right, up, down). They cannot jump over the wall nor outside of the grid.
- catJump, mouseJump are the maximum lengths Cat and Mouse can jump at a time, respectively. Cat and Mouse can jump less than the maximum length.
- Staying in the same position is allowed.
- Mouse can jump over Cat.
The game can end in 4 ways:
- If Cat occupies the same position as Mouse, Cat wins.
- If Cat reaches the food first, Cat wins.
- If Mouse reaches the food first, Mouse wins.
- If Mouse cannot get to the food within 1000 turns, Cat wins.
Given a rows x cols matrix grid and two integers catJump and mouseJump, return true if Mouse can win the game if both Cat and Mouse play optimally, otherwise return false.
Solution
/**
* @param {string[]} grid
* @param {number} catJump
* @param {number} mouseJump
* @return {boolean}
*/
var canMouseWin = function(grid, catJump, mouseJump) {
if (typeof grid === 'string') grid = [grid];
const rows = grid.length;
const cols = grid[0].length;
const dirs = [[1, 0], [-1, 0], [0, 1], [0, -1]];
let mouse;
let cat;
let food;
let available = 0;
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
const cell = grid[i][j];
if (cell !== '#') available++;
if (cell === 'M') mouse = [i, j];
else if (cell === 'C') cat = [i, j];
else if (cell === 'F') food = [i, j];
}
}
const memo = new Map();
return canWin(0, mouse, cat);
function getKey(turn, [mr, mc], [cr, cc]) {
return `${turn},${mr},${mc},${cr},${cc}`;
}
function isValid(r, c) {
return r >= 0 && r < rows && c >= 0 && c < cols && grid[r][c] !== '#';
}
function canWin(turn, mousePos, catPos) {
if (turn >= available * 2) return false;
const key = getKey(turn, mousePos, catPos);
if (memo.has(key)) return memo.get(key);
const isMouseTurn = turn % 2 === 0;
const [r, c] = isMouseTurn ? mousePos : catPos;
const maxJump = isMouseTurn ? mouseJump : catJump;
for (const [dr, dc] of dirs) {
for (let jump = 0; jump <= maxJump; jump++) {
const nr = r + dr * jump;
const nc = c + dc * jump;
if (!isValid(nr, nc)) break;
if (isMouseTurn) {
if (grid[nr][nc] === 'F' || canWin(turn + 1, [nr, nc], catPos)) {
memo.set(key, true);
return true;
}
} else {
if (grid[nr][nc] === 'F' || (nr === mousePos[0] && nc === mousePos[1])
|| !canWin(turn + 1, mousePos, [nr, nc])) {
memo.set(key, false);
return false;
}
}
}
}
memo.set(key, !isMouseTurn);
return !isMouseTurn;
}
};