I have a minimax algorithm with alpha beta pruning for tic-tac-toe. I have trouble adding a depth limit to how minimax evatuates the board. When I add a depth limit it only partially works. For example, if I move first in the middle, it will choose a losing position on its second move.
(Computer is O. Player is X.)
_ _ _ o _ _ o _ x o o x
_ x _ _ x _ _ x _ _ x _
_ _ _ _ _ _ _ _ _ _ _ _
But if i begin in the first position, it works fine. It seems to be ignoring my 2-in-a-rows. If I remove the depth limit all together it works correctly all of the time. How can I add a depth limit to the algorithm that lets it work correctly all of the time? Why does it not work with a depth limit?
This is the evaluation part the without a depth limit that fully works at the top of the minimax method:
int score = evaluate(board);
//if maximizer won
if (score == 10) {
return score;
}
//if minimizer won
if (score == -10) {
return score;
}
if (hasCellsLeft(board) == false) {
return 0;
}
This is the evaluation with a depth limit at the top of the method:
if (depth == 6||isGameOver(board)) {
return evaluate(board);
}
This is the evaluation method:
public int evaluate(Cell[] board) {
//rows across
if (isGameOver(board) && endStates.checkWinByRow(board, playerToken) || isGameOver(board) && endStates.checkWinByColumn(board, playerToken) || isGameOver(board) && endStates.checkWinByDiagonal(board, playerToken)) {
return 10;
}
if (isGameOver(board) && endStates.checkWinByRow(board, opponentToken) || isGameOver(board) && endStates.checkWinByColumn(board, opponentToken) || isGameOver(board) && endStates.checkWinByDiagonal(board, opponentToken)) {
return -10;
}
else {
return 0;
}
}
This is the isGameOver method:
public boolean isGameOver(Cell[] board) {
if (endStates.checkWinByRow(board, playerToken) || endStates.checkWinByColumn(board, playerToken) || endStates.checkWinByDiagonal(board, playerToken)) {
return true;
}
if (endStates.checkWinByRow(board, opponentToken) || endStates.checkWinByColumn(board, opponentToken) || endStates.checkWinByDiagonal(board, opponentToken)) {
return true;
}
return false;
}
Here is the minimax algorithm:
public int alphaBeta(Cell[] board, int depth, int nodeIndex, boolean isMax, int alpha, int beta) {
if (depth == 6||isGameOver(board)) {
return evaluate(board);
}
if (isMax) {
int best = MIN;
// Recur for left and right children
for (int i=0; i<board.length; i++) {
if (board[i].getToken() == Token.EMPTY) {
board[i].setToken(playerToken);
int val = alphaBeta(board, depth + 1, nodeIndex * 2 + i, false, alpha, beta);
//int best = beta;
best = Math.max(best, val);
alpha = Math.max(alpha, best);
board[i].resetMarker();
// Alpha Beta Pruning
if (beta <= alpha) {
break;
}
}
}
return best;
}
else {
int best = MAX;
// Recur for left and right children
for (int i=0; i<board.length; i++) {
if (board[i].getToken() == Token.EMPTY) {
board[i].setToken(opponentToken);
int val = alphaBeta(board, depth + 1, nodeIndex * 2 + i, true, alpha, beta);
//int best = beta;
best = Math.min(best, val);
beta = Math.min(beta, best);
board[i].resetMarker();
}
// Alpha Beta Pruning
if (beta <= alpha) {
break;
}
}
return best;
}
}