/**
 * TicTacToePlayer.java
 *
 * @author Natalie Beckwith
 * @version 1
 */

public interface TicTacToePlayer
{ 
    void init (String marker); 
    String name (); 
    void move (); 
        
}
/**
 * TicTacToeThreeInARow.java
 *
 * @author Natalie Beckwith
 * @version 1
 */
 
public class TicTacToeThreeInARow
{
    private int _numRows;
    private int _numColumns;

    public TicTacToeThreeInARow(int numRows, int numColumns)
    {
        _numRows = numRows;
        _numColumns = numColumns;
    }
    
    public boolean isFourInARow(String[][] board)
    {
        boolean threeInARow1 = threeInARowHorizontal(board);
        boolean threeInARow2 = threeInARowVertical(board);
        boolean threeInARow3 = threeInARowDiagonal(board);

        return threeInARow1 || threeInARow2 || threeInARow3;
    }
    
    public boolean threeInARowHorizontal(String[][] board)
    {
        boolean threeInARow = false;

        for (int row = 0; row < _numRows; row++)
        {
            String token = board[row][0];

            if (!token.equals(" "))
            {
                if (board[row][1].equals(token) && board[row][2].equals(token))
                {
                    return true;
                }
            }
        }
        return threeInARow;
    }
    
    public boolean threeInARowVertical(String[][] board)
    {
        boolean threeInARow = false;

            for (int column = 0; column < _numColumns; column++)
            {
                String token = board[0][column];
                if (!token.equals(" "))
                {
                    if (board[0][column].equals(token) &&
                        board[1][column].equals(token) &&
                        board[2][column].equals(token))
                    {
                        return true;
                    }
                }
            }
        
        return threeInARow;
    }
    
    public boolean threeInARowDiagonal(String[][] board)
    {
        boolean threeInARow = false;

        String token = board[0][0];
        if (!token.equals(" "))
        {
            if (board[1][1].equals(token) &&
                board[2][2].equals(token))
            {
                return true;
            }
        }
         token = board[0][2];
        if (!token.equals(" "))
        {
            if (board[1][1].equals(token) &&
                board[2][0].equals(token))
            {
                return true;
            }
        }
                
        return threeInARow;
    }
}
/**
 * TicTacToeBoard.java
 *
 * @author Natalie Beckwith
 * @version 1
 */

public class TicTacToeBoard
{
    public static final int NUM_COLUMNS = 3;
    public static final int NUM_ROWS = 3;
    private String board[][];
    
    public static final int PLAYER1 = 1;
    public static final int PLAYER2 = 0;
    
    private TicTacToeThreeInARow _threeInARow = new TicTacToeThreeInARow(NUM_ROWS, NUM_COLUMNS);

    
    public TicTacToeBoard()
    {
        board = new String [NUM_ROWS][NUM_COLUMNS];
        
        for (int row = 0; row < NUM_ROWS; row++)
        {
            for (int column = 0; column < NUM_COLUMNS; column++)
            {
                board[row][column] = new String(" ");
            }
        }
    }
    
    public String[][] getBoard()
    {
        return board;
    }
    
    
    public boolean makeMove(int row, int column, String marker)
    {
        if (!board[row][column].equals(" "))
        {
            return false;
        }
        board[row][column] = marker;
        return true;    
    }
    
    
    public boolean isThreeInARow()
    {
        return _threeInARow.isFourInARow(board);
    }
    
}
/**
 * TicTacToeComputer.java
 *
 * @author Natalie Beckwith
 * @version 1
 */
 
import java.lang.Math;
import java.util.Scanner;

public class TicTacToeComputer implements TicTacToePlayer
{
    private String _name;
    private String _marker;
    private TicTacToeBoard _board;

    public TicTacToeComputer(String name,
                            int numColumns,
                            int numRows,
                            TicTacToeBoard board) 
    {
        _name = name;
        _board = board;
        _numColumns = numColumns;
        _numRows = numRows;
    }


    @Override
    public void init(String marker)
    {
        _marker = marker;
    }

    @Override
    public String name()
    {
        return _name;
    }


    @Override
    public void move()
    {
        boolean goodMove = false;

        int randomRow;
        int randomColumn;
        
        do
        {
            randomRow = randomNum(3);
            randomColumn = randomNum(3);
     
            goodMove = _board.makeMove(randomRow, randomColumn, _marker);
            
        }
        while (!goodMove);
        
        System.out.printf("\n%s entered a row and column: %d %d\n",
                _name,
                randomRow,
                randomColumn);
    }
    
    public static int randomNum(int max)
    {
        double range = Math.random();
        
        range *= 3.0;
        int num = (int) Math.floor(range);
        
        return num;
        
    }

}
/**
 * TicTacToeHuman.java
 *
 * @author Natalie Beckwith
 * @version 1
 */
 
import java.util.InputMismatchException;
import java.util.Scanner;

public class TicTacToeHuman implements TicTacToePlayer
{
    private String _name;
    private String _marker;
    private TicTacToeBoard _board;
    private Scanner _input;
    
    
    public TicTacToeHuman(String name, TicTacToeBoard board)
    {
        _name = name;
        _input = new Scanner(System.in);
        _board = board;
        

    }

    @Override
    public void init(String marker)
    {
        _marker = marker;
    }

    @Override
    public String name()
    {
        return _name;
    }

    @Override
    public void move()
    {
        boolean goodMove = false;
        do
        {
            try
            {
                System.out.printf("\n%s, enter a row and column: ", _name);

                int row = _input.nextInt();
                int column = _input.nextInt();

                goodMove = _board.makeMove(row, column, _marker);
                
                if (!goodMove)
                {
                    System.out.printf("%s, invalid move. Try again:", _name);
                }
            }
            catch (InputMismatchException e)
            {
                System.out.printf("\n%s, invalid entry. Try separating your move with spaces:\n", _name);
                if (_input.hasNextLine())
                {
                    _input.nextLine();
                }
            }
        }
        while (!goodMove);

    }
}
/**
 * TicTacToePlay.java
 *
 * @author Natalie Beckwith
 * @version 1
 */

public class TicTacToePlay
{
    
    public void play(int mode)
    {
        TicTacToeBoard board = new TicTacToeBoard();
                
        TicTacToePlayer[] players = new TicTacToePlayer[2];

        TicTacToePlayer human = new TicTacToeHuman("Player #1", board);
        TicTacToePlayer human2 = new TicTacToeHuman("Player #2", board);

        TicTacToePlayer computer1 = new TicTacToeComputer("Computer #1",
                TicTacToeBoard.NUM_COLUMNS, TicTacToeBoard.NUM_ROWS, board);
        TicTacToePlayer computer2 = new TicTacToeComputer("Computer #2",
                TicTacToeBoard.NUM_COLUMNS, TicTacToeBoard.NUM_ROWS, board);
        
        if(mode == 1)
        {
            players[0] = human;
            players[1] = computer2;
        }
        
        if(mode == 2)
        {
            players[0] = human;
            players[1] = human2;
        }
        
        if(mode == 3)
        {
            players[0] = computer1;
            players[1] = computer2;
        }
        
        players[0].init("X");
        players[1].init("O");
        
        boolean gameWon = false;
        
        for (int loop = 0; loop < 9; loop++)
        {
            int playerNum = loop % 2;
            gameWon = playerTakeTurn(players[playerNum], board);
            
            if(gameWon)
            {
                break;
            }

        }
        if ((!gameWon))
        {
            System.out.printf("\nDraw!\n");
            printBoard(board);
        }
        
        
    }
    
    public static boolean playerTakeTurn(TicTacToePlayer player, TicTacToeBoard board)
    {
        printBoard(board);
        player.move();
        boolean gameWon = board.isThreeInARow();
        
        if (gameWon)
        {
            System.out.printf("\n%s Wins!\n", player.name());
            printBoard(board);
        }
    
        return gameWon;
    }
    
    public static void printBoard(TicTacToeBoard board)
    {
        String[][] gameBoard = board.getBoard();
        
        System.out.printf("\n\n    BOARD\n");
        System.out.printf("-------------\n");
        for (int row = 0; row < TicTacToeBoard.NUM_ROWS; row++)
        {
            System.out.printf("%d ", row);
            for (int column = 0; column < TicTacToeBoard.NUM_COLUMNS; column++)
            {
                System.out.printf("[%s] ", gameBoard[row][column]);
            }
            System.out.printf("\n");
        }
        
        System.out.printf("   0   1   2\n");
        System.out.printf("-------------\n\n");
    }
}
/**
 * Menu.java
 *
 * @author Natalie Beckwith
 * @version 1
 */

import java.util.Scanner;

public class Menu
{
    Scanner sc = new Scanner(System.in);

    public void menuGo()
    {
        boolean quit = false;
        do
        {
            try
            {
                print();
                int choice = sc.nextInt();
                System.out.print("" + choice + ": ");
                quit = this.action(choice);
            }
            catch (Exception e)
            {
                sc.nextLine();
                System.out.println(e + ": Not a number, try again.");
            }
        }
        while (!quit);

    }

    private void print()
    {
        System.out.println("---------------------------------------");
        System.out.println("How would you like to play Tic-Tac-Toe?");
        System.out.println("---------------------------------------");
        System.out.println("1 - Human VS Computer");
        System.out.println("2 - Human VS Human");
        System.out.println("3 - Computer VS Computer");
        System.out.println("0 - Quit");
        System.out.println("---------------------------------------\n");
    }

    private boolean action(int selection)
    {
        boolean quit = false;

        TicTacToePlay ticTacToePlay = new TicTacToePlay();

        switch (selection)
        {
            case 0:
                System.out.print("Goodbye, World!");
                quit = true;
                break;
            case 1:
                System.out.print("Human VS Computer\n");
                ticTacToePlay.play(1);
                break;
            case 2:
                System.out.print("Human VS Human\n");
                ticTacToePlay.play(2);
                break;
            case 3:
                System.out.print("Computer VS Computer\n");
                ticTacToePlay.play(3);
                break;

            default:
                System.out.print("Unexpected choice, try again.");
        }
        return quit;
    }

    static public void main(String[] args)
    {

    }
}
Menu menu = new Menu();
menu.menuGo();
---------------------------------------
How would you like to play Tic-Tac-Toe?
---------------------------------------
1 - Human VS Computer
2 - Human VS Human
3 - Computer VS Computer
0 - Quit
---------------------------------------

3: Computer VS Computer
jdk.jshell.spi.SPIResolutionException: resolution exception: Not a number, try again.
---------------------------------------
How would you like to play Tic-Tac-Toe?
---------------------------------------
1 - Human VS Computer
2 - Human VS Human
3 - Computer VS Computer
0 - Quit
---------------------------------------

1: Human VS Computer
jdk.jshell.spi.SPIResolutionException: resolution exception: Not a number, try again.
---------------------------------------
How would you like to play Tic-Tac-Toe?
---------------------------------------
1 - Human VS Computer
2 - Human VS Human
3 - Computer VS Computer
0 - Quit
---------------------------------------

|   menu.menuGo();
Evaluation interrupted.

Documentation / Analysis:

  • A class is defined inside of a Java file. Every file has one class.
  • Instances of the Menu Class are instantiated inside its constructor.
  • An object can call a method by using the . operator. This is equivalent to a pointer in java. For example, in System.out.println() the System Class calls the out method and the println method.
    • Another example is _board.makeMove(row, column, _marker); The board calls a method in order for the player to make a move
  • A mutator variable or also commonly known as a setter, assigns a value to a variable. For example, in public final String DEFAULT = "\u001B[0m"; the constant String DEFAULT is assigned a value of "\u001B[0m".
    • Another example is in _board = board; The variable _board is declared as type TicTacToeBoard and then instantiated inside the constructor of the TicTacToeBoard Class.
  • In the Console Menu, there are a lot of try/catch blocks to carry out the methods in the menu. The GUI Menu implements the ActionListener Class and also extends the JFrame Class in order to make the menu.