w3resource

Tic-Tac-Toe Game in Java: Two-Player Console-Based Project


Introduction to the Java Tic-Tac-Toe Game:

Tic-Tac-Toe Game:

Create a simple two-player console-based game.

A console-based version of the classic two-player game. Players take turns marking spaces on a 3x3 grid, and the first player to get three marks in a row (horizontally, vertically, or diagonally) wins.

Input: Player move (row and column) on a 3x3 grid.
Output: Updated game board and game status (win/draw/continue).

Example:

  • Input: Player 1 selects row 1, column 2
  • Output: Updated grid, "Player 1 wins" or "Continue playing"

Solution 1: Tic-Tac-Toe with Simple Array and Loops

Code:

import java.util.Scanner;

public class TicTacToeSimple {
    // Declare a 3x3 array to represent the game board
    static char[][] board = new char[3][3];

    public static void main(String[] args) {
        // Initialize the board with empty spaces
        initializeBoard();

        // Variable to keep track of the current player ('X' or 'O')
        char currentPlayer = 'X';

        // Variable to check if the game is won or drawn
        boolean gameWon = false;
        boolean draw = false;

        // Create a Scanner object for input
        Scanner scanner = new Scanner(System.in);

        // Main game loop, runs until a player wins or it's a draw
        while (!gameWon && !draw) {
            printBoard();  // Display the board
            System.out.println("Player " + currentPlayer + "'s turn. Enter row and column (1-3):");

            // Get player input for row and column
            int row = scanner.nextInt() - 1;
            int col = scanner.nextInt() - 1;

            // Check if the selected cell is empty
            if (board[row][col] == ' ') {
                // Place the current player's mark on the board
                board[row][col] = currentPlayer;

                // Check if the current player has won the game
                gameWon = checkWin(currentPlayer);

                if (!gameWon) {
                    // Check if the board is full (draw)
                    draw = checkDraw();

                    // Switch the player if the game isn't won
                    currentPlayer = (currentPlayer == 'X') ? 'O' : 'X';
                }
            } else {
                System.out.println("Cell is already occupied! Try again.");
            }
        }

        // Display the final board
        printBoard();

        // Display game result
        if (gameWon) {
            System.out.println("Player " + currentPlayer + " wins!");
        } else {
            System.out.println("It's a draw!");
        }

        // Close the scanner
        scanner.close();
    }

    // Function to initialize the board with empty spaces
    public static void initializeBoard() {
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                board[i][j] = ' ';
            }
        }
    }

    // Function to print the current state of the board
    public static void printBoard() {
        System.out.println("  1 2 3");
        for (int i = 0; i < 3; i++) {
            System.out.print((i + 1) + " ");
            for (int j = 0; j < 3; j++) {
                System.out.print(board[i][j]);
                if (j < 2) System.out.print("|");
            }
            System.out.println();
            if (i < 2) System.out.println("  -----");
        }
    }

    // Function to check if the current player has won
    public static boolean checkWin(char player) {
        // Check rows, columns, and diagonals
        for (int i = 0; i < 3; i++) {
            if (board[i][0] == player && board[i][1] == player && board[i][2] == player)
                return true;
            if (board[0][i] == player && board[1][i] == player && board[2][i] == player)
                return true;
        }
        if (board[0][0] == player && board[1][1] == player && board[2][2] == player)
            return true;
        if (board[0][2] == player && board[1][1] == player && board[2][0] == player)
            return true;
        return false;
    }

    // Function to check if the board is full (indicating a draw)
    public static boolean checkDraw() {
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                if (board[i][j] == ' ') {
                    return false;  // If any cell is empty, it's not a draw
                }
            }
        }
        return true;  // If no empty cells, it's a draw
    }
}   

Output:

1 2 3
1  | | 
  -----
2  | | 
  -----
3  | | 
Player X's turn. Enter row and column (1-3):
 1
 1
  1 2 3
1 X| | 
  -----
2  | | 
  -----
3  | | 
Player O's turn. Enter row and column (1-3):
 1
 2
  1 2 3
1 X|O| 
  -----
2  | | 
  -----
3  | | 
Player X's turn. Enter row and column (1-3):
 3
 3
  1 2 3
1 X|O| 
  -----
2  | | 
  -----
3  | |X
Player O's turn. Enter row and column (1-3):
 3
 2
  1 2 3
1 X|O| 
  -----
2  | | 
  -----
3  |O|X
Player X's turn. Enter row and column (1-3):
 2
 2
  1 2 3
1 X|O| 
  -----
2  |X| 
  -----
3  |O|X
Player X wins!

Explanation :

  • Game Board: The board is represented by a 2D array, initialized with empty spaces.
  • Player Input: Players input the row and column to place their mark.
  • Win/Draw Conditions: The game checks for a win by rows, columns, or diagonals. It also checks if all spaces are filled (indicating a draw).
  • Switching Players: The game alternates between 'X' and 'O'.
  • Loop: The game loop runs until either a player wins or the game ends in a draw.

Solution 2: Using Object-Oriented Approach

Key points:

  • Game Board Initialization: The board is initialized as a 3x3 grid, filled with empty spaces.
  • Player Moves: The user inputs row and column values, and the game updates the board accordingly if the input is valid.
  • Win Condition: After every move, the game checks if the current player has completed a row, column, or diagonal.
  • Draw Condition: If the board is full and no one wins, the game is a draw.
  • Switching Players: After every valid move, the current player switches between 'X' and 'O'.
  • Game Restart: After a game ends, players can choose to restart the game with an empty board or end the session.
  • Statistics: The game keeps track of how many games each player has won and the number of draws.
  • Input Validation: Invalid inputs (e.g., selecting already occupied spots or entering out-of-bound values) are handled, and the player is prompted to re-enter their move.

Code:

TicTacToe.java

import java.util.Scanner;

class TicTacToe {
    private char[][] board;
    private char currentPlayer;
    private int playerXWins;
    private int playerOWins;
    private int draws;

    // Constructor to initialize the board and set the starting player
    public TicTacToe() {
        board = new char[3][3];
        currentPlayer = 'X';
        initializeBoard();
        playerXWins = 0;  // Track Player X wins
        playerOWins = 0;  // Track Player O wins
        draws = 0;        // Track draws
    }

    // Initialize the board with empty spaces
    private void initializeBoard() {
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                board[i][j] = ' ';
            }
        }
    }

    // Display the current state of the board
    public void printBoard() {
        System.out.println("  1 2 3");
        for (int i = 0; i < 3; i++) {
            System.out.print((i + 1) + " ");
            for (int j = 0; j < 3; j++) {
                System.out.print(board[i][j]);
                if (j < 2) System.out.print("|");
            }
            System.out.println();
            if (i < 2) System.out.println("  -----");
        }
    }

    // Switch the current player between 'X' and 'O'
    public void switchPlayer() {
        currentPlayer = (currentPlayer == 'X') ? 'O' : 'X';
    }

    // Handle player input and update the board
    public boolean makeMove(int row, int col) {
        if (row >= 0 && row < 3 && col >= 0 && col < 3 && board[row][col] == ' ') {
            board[row][col] = currentPlayer;
            return true;
        }
        return false;
    }

    // Check if the current player has won the game
    public boolean checkWin() {
        for (int i = 0; i < 3; i++) {
            if (board[i][0] == currentPlayer && board[i][1] == currentPlayer && board[i][2] == currentPlayer)
                return true;
            if (board[0][i] == currentPlayer && board[1][i] == currentPlayer && board[2][i] == currentPlayer)
                return true;
        }
        if (board[0][0] == currentPlayer && board[1][1] == currentPlayer && board[2][2] == currentPlayer)
            return true;
        if (board[0][2] == currentPlayer && board[1][1] == currentPlayer && board[2][0] == currentPlayer)
            return true;
        return false;
    }

    // Check if the board is full (indicating a draw)
    public boolean checkDraw() {
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                if (board[i][j] == ' ') {
                    return false;
                }
            }
        }
        return true;
    }

    // Get the current player
    public char getCurrentPlayer() {
        return currentPlayer;
    }

    // Update the win count for the current player
    public void updateWinCount() {
        if (currentPlayer == 'X') {
            playerXWins++;
        } else {
            playerOWins++;
        }
    }

    // Increment the draw count
    public void updateDrawCount() {
        draws++;
    }

    // Display the game statistics
    public void displayStatistics() {
        System.out.println("Player X Wins: " + playerXWins);
        System.out.println("Player O Wins: " + playerOWins);
        System.out.println("Draws: " + draws);
    }

    // Reset the board for a new game
    public void resetBoard() {
        initializeBoard();
        currentPlayer = 'X';  // Player X always starts the new game
    }
} 

Explanation:

  • Class Definition:
    • The TicTacToe class manages the game's logic, including the board, player turns, win checks, and statistics.
  • Fields:
    • board: A 3x3 char array representing the game board.
    • currentPlayer: Tracks the current player ('X' or 'O').
    • playerXWins, playerOWins, draws: Integers to track the number of wins for each player and the number of draws.
  • Constructor:
    • Initializes the board, sets the starting player to 'X', and resets the win/draw counters.
  • initializeBoard():
    • Fills the board with empty spaces (' ') to prepare it for gameplay.
  • printBoard():
    • Displays the current state of the board in a 3x3 grid format with row and column numbers for easy reference.
  • switchPlayer():
    • Switches the current player between 'X' and 'O' after each turn.
  • makeMove(int row, int col):
    • Places the current player's mark on the specified row and column, if the spot is valid and unoccupied.
    • Returns true if the move is valid, otherwise false.
  • checkWin():
    • Checks all possible winning conditions (rows, columns, and diagonals) to determine if the current player has won.
    • Returns true if there is a win, otherwise false.
  • checkDraw():
    • Checks if the board is full and no moves are possible, indicating a draw.
    • Returns true if the game is a draw, otherwise false.
  • getCurrentPlayer():
    • Returns the current player ('X' or 'O').
  • updateWinCount():
    • Increments the win count for the current player (either playerXWins or playerOWins).
  • updateDrawCount():
    • Increments the draw count (draws).
  • displayStatistics():
    • Displays the current statistics: number of wins for Player X, Player O, and the number of draws.
  • resetBoard():
    • Resets the board for a new game, clearing all spaces and setting the starting player to 'X'.

TicTacToeOOP.java

import java.util.Scanner; 
public class TicTacToeOOP {
    public static void main(String[] args) {
        TicTacToe game = new TicTacToe(); // Create a new TicTacToe game object
        Scanner scanner = new Scanner(System.in);
        boolean playAgain = true;

        // Main game loop (for multiple games)
        while (playAgain) {
            boolean gameWon = false;
            boolean draw = false;

            // Game loop for a single game
            while (!gameWon && !draw) {
                game.printBoard();  // Display the game board
                System.out.println("Player " + game.getCurrentPlayer() + "'s turn. Enter row and column (1-3):");

                // Get player input for row and column
                int row = scanner.nextInt() - 1;
                int col = scanner.nextInt() - 1;

                // Make the move and check for validity
                if (game.makeMove(row, col)) {
                    gameWon = game.checkWin();
                    if (!gameWon) {
                        draw = game.checkDraw();
                        if (!draw) {
                            game.switchPlayer();  // Switch player if game isn't won
                        } else {
                            System.out.println("It's a draw!");
                            game.updateDrawCount();  // Increment the draw count
                        }
                    } else {
                        game.printBoard();
                        System.out.println("Player " + game.getCurrentPlayer() + " wins!");
                        game.updateWinCount();  // Increment win count for the current player
                    }
                } else {
                    System.out.println("Invalid move. Try again.");
                }
            }

            // Display the game statistics
            game.displayStatistics();

            // Ask if the players want to play again
            System.out.println("Do you want to play again? (yes/no):");
            String response = scanner.next();

            if (response.equalsIgnoreCase("yes")) {
                game.resetBoard();  // Reset the board for a new game
            } else {
                playAgain = false;  // Exit the game loop
            }
        }

        System.out.println("Thanks for playing!");
        scanner.close();
    }
} 

Explanation:

  • Imports:
    • java.util.Scanner: Used to capture user input from the console.
  • main() Method:
    • The entry point of the program where the game logic is executed.
  • Game Initialization:
    • Creates a TicTacToe object named game.
    • Initializes a Scanner object to read user input.
  • Main Game Loop (while (playAgain)):
    • Controls whether the players want to play another game.
    • Runs continuously until the players choose not to play again.
  • Single Game Loop:
    • Repeats until either a player wins or the game ends in a draw.
    • Calls game.printBoard() to display the current state of the board.
    • Prompts the current player to enter row and column coordinates for their move.
  • Input Handling:
    • Player enters row and column (1-3), which are converted to 0-indexed values.
    • Checks if the move is valid using game.makeMove().
  • Move Validation:
    • If the move is valid:
      • Calls game.checkWin() to see if the current player has won.
      • If no one has won, calls game.checkDraw() to see if the game is a draw.
      • If the game is ongoing, switches the player using game.switchPlayer().
  • Winning Condition:
    • If a player wins, the board is displayed, and the current player is declared the winner.
    • Increments the win count for the winner using game.updateWinCount().
  • Draw Condition:
    • If the game is a draw, displays a message and increments the draw count with game.updateDrawCount().
  • Display Statistics:
    • After each game, game.displayStatistics() shows the win and draw counts.
  • Play Again Prompt:
    • Asks the players if they want to play another game.
    • If they answer "yes", the board is reset using game.resetBoard().
    • If "no", the loop ends, and the game concludes.
  • Game Conclusion:
    • Displays a thank-you message and closes the Scanner object.

Output:

1 2 3
1  | |
  -----
2  | |
  -----
3  | |
Player X's turn. Enter row and column (1-3):
1
1
  1 2 3
1 X| |
  -----
2  | |
  -----
3  | |
Player O's turn. Enter row and column (1-3):
1
3
  1 2 3
1 X| |O
  -----
2  | |
  -----
3  | |
Player X's turn. Enter row and column (1-3):
2
1
  1 2 3
1 X| |O
  -----
2 X| |
  -----
3  | |
Player O's turn. Enter row and column (1-3):
3
1
  1 2 3
1 X| |O
  -----
2 X| |
  -----
3 O| |
Player X's turn. Enter row and column (1-3):
2
2
  1 2 3
1 X| |O
  -----
2 X|X|
  -----
3 O| |
Player O's turn. Enter row and column (1-3):
2
3
  1 2 3
1 X| |O
  -----
2 X|X|O
  -----
3 O| |
Player X's turn. Enter row and column (1-3):
1
2
  1 2 3
1 X|X|O
  -----
2 X|X|O
  -----
3 O| |
Player O's turn. Enter row and column (1-3):
3
3
  1 2 3
1 X|X|O
  -----
2 X|X|O
  -----
3 O| |O
Player O wins!
Player X Wins: 0
Player O Wins: 1
Draws: 0
Do you want to play again? (yes/no):
no
Thanks for playing!

Java Code Editor:




Become a Patron!

Follow us on Facebook and Twitter for latest update.

It will be nice if you may share this link in any developer community or anywhere else, from where other developers may find this content. Thanks.

https://w3resource.com/projects/java/java-project-tic-tac-toe-game.php