Click here to Skip to main content
15,890,438 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
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 .

Java
import java.util.*;

class Point {

    int x, y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

public class Board {

    //Copy constructor
    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;

    //Get the playable locations available on the board
    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() {
        //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 void placeAMove(Point point, int player) {
        board[point.x][point.y] = player;   //player = 1 for X, 2 for O
    }

    class PointsAndScores {

        int score;
        Point point;

        PointsAndScores(int score, Point point) {
            this.score = score;
            this.point = point;
        }
    }

    List<PointsAndScores> pointsAndScores = new ArrayList<>();

//The actual MinMax Logic is in this function
    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;
        }
    }

    //Return the move with the highest score
    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!");
        }
    }
}
Posted
Updated 27-Aug-14 3:56am
v3

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900