Tic Tac Toe

Requirements

  1. A simple Tic Tac Toe game for two players.

  2. A 3x3 board where players alternately place their pieces (X or O).

  3. The game ends when:

    • A player gets 3 of their pieces in a row, column, or diagonal.

    • The board is full, resulting in a draw.


Class Diagram

+------------------+
|      Player      |
+------------------+
| - name: String   |
| - piece: PieceType|
+------------------+
| + getName(): String   |
| + getPiece(): PieceType|
+------------------+

+------------------+
|   PlayingPiece   |
+------------------+
| - type: PieceType|
+------------------+
| + getType(): PieceType|
+------------------+

+------------------+
|   PlayingPieceO  |
+------------------+
| - type: O        |
+------------------+
| + getType(): PieceType|
+------------------+

+------------------+
|   PlayingPieceX  |
+------------------+
| - type: X        |
+------------------+
| + getType(): PieceType|
+------------------+

+------------------+
|      Board       |
+------------------+
| - grid: PieceType[][]|
+------------------+
| + placePiece(row: int, col: int, piece: PlayingPiece): boolean|
| + isFull(): boolean|
| + display(): void|
| + checkWin(row: int, col: int, piece: PieceType): boolean|
+------------------+

+------------------+
|       Game       |
+------------------+
| - board: Board   |
| - players: Player[]|
| - currentPlayer: int|
+------------------+
| + play(): void   |
| + switchPlayer(): void|
+------------------+

Implementation

1. Enum: PieceType

Defines the type of pieces (X, O, or EMPTY).

public enum PieceType {
    X,
    O,
    EMPTY
}

2. Abstract Class: PlayingPiece

Represents a generic playing piece.

public abstract class PlayingPiece {
    protected PieceType type;

    public PieceType getType() {
        return type;
    }
}

3. Concrete Classes: PlayingPieceO and PlayingPieceX

Define specific implementations for X and O.

public class PlayingPieceO extends PlayingPiece {
    public PlayingPieceO() {
        this.type = PieceType.O;
    }
}

public class PlayingPieceX extends PlayingPiece {
    public PlayingPieceX() {
        this.type = PieceType.X;
    }
}

4. Player Class

Represents a player with a name and their associated piece.

public class Player {
    private String name;
    private PieceType piece;

    public Player(String name, PieceType piece) {
        this.name = name;
        this.piece = piece;
    }

    public String getName() {
        return name;
    }

    public PieceType getPiece() {
        return piece;
    }
}

5. Board Class

Handles the game board and its operations.

public class Board {
    private PieceType[][] grid;

    public Board() {
        grid = new PieceType[3][3];
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                grid[i][j] = PieceType.EMPTY;
            }
        }
    }

    public boolean placePiece(int row, int col, PlayingPiece piece) {
        if (row < 0 || row >= 3 || col < 0 || col >= 3 || grid[row][col] != PieceType.EMPTY) {
            return false;
        }
        grid[row][col] = piece.getType();
        return true;
    }

    public boolean isFull() {
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                if (grid[i][j] == PieceType.EMPTY) {
                    return false;
                }
            }
        }
        return true;
    }

    public boolean checkWin(int row, int col, PieceType piece) {
        // Check row
        if (grid[row][0] == piece && grid[row][1] == piece && grid[row][2] == piece) {
            return true;
        }
        // Check column
        if (grid[0][col] == piece && grid[1][col] == piece && grid[2][col] == piece) {
            return true;
        }
        // Check diagonals
        if (row == col && grid[0][0] == piece && grid[1][1] == piece && grid[2][2] == piece) {
            return true;
        }
        if (row + col == 2 && grid[0][2] == piece && grid[1][1] == piece && grid[2][0] == piece) {
            return true;
        }
        return false;
    }

    public void display() {
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                System.out.print(grid[i][j] + " ");
            }
            System.out.println();
        }
    }
}

6. Game Class

Manages the overall gameplay and switching turns.

import java.util.Scanner;

public class Game {
    private Board board;
    private Player[] players;
    private int currentPlayer;

    public Game(Player player1, Player player2) {
        this.board = new Board();
        this.players = new Player[]{player1, player2};
        this.currentPlayer = 0;
    }

    public void play() {
        Scanner scanner = new Scanner(System.in);

        while (true) {
            board.display();
            Player player = players[currentPlayer];
            System.out.println(player.getName() + "'s turn (" + player.getPiece() + ")");
            System.out.println("Enter row and column (0, 1, 2):");

            int row = scanner.nextInt();
            int col = scanner.nextInt();

            if (!board.placePiece(row, col, createPlayingPiece(player.getPiece()))) {
                System.out.println("Invalid move. Try again.");
                continue;
            }

            if (board.checkWin(row, col, player.getPiece())) {
                board.display();
                System.out.println(player.getName() + " wins!");
                break;
            }

            if (board.isFull()) {
                board.display();
                System.out.println("The game is a draw!");
                break;
            }

            switchPlayer();
        }

        scanner.close();
    }

    private void switchPlayer() {
        currentPlayer = (currentPlayer + 1) % 2;
    }

    private PlayingPiece createPlayingPiece(PieceType type) {
        return type == PieceType.X ? new PlayingPieceX() : new PlayingPieceO();
    }
}

7. Main Class

The entry point for the application.

public class Main {
    public static void main(String[] args) {
        Player player1 = new Player("Alice", PieceType.X);
        Player player2 = new Player("Bob", PieceType.O);

        Game game = new Game(player1, player2);
        game.play();
    }
}

Execution Flow

  1. Start: The game initializes with a 3x3 board and two players.

  2. Gameplay:

    • Each player takes turns placing their piece on the board.

    • The system validates moves and checks for a win or draw after every move.

  3. End:

    • A player wins if they align three of their pieces.

    • The game ends in a draw if the board is full with no winner.


Output Example

Game Starts:

EMPTY EMPTY EMPTY 
EMPTY EMPTY EMPTY 
EMPTY EMPTY EMPTY 
Alice's turn (X)
Enter row and column (0, 1, 2):
0 0

Winning Condition:

X X X 
EMPTY EMPTY EMPTY 
O O EMPTY 
Alice wins!

Draw Condition:

X O X 
X O O 
O X O 
The game is a draw!

Last updated