I am using MinMax algorithm for Tictactoe, and it works correctly, but it's trying to win, without blocking my turns, so it's very easy to win the AI. I added the depth parameter, but the result is the same. Here you can see my code:
public int playMiniMax(Tableau t, char maximizingPlayer,int depth) {
int bestMove = 0;
if (t.getWinner() == 'x') {
return 1;
} else if (t.getWinner() == 'd') {
return 0;
} else if (t.getWinner() == 'o') {
return -1;
}
else if (depth == 0) {
return 0;
}
else {
int bestValue;
int i;
if (maximizingPlayer == 'x') {
bestValue = Integer.MIN_VALUE;
for(i = 1; i <= 9; ++i) {
if (t.chooseCase(i, 'x')) {
bestValue = Math.max(bestValue, this.playMiniMax(t, 'o',depth-1));
bestMove = i;
t.resetCase(i);
}
}
} else {
bestValue = Integer.MAX_VALUE;
for(i = 1; i <= 9; ++i) {
if (t.chooseCase(i, 'o')) {
bestValue = Math.min(bestValue, this.playMiniMax(t, 'x',depth-1));
bestMove = i;
t.resetCase(i);
}
}
}
return bestMove;
}
}
The "bestMove" is the best case(from 1 to 9) proposed by minmax, and in my main I am calling it like this:
int movei = playerAI.playMiniMax(t, 'x',3);
correctCase = t.chooseCase(movei, 'x');
public class Tableau {
private char[][] charArray;
private boolean gameIsOver = false;
private char winner = 'n';
Tableau(char[][] _charArray) {
this.charArray = _charArray;
this.moves = new Stack();
this.ceateTable();
}
public void ceateTable() {
for(int i = 0; i < this.charArray.length; ++i) {
for(int j = 0; j < this.charArray.length; ++j) {
if (j != 2 && j != 5) {
System.out.print(this.charArray[i][j] + "|");
} else {
System.out.println(this.charArray[i][j] + "|");
}
}
}
}
public boolean chooseCase(int number, char player) {
int c = 1;
for(int i = 0; i < this.charArray.length; ++i) {
for(int j = 0; j < this.charArray.length; ++j) {
if (c == number && this.charArray[i][j] == '-') {
this.charArray[i][j] = player;
return true;
}
++c;
}
}
return false;
}
public void resetCase(int number) {
int c = 1;
for(int i = 0; i < this.charArray.length; ++i) {
for(int j = 0; j < this.charArray.length; ++j) {
if (c == number) {
this.charArray[i][j] = '-';
}
++c;
}
}
}
public boolean checkWin(char player) {
if ((this.charArray[0][0] != player || this.charArray[0][1] != player || this.charArray[0][2] != player) && (this.charArray[1][0] != player || this.charArray[1][1] != player || this.charArray[1][2] != player) && (this.charArray[2][0] != player || this.charArray[2][1] != player || this.charArray[2][2] != player) && (this.charArray[0][0] != player || this.charArray[1][0] != player || this.charArray[2][0] != player) && (this.charArray[0][1] != player || this.charArray[1][1] != player || this.charArray[2][1] != player) && (this.charArray[0][2] != player || this.charArray[1][2] != player || this.charArray[2][2] != player) && (this.charArray[0][0] != player || this.charArray[1][1] != player || this.charArray[2][2] != player) && (this.charArray[0][2] != player || this.charArray[1][1] != player || this.charArray[2][0] != player)) {
return false;
} else {
System.out.println("Dear " + player + " you won!");
this.gameIsOver = true;
this.winner = player;
return true;
}
}
public boolean isFull() {
for(int i = 0; i < this.charArray.length; ++i) {
for(int j = 0; j < this.charArray.length; ++j) {
if (this.charArray[i][j] == '-') {
return false;
}
}
}
return true;
}
public boolean checkDraw(char player) {
if (!this.checkWin(player) && this.isFull()) {
System.out.println("It's a draw!!!");
this.winner = 'd';
return true;
} else {
return false;
}
}
public boolean checkLoose(char player) {
return player == 'x' ? this.checkWin('o') : false;
}
public boolean isGameIsOver() {
return this.gameIsOver;
}
public void setGameIsOver(boolean gameIsOver) {
this.gameIsOver = gameIsOver;
}
public char getWinner() {
return this.winner;
}
public void setWinner(char winner) {
this.winner = winner;
}
}
The problem relies on the ChooseCase
function, because it is just evaluating where the board is empty, so it would choose the first or the last empy space ( depending on minimizing or maximizing). You need to adjust your Evaluation Function
to be aware of the board state so it can makes a better choice, taking this exmple:
https://kartikkukreja.wordpress.com/2013/03/30/heuristic-function-for-tic-tac-toe/
In Java should look like this:
private final int possibleWins = 8;
private final int[][] threeInARow = {
{ 0, 1, 2 },
{ 3, 4, 5 },
{ 6, 7, 8 },
{ 0, 3, 6 },
{ 1, 4, 7 },
{ 2, 5, 8 },
{ 0, 4, 8 },
{ 2, 4, 6 }
};
private final int[][] heuristicArray = {
{ 0, -10, -100, -1000 },
{ 10, 0, 0, 0 },
{ 100, 0, 0, 0 },
{ 1000, 0, 0, 0 }
};
public int evaluatePosition(char[] board, char player) {
char opponent = (player == 'X') ? 'O' : 'X', piece;
int players, others, t = 0, i, j;
for (i = 0; i < 8; i++) {
players = others = 0;
for (j = 0; j < 3; j++) {
piece = board[threeInARow[i][j]];
if (piece == player)
players++;
else if (piece == opponent)
others++;
}
t += heuristicArray[players][others];
}
return t;
}
Also check the previous question: How to create an evaluation function for a TIC-TAC-TOE variant game