Python for Humans · Draft v0.2 · Oct 28, 2026 · Project · Use Download .py to keep your edited game.
Tic-Tac-Toe: Your Move
Course project · After Keeping Things Together
- You should already know
- Input, decisions, number conversion, while loops, lists, indexing, and replacing list items through Keeping Things Together.
- You'll build
- A numbered board that remembers X and O and refuses occupied squares.
- You'll be able to
- Translate square numbers to list positions and preserve state between turns.
- You need
- This page. Each example runs in its own code box; nothing to install.
Our castle can keep a bag of supplies. A tic-tac-toe game needs a different collection: nine squares, each remembering what's in it.
Let's build that board. First we'll place one X. Then we'll let two people take turns on the same computer.
Allow about 10–15 minutes for the first move, then another 10–15 for taking turns. You can stop between them. This version doesn't recognize a winner yet; the players do that part.
Start with Nine Squares
Tic-tac-toe uses a three-by-three board. Two players, X and O, take turns choosing empty squares. Three matching marks in a row, column, or diagonal win.
Here's our first version. It displays square numbers and asks for a choice:
print("1 | 2 | 3") print("--+---+--") print("4 | 5 | 6") print("--+---+--") print("7 | 8 | 9") square = input("X, choose a square from 1 to 9: ") print("You chose square", square)
Try choosing 5. The program remembers your answer in square, but the picture hasn't changed. Those numbers are fixed strings inside print() calls. We need the picture to come from values we can change.
Remember One Move
We'll keep the nine labels in a list. The first three items form the first row, the next three form the second, and the last three form the third.
The player sees squares 1 through 9, but Python's positions start at 0. Subtracting one translates between them: square 5 lives at index 4.
Run this version and choose 5:
board = ['1', '2', '3', '4', '5', '6', '7', '8', '9'] print(f"{board[0]} | {board[1]} | {board[2]}") print("--+---+--") print(f"{board[3]} | {board[4]} | {board[5]}") print("--+---+--") print(f"{board[6]} | {board[7]} | {board[8]}") square = input("X, choose a square from 1 to 9: ") if square in ['1', '2', '3', '4', '5', '6', '7', '8', '9']: position = int(square) - 1 board[position] = 'X' else: print("Please choose a number from 1 to 9.") print(f"{board[0]} | {board[1]} | {board[2]}") print("--+---+--") print(f"{board[3]} | {board[4]} | {board[5]}") print("--+---+--") print(f"{board[6]} | {board[7]} | {board[8]}")
The program prints the board before and after the move. The middle square becomes X because board[position] = 'X' replaces one list item.
Before converting the answer, we check whether it's one of the nine allowed strings. Try hello, 0, or an empty answer. None reaches int() or the list lookup, so none crashes the program.
Work through these changes in that box:
- Choose a corner. Predict its index before running.
- Change the assigned mark to
'O'. Check that it changes just one square. - Put
'X'back, then add a message showing the chosen position. Does square9become position8?
You've got a board that remembers a move. This is a good stopping point.
Keep the Board Between Turns
Now we need a loop. Here's the plan before we write it:
- Create the board once, before the loop.
- Start with player X and zero accepted moves.
- Ask for a square, check it, and update the board.
- Only after a successful move, count it and switch players.
- Stop after nine accepted moves, or when someone types
quit.
In your one-move box, start by adding player = 'X' and moves = 0 before the question. Put the display and question inside while moves < 9:. Use player instead of the fixed 'X' when assigning the square.
Next add the occupied-square check. We already know how to ask whether a value appears in a list: board[position] in ['X', 'O']. If it does, print a message. Otherwise, place the mark, add one to moves, and change X to O or O to X.
Keep that update and switch together inside the successful branch. A rejected answer should leave the same player in charge.
Here's a complete version to compare after you've tried. All the starting values are included:
board = ['1', '2', '3', '4', '5', '6', '7', '8', '9'] player = 'X' moves = 0 while moves < 9: print(f"{board[0]} | {board[1]} | {board[2]}") print("--+---+--") print(f"{board[3]} | {board[4]} | {board[5]}") print("--+---+--") print(f"{board[6]} | {board[7]} | {board[8]}") square = input(f"{player}, choose a square (1-9, or quit): ") if square == 'quit': break if square in ['1', '2', '3', '4', '5', '6', '7', '8', '9']: position = int(square) - 1 if board[position] in ['X', 'O']: print("That square is taken. Try another one.") else: board[position] = player moves = moves + 1 if player == 'X': player = 'O' else: player = 'X' else: print("Please choose a number from 1 to 9.") print(f"{board[0]} | {board[1]} | {board[2]}") print("--+---+--") print(f"{board[3]} | {board[4]} | {board[5]}") print("--+---+--") print(f"{board[6]} | {board[7]} | {board[8]}") print("Thanks for playing!")
Try 5, then 5 again, then 1. X should keep the center; the rejected move should still leave O choosing the next square. Try hello, too. Does it change whose turn it is?
For now, type quit when either player gets three in a row. Nine accepted moves end the practice automatically, but this version doesn't decide whether they made a win or a draw.
Turn the Knobs
Choose one bonus:
- Display how many moves have been accepted. Try an occupied square and check that the count stays put.
- Make a short set of instructions appear once, before the loop. Keep the changing board inside it.
Click Download .py below your working game to keep a copy of your edited code. Your edits also remain in this browser when you return to this page. The next installment has its own complete starter; it doesn't automatically include your changes.
We repeated the five display lines to show the board at the end. If we improve its appearance, we'll have to change every copy. In Three in a Row, functions will give that work a name, and we'll teach the program to recognize a winner.
Write one sentence about why the board is created outside the loop, while a new square is chosen inside it.