Teaching JS trying to do a ticatactoe game and got a problem with recursion, specifically with 'Maximum call stack size exceeded'.
When I try to make a random move by the computer player I make a random number, which then will be checked of his value in array called gameState. So, if this value has defined already (there are X or O mark in a cell), I need to generate a random number again. So I need to stop this infinite loop when all cells are marked or I'll get an error 'Maximum call stack size exceeded'.
Can you please take a look, am I do the right thing or it is generally wrong? Actually, I thought about another implementation of this game scene, but I thought if i've started with that I should lead this to the end
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()
};
};
CodePudding user response:
There's a very slim chance that repeated random calls will keep on getting the same number, so it's not strictly speaking correct to simply keep rolling random numbers (even though in practice it's highly unlikely to ever be encountered).
To avoid the situation with certainty, one should only take a random number based on the unused places. For example:
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();
}
No recursive calls.
CodePudding user response:
You have to check when the game has ended and stop the recursive calls. I don't know the implementation of resultCheck
but it should return information about whether the game is finished or not. If it is finished, don't do the recursive call.
That being said, the performance of your code is unpredictable because it generates random numbers even before knowing if the resulting slot is available. You could optimize it like this:
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();
};