Skip to content
BetaDE
Lesson 23 of 35Python

Python · Lesson 23 · 45 minutes

Project: tic-tac-toe

What you'll learn

  • Represent a board as a list of lists
  • Check rows, columns and diagonals
  • Split a game into clear functions

A board made of lists

A tic-tac-toe board has 3 × 3 squares. In Python you build something like that as a list of lists – a list with three rows, each row a list with three squares:

board = [
    ["X", "O", "X"],
    [" ", "X", "O"],
    ["O", " ", "X"],
]
print(board[0][2])     # row 0, column 2
print(board[1])        # the whole middle row

The first index picks the row, the second the column.

Showing the board nicely

def draw(board):
    for number, row in enumerate(board):
        print(" " + " | ".join(row))
        if number < 2:
            print("---+---+---")


draw([["X", "O", "X"], [" ", "X", "O"], ["O", " ", "X"]])

When has someone won?

There are eight winning lines: three rows, three columns and two diagonals. A good trick: collect all eight lines in a list and then check them all with the same loop – instead of writing eight separate if checks.

a, b, c = "X", "X", "X"
print(a == b == c)        # Python can chain comparisons!
print("O" == "O" == "X")

The whole game

With your winner function it becomes a game for two people:

def winner(board):
    lines = [board[i] for i in range(3)] + [[board[0][i], board[1][i], board[2][i]] for i in range(3)]
    lines += [[board[0][0], board[1][1], board[2][2]], [board[0][2], board[1][1], board[2][0]]]
    for line in lines:
        if line[0] != " " and line[0] == line[1] == line[2]:
            return line[0]
    return None


board = [[" "] * 3 for _ in range(3)]
player = "X"
for move in range(9):
    for row in board:
        print(" " + " | ".join(row))
    # Keep asking until a number from 1 to 9 comes
    while True:
        entry = input(f"{player} – square 1 to 9: ")
        try:
            number = int(entry) - 1
        except ValueError:
            print("Please type a number.")
            continue
        if 0 <= number <= 8:
            break
        print("Only squares 1 to 9!")
    board[number // 3][number % 3] = player
    if winner(board):
        print(player, "wins!")
        break
    player = "O" if player == "X" else "X"
else:
    print("It's a draw!")

Keep building: check whether the square is already taken. Or build a computer opponent that picks a free square with random.choice.

Your task

Your turn

Write the function winner(board). The board is a list of three rows with three entries each ("X", "O" or " "). The function returns "X" or "O" if someone has three in a row, column or diagonal – otherwise None.

def winner(board):
    pass


board = [
    ["X", "O", " "],
    ["X", "O", " "],
    ["X", " ", "O"],
]
print(winner(board))

Solve it without hints to earn a ⭐ on your progress page.

Show solution

Try it yourself first – mistakes teach you the most. Here is one possible solution:

def winner(board):
    lines = []
    for i in range(3):
        lines.append(board[i])                                 # row
        lines.append([board[0][i], board[1][i], board[2][i]])  # column
    lines.append([board[0][0], board[1][1], board[2][2]])      # diagonal
    lines.append([board[0][2], board[1][1], board[2][0]])      # other diagonal
    for line in lines:
        if line[0] != " " and line[0] == line[1] == line[2]:
            return line[0]
    return None


board = [
    ["X", "O", " "],
    ["X", "O", " "],
    ["X", " ", "O"],
]
print(winner(board))