I am confused with minimax algorithm. I have spent 2 days and still can't find mistake. Could you glance at my code to help me to find any bugs?
export default class TicTacToeController {
/*@ngInject*/
constructor($scope) {
this.board = new Array(3);
this.players = {
ai: "x",
player: "o"
};
this.move = {
row: "",
cell: ""
};
this.currentPlayer = this.players.ai;
this.humanMove = false;
this.fillBoard();
this.board[0][0] = "x";
this.board[0][1] = "o";
this.board[0][2] = "x";
this.board[1][0] = "x";
this.board[1][1] = "x";
this.board[1][2] = "o";
this.board[2][0] = "o";
$scope.$watch("ticTac.currentPlayer", player => {
if(player === this.players.player) {
this.humanMove = true;
} else {
this.aiMove(this.board);
}
})
}
fillBoard() {
for (var i = 0; i < 3; i++) {
this.board[i] = new Array(3);
for (var j = 0; j < 3; j++) {
this.board[i][j] = " ";
}
}
}
evaluate(board) {
//check rows
for (var row = 0; row < 3; row++) {
if (board[row][0] === board[row][1] && board[row][1] === board[row][2]) {
if (board[row][0] === this.players.ai) {
return 10;
} else if (board[row][0] === this.players.player) {
return -10;
}
}
}
//check columns
for (var col = 0; col < 3; col++) {
if (board[0][col] === board[1][col] && board[1][col] === board[2][col]) {
if (board[0][col] === this.players.ai) {
return 10;
} else if (board[0][col] === this.players.player) {
return -10;
}
}
}
//check for diagonals
if (board[0][0] === board[1][1] && board[1][1] === board[2][2]) {
if (board[0][0] === this.players.ai) {
return 10;
} else if (board[0][0] === this.players.player) {
return -10;
}
}
if (board[0][2] === board[1][1] && board[1][1] === board[2][0]) {
if (board[0][2] === this.players.ai) {
return 10;
} else if (board[0][2] === this.players.player) {
return -10;
}
}
//if no player won return 0
return 0;
}
minimax(board, isMax) {
var score = this.evaluate(board);
if (score === 10 || score === -10) {
return score;
}
if(!this.isMoveLeft(board)) {
return 0;
}
if (isMax) {
var best = -1000;
for (var i = 0; i < 3; i++) {
for (var j = 0; j < 3; j++) {
if (board[i][j] === " ") {
board[i][j] = this.players.ai;
best = Math.max(best, this.minimax(board, !isMax));
board[i][j] = " ";
}
}
}
return best;
} else {
var best = 1000;
for (var i = 0; i < 3; i++) {
for (var j = 0; j < 3; j++) {
if (board[i][j] === " ") {
board[i][j] = this.players.player;
best = Math.min(best, this.minimax(board, !isMax));
board[i][j] = " ";
}
}
}
return best;
}
}
isMoveLeft(board) {
for (var i = 0; i < 3; i++) {
for (var j = 0; j < 3; j++) {
if (board[i][j] === " ")
return true;
}
}
return false;
}
makeBestMove(board) {
var bestVal = -1000;
this.move.row = -1;
this.move.cell = -1;
for(var i = 0; i < 3; i++) {
for (var j = 0; j < 3; j++) {
if(board[i][j] === " ") {
board[i][j] === this.currentPlayer;
var moveVal = this.minimax(board, false);
board[i][j] === " ";
if(moveVal > bestVal) {
this.move.row = i;
this.move.cell = j;
bestVal = moveVal;
}
}
}
}
return this.move;
}
aiMove(board) {
var currentMove;
if(!this.isMoveLeft(this.board)) {
return;
}
currentMove = this.makeBestMove(board);
board[currentMove.row][currentMove.cell] = this.currentPlayer;
this.currentPlayer = this.players.player;
}
makeMove(row, cell) {
if(this.board[row][cell] === " ") {
this.board[row][cell] = this.players.player;
}
this.currentPlayer = this.players.ai;
}
}
export default TicTacToeController;
Start board looks that:
As you can see, the next optimal move should be (2,2), but minimax algorithm put value to (2,0).
I tried to debug, but I still cant find a mistake.
In your makeBestMove
method, these lines:
board[i][j] === this.currentPlayer;
...
board[i][j] === " ";
should be using an assignment equals:
board[i][j] = this.currentPlayer;
...
board[i][j] = " ";
===
is a comparison operator. So you are comparing the values and getting true
without changing anything.
Currently, as you are not making an assignment prior to calling minimax
the same best
result is returned each time.