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