By the end of this lesson, you'll have created a fully functional game that looks like this:
We'll understand the LOGIC first, then code it step by step!
Before we write a single line of code, let's understand exactly what our game needs to do. This flowchart shows the complete logic:
Look at the flowchart above and trace through what happens when:
Can you follow the flowchart without getting lost? If not, study it until it makes sense - this is your roadmap!
Our master flowchart has 4 main sections. We'll code each one separately:
What it does: Gets a number from the player and makes sure it's valid (1-9)
What it does: Checks if the chosen spot is empty and places the symbol
What it does: Checks if someone won or if it's a tie
What it does: Switches between X and O players and keeps the game going
Code each section one at a time, test it, then move to the next!
Let's start with the most basic building block - how do we represent a tic-tac-toe board in code?
A tic-tac-toe board has 9 positions arranged like this:
We'll use a list to store our board. Think of it like 9 boxes in a row:
Notice that lists start counting at 0, but players think in terms of 1-9. We'll need to handle this!
So when a player says "5" (center), we need list position 4
.
# 🔧 Mini-Challenge 1: Create and Explore the Board
# Create an empty board (9 empty spaces)
board = [" "] * 9
print("Our empty board list:")
print(board)
print()
# Let's see what's at different positions
print("Position 0 (top-left):", repr(board[0]))
print("Position 4 (center):", repr(board[4]))
print("Position 8 (bottom-right):", repr(board[8]))
print()
# Now let's place some symbols and see what happens
board[0] = "X" # Top-left
board[4] = "O" # Center
board[8] = "X" # Bottom-right
print("After placing some symbols:")
print(board)
[" "] * 9
do? Try changing the 9 to a different number and see what happens.repr(board[0])
instead of just board[0]
?Make sure you understand how list indexing works before continuing!
Our list [" ", " ", "X", " ", "O", " ", " ", " ", " "]
doesn't look much like a tic-tac-toe board! Let's fix that.
We need to turn this:
Into this:
# 🔧 Mini-Challenge 2: Build the Display Pattern
# Let's start with a board that has some moves
board = [" ", " ", "X", " ", "O", " ", " ", " ", "X"]
print("Current board list:", board)
print()
print("Let's display it as a tic-tac-toe board:")
print()
# First, let's just print the positions manually to understand the pattern
print("Row 1 - positions 0, 1, 2:")
print(f" {board[0]} | {board[1]} | {board[2]}")
print("Separator:")
print("---+---+---")
print("Row 2 - positions 3, 4, 5:")
print(f" {board[3]} | {board[4]} | {board[5]}")
print("Separator:")
print("---+---+---")
print("Row 3 - positions 6, 7, 8:")
print(f" {board[6]} | {board[7]} | {board[8]}")
Typing out the board display every time would be tedious! This is where functions come in.
A function is like a recipe - you write the steps once, then you can use them over and over.
Let's create a print_board()
function!
# 🔧 Mini-Challenge 3: Create Your First Function
def print_board(board):
"""This function takes a board list and displays it as tic-tac-toe"""
print() # Empty line for spacing
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() # Empty line for spacing
# Test it out!
test_board = ["X", " ", "O", " ", "X", " ", "O", " ", " "]
print("Testing our function:")
print_board(test_board)
# Try with a different board
another_board = [" "] * 9 # Empty board
print("Empty board:")
print_board(another_board)
Now we tackle the first section of our flowchart:
# 🔧 Mini-Challenge 4: Understanding Input
# Let's see what happens with basic input
player_input = input("Enter a position (1-9): ")
print(f"You entered: '{player_input}'")
print(f"Type of input: {type(player_input)}")
# Try to use it as a number
try:
position = int(player_input)
print(f"As a number: {position}")
# Convert to list index (subtract 1)
list_index = position - 1
print(f"List index: {list_index}")
except ValueError:
print("Oops! That's not a number!")
Run the cell above and try these inputs to see what happens:
5
(normal input)hello
(not a number)15
(too big)0
(too small)3.5
(decimal)What problems did you find? How should our game handle each case?
The Solution: We need to check for these problems and ask again if there's an issue!
# 🔧 Mini-Challenge 5: Build Input Validation
def get_valid_input():
"""Get a valid position (1-9) from the player"""
while True: # Keep asking until we get valid input
player_input = input("Enter your move (1-9): ")
# Check if it's a number
if not player_input.isdigit():
print("Invalid input! Please enter a number.")
continue # Go back to start of loop
# Convert to integer
position = int(player_input)
# Check if it's in the right range
if position < 1 or position > 9:
print("Invalid position! Choose a number between 1 and 9.")
continue # Go back to start of loop
# If we get here, the input is valid!
return position - 1 # Convert to list index (0-8)
# Test it!
print("Testing input validation:")
print("Try entering invalid inputs to see what happens!")
valid_index = get_valid_input()
print(f"Great! You chose list index: {valid_index}")
Now for section 2 of our flowchart:
board[index] == " "
?board[index] = "X"
or "O"
# 🔧 Mini-Challenge 6: Handle Move Placement
def make_move(board, position, player):
"""Try to place a player's symbol at the given position"""
if board[position] != " ":
print("That spot is already taken! Try again.")
return False # Move failed
else:
board[position] = player
return True # Move succeeded
# Test it!
test_board = [" "] * 9
print("Starting with empty board:")
print_board(test_board)
# Try to place X at position 4 (center, list index 4)
print("Player X chooses center (position 5):")
success = make_move(test_board, 4, "X")
if success:
print_board(test_board)
# Try to place O in the same spot
print("Player O also tries center:")
success = make_move(test_board, 4, "O")
if success:
print_board(test_board)
else:
print("Move was blocked!")
Time for section 3 of our flowchart - the tricky part!
There are exactly 8 ways to win tic-tac-toe:
In list indices (0-8):
# 🔧 Mini-Challenge 7: Build Win Detection
def check_winner(board, player):
"""Check if the given player has won"""
# All possible winning combinations (as list indices)
win_combinations = [
[0, 1, 2], # Top row
[3, 4, 5], # Middle row
[6, 7, 8], # Bottom row
[0, 3, 6], # Left column
[1, 4, 7], # Middle column
[2, 5, 8], # Right column
[0, 4, 8], # Top-left to bottom-right diagonal
[2, 4, 6] # Top-right to bottom-left diagonal
]
# Check each winning combination
for combo in win_combinations:
if (board[combo[0]] == player and
board[combo[1]] == player and
board[combo[2]] == player):
return True
return False
# Test it!
# Create a winning board for X
winning_board = ["X", "X", "X", " ", "O", " ", " ", "O", " "]
print("Test board:")
print_board(winning_board)
print("Does X win?", check_winner(winning_board, "X"))
print("Does O win?", check_winner(winning_board, "O"))
# 🔧 Mini-Challenge 8: Detect Ties
def is_tie(board):
"""Check if the board is full (tie game)"""
return " " not in board
# Test it!
full_board = ["X", "O", "X", "O", "X", "O", "O", "X", "O"]
print("Full board:")
print_board(full_board)
print("Is it a tie?", is_tie(full_board))
partial_board = ["X", "O", " ", "O", "X", " ", " ", " ", " "]
print("\nPartial board:")
print_board(partial_board)
print("Is it a tie?", is_tie(partial_board))
Final section of our flowchart:
# 🎮 The Complete Game!
def play_game():
"""Run a complete game of tic-tac-toe"""
# Initialize the game
board = [" "] * 9
current_player = "X"
print("🎮 Welcome to Tic-Tac-Toe!")
print("Players take turns choosing a position (1–9).")
print()
# Show reference board
reference = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
print("Board positions:")
print_board(reference)
# Main game loop
while True:
print(f"Current board:")
print_board(board)
# Get valid input
print(f"Player {current_player}'s turn!")
position = get_valid_input()
# Try to make the move
if make_move(board, position, current_player):
# Move successful - check for win/tie
if check_winner(board, current_player):
print_board(board)
print(f"🎉 Player {current_player} wins!")
break
if is_tie(board):
print_board(board)
print("🤝 It's a tie!")
break
# Switch players
current_player = "O" if current_player == "X" else "X"
# If move failed, player tries again (don't switch players)
# Run the game!
if __name__ == "__main__":
play_game()
You've built a complete tic-tac-toe game! Let's reflect on what you accomplished:
print_board()
- Organized code into reusable blocksget_valid_input()
- Handled user input and validationmake_move()
- Processed game movescheck_winner()
- Implemented game logicif/else
for decision makingwhile
for input validation and game loopinput()
for getting user dataprint()
for displaying informationReady for more? Try adding these features:
Go back through your code and see if you can:
You're now a game developer! 🎮
Understand the problem before you code the solution!
The flowchart was your roadmap. Every piece of code maps directly to a step in that flowchart. This is how professional developers work on complex projects.
🎉 You're ready for your next coding adventure!