如何避免递归或解决问题?

how to avoid recursion or to fix the problem?

教 JS 尝试做一个 ticatactoe 游戏并遇到递归问题,特别是 'Maximum call stack size exceeded'。

当我尝试让计算机玩家随机移动时,我生成了一个随机数,然后将在名为 gameState 的数组中检查其值。 所以,如果这个值已经定义(单元格中有 X 或 O 标记),我需要再次生成一个随机数。所以我需要在所有单元格都被标记时停止这个无限循环,否则我会得到一个错误 'Maximum call stack size exceeded'。

你能看看,我做的是对的还是通常是错的? 其实,我想过这个游戏场景的另一种实现,但我想如果我从那个开始,我应该把它引到最后


let gameState = ["", "", "", "", "", "", "", "", "",];
let playerAi = "O";

// ai's turn
function playerAiTurn() {
    let randomNum = Math.floor(Math.random() * gameState.length);
    if (gameState[randomNum] === '') {
    gameState[randomNum] = playerAi;
    document.getElementById(randomNum).innerHTML = playerAi;
    resultCheck();
    } else {
        playerAiTurn()
    };
};

你必须检查游戏何时结束并停止递归调用。我不知道 resultCheck 的实现,但它应该 return 关于游戏是否完成的信息。如果完成了,就不要做递归调用了。

也就是说,您的代码的性能是不可预测的,因为它甚至在不知道生成的插槽是否可用之前就生成了随机数。你可以这样优化它:

const gameState = ["", "", "", "", "", "", "", "", "",];
const playerAi = "O";

function playerAiTurn() {
    const emptySlots = Array.from(gameState.entries()).filter(([, x]) => x === '');
    if(!emptySlots.length)
        return; //Game over
    const randomNum = Math.floor(Math.random() * emptySlots.length);
    const [index] = emptySlots[randomNum];
    gameState[index] = playerAi;
    document.getElementById(index).innerHTML = playerAi;
    resultCheck();
};

重复随机调用将继续获得相同数字的可能性非常小,因此严格来说,简单地继续滚动随机数是不正确的(尽管在实践中这种情况极不可能遇到)。

为了避免出现这种情况,应该只根据未使用的地方取一个随机数。 例如:

let gameState = ["", "", "", "", "", "", "", "", "",];
let playerAi = "O";

// ai's turn
function playerAiTurn() {
  let unusedPlaces = [];
  for(let i=0; i<gameState.length; i++) {
    if( gameState[i] === "") unusedPlaces.push(i); 
  }
  if( unusedPlaces.length > 0 ){
    let randomUnusedPlace = unusedPlaces [ Math.floor(Math.random() * unusedPlaces.length) ];
    gameState[randomUnusedPlace] = playerAi
    document.getElementById(randomUnusedPlace).innerHTML = playerAi; 
  }

  resultCheck();
}

没有递归调用。