w3resource

Python Two-Player Tic-Tac-Toe Project - Solutions and Explanations


Tic-Tac-Toe Game:

Implement a two-player tic-tac-toe game.

From Wikipedia - Tic-tac-toe (American English), noughts and crosses (Commonwealth English), or Xs and Os (Canadian or Irish English) is a paper-and-pencil game for two players who take turns marking the spaces in a three-by-three grid with X or O. The player who succeeds in placing three of their marks in a horizontal, vertical, or diagonal row is the winner. It is a solved game, with a forced draw assuming best play from both players

Input values:
Players input their moves by specifying the row and column for placing their 'X' or 'O'.

Output value:
Current state of the tic-tac-toe board and feedback on the game state (e.g., win, draw).

Example:

Input values:
Player 1, enter your move (row and column): 1, 1 
Output value:
Current state:
| X |  |  |
-------------
|   |   |  |
-------------
|   |   |   |
Input values:
Player 2, enter your move (row and column): 2, 2
Output value:
Current state:
| X |  |  |
-------------
|   | 0 |  |
-------------
|   |   |   |
Input values:
Player 1, enter your move (row and column): 1, 2
Output value:
Current state:
| X | X |  |
-------------
|  | 0 |  |
-------------
|   |   |   |
Input values:
Player 2, enter your move (row and column): 2, 1
Output value:
Current state:
X | X |  |
-------------
O | O |  |
-------------
|   |   |   |
Input values:
Player 1, enter your move (row and column): 1, 3
Output value:
Current state:
| X | X | X |
---------------
| 0 | 0 |    |
---------------
|    |   |    |
Output value:
Player 1 wins!

Here are two different solutions for a two-player Tic-Tac-Toe game in Python. The game will allow two players to input their moves by specifying the row and column, and it will provide feedback on the game's current state and the outcome (win, draw, or continue).

Solution 1: Basic Approach Using Functions and Lists

Code:

# Solution 1: Basic Approach Using Functions and Lists

# Initialize the Tic-Tac-Toe board as a 3x3 matrix with empty strings
board = [[' ' for _ in range(3)] for _ in range(3)]

def print_board():
    """Print the current state of the Tic-Tac-Toe board."""
    print("\nCurrent state:")
    for row in range(3):
        print("|".join(board[row]))
        if row < 2:
            print("-----")

def check_winner(player):
    """Check if the given player has won the game."""
    # Check rows and columns
    for i in range(3):
        if all(board[i][j] == player for j in range(3)) or all(board[j][i] == player for j in range(3)):
            return True

    # Check diagonals
    if all(board[i][i] == player for i in range(3)) or all(board[i][2 - i] == player for i in range(3)):
        return True

    return False

def check_draw():
    """Check if the game is a draw (i.e., all cells are filled with no winner)."""
    return all(board[row][col] != ' ' for row in range(3) for col in range(3))

def make_move(player):
    """Prompt the current player to make a move and update the board."""
    while True:
        try:
            move = input(f"Player {player}, enter your move (row and column): ")
            row, col = map(int, move.split(","))
            if board[row - 1][col - 1] == ' ':
                board[row - 1][col - 1] = player
                break
            else:
                print("This cell is already occupied. Please try again.")
        except (ValueError, IndexError):
            print("Invalid input. Please enter row and column as two numbers separated by a comma (e.g., 1, 2).")

def play_game():
    """Main function to handle the flow of the Tic-Tac-Toe game."""
    current_player = 'X'
    print("Welcome to Tic-Tac-Toe!")

    while True:
        print_board()
        make_move(current_player)

        # Check if the current player has won
        if check_winner(current_player):
            print_board()
            print(f"Player {current_player} wins!")
            break

        # Check if the game is a draw
        if check_draw():
            print_board()
            print("It's a draw!")
            break

        # Switch players
        current_player = 'O' if current_player == 'X' else 'X'

# Start the Tic-Tac-Toe game
play_game()

Output:

Welcome to Tic-Tac-Toe!

Current state:
 | | 
-----
 | | 
-----
 | | 
Player X, enter your move (row and column): 1,1

Current state:
X| | 
-----
 | | 
-----
 | | 
Player O, enter your move (row and column): 1,3

Current state:
X| |O
-----
 | | 
-----
 | | 
Player X, enter your move (row and column): 3,1

Current state:
X| |O
-----
 | | 
-----
X| | 
Player O, enter your move (row and column): 2,3

Current state:
X| |O
-----
 | |O
-----
X| | 
Player X, enter your move (row and column): 3,3

Current state:
X| |O
-----
 | |O
-----
X| |X
Player O, enter your move (row and column): 2,1

Current state:
X| |O
-----
O| |O
-----
X| |X
Player X, enter your move (row and column): 3,2

Current state:
X| |O
-----
O| |O
-----
X|X|X
Player X wins!

Explanation:

Uses a 2D list board to represent the Tic-Tac-Toe grid.

  • print_board() function displays the current state of the game board.
  • check_winner() function checks if the current player has won by checking all rows, columns, and diagonals.
  • check_draw() function checks if the game is a draw (i.e., all cells are filled).
  • make_move() function handles user input for placing 'X' or 'O' and validates the input.
  • play_game() function orchestrates the game flow, switching players and checking for win/draw conditions.

Solution 2: Using a Class-Based approach for Better Organization

Code:

# Solution 2: Using a Class-Based Approach for Better Organization

class TicTacToe:
    """Class to handle the Tic-Tac-Toe game."""

    def __init__(self):
        """Initialize the Tic-Tac-Toe board and current player."""
        self.board = [[' ' for _ in range(3)] for _ in range(3)]
        self.current_player = 'X'

    def print_board(self):
        """Print the current state of the Tic-Tac-Toe board."""
        print("\nCurrent state:")
        for row in range(3):
            print("|".join(self.board[row]))
            if row < 2:
                print("-----")

    def check_winner(self):
        """Check if the current player has won the game."""
        # Check rows and columns
        for i in range(3):
            if all(self.board[i][j] == self.current_player for j in range(3)) or \
               all(self.board[j][i] == self.current_player for j in range(3)):
                return True

        # Check diagonals
        if all(self.board[i][i] == self.current_player for i in range(3)) or \
           all(self.board[i][2 - i] == self.current_player for i in range(3)):
            return True

        return False

    def check_draw(self):
        """Check if the game is a draw (i.e., all cells are filled with no winner)."""
        return all(self.board[row][col] != ' ' for row in range(3) for col in range(3))

    def make_move(self):
        """Prompt the current player to make a move and update the board."""
        while True:
            try:
                move = input(f"Player {self.current_player}, enter your move (row and column): ")
                row, col = map(int, move.split(","))
                if self.board[row - 1][col - 1] == ' ':
                    self.board[row - 1][col - 1] = self.current_player
                    break
                else:
                    print("This cell is already occupied. Please try again.")
            except (ValueError, IndexError):
                print("Invalid input. Please enter row and column as two numbers separated by a comma (e.g., 1, 2).")

    def play(self):
        """Main function to handle the flow of the Tic-Tac-Toe game."""
        print("Welcome to Tic-Tac-Toe!")

        while True:
            self.print_board()
            self.make_move()

            # Check if the current player has won
            if self.check_winner():
                self.print_board()
                print(f"Player {self.current_player} wins!")
                break

            # Check if the game is a draw
            if self.check_draw():
                self.print_board()
                print("It's a draw!")
                break

            # Switch players
            self.current_player = 'O' if self.current_player == 'X' else 'X'

# Create an instance of the TicTacToe class and start the game
game = TicTacToe()
game.play()
Welcome to Tic-Tac-Toe!

Current state:
 | | 
-----
 | | 
-----
 | | 
Player X, enter your move (row and column): 1,1

Current state:
X| | 
-----
 | | 
-----
 | | 
Player O, enter your move (row and column): 2,2

Current state:
X| | 
-----
 |O| 
-----
 | | 
Player X, enter your move (row and column): 2,1

Current state:
X| | 
-----
X|O| 
-----
 | | 
Player O, enter your move (row and column): 3,1

Current state:
X| | 
-----
X|O| 
-----
O| | 
Player X, enter your move (row and column): 1,3

Current state:
X| |X
-----
X|O| 
-----
O| | 
Player O, enter your move (row and column): 3,3

Current state:
X| |X
-----
X|O| 
-----
O| |O
Player X, enter your move (row and column): 3,2

Current state:
X| |X
-----
X|O| 
-----
O|X|O
Player O, enter your move (row and column): 2,3

Current state:
X| |X
-----
X|O|O
-----
O|X|O
Player X, enter your move (row and column): 1,2

Current state:
X|X|X
-----
X|O|O
-----
O|X|O
Player X wins!

Explanation:

  • Encapsulates the game logic within a ‘TicTacToe’ class, making the code more modular and organized.
  • The ' __init__' method initializes the game board and sets the current player.
  • The 'print_board()', 'check_winner()', 'check_draw()', and 'make_move()' methods handle various game functionalities, such as printing the board, checking for a winner or draw, and making moves.
  • The 'play()' method manages the game loop, switches players, and checks for win/draw conditions.


Follow us on Facebook and Twitter for latest update.