如何避免递归或解决问题?
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();
}
没有递归调用。
教 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();
}
没有递归调用。