Tic-Tac-Toe in Python

Tic-Tac-Toe is one of those simplest two-player games. It appears at times as one of the most introductory projects to a programming language. In this explanation, I will break the entire process of building a Tic-Tac-Toe game in Python from scratch.

Understanding the Game

Tic-Tac-Toe is played on a 3×3 grid. Two players take turns placing their marks:

  • Player 1: X
  • Player 2: O

The game ends when:

  1. A player gets three of their marks in a row (horizontally, vertically, or diagonally).
  2. The board is full (resulting in a draw).

Steps to Build Tic-Tac-Toe in Python

We will break the project into these steps:

  1. Display the board
  2. Take player input
  3. Check for a win or a draw
  4. Switch turns between players
  5. Repeat until the game ends

Step 1: Creating the Board

We need a data structure to store the game board. A list of lists is a good choice, where:

  • Each row is a list.
  • Each column contains either 'X', 'O', or a blank space ' '.
def create_board():
    return [[' ' for _ in range(3)] for _ in range(3)]

Explanation

  • This function creates a 3×3 grid filled with spaces (' ').
  • The for loops generate a 3-row list with 3 empty spaces in each row.

Initial Board Output

[[' ', ' ', ' '], 
 [' ', ' ', ' '], 
 [' ', ' ', ' ']]

Step 2: Displaying the Board

We need a function to print the board in a readable format.

Explanation

  • This function loops through each row and prints the values separated by | to mimic a real Tic-Tac-Toe grid.
  • It prints ----- between rows to create horizontal lines.

Example Output

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

Step 3: Taking Player Input

Players should enter a row (0-2) and column (0-2) to place their mark.

def get_player_input(board, player):
    while True:
        try:
            row = int(input(f"Player {player}, enter row (0-2): "))
            col = int(input(f"Player {player}, enter column (0-2): "))

            if row in range(3) and col in range(3) and board[row][col] == ' ':
                board[row][col] = player
                break
            else:
                print("Invalid move. Try again.")
        except ValueError:
            print("Please enter numbers between 0 and 2.")

Explanation

  • The function solicits row and column numbers.
  • It will check if the cell is blank before placing the mark.
  • If input is invalid, it asks again.
  • This loop prevents crashes when the user inputs non-numeric values.

Example Interaction

Player X, enter row (0-2): 1
Player X, enter column (0-2): 1

Board After Input

 | | 
-----
 |X| 
-----
 | | 

Step 4: Checking for a Win

We need to check if a player has won.

def check_winner(board, player):
    # Check rows
    for row in board:
        if all(cell == player for cell in row):
            return True

    # Check columns
    for col in range(3):
        if all(board[row][col] == player for row 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

Explanation

  • Checks rows (X X X or O O O).
  • Checks columns (X or O in all three rows in the same column).
  • Checks diagonals (X or O from top-left to bottom-right OR top-right to bottom-left).

Example Winning Board

Win by Row:

X|X|X
-----
O| | 
-----
O| | 

Win by Column:

X|O| 
-----
X|O| 
-----
X| | 

Win by Diagonal:

X|O| 
-----
O|X| 
-----
O| |X

Step 5: Checking for a Draw

If the board is full and no player has won, the game is a draw.

def check_draw(board):
    return all(cell != ' ' for row in board for cell in row)

Explanation

  • The function checks if all cells are filled.
  • If there are no empty spaces left and no winner, it’s a draw.

Example Draw Board

X|O|X
-----
O|X|O
-----
O|X|O

Step 6: Switching Players

After every turn, we switch between ‘X’ and ‘O’.

def switch_player(player):
    return 'O' if player == 'X' else 'X'

Example Switches

Player X → Player O
Player O → Player X

Step 7: Running the Game

We now combine all functions into one game loop.

def play_tic_tac_toe():
    board = create_board()
    player = 'X'

    while True:
        display_board(board)
        get_player_input(board, player)

        if check_winner(board, player):
            display_board(board)
            print(f"Player {player} wins!")
            break
        elif check_draw(board):
            display_board(board)
            print("It's a draw!")
            break

        player = switch_player(player)

play_tic_tac_toe()
  • The game runs in a loop.
  • It displays the board, takes input, and checks for a winner or draw.
  • If the game is not over, it switches players and repeats.

Final Working Code

Here is the complete Python program:

def create_board():
    return [[' ' for _ in range(3)] for _ in range(3)]

def display_board(board):
    for row in board:
        print('|'.join(row))
        print('-' * 5)

def get_player_input(board, player):
    while True:
        try:
            row = int(input(f"Player {player}, enter row (0-2): "))
            col = int(input(f"Player {player}, enter column (0-2): "))

            if row in range(3) and col in range(3) and board[row][col] == ' ':
                board[row][col] = player
                break
            else:
                print("Invalid move. Try again.")
        except ValueError:
            print("Please enter numbers between 0 and 2.")

def check_winner(board, player):
    for row in board:
        if all(cell == player for cell 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

def check_draw(board):
    return all(cell != ' ' for row in board for cell in row)

def switch_player(player):
    return 'O' if player == 'X' else 'X'

def play_tic_tac_toe():
    board = create_board()
    player = 'X'

    while True:
        display_board(board)
        get_player_input(board, player)

        if check_winner(board, player):
            display_board(board)
            print(f"Player {player} wins!")
            break
        elif check_draw(board):
            display_board(board)
            print("It's a draw!")
            break

        player = switch_player(player)

play_tic_tac_toe()

How to Play

  1. Run the script in Python.
  2. Players take turns entering row and column numbers (0, 1, or 2).
  3. The game ends when a player wins or the board is full (draw).

Summary

  • We built a 3×3 board as lists.
  • We also wrote functions to print the board, take input, check for wins, check for draws, and switch player.
  • It runs in a loop until somebody wins or it results in a draw.