Tic Tac Toe is a well-known, straightforward game that serves as an excellent tool for illustrating fundamental programming principles.

In this article, we will walk you through the process of developing a Tic Tac Toe game using Python.

Initially, the game will be designed for two human players, and later, we will enhance it by incorporating an artificial intelligence (AI) opponent.

The game will utilize the command line for both input and output.

If you want a more visual implementation built with Pygame, check this article: https://developer-service.blog/how-to-make-a-tic-tac-toe-game-in-pygame/


Step 1: Setting Up the Game Board

Let's start our game by creating a main.py file.

First, we need to create the game board. A Tic Tac Toe board is a 3x3 grid:

# Create a 3x3 board
def create_board():
    return [[' ' for _ in range(3)] for _ in range(3)]


# Print the board
def print_board(board):
    for row in board:
        print('|'.join(row))
        print('-' * 5)


# Main function to run the game
if __name__ == '__main__':
    _board = create_board()
    print_board(_board)

This code defines two functions, create_board() and print_board(board), which are used to create and display a 3x3 Tic Tac Toe game board.

  • create_board(): This function generates a 3x3 board by creating a list of lists, where each inner list represents a row, and each element in the inner list represents a column. The elements are initialized with a single space character (' '), indicating an empty cell on the board.
  • print_board(_board): This function takes a 3x3 board (a list of lists) as an input and prints it in a visually appealing format. It iterates through each row of the board and uses the '|'.join(row) method to join the elements within a row with a vertical bar ('|') as a separator. After printing each row, it also prints a horizontal line of five dashes ('-') to separate the rows visually. This creates the appearance of a grid.
  • if __name__ == '__main__': main function that runs the game. It create a board with create_board() which is stored in _board and then prints the game board with print_board() passing the _board variable.

You can run the game as you would any Python script with:

python main.py

And you should see the following output in the terminal:

 | | 
-----
 | | 
-----
 | | 
-----

This shows the Tic Tac Toe board of 3x3 rows and columns.


Excited to dive deeper into the world of Python programming? Look no further than my latest ebook, "Python Tricks - A Collection of Tips and Techniques".

Get the eBook

Inside, you'll discover a plethora of Python secrets that will guide you through a journey of learning how to write cleaner, faster, and more Pythonic code. Whether it's mastering data structures, understanding the nuances of object-oriented programming, or uncovering Python's hidden features, this ebook has something for everyone.

Step 2: Handling Player Moves

Next, we need to handle player moves. We'll create functions to check if a move is valid, place a move on the board, and check for a win or a draw.

# Check if a move is valid
def is_valid_move(board, row, col):
    return board[row][col] == ' '


# Make a move
def make_move(board, row, col, player):
    if is_valid_move(board, row, col):
        board[row][col] = player
        return True
    return False


# Check if the game is over
def check_win(board, player):
    # Check rows, columns and diagonals for a win
    for row in board:
        if all(s == player for s in row):
            return True
    for col in range(3):
        if all(board[row][col] == player for row in range(3)):
            return True
    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


# Check if the game is a draw
def check_draw(board):
    return all(all(cell != ' ' for cell in row) for row in board)

Here's a description of each function:

  • is_valid_move(board, row, col): This function checks if a move is valid by examining the game board, specified row, and column. It returns True if the cell at the given row and column is empty (contains a space character), indicating that the move is valid. Otherwise, it returns False.
  • make_move(board, row, col, player): This function makes a move for the specified player on the game board at the given row and column. It first checks if the move is valid using the is_valid_move() function. If the move is valid, it updates the cell with the player's symbol (either 'X' or 'O') and returns True. Otherwise, it does not modify the board and returns False.
  • check_win(board, player): This function checks if the specified player has won the game by examining the game board. It looks for winning patterns in rows, columns, and diagonals. If the player has won, the function returns True. Otherwise, it returns False.
  • check_draw(board): This function checks if the game is a draw by examining the game board. It returns True if all cells on the board are occupied (not containing a space character), indicating that there are no more valid moves and no player has won. Otherwise, it returns False.

Step 3: Two Human Players

Now we can create a game loop for two human players.

# Human vs human game
def human_vs_human():
    # Initialize the game
    board = create_board()
    current_player = 'X'

    # Play the game
    while True:
        # Print the board
        print_board(board)
        # Get the move from the current player
        row = int(input(f"Player {current_player}, enter row (0, 1, 2): "))
        col = int(input(f"Player {current_player}, enter col (0, 1, 2): "))

        # Make the move (if valid)
        if make_move(board, row, col, current_player):
            # Check if the game is over
            if check_win(board, current_player):
                print_board(board)
                print(f"Player {current_player} wins!")
                break
            # Check if the game is a draw
            if check_draw(board):
                print_board(board)
                print("It's a draw!")
                break
            # Switch players
            current_player = 'O' if current_player == 'X' else 'X'
        else:
            print("Invalid move. Try again.")


# Main function to run the game
if __name__ == '__main__':
    human_vs_human()

Here's a detailed description of the human_vs_human() function:

  • The function initializes the game by creating a new 3x3 game board using the create_board() function and setting the current player to 'X'.
  • It then enters an infinite loop, which represents the main gameplay. The loop continues until the game is over (either a player wins or it's a draw).
  • Inside the loop, the function first prints the current state of the game board using the print_board(board) function.
  • It then prompts the current player to enter the row and column for their move.
  • The function attempts to make the move on the game board using the make_move(board, row, col, current_player) function. If the move is valid, it proceeds to the next steps. Otherwise, it displays an "Invalid move. Try again." message and continues with the same player's turn.
  • If the move is valid, the function checks if the game is over. It first checks if the current player has won using the check_win(board, current_player) function. If the player has won, it prints the final game board and a victory message, then breaks the loop to end the game.
  • If the game is not yet over, the function checks if it's a draw using the check_draw(board) function. If it is, it prints the final game board and a draw message, then breaks the loop to end the game.
  • If the game is still in progress, the function switches the current player to the other player ('O' if the current player is 'X', and vice versa).

It also includes the updated main entry point of the game, which executes the human_vs_human() function when the script is run.

Running the game now, you can now play against yourself (for example):

 | | 
-----
 | | 
-----
 | | 
-----
Player X, enter row (0, 1, 2): 0
Player X, enter col (0, 1, 2): 0
X| | 
-----
 | | 
-----
 | | 
-----
Player O, enter row (0, 1, 2): 1
Player O, enter col (0, 1, 2): 1
X| | 
-----
 |O| 
-----
 | | 
-----

[ ... More moves, hidden for brevity ... ]

X|O|O
-----
X|O| 
-----
O|X|X
-----
Player O wins!

Step 4: Adding an AI Player

To add an AI player, we need to implement a basic strategy. A simple AI can make random valid moves.

Tagged in: