3
\$\begingroup\$

I have created a logical AI for a Tic-Tac-Toe game. I want a review about how good or bad it is. Here the computer AI will defend all your attacks as well as try to attack whenever it gets a chance.

import java.util.Random;
    import java.util.Scanner;

    public class TicTac {
        static {
            System.out.println("\t\t\t\t\t\t\t\t\t\t\t\t\t  ________________________    _______________________");
            System.out.println("\t\t\t\t\t\t\t\t\t\t\t\t\t |___  ___|___  ___|  ____|  |___  ___|   _   |  ____|");
            System.out.println("\t\t\t\t\t\t\t\t\t\t\t\t\t    |  |     |  |  |  |   ____  |  |  |  |_|  |  |");
            System.out.println("\t\t\t\t\t\t\t\t\t\t\t\t\t    |  |     |  |  |  |  |____| |  |  |   _   |  |");
            System.out.println("\t\t\t\t\t\t\t\t\t\t\t\t\t    |  |   __|  |__|  |___      |  |  |  | |  |  |___");
            System.out.println("\t\t\t\t\t\t\t\t\t\t\t\t\t    |__|  |________|______|     |__|  |__| |__|______|");
            System.out.println("\n\n");
        }

        private Scanner sc = new Scanner(System.in);
        private char board[] = new char[9];
        private char turn = 'X';
        private Random random = new Random();
        private int winX = 0;
        private int winO = 0;
        private String name;
        private char myChoice = 'O';

        public static void main(String[] args) {
            TicTac t = new TicTac();
            int numberOfGames = 1;
            long start, end;
            start = System.currentTimeMillis();
            do {
                t.play();
                numberOfGames--;
                System.out.println("\t\t\t\t\t\t\t\t\t\t\t----------------------------------------------------------------");
            } while (numberOfGames > 0);
            end = System.currentTimeMillis();
            System.out.println("\n\t\t\t\t\t\t\t\t\t\t\tTime taken by the program to execute is : " + (end - start));
        }

        private void playersTurn() {
            int r;
            boolean check = false;
            do {
                System.out.println("\n\n\t\t\t\t\t\t\t\twhere do you wnt to play , select from 1 - 9 :");
                try {
                    r = getChoice();
                    if (!(r > 0 && r < 10)) {
                        Thread.sleep(1000);
                        throw new NumberFormatException("Invalid Number");
                    }
                    r -= 1;
                    if (board[r] == ' ') {
                        board[r] = turn;
                        check = true;
                    } else if (board[r] == myChoice) {
                        System.out.println("you already played there");
                    } else {
                        System.out.println("computer played there");
                    }
                } catch (Exception e) {
                    System.out.println("invalid choice. \t " + e);
                }
            } while (!check);
        }

        private int getChoice() {
            String x = "1";

            try {
                x = sc.nextLine();
                if (x.matches("^[1-9]$")) {

                } else {

                    throw new NumberFormatException("Invalid Number");
                }
            } catch (Exception e) {
                System.out.println(e.getMessage());
            }
            return Integer.parseInt(x);
        }

        private void initializeBoard() {
            for (int i = 0; i < 9; i++) {
                board[i] = ' ';
            }
        }

        private void switchPlayer() {
            if (turn == 'X') {
                turn = 'O';
            } else if (turn == 'O') {
                turn = 'X';
            }
        }

        private boolean win() {
            for (int i = 0; i < 9; i++) {
                if (board[i] == ' ') {
                    board[i] = turn;
                    if (checkWin()) {
                        return true;
                    } else {
                        board[i] = ' ';
                    }
                }
            }
            return false;
        }

        private void attack() {
            boolean check = false;
            do {
                int r = random.nextInt(9);
                if (r == 1 || r == 3 || r == 5 || r == 7 || r != 4) {
                    r = random.nextInt(9);
                }
                if (board[r] == ' ') {
                    board[r] = turn;
                    check = true;
                }
            } while (!check);
        }

        private boolean defence() {
            switchPlayer();
            for (int i = 0; i < 9; i++) {
                if (board[i] == ' ') {
                    board[i] = turn;
                    if (checkWin()) {
                        switchPlayer();
                        board[i] = turn;
                        return true;
                    } else {
                        board[i] = ' ';
                    }
                }
            }
            switchPlayer();
            return false;
        }

        private boolean filled() {
            for (int i = 0; i < 9; i++) {
                if (board[i] == ' ') {
                    return false;
                }
            }
            return true;
        }

        private void play() {
            initializeBoard();
            do {
                if (win()) {
                    switchPlayer();
                } else if (defence()) {
                    switchPlayer();
                } else {
                    attack();
                    switchPlayer();
                }
                display();
                if (turn == myChoice && !checkWin() && !filled()) {
                    playersTurn();
                    switchPlayer();
                    display();
                }
                /*try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }*/
                System.out.println("\n");
            } while (!filled() && !checkWin());
            if (checkWin()) {
                switchPlayer();
                if (turn == 'O') {
                    winO++;
                } else {
                    winX++;
                }
                System.out.println();
                display();
                System.out.println("\n");
                System.out.println("\t\t\t\t\t\t\t\t\t\t\t  " + turn + "  has won");
            } else {
                System.out.println();
                display();
                System.out.println("\n");
                System.out.println("\t\t\t\t\t\t\t\t\t\t\t   game is draw... ");
            }
        }

        private boolean checkWin() {
            return (board[0] == board[1] && board[1] == board[2] && board[0] != ' ' || board[3] == board[4] && board[4] == board[5] && board[3] != ' ' || board[6] == board[7] && board[7] == board[8] && board[6] != ' ' || board[0] == board[4] && board[4] == board[8] && board[0] != ' ' || board[2] == board[4] && board[4] == board[6] && board[2] != ' ' || board[0] == board[3] && board[3] == board[6] && board[0] != ' ' || board[1] == board[4] && board[4] == board[7] && board[1] != ' ' || board[2] == board[5] && board[5] == board[8] && board[2] != ' ');
        }

        private void display() {

            System.out.println("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t " + board[0] + " | " + board[1] + " | " + board[2] + "           PLAYER 'O' | CPU  'X'");
            System.out.println("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t---|---|---            ---------|---------");
            System.out.println("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t " + board[3] + " | " + board[4] + " | " + board[5] + "                      |");
            System.out.println("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t---|---|---" + "\t   \t\t\t\t" + winO + "\t|\t" + winX);
            System.out.println("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t " + board[6] + " | " + board[7] + " | " + board[8] + "                      |");
        }
    }
\$\endgroup\$

2 Answers 2

4
\$\begingroup\$

Nice work. I got pretty distracted playing a few games before starting the review.

Your 'Tic-Tac' display should be put inside a method, and called on the constructor. This way you're explicitly stating when the title will be printed, rather than leaving it up to the JVM. (The static block will be executed the first time the class is referenced).

I believe you should use a for-loop instead of a while loop;

for (int i = 0; i < numberOfGames; i++)

You should declare a variable for all of your whitespace. This will make it easier to edit/maintain:

private static final WHITESPACE = "\t\t\t\t\t\t\t\t\t\t\t\t\t";

It's very rare for one character long variable names to be considered acceptable. 'r' is a bad variable name for the users choice.

'check' should also be renamed to 'validChoice' or similar.

If you need to initialize a variable, set it to null, not "1".

Rename 'x' to something more meaningful, such as 'userChoice'.

Get rid of the empty if statement E.G:

if (!x.matches("^[1-9]$")) {
    throw new NumberFormatException("Invalid Number");
}

Actually, you don't need to throw an exception. You should use Exceptions for exceptional cases and avoid them when possible. Throwing / catching exceptions is also very slow.

Your if statement could be made more clear by changing it from:

if (!(r > 0 && r < 10)) {

to:

if (r <= 0 || r >= 10) {

Your switchPlayer method can be simplified to use a ternary E.G:

turn = turn == 'X' ? 'O' : 'X'

This if statement can be simplified from:

if (win()) {
    switchPlayer();
} else if (defence()) {
    switchPlayer();
} else {
    attack();
    switchPlayer();
}

to:

if (!win() && !defence()) {
    attack();
}
switchPlayer();

Your 'checkWin' method could be broken down. For example, if you had a list of possibilities for wins such as: {0, 1, 2}, {3, 4, 5} etc.

Feature consideration:

You could add numbers indicating the location of the squares. For example:

 X | O | 3 
---|---|---
 4 | 5 | 6
---|---|---
 7 | 8 | X 
\$\endgroup\$
0
2
\$\begingroup\$

getChoice can be optimized as such. My code uses Character input as opposed to a string value. In the previous case, user can enter a String which is a costly operation and the previous code has an empty if block, which is not good coding practice.

private int getChoice() {
    char x = '1';

    try {
        x = sc.next().charAt(0);
        if(x < 48 || x > 57) {
            throw new NumberFormatException("Invalid Number");
        }
    } catch (Exception e) {
        System.out.println(e.getMessage());
    }
    return Character.getNumericValue(x); 
}
\$\endgroup\$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.