I've written this code for tic-tac-toe AI implementing the MiniMax algorithm. The AI wins every time (full depth search). But there might be something wrong. Here's the output that makes me suspect the implementations correctness:
0 0 0
0 0 0
0 0 0
Who's gonna move first? (1) You : (2) Me?
1
Your move:
2 2
0 0 0
0 0 0
0 0 2
Score: -1 Point: 0 0
Score: -1 Point: 0 1
Score: -1 Point: 0 2
Score: -1 Point: 1 0
Score: 0 Point: 1 1
Score: 0 Point: 1 2
Score: 0 Point: 2 0
Score: 0 Point: 2 1
0 0 0
0 1 0
0 0 2
Your move:
0 0
2 0 0
0 1 0
0 0 2
Score: 0 Point: 0 1
Score: 0 Point: 0 2
Score: 0 Point: 1 0
Score: 0 Point: 1 2
Score: 0 Point: 2 0
Score: 0 Point: 2 1
2 1 0
0 1 0
0 0 2
Now all 0s at these points mean that the AI can draw the match in the worst case if it takes turn and mark these points as 1. (AI is 1, Player is 2). That is definitely wrong. There are two points where defeat is for sure (Point 0 2 and Point 2 0) so there must have been -1 for the score at these points. But the AI takes the first move and wins (Coincidence, I guess). I've not been able to defeat the AI, even with thousands of random tests.
Here is the full code. It'd be very helpful if you could tell me the things that are going wrong .
import java.util.*;
class Point {
int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
public class Board {
public Board(Board b) {
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
this.board[i][j] = b.board[i][j];
}
}
}
public Board() {
}
int[][] board = new int[3][3];
List<Point> availablePoints;
public List<Point> getAvailableStates() {
availablePoints = new ArrayList<>();
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
if (board[i][j] == 0) {
availablePoints.add(new Point(i, j));
}
}
}
return availablePoints;
}
public boolean isGameOver() {
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)) {
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))) {
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)) {
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)) {
return true;
}
}
return false;
}
public void placeAMove(Point point, int player) {
board[point.x][point.y] = player;
}
class PointsAndScores {
int score;
Point point;
PointsAndScores(int score, Point point) {
this.score = score;
this.point = point;
}
}
List<PointsAndScores> pointsAndScores = new ArrayList<>();
public int evaluateBoardPositions(int RecursionTurn) {
List<Point> availablePoints = getAvailableStates();
if (hasXWon()) {
return +1;
} else if (hasOWon()) {
return -1;
} else if (availablePoints.isEmpty()) {
return 0;
} else {
int minOrMax = -999;
List<Integer> scores = new ArrayList<>();
for (Point point : availablePoints) {
Board newBoard = new Board(this);
if (RecursionTurn == 1) {
newBoard.placeAMove(point, 1);
scores.add(newBoard.evaluateBoardPositions(2));
Collections.sort(scores, Collections.reverseOrder());
minOrMax = scores.get(0);
}
if (RecursionTurn == 2) {
newBoard.placeAMove(point, 2);
scores.add(newBoard.evaluateBoardPositions(1));
Collections.sort(scores);
minOrMax = scores.get(0);
}
pointsAndScores.add(new PointsAndScores(minOrMax, point));
}
return minOrMax;
}
}
public Point returnBestMove() {
int MAX = -100000;
int best = -1;
for (int i = 0; i < pointsAndScores.size(); ++i) {
if (MAX < pointsAndScores.get(i).score) {
MAX = pointsAndScores.get(i).score;
best = i;
}
}
return pointsAndScores.get(best).point;
}
Scanner scan = new Scanner(System.in);
void takeHumanInput() {
System.out.println("Your move: ");
int x = scan.nextInt();
int y = scan.nextInt();
Point point = new Point(x, y);
placeAMove(point, 2);
}
public void displayBoard() {
System.out.println();
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
System.out.print(board[i][j] + " ");
}
System.out.println();
}
}
void placeFirstMove() {
Random rand = new Random();
Point p = new Point(rand.nextInt(3), rand.nextInt(3));
placeAMove(p, 1);
}
public static void main(String[] args) {
Board b = new Board();
b.displayBoard();
System.out.println("Who's gonna move first? (1) You : (2) Me?");
Scanner scan = new Scanner(System.in);
int choice = scan.nextInt();
if (choice == 2) {
b.placeFirstMove();
b.displayBoard();
}
while (!b.isGameOver()) {
b.takeHumanInput();
if (b.isGameOver()) {
break;
}
b.displayBoard();
b.pointsAndScores.clear();
b.evaluateBoardPositions(1);
Point p = b.returnBestMove();
for (PointsAndScores pas : b.pointsAndScores) {
System.out.println("Score: " + pas.score + " Point: " + pas.point.x + " " + pas.point.y);
}
b.placeAMove(p, 1);
b.displayBoard();
}
if (b.hasXWon()) {
System.out.println("Unfortunately, you lost!");
} else if (b.hasOWon()) {
System.out.println("Never gets displayed. Computer Always wins or draws");
} else {
System.out.println("It's a draw!");
}
}
}