For some reason my supposedly unbeatable AI is easily bypass-able because it never tries to prevent me from winning. I’ve tried restarting functions, like my checkForWinner, which I separated into two functions now, one to return a winner value (can be null) and one to check if the game is actually over and prevent anyone from continuing. I don’t know what to do anymore and have basically given up on this project.
const GameBoard = (() => {
const winConditions = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6]
];
let slots = ["", "", "", "", "", "", "", "", ""];
const player1 = "X";
const player2 = "O";
let currentPlayer = player2;
const playerText = document.querySelector(".player");
const computerText = document.querySelector(".computer");
const winnerText = document.querySelector(".winner-text");
let isRunning = false;
const cells = document.querySelectorAll(".card");
function initializeGame() {
computerText.classList.add("highlighted");
isRunning = true;
ComputerPlayer.bestMove();
cells.forEach((cell) => {
cell.addEventListener("click", function () {
cellClicked(cell);
});
const restartBtn = document.querySelector("#restart-btn");
restartBtn.addEventListener("click", function () {
restartGame(cell);
initializeGame();
});
});
}
function cellClicked(item) {
const cellIndex = item.getAttribute("cell-index");
if (currentPlayer === player1) {
if (slots[cellIndex] != "" || !isRunning) {
return;
}
updateCell(item, cellIndex);
checkGameOver();
switchPlayers();
}
}
function updateCell(cell, index) {
slots[index] = currentPlayer;
console.log(slots);
cell.textContent = currentPlayer;
}
function switchPlayers() {
if (isRunning) {
if (currentPlayer === player1) {
currentPlayer = player2;
ComputerPlayer.bestMove();
checkGameOver();
console.log(`current player is: ${currentPlayer}`);
playerText.classList.remove("highlighted");
computerText.classList.add("highlighted");
} else if (currentPlayer === player2) {
currentPlayer = player1;
console.log(`current player is: ${currentPlayer}`);
computerText.classList.remove("highlighted");
playerText.classList.add("highlighted");
}
}
}
function equals3(a,b,c) {
if (a == b && b == c) {
return true;
}
return false;
}
function checkForWinner() {
let winner = null;
for(let i = 0; i < winConditions.length; i++) {
if (equals3(slots[winConditions[i][0]],slots[winConditions[i][1]],slots[winConditions[i][2]]) == true && slots[winConditions[i][0]] != '') {
winner = slots[winConditions[i][0]];
}
if ((winner === null) && (slots[0] != '' && slots[1] != '' && slots[2] != '' && slots[3] != '' && slots[4] != '' && slots[5] != '' && slots[6] != '' && slots[7] != '' && slots[8] != '')) {
winner = 'Tie';
}
}
return winner;
console.log(winner)
}
function checkGameOver() {
let result = checkForWinner();
if (result != null) {
if(result === 'Tie') {
winnerText.classList.add('winner-visible');
winnerText.textContent = 'Tie!'
isRunning = false;
} else {
winnerText.classList.add('winner-visible');
winnerText.textContent = result + ' Wins!'
isRunning = false;
}
}
}
function restartGame(card) {
isRunning = true;
slots = ["", "", "", "", "", "", "", "", ""];
console.log(slots);
card.textContent = "";
winnerText.classList.remove("winner-visible");
}
return {
winConditions,
slots,
player1,
player2,
currentPlayer,
playerText,
computerText,
winnerText,
cells,
isRunning,
switchPlayers,
updateCell,
checkForWinner,
initializeGame,
checkGameOver
};
})();
const ComputerPlayer = (() => {
function bestMove() {
if (GameBoard.currentPlayer === GameBoard.player2 ) {
let bestScore = -Infinity;
let move;
for(let i = 0 ; i < GameBoard.slots.length; i++) {
if (GameBoard.slots[i] === '') {
GameBoard.slots[i] = 'O';
let score = minimax(GameBoard.slots, 0, false);
console.log('score is ' + score);
GameBoard.slots[i] = '';
if (score > bestScore) {
bestScore = score;
console.log(bestScore + ' is bestScore');
move = i;
}
}
}
GameBoard.slots[move] = 'O';
GameBoard.cells[move].textContent = 'O';
GameBoard.switchPlayers();
}
}
function minimax(board, depth, isMaximizing) {
let result = GameBoard.checkForWinner();
if (result == 'X') {
return 1;
} else if (result == 'O') {
return -1;
} else if(result == 'Tie') {
return 0;
}
if (isMaximizing) {
let bestScore = -Infinity;
for(let i = 0 ; i < board.length; i++) {
if(board[i] === '') {
board[i] = 'O';
let score = minimax(board, depth + 1, false);
board[i] = '';
bestScore = Math.max(score, bestScore);
}
}
return bestScore;
} else {
let bestScore = Infinity;
for(let i = 0; i < board.length; i++ ) {
if(board[i] === '') {
board[i] = 'X';
let score = minimax(board, depth +1, true);
board[i] = '';
bestScore = Math.min(score, bestScore);
}
}
return bestScore;
}
}
return {
bestMove,
minimax
}
})();
GameBoard.initializeGame();