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
Related
I have written a program for an assignment where we had to write a simple Gomoku program. I thought I had it all, but when I compile and run, it sets off the win scenario even if I only have 4 of a kind and even if they're not next to each other. (It should only set off a win if there are five in a row of one kind...X's or O's). I feel like I should be resetting my counter back to 0 before each turn, but I'm not sure where I should be doing that. Any tips would be appreciated!
import java.util.Scanner;
public class Gomoku1
{
public static void main (String[] args)
{
Scanner input = new Scanner(System.in);
char[][] map = new char [19][19];
int row = 0;
int column = 0;
//fill game with dots
for (int i = 0; i < map.length; i++)
{
for (int j = 0; j < map[i].length; j++)
{
map[i][j] = '.';
}
}
printMap(map);
char player1Choice = 'X';
char player2Choice = 'O';
int [] place;
while (true)
{
System.out.println("Player 1's turn!");
place = userTurn(map, player1Choice);
if (isValidMove(map, place[0], place[1]) == false)
{
System.out.println("Invalid move! Try again!");
place = userTurn(map, player1Choice);
}
if (isValidMove(map, place[0], place[1])) {
map[place[0]][place[1]] = player1Choice;
printMap(map);
}
if (isBoardFull(map) == true)
{
System.out.println("Board is full. Tied game.");
break;
}
if (hasPlayerWon(map, player1Choice) == true)
{
System.out.println("Player 1 Wins!");
break;
}
else
{
System.out.println("Player 2's turn!: ");
place = userTurn(map, player2Choice);
//System.out.println(isValidMove(map, row, column));
if (isValidMove(map, place[0], place[1]) == false)
{
System.out.println("Invalid move! Try again!");
place = userTurn(map, player2Choice);
}
if (isValidMove(map, place[0], place[1])) {
map[place[0]][place[1]] = player2Choice;
printMap(map);
}
if (isBoardFull(map) == true)
{
System.out.println("Board is full. Tied game.");
break;
}
if (hasPlayerWon(map, player2Choice) == true)
{
System.out.println("Player 2 Wins!");
break;
}
}
}
}
public static void printMap (char[][] map)
{
for (int i = 0; i < map.length; i++)
{
for (int j = 0; j < map[i].length; j++)
{
System.out.printf("%2c", map[i][j]);
}
System.out.println();
}
}
public static int [] userTurn (char[][] map, char playerChoice)
{
Scanner input = new Scanner(System.in);
System.out.print("Enter row: ");
int row = input.nextInt();
System.out.print("Enter column: ");
int column = input.nextInt();
int place [] = {row, column};
return place;
}
public static boolean isValidMove (char[][] map, int row, int column)
{
//System.out.println ("n is valid move");
if (row < 0 || row > 18 || column < 0 || column > 18 || map[row][column]=='O' || map[row][column]=='X')
{
return false;
}
else
{
return true;
}
}
public static boolean isBoardFull (char[][] map)
{
int openSpots = 0;
for (int i = 0; i < map.length; i++)
{
for (int j = 0; j < map.length; j++)
{
if (!(map[i][j]=='.'))
openSpots++;
}
}
if (openSpots == 361)
{
return true;
}
return false;
}
public static boolean hasPlayerWon(char[][] map, int player)
{
if (isHorizontalWin(map, player) == true || isVerticalWin(map, player) == true || isDiagonalWin(map, player) == true)
{
return true;
}
return false;
}
public static boolean isHorizontalWin(char[][] map, int player)
{
int count = 0;
int r;
int c;
for (int i = 0; i < map.length; i++)
{
for (int j = 0; j < map.length; j++)
{
if (map[i][j]==(player))
{
r = i;
c = j;
while (r >= 0 && r <= 18 && c >= 0 && c <= 18 && map[r][c] == player)
{
count ++;
r += 0;
c += 1;
}
}
}
}
if (count == 5)
{
return true;
}
return false;
}
public static boolean isVerticalWin(char[][] map, int player)
{
int count = 0;
int r;
int c;
for (int i = 0; i < map.length; i++)
{
for (int j = 0; j < map.length; j++)
{
if (map[i][j]==(player))
{
r = i;
c = j;
while (r >= 0 && r <= 18 && c >= 0 && c <= 18 && map[r][c] == player)
{
count ++;
r += 1;
c += 0;
}
}
}
}
if (count == 5)
{
return true;
}
return false;
}
public static boolean isDiagonalWin(char[][] map, int player)
{
int count = 0;
int r;
int c;
for (int i = 0; i < map.length; i++)
{
for (int j = 0; j < map.length; j++)
{
if (map[i][j]==(player))
{
r = i;
c = j;
while (r >= 0 && r <= 18 && c >= 0 && c <= 18 && map[r][c] == player)
{
count++;
r += 1;
c += 1;
}
}
}
}
if (count == 5)
{
return true;
}
return false;
}
}
You have problems in all three of the function that check win conditions: isHorizontalWin, isVerticalWin, and isDiagonalWin. All three increment the variable count, but this variable is never set back to zero. Additionally, the check to see if count == 5 should be made inside the loop. Here is an example on how to fix isHorizontalWin:
public static boolean isHorizontalWin(char[][] map, int player)
{
int count = 0;
int r;
int c;
for (int i = 0; i < map.length; i++)
{
for (int j = 0; j < map.length; j++)
{
if (map[i][j]==(player))
{
r = i;
c = j;
while (r >= 0 && r <= 18 && c >= 0 && c <= 18 && map[r][c] == player)
{
count ++;
r += 0;
c += 1;
}
if (count == 5)
{
return true;
} else {
count = 0;
}
}
}
}
return false;
}
How do I prevent the same tic tac toe coordinate from being inputted by the user?
The user input is taken at the main method in the Game Class.
The tic tac toe cells with [x, y] coordinates ranging from (0-2) can be either:
0(_), 1 (X) or 2 (O)
Grid Class with alpha beta search tree pruning algorithm
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
class Grid {
List<Cell> availableCells;
int[][] board = new int[3][3];
Scanner scan = new Scanner(System.in);
// Set limit to search tree depth
int treeDepth = 9;
List<CellsAndScores> rootsChildrenScore = new ArrayList<>();
public int score() {
int score = 0;
// Check all columns
for (int j = 0; j < 3; ++j) {
int X = 0;
int O = 0;
for (int i = 0; i < 3; ++i) {
if (board[i][j] == 0) {
} else if (board[i][j] == 1) {
X++;
} else {
O++;
}
}
score += changeInScore(X, O);
}
// Check all rows
for (int i = 0; i < 3; ++i) {
int X = 0;
int O = 0;
for (int j = 0; j < 3; ++j) {
if (board[i][j] == 0) {
} else if (board[i][j] == 1) {
X++;
} else {
O++;
}
}
score += changeInScore(X, O);
}
int X = 0;
int O = 0;
// Check diagonal (first)
for (int i = 0, j = 0; i < 3; ++i, ++j) {
if (board[i][j] == 1) {
X++;
} else if (board[i][j] == 2) {
O++;
} else {
}
}
score += changeInScore(X, O);
X = 0;
O = 0;
// Check Diagonal (Second)
for (int i = 2, j = 0; i > -1; --i, ++j) {
if (board[i][j] == 1) {
X++;
} else if (board[i][j] == 2) {
O++;
} else {
}
}
score += changeInScore(X, O);
return score;
}
private int changeInScore(int X, int O) {
int change;
if (X == 3) {
change = 100;
} else if (X == 2 && O == 0) {
change = 10;
} else if (X == 1 && O == 0) {
change = 1;
} else if (O == 3) {
change = -100;
} else if (O == 2 && X == 0) {
change = -10;
} else if (O == 1 && X == 0) {
change = -1;
} else {
change = 0;
}
return change;
}
public int alphaBetaMinimax(int alpha, int beta, int depth, int turn) {
if (beta <= alpha) {
System.out.println("Pruning at tree depth = " + depth + " alpha: " + alpha + " beta: " + beta);
if (turn == 1)
return Integer.MAX_VALUE;
else
return Integer.MIN_VALUE;
}
if (depth == treeDepth || gameOver()) {
return score();
}
List<Cell> cellsAvailable = getAvailableStates();
if (cellsAvailable.isEmpty()) {
return 0;
}
if (depth == 0) {
rootsChildrenScore.clear();
}
int maxValue = Integer.MIN_VALUE, minValue = Integer.MAX_VALUE;
for (int i = 0; i < cellsAvailable.size(); ++i) {
Cell cell = cellsAvailable.get(i);
int currentScore = 0;
if (turn == 1) {
placeAMove(cell, 1);
currentScore = alphaBetaMinimax(alpha, beta, depth + 1, 2);
maxValue = Math.max(maxValue, currentScore);
// Set alpha
alpha = Math.max(currentScore, alpha);
if (depth == 0) {
rootsChildrenScore.add(new CellsAndScores(currentScore, cell));
}
} else if (turn == 2) {
placeAMove(cell, 2);
currentScore = alphaBetaMinimax(alpha, beta, depth + 1, 1);
minValue = Math.min(minValue, currentScore);
// Set beta
beta = Math.min(currentScore, beta);
}
// reset board
board[cell.x][cell.y] = 0;
// Do not evaluate the rest of the branches after search tree is pruned
if (currentScore == Integer.MAX_VALUE || currentScore == Integer.MIN_VALUE)
break;
}
return turn == 1 ? maxValue : minValue;
}
public boolean gameOver() {
// Game is over is someone has won, or board is full (draw)
return (hasXWon() || hasOWon() || getAvailableStates().isEmpty());
}
public boolean hasXWon() {
if ((board[0][0] == board[1][1] && board[0][0] == board[2][2] && board[0][0] == 1)
|| (board[0][2] == board[1][1] && board[0][2] == board[2][0] && board[0][2] == 1)) {
// System.out.println("X Diagonal Win");
return true;
}
for (int i = 0; i < 3; ++i) {
if (((board[i][0] == board[i][1] && board[i][0] == board[i][2] && board[i][0] == 1)
|| (board[0][i] == board[1][i] && board[0][i] == board[2][i] && board[0][i] == 1))) {
// System.out.println("X Row or Column win");
return true;
}
}
return false;
}
public boolean hasOWon() {
if ((board[0][0] == board[1][1] && board[0][0] == board[2][2] && board[0][0] == 2)
|| (board[0][2] == board[1][1] && board[0][2] == board[2][0] && board[0][2] == 2)) {
// System.out.println("O Diagonal Win");
return true;
}
for (int i = 0; i < 3; ++i) {
if ((board[i][0] == board[i][1] && board[i][0] == board[i][2] && board[i][0] == 2)
|| (board[0][i] == board[1][i] && board[0][i] == board[2][i] && board[0][i] == 2)) {
// System.out.println("O Row or Column win");
return true;
}
}
return false;
}
public List<Cell> getAvailableStates() {
availableCells = new ArrayList<>();
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
if (board[i][j] == 0) {
availableCells.add(new Cell(i, j));
}
}
}
return availableCells;
}
public void placeAMove(Cell Cell, int player) {
board[Cell.x][Cell.y] = player; // player = 1 for X, 2 for O
}
public Cell returnBestMove() {
int MAX = -100000;
int best = -1;
for (int i = 0; i < rootsChildrenScore.size(); ++i) {
if (MAX < rootsChildrenScore.get(i).score) {
MAX = rootsChildrenScore.get(i).score;
best = i;
}
}
return rootsChildrenScore.get(best).cell;
}
public void displayBoard() {
System.out.println();
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
if (board[i][j] == 0)
System.out.print("_" + " ");
if (board[i][j] == 1)
System.out.print("X" + " ");
if (board[i][j] == 2)
System.out.print("O" + " ");
}
System.out.println("");
}
System.out.println();
}
public void resetGrid() {
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
board[i][j] = 0;
}
}
}
}
Cell class
class Cell {
int x, y;
public Cell(int x, int y) {
this.x = x;
this.y = y;
}
public String toString() {
return "[" + x + ", " + y + "]";
}
}
class CellsAndScores {
int score;
Cell cell;
CellsAndScores(int score, Cell cell) {
this.score = score;
this.cell = cell;
}
}
Game Class with main method - takes user input
import java.util.Random;
public class Game {
public static void main(String[] args) {
Grid grid = new Grid();
Random random = new Random();
grid.displayBoard();
System.out.print("Who moves first? [1]Computer(X) [2]User(O): ");
int turn = grid.scan.nextInt();
if (turn == 1) {
Cell p = new Cell(random.nextInt(3), random.nextInt(3));
grid.placeAMove(p, 1);
grid.displayBoard();
}
while (!grid.gameOver()) {
int x = 0, y = 0;
System.out.print("Please enter an x coordinate [0-2]: ");
x = grid.scan.nextInt();
System.out.print("Please enter an y coordinate [0-2]: ");
y = grid.scan.nextInt();
Cell userMove = new Cell(y, x);
grid.placeAMove(userMove, 2); // 2 for O and O is the user
grid.displayBoard();
if (grid.gameOver())
break;
grid.alphaBetaMinimax(Integer.MIN_VALUE, Integer.MAX_VALUE, 0, 1);
for (CellsAndScores pas : grid.rootsChildrenScore)
System.out.println("Cell: " + pas.cell + " Score: " + pas.score);
grid.placeAMove(grid.returnBestMove(), 1);
grid.displayBoard();
}
if (grid.hasXWon()) {
System.out.println("Unfortunately, you lost!");
grid.resetGrid();
} else if (grid.hasOWon()) {
System.out.println("You win!");
grid.resetGrid();
} else {
System.out.println("It's a draw!");
grid.resetGrid();
}
}
}
My answer would be to add a boolean check method into your Grid.java class and then in your main method - call this boolean check method before the placeAMove() method.
For example, in your Grid.java class, adding the following method:
/*
* Return true if space is ok to use.
*/
public boolean isMoveOK(Cell cell) {
return board[cell.x][cell.y] == 0;
}
This way, using your pre-existing 0/1/2 values that keep track of empty/X/O space values, you may provide a check to see if the space value is zero or not.
This would be one way to use it in your main method, to answer your question of, 'How do I prevent the same tic tac toe coordinate from being inputted by the user?'
Cell userMove = new Cell(y, x);
if (grid.isMoveOK(userMove)) {
grid.placeAMove(userMove, 2); // 2 for O and O is the user
} else {
System.out.println("Please try a different space/cell");
continue;
}
grid.displayBoard();
if (grid.gameOver())
break;
In this way, I am skipping the remaining loop code in your main method loop, until there's a valid open space. (When there is, then the program should proceed to check for winning values or whether to proceed playing)
Hope this answers your question! :)
Cheers
Like many other person as I see, I have a problem with my implementation of the Minmax algorithm for the game Connect4. In the example below, the algorithm doesn't try to block the player two and I don't understand why. My evaluation function is really basic: a binary function. Maybe it's because of the depth, but I don't think so.
Code:
public class Moteur {
public static void addPawn(int[][] game, int column, int player) {
int i = 0;
while (i < 6 && game[i][column] == 0) {
i++;
}
game[(i==6)?5:i-1][column] = player;
}
public static void kickPawn(int[][] game, int column) {
int i = 0;
while (i < 6 && game[i][column] == 0) {
i++;
}
game[i][column] = 0;
}
public static void AI_play(int[][] game, int depth) {
int max = -10000;
int tmp = 0;
int maxj = -5;
int j;
for (j = 0; j < 7; j++) {
if(game[0][j] == 0) {
addPawn(game, j, 1);
tmp = Min(game, depth - 1);
if (tmp > max) {
max = tmp;
maxj = j;
}
kickPawn(game, j);
}
}
addPawn(game, maxj, 1);
}
public static int Max(int[][] game, int depth) {
if(depth == 0 || winner(game) != 0) {
return eval(game);
}
int max = -10000;
int j, tmp;
for(j = 0; j < 7; j++) {
if(game[0][j] == 0) {
addPawn(game, j, 2);
tmp = Min(game, depth - 1);
if(tmp > max) {
max = tmp;
}
kickPawn(game, j);
}
}
return max;
}
public static int Min(int[][] game, int depth) {
if(depth == 0 || winner(game) != 0) {
return eval(game);
}
int min = 10000;
int j, tmp;
for(j = 0; j < 7; j++) {
if(game[0][j] == 0) {
addPawn(game, j, 1);
tmp = Max(game, depth - 1);
if(tmp < min) {
min = tmp;
}
kickPawn(game, j);
}
}
return min;
}
public static int eval(int[][] game) {
int gameWinner, nb_pawns = 0;
for (int i = 0; i < game.length; i++) {
for (int j = 0; j < game[0].length; j++) {
if(game[i][j] != 0) {
nb_pawns++;
}
}
}
gameWinner = winner(game);
if(gameWinner == 1) {
return 1000 - nb_pawns;
} else if (gameWinner == 2) {
return -1000 + nb_pawns;
} else {
return 0;
}
}
public static int winner(int[][] game) {
Integ e = new Integ();
for (int j = 0; j < game[0].length; j++) {
if(game[5][j]!= 0 && testWinner(game, j, e)) {
return e.getI();
}
}
return 0;
}
public static boolean testWinner(int[][] game, int lastColumn, Integ e) {
int lastRow = 0;
while (lastRow < 6 && game[lastRow][lastColumn] == 0) {
lastRow++;
}
lastRow = lastRow;
int i = 0;
int j = 0;
int currentPlayer = game[lastRow][lastColumn];
e.setI(currentPlayer);
int sequence = 0;
i = lastRow;
boolean b = i < 3
&& game[i][lastColumn] == currentPlayer
&& game[i+1][lastColumn] == currentPlayer
&& game[i+2][lastColumn] == currentPlayer
&& game[i+3][lastColumn] == currentPlayer;
if(b) {
return true;
}
sequence = 0;
j = lastColumn;
do {
j--;
} while(0 < j && game[lastRow][j] == currentPlayer);
if(j < 0 || game[lastRow][j] != currentPlayer) {
j++;
}
while(j <= 6 && game[lastRow][j] == currentPlayer) {
j++;
sequence++;
}
if (sequence >= 4) {
return true;
}
sequence = 0;
i = lastRow;
j = lastColumn;
do {
i--;
j--;
} while(0 < i && 0 < j && game[i][j] == currentPlayer);
if(i < 0 || j < 0 || game[i][j] != currentPlayer) {
i++;
j++;
}
while(i <= 5 && j <= 6 && game[i][j] == currentPlayer) {
i++;
j++;
sequence++;
}
if (sequence >= 4) {
return true;
}
sequence = 0;
i = lastRow;
j = lastColumn;
do {
i++;
j--;
} while(i < 5 && 0 < j && game[i][j] == currentPlayer);
if (5 < i || j < 0 || game[i][j] != currentPlayer) {
i--;
j++;
}
while(0 <= i && j <= 6 && game[i][j] == currentPlayer) {
i--;
j++;
sequence++;
}
if (sequence >= 4) {
return true;
}
return false;
}
public static void main(String[] args) {
int[][] game = new int[6][7];
int depth = 5;
game[5][3] = 2;
game[4][3] = 2;
game[3][3] = 2;
AI_play(game, depth);
//game[4][0] = 2;
//AI_play(game, depth);
//game[5][2] = 2;
//AI_play(game, depth);
//game[5][3] = 2;
//AI_play(game, depth);
//game[1][0] = 2;
//AI_play(game, depth);
//game[5][4] = 2;
//AI_play(game, depth);
for (int i = 0; i < game.length; i++) {
for (int j = 0; j < game[0].length; j++) {
System.out.print(game[i][j]);
}
System.out.println("");
}
}
private static class Integ {
private int i;
public Integ() {
this.i = 0;
}
public void increment() {
this.i = this.i + 1;
}
public int getI() {
return this.i;
}
public void setI(int i) {
this.i = i;
}
}
}
public Generation newGen() {
Generation newGen = new Generation();
for (int i = 1; i < 26; i++) {
for (int j = 1; j < 76; j++) {
int x = i;
int y = j;
int n = numberOfNeighbors(x, y);
if (gen[i][j] == 'X') {
if (n == 2 || n == 3) {
newGen.gen[i][j] = 'X';
}
if (n != 2 || n != 3) {
newGen.gen[i][j] = '.';
}
}
if (gen[i][j] == '.') {
if (n == 3) {
newGen.gen[i][j] = 'X';
}
if (n != 3) {
newGen.gen[i][j] = '.';
}
}
}
}
return newGen;
}
//I'm taking a char[][] called gen and passing it to the above method, and it is supposed to return a value of type Generation(the class) and then print it to the console in the method below.
public void printToScreenNew() {
Generation newGen = newGen();
System.out.print(newGen.toString());
System.out.println("\n");
}
Have tried debugging my MiniMax code but can't seem to find the problem here. The main method is evalGame(), which returns 1 for a win, -1 for a loss and 0 for a tie. Are there any obvious errors I have made in the MiniMax algorithm?
package tictactoe;
import static tictactoe.Player.*;
import java.util.Arrays;
public class MinimaxTTT {
private Player[] board = new Player[9];
private static Player player = X;
public MinimaxTTT() {
Arrays.fill(board, E);
player = X;
}
public MinimaxTTT(Player[] b, Player p) {
for(int i = 0; i < 9; i++) {
board[i] = b[i];
}
player = p;
}
public static boolean checkWin(Player[] b, Player p) {
if((b[0]==p&&b[1]==p&&b[2]==p)
|| (b[0]==p&&b[3]==p&&b[6]==p)
|| (b[0]==p&&b[4]==p&&b[8]==p)
|| (b[0]==p&&b[1]==p&&b[2]==p)
|| (b[1]==p&&b[4]==p&&b[7]==p)
|| (b[0]==p&&b[1]==p&&b[2]==p)
|| (b[2]==p&&b[4]==p&&b[6]==p)
|| (b[2]==p&&b[5]==p&&b[8]==p)
|| (b[0]==p&&b[3]==p&&b[6]==p)
|| (b[3]==p&&b[4]==p&&b[5]==p)
|| (b[0]==p&&b[4]==p&&b[8]==p)
|| (b[2]==p&&b[4]==p&&b[6]==p)
|| (b[3]==p&&b[4]==p&&b[5]==p)
|| (b[1]==p&&b[4]==p&&b[7]==p)
|| (b[2]==p&&b[5]==p&&b[8]==p)
|| (b[3]==p&&b[4]==p&&b[5]==p)
|| (b[0]==p&&b[3]==p&&b[6]==p)
|| (b[2]==p&&b[4]==p&&b[6]==p)
|| (b[6]==p&&b[7]==p&&b[8]==p)
|| (b[6]==p&&b[7]==p&&b[8]==p)
|| (b[1]==p&&b[4]==p&&b[7]==p)
|| (b[2]==p&&b[5]==p&&b[8]==p)
|| (b[6]==p&&b[7]==p&&b[8]==p)
|| (b[0]==p&&b[4]==p&&b[8]==p)) {
return true;
}
return false;
}
private static boolean checkTie(Player[] b) {
for(int i = 0; i < 9; i++) {
if(b[i] == E) {
return false;
}
}
return true;
}
private static boolean gameOver(Player[] b) {
return checkTie(b) || checkWin(b, X) || checkWin(b, O);
}
private static int maxOfArray(int[] a) {
int max = a[0];
for (int i : a)
if (max < i)
max = i;
return max;
}
private static int minOfArray(int[] a) {
int min = a[0];
for (int i : a)
if (min > i)
min = i;
return min;
}
private static int getEmptyNumber(Player[] b) {
int spaces = 0;
for(int i = 0; i < 9; i++) {
if(b[i] == E)
spaces++;
}
return spaces;
}
private static int evalGame(Player[] b, Player p, Player currentP) {
if(gameOver(b)) {
if(checkWin(b, p)) {
return 1;
} else if(checkWin(b, p == X ? O : X)) {
return -1;
} else {
return 0;
}
} else {
int[] arrayEval = new int[getEmptyNumber(b)];
for(int i = 0; i < getEmptyNumber(b); i++) {
arrayEval[i] = evalGame(possibleBoards(b, currentP)[i], p, currentP == X ? O : X);
}
if(currentP == p) {
return maxOfArray(arrayEval);
} else {
return minOfArray(arrayEval);
}
}
}
public static Player[][] possibleBoards(Player[] b, Player p) {
Player[][] toReturn = new Player[getEmptyNumber(b)][9];
int spaces = 0;
for(int i = 0; i < 9; i++) {
if(b[i] == E) {
for(int j = 0; j < 9; j++) {
toReturn[spaces][j] = b[j];
}
toReturn[spaces][i] = p;
spaces++;
}
}
return toReturn;
}
public static void main(String[] args) {
Player[] p = new Player[9];
Arrays.fill(p, E);
p[1] = X;
System.out.println(evalGame(p, O, O));
}
}
I have since looked over my code and realised I made a mistake while debugging. The working, edited code is below for anyone who is interested
package tictactoe;
import static tictactoe.Player.*;
import java.util.Arrays;
public class MinimaxTTT {
private Player[] board = new Player[9];
private Player player = X;
public MinimaxTTT() {
Arrays.fill(board, E);
player = X;
}
public MinimaxTTT(Player[] b, Player p) {
for(int i = 0; i < 9; i++) {
board[i] = b[i];
}
player = p;
}
public Player[] getBoard() {
return board;
}
public void setBoard(Player[] b) {
for(int i = 0; i < 9; i++) {
board[i] = b[i];
}
}
public void setSquare(int i, Player p) {
board[i] = p;
}
public Player getPlayer() {
return player;
}
public void setPlayer(Player p) {
player = p;
}
public void switchPlayers() {
player = player == X ? O : X;
}
public boolean checkWin(Player[] b, Player p) {
if(b[0] == p && b[1] == p && b[2] == p
|| b[3] == p && b[4] == p && b[5] == p
|| b[6] == p && b[7] == p && b[8] == p
|| b[0] == p && b[3] == p && b[6] == p
|| b[1] == p && b[4] == p && b[7] == p
|| b[2] == p && b[5] == p && b[8] == p
|| b[0] == p && b[4] == p && b[8] == p
|| b[2] == p && b[4] == p && b[6] == p) {
return true;
}
return false;
}
public boolean checkTie(Player[] b) {
for(int i = 0; i < 9; i++) {
if(b[i] == E) {
return false;
}
}
return true;
}
public boolean gameOver(Player[] b) {
return checkTie(b) || checkWin(b, X) || checkWin(b, O);
}
private int maxOfArray(int[] a) {
int max = a[0];
for (int i : a)
if (max < i)
max = i;
return max;
}
private int minOfArray(int[] a) {
int min = a[0];
for (int i : a)
if (min > i)
min = i;
return min;
}
private int getEmptyNumber(Player[] b) {
int spaces = 0;
for(int i = 0; i < 9; i++) {
if(b[i] == E)
spaces++;
}
return spaces;
}
private int findIndex(int[] is, int n) {
for(int i = 0; i < is.length; i++) {
if(is[i] == n)
return i;
}
return 0;
}
private int[] evalGame(Player[] b, Player p, Player currentP, int depth) {
int[] toReturn = new int[2];
if(gameOver(b)) {
if(checkWin(b, p)) {
toReturn[1] = 10 + depth;
return toReturn;
} else if(checkWin(b, p == X ? O : X)) {
toReturn[1] = depth - 10;
return toReturn;
} else {
toReturn[1] = 0;
return toReturn;
}
} else {
depth += 1;
int[] arrayEval = new int[getEmptyNumber(b)];
for(int i = 0; i < getEmptyNumber(b); i++) {
arrayEval[i] = evalGame(possibleBoards(b, currentP)[i], p, currentP == X ? O : X, depth)[1];
}
if(currentP == p) {
int position = findIndex(arrayEval, maxOfArray(arrayEval));
int base = 0;
for(int i = 0; i < 9; i++) {
if(b[i] == E) {
if(base == position) {
toReturn[0] = i;
break;
} else {
base++;
}
}
}
toReturn[1] = maxOfArray(arrayEval);
return toReturn;
} else {
toReturn[1] = minOfArray(arrayEval);
return toReturn;
}
}
}
public Player[][] possibleBoards(Player[] b, Player p) {
Player[][] toReturn = new Player[getEmptyNumber(b)][9];
int spaces = 0;
for(int i = 0; i < 9; i++) {
if(b[i] == E) {
for(int j = 0; j < 9; j++) {
toReturn[spaces][j] = b[j];
}
toReturn[spaces][i] = p;
spaces++;
}
}
return toReturn;
}
public Player[] move() {
if(!gameOver(board)) {
board[evalGame(board, player, player, 0)[0]] = player;
}
return board;
}
}