I'm working on a Tic-Tac-Toe AI implementation using the Alpha-Beta Pruning Minimax algorithm. The goal is to find the optimal move for the AI player (X) on the given board. However, I'm encountering an issue where the algorithm is not returning the correct optimal move index.
The optimal move for the AI player (X) should be at index 4, but my minimax function is returning bestAIMove.index = 8.
Here is my code:
let humanPlayer = "O";
let aiPlayer = "X";
let origBoard = ["X", "O", 2, "X", 4, 5, "O", 7, 8];
let MAX = {index: 99, score: 1000};
let MIN = {index: 99, score: -1000}
let fc = 0;
function checkAvailableMoves(board) {
return board.filter(s => s !== "O" && s !== "X");
}
function winning(board, player) {
const winningCombinations = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6]
];
return winningCombinations.some(combination =>
combination.every(cell => board[cell] === player)
);
}
function max(a,b) {return a.score > b.score ? a : b;}
function min(a,b) {return a.score < b.score ? a : b;}
function minimax(newBoard, depth, player, alpha, beta) {
const availableMoves = checkAvailableMoves(newBoard);
let theBestMove = {};
fc++
if (winning(newBoard, humanPlayer)) {return { score: -10 + depth }}
else if (winning(newBoard, aiPlayer)) {return { score: 10 - depth }}
else if (availableMoves.length === 0) {return { score: 0 }};
if (player === aiPlayer) {
for (let i = 0; i < availableMoves.length; i++) {
const index = availableMoves[i];
newBoard[index] = player;
let result = minimax(newBoard, depth + 1, humanPlayer, alpha, beta);
result.index = index;
alpha = max(alpha,result)
newBoard[index] = index;
if (alpha.score >= beta.score) {break}
}
theBestMove = alpha;
} else if (player === humanPlayer) {
for (let i = 0; i < availableMoves.length; i++) {
const index = availableMoves[i];
newBoard[index] = player;
let result = minimax(newBoard, depth + 1, aiPlayer, alpha, beta);
result.index = index;
beta = min(beta, result);
newBoard[index] = index;
if (alpha.score >= beta.score){break}
}
theBestMove = beta;
}
return theBestMove;
}
bestAIMove = minimax(origBoard,0,aiPlayer,MIN,MAX)
console.log(bestAIMove)
console.log(fc)
What might be causing the problem?
There are two related issues in your code:
The
minandmaxfunctions will choosebwhen the score is equal. But as you callminandmaxwithresultas second argument, this always gives precedence to a newer score. As you may get the same score back because of alpha beta pruning, you should give precedence to the move that "set the bar", i.e. toalphaorbeta. So either swap the arguments you pass tominandmaxor change those functions so they chooseain case of equal scores.result.index = indexmutates an object that potentially isalphaorbeta. You don't want this to happen. Deal with these objects as immutable. So instead doresult = {...result, index}With these two fixes it will work.
Demo
Here is your corrected code, with human player playing first, in an interactive snippet: