Build Your First Python Game - Tic Tac Toe
What You'll Build
By the end of this lesson, you'll have created a fully functional game that looks like this:
What You'll Learn
- Lists: Store game data
- Functions: Organize your code
- Conditionals: Make decisions
- Loops: Repeat actions
- Input/Output: Interact with players
- Boolean Logic: Detect wins and ties
๐ฏ Important
We'll understand the LOGIC first, then code it step by step!
The Game Master Plan
Before we write a single line of code, let's understand exactly what our game needs to do. This flowchart shows the complete logic:
๐ค Think About This
Look at the flowchart above and trace through what happens when:
- Player X wants to place their symbol in position 5
- Player O tries to place their symbol in a spot that's already taken
- Player X gets three in a row
โ Checkpoint
Can you follow the flowchart without getting lost? If not, study it until it makes sense - this is your roadmap!
Breaking Down the Plan
Our master flowchart has 4 main sections. We'll code each one separately:
Section 1: Input & Validation
What it does: Gets a number from the player and makes sure it's valid (1-9)
Section 2: Move Processing
What it does: Checks if the chosen spot is empty and places the symbol
Section 3: Win/Tie Detection
What it does: Checks if someone won or if it's a tie
Section 4: Game State Management
What it does: Switches between X and O players and keeps the game going
๐ฏ Our Plan
Code each section one at a time, test it, then move to the next!
Phase 1: Understanding the Game Board
Let's start with the most basic building block - how do we represent a tic-tac-toe board in code?
The Board Layout
A tic-tac-toe board has 9 positions arranged like this:
In Python: A List
We'll use a list to store our board. Think of it like 9 boxes in a row:
โ ๏ธ Important
Notice that lists start counting at 0, but players think in terms of 1-9. We'll need to handle this!
Position Mapping
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)
๐ค Think About This
- What does
[" "] * 9
do? Try changing the 9 to a different number and see what happens. - Why do we use
repr(board[0])
instead of justboard[0]
? - If a player wants to place their symbol at position 7, what list index should we use?
โ Checkpoint
Make sure you understand how list indexing works before continuing!
Phase 2: Making the Board Look Like Tic-Tac-Toe
Our list [" ", " ", "X", " ", "O", " ", " ", " ", " "]
doesn't look much like a tic-tac-toe board! Let's fix that.
The Display Pattern
We need to turn this:
Into this:
The Mapping
# ๐ง 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]}")
๐ฏ Making It Automatic
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)
Phase 3: Getting Player Input
Now we tackle the first section of our flowchart:
What We Need To Handle:
- Get input from player: "Enter your move (1-9)"
- Validate it's a number: What if they type "hello"?
- Check it's in range: What if they type "15"?
- Convert to list index: Player says "5", we need index 4
# ๐ง 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!")
๐จ What Could Go Wrong?
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)
๐ค Think About This
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}")
Phase 4: Processing Moves
Now for section 2 of our flowchart:
What We Need:
- Check if spot is empty: Is
board[index] == " "
? - Handle taken spots: Tell player and let them try again
- Place the symbol:
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!")
Phase 5: Detecting Wins
Time for section 3 of our flowchart - the tricky part!
All the Ways to Win
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))
Phase 6: The Game Loop
Final section of our flowchart:
The Game Loop Structure:
- Initialize: Empty board, start with X
- Loop until game ends:
- Show board
- Get valid input
- Try to make move
- Check for win/tie
- Switch players
- Announce result
# ๐ฎ 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()
Congratulations!
You've built a complete tic-tac-toe game! Let's reflect on what you accomplished:
What You Learned
๐ Lists and Indexing
- Used a list to represent the game board
- Learned about 0-based indexing vs 1-based user input
- Manipulated list elements
๐ง Functions
print_board()
- Organized code into reusable blocksget_valid_input()
- Handled user input and validationmake_move()
- Processed game movescheck_winner()
- Implemented game logic
๐ฏ Control Flow
- Conditionals:
if/else
for decision making - Loops:
while
for input validation and game loop - Boolean logic: Combined conditions for win detection
๐ฌ Input/Output
input()
for getting user dataprint()
for displaying information- String formatting with f-strings
๐ค Reflection Questions
- Which part was most challenging? Why?
- How does the flowchart help in understanding the code?
- What would you improve about this game?
Enhancement Ideas
Ready for more? Try adding these features:
Easy Enhancements:
- Player names: Let players enter custom names instead of X/O
- Score tracking: Keep track of wins across multiple games
- Better display: Add colors or emojis
Medium Enhancements:
- Computer player: Add an AI opponent that makes random moves
- Input shortcuts: Allow 'q' to quit, 'r' to restart
Challenge Enhancements:
- Smart AI: Make the computer player strategic
- Different board sizes: 4x4 or 5x5 tic-tac-toe
- Network play: Play against someone online
๐ Code Review
Go back through your code and see if you can:
- Trace through the flowchart with your actual code
- Identify each function's purpose
- Understand why each loop and condition is necessary
- Spot potential improvements
You're now a game developer! ๐ฎ
Key Takeaways
The Development Process We Used:
- ๐ Plan First: Created flowchart before coding
- ๐งฉ Break It Down: Divided into manageable pieces
- ๐ง Build Incrementally: Coded and tested each part
- ๐ Integrate: Combined pieces into final game
- ๐ฏ Test & Reflect: Verified it works and considered improvements
Programming Concepts Mastered:
- Data Structures: Lists for storing game state
- Functions: Organizing code into reusable blocks
- Control Flow: Loops and conditionals for game logic
- Input Validation: Handling user errors gracefully
- Boolean Logic: Complex condition checking
- Code Organization: Breaking complex problems into simple parts
Most Important Lesson:
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!