In this article, we will be going through the steps of creating Tic-tac-toe using Python Language from scratch.
About the game
Tic-tac-toe is a two-player game, that is played on a 3×3 square grid. Each player occupies a cell in turns, with the objective of placing three marks in a horizontal, vertical, or diagonal pattern. One player uses cross 'X'
as his marker, while the other uses a naught 'O'
.
Step 1: Tic-tac-toe Design
We will be playing Tic-tac-toe on the command line, therefore, the first thing we have to do is create a design for our tic-tac-toe.

If a player has to mark a particular box, he must enter the corresponding number shown in the grid. Suppose, we wish to occupy the center block, then we will input 5
in the terminal. This grid can be generated by:
# Function to print Tic Tac Toe
def print_tic_tac_toe(values):
print("\n")
print("\t | |")
print("\t {} | {} | {}".format(values[0], values[1], values[2]))
print('\t_____|_____|_____')
print("\t | |")
print("\t {} | {} | {}".format(values[3], values[4], values[5]))
print('\t_____|_____|_____')
print("\t | |")
print("\t {} | {} | {}".format(values[6], values[7], values[8]))
print("\t | |")
print("\n")
In the code above, the function creates our tic-tac-toe game according to the values delivered as an argument. Here the argument, values
is a list containing the status of each cell in the grid.
Step 2: Store information using data structures
The core of any game is the game mechanics behind it. Since this is a fairly easy game to create, the mechanics involved are simple too.
At any instant of time, we need two crucial information:
- Status of the grid – We must have a data structure that stores each cell’s state, that is, whether it is occupied or vacant.
- Each player’s moves – We must somehow have the knowledge of each player’s past and present moves, that is, the positions occupied by
'X'
and'O'
.
Note: Both the information could have been accessed using the status of the grid, but it would have required to traverse it every time we need the player’s positions. This can be called as time vs. space complexity trade-off. It is a general technique to conserve time.
# Function for a single game of Tic Tac Toe
def single_game(cur_player):
# Represents the Tic Tac Toe
values = [' ' for x in range(9)]
# Stores the positions occupied by X and O
player_pos = {'X':[], 'O':[]}
Status of the grid is managed by a list of characters, which can have three possible values,
-
' '
– A vacant cell 'X'
– A cell occupied by player X'O'
– A cell occupied by player O
Each player’s moves are stored as a dictionary of a list of integers. The keys are 'X'
and 'O'
for the respective player. Their corresponding lists contain the numbers given to the grid cells, they occupy.
Note: The variable
cur_player
, stores the current player making the move, as in'X'
or'O'
.
Step 3: Game Loop
Every game, has some kind of game loop, which runs until some player wins or the game ends in a draw. In tic-tac-toe, each loop iteration refers to a single move any player makes.
# Game Loop for a single game of Tic Tac Toe
while True:
print_tic_tac_toe(values)
Step 4: Handle player input
In every game iteration, a player must input his move.
# Try exception block for MOVE input
try:
print("Player ", cur_player, " turn. Which box? : ", end="")
move = int(input())
except ValueError:
print("Wrong Input!!! Try Again")
continue
# Sanity check for MOVE inout
if move < 1 or move > 9:
print("Wrong Input!!! Try Again")
continue
# Check if the box is not occupied already
if values[move-1] != ' ':
print("Place already filled. Try again!!")
continue
We create a try
block, in case a player enters some unintended value. Such an event must not stop the game, therefore, we handle the exception of ValueError
and continue with our game.
We need to perform some sanity checks, like value entered is a valid position and if it is a valid position, is it already occupied?
Step 5: Update information
According to the player input, we need to update the information for the smooth functioning of the game.
# Update game information
# Updating grid status
values[move-1] = cur_player
# Updating player positions
player_pos[cur_player].append(move)
The values
list updates the cell occupied according to the current player. The player position adds the position just taken by the current player.
After updating the values
list and calling the print_tic_tac_toe()
function, the grid looks like this:

Last move: ‘X’ at 2
Step 6: Check win or draw
After each move, we have to check whether any player won the game or the game has been drawn. It can be checked by:
Function Calls:
# Function call for checking win
if check_win(player_pos, cur_player):
print_tic_tac_toe(values)
print("Player ", cur_player, " has won the game!!")
print("\n")
return cur_player
# Function call for checking draw game
if check_draw(player_pos):
print_tic_tac_toe(values)
print("Game Drawn")
print("\n")
return 'D'
If any player wins, then the single_game()
function returns the current player, who made the move. In case, the game is drawn, 'D'
is sent back.
Functions:
# Function to check if any player has won
def check_win(player_pos, cur_player):
# All possible winning combinations
soln = [[1, 2, 3], [4, 5, 6], [7, 8, 9], [1, 4, 7], [2, 5, 8], [3, 6, 9], [1, 5, 9], [3, 5, 7]]
# Loop to check if any winning combination is satisfied
for x in soln:
if all(y in player_pos[cur_player] for y in x):
# Return True if any winning combination satisfies
return True
# Return False if no combination is satisfied
return False
# Function to check if the game is drawn
def check_draw(player_pos):
if len(player_pos['X']) + len(player_pos['O']) == 9:
return True
return False
check_win() – The function has all the winning combinations. All it does is, it checks whether any of the winning combinations is satisfied by the current player’s positions. If it does, it returns True
. If none of the combinations is satisfied, then the function returns False
.
check_draw() – The draw condition is fairly simple, as the game is drawn when all ‘nine’ positions are taken.
Step 7: Switch the current player
Since each player only moves once at a time, therefore after every successful move, we have to swap the current player.
# Switch player moves
if cur_player == 'X':
cur_player = 'O'
else:
cur_player = 'X'
As far as a single game is concerned, this is all we need to do. But this article also presents a scoreboard system for keeping track, if the players want to play multiple games.
Step 8: Enter player names
It is mandatory for any scoreboard to display each player names.
if __name__ == "__main__":
print("Player 1")
player1 = input("Enter the name : ")
print("\n")
print("Player 2")
player2 = input("Enter the name : ")
print("\n")
Step 9: Store game-related information
The information like the current player, the choice of players (take cross or naught), the available options (cross and naught), and the scoreboard need to be stored.
# Stores the player who chooses X and O
cur_player = player1
# Stores the choice of players
player_choice = {'X' : "", 'O' : ""}
# Stores the options
options = ['X', 'O']
# Stores the scoreboard
score_board = {player1: 0, player2: 0}
By default, the current player is the player who entered the name first.
Step 10: Design scoreboard
The scoreboard is stored as a dictionary, where keys are the player names and values are their win number.
# Function to print the score-board
def print_scoreboard(score_board):
print("--------------------------------")
print(" SCOREBOARD ")
print("--------------------------------")
players = list(score_board.keys())
print(" ", players[0], " ", score_board[players[0]])
print(" ", players[1], " ", score_board[players[1]])
print("--------------------------------\n")
To display the scoreboard, we need the player names. The keys are extracted using .keys()
function and then converted to list, so that it can indexed while displaying the scores.
Step 11: Outer Game Loop
We need another game loop, for managing multiple matches of Tic-tac-toe. Each match, the current player chooses his mark ('X'
or 'O'
). The menu for choosing must be displayed in every game iteration:
# Game Loop for a series of Tic Tac Toe
# The loop runs until the players quit
while True:
# Player choice Menu
print("Turn to choose for", cur_player)
print("Enter 1 for X")
print("Enter 2 for O")
print("Enter 3 to Quit")
The scoreboard and menu look like this:

Step 12: Handle and Assign Player Choice
Each iteration, we have to handle and store current player’s choice.
# Try exception for CHOICE input
try:
choice = int(input())
except ValueError:
print("Wrong Input!!! Try Again\n")
continue
# Conditions for player choice
if choice == 1:
player_choice['X'] = cur_player
if cur_player == player1:
player_choice['O'] = player2
else:
player_choice['O'] = player1
elif choice == 2:
player_choice['O'] = cur_player
if cur_player == player1:
player_choice['X'] = player2
else:
player_choice['X'] = player1
elif choice == 3:
print("Final Scores")
print_scoreboard(score_board)
break
else:
print("Wrong Choice!!!! Try Again\n")
According to the player’s choice, the data has been stored. This is important since after each game finishes, it will tell us which player won.
Step 13: Execute the match
After storing all the necessary information, it is time to execute an independent match and store the winning mark.
# Stores the winner in a single game of Tic-tac-toe
winner = single_game(options[choice-1])
Step 14: Update the scoreboard
We need to update the scoreboard after each match of Tic-tac-toe.
# Updates the scoreboard according to the winner
if winner != 'D' :
player_won = player_choice[winner]
score_board[player_won] = score_board[player_won] + 1
print_scoreboard(score_board)
If the game has not ended in a draw, then we update the scoreboard.
Step 15: Switch the choosing player
It is a generous idea, that each player must have the opportunity to choose which mark they want. To do so, we swap the value in cur_player
.
# Switch player who chooses X or O
if cur_player == player1:
cur_player = player2
else:
cur_player = player1
Complete Working Code
# Function to print Tic Tac Toe
def print_tic_tac_toe(values):
print("\n")
print("\t | |")
print("\t {} | {} | {}".format(values[0], values[1], values[2]))
print('\t_____|_____|_____')
print("\t | |")
print("\t {} | {} | {}".format(values[3], values[4], values[5]))
print('\t_____|_____|_____')
print("\t | |")
print("\t {} | {} | {}".format(values[6], values[7], values[8]))
print("\t | |")
print("\n")
# Function to print the score-board
def print_scoreboard(score_board):
print("\t--------------------------------")
print("\t SCOREBOARD ")
print("\t--------------------------------")
players = list(score_board.keys())
print("\t ", players[0], "\t ", score_board[players[0]])
print("\t ", players[1], "\t ", score_board[players[1]])
print("\t--------------------------------\n")
# Function to check if any player has won
def check_win(player_pos, cur_player):
# All possible winning combinations
soln = [[1, 2, 3], [4, 5, 6], [7, 8, 9], [1, 4, 7], [2, 5, 8], [3, 6, 9], [1, 5, 9], [3, 5, 7]]
# Loop to check if any winning combination is satisfied
for x in soln:
if all(y in player_pos[cur_player] for y in x):
# Return True if any winning combination satisfies
return True
# Return False if no combination is satisfied
return False
# Function to check if the game is drawn
def check_draw(player_pos):
if len(player_pos['X']) + len(player_pos['O']) == 9:
return True
return False
# Function for a single game of Tic Tac Toe
def single_game(cur_player):
# Represents the Tic Tac Toe
values = [' ' for x in range(9)]
# Stores the positions occupied by X and O
player_pos = {'X':[], 'O':[]}
# Game Loop for a single game of Tic Tac Toe
while True:
print_tic_tac_toe(values)
# Try exception block for MOVE input
try:
print("Player ", cur_player, " turn. Which box? : ", end="")
move = int(input())
except ValueError:
print("Wrong Input!!! Try Again")
continue
# Sanity check for MOVE inout
if move < 1 or move > 9:
print("Wrong Input!!! Try Again")
continue
# Check if the box is not occupied already
if values[move-1] != ' ':
print("Place already filled. Try again!!")
continue
# Update game information
# Updating grid status
values[move-1] = cur_player
# Updating player positions
player_pos[cur_player].append(move)
# Function call for checking win
if check_win(player_pos, cur_player):
print_tic_tac_toe(values)
print("Player ", cur_player, " has won the game!!")
print("\n")
return cur_player
# Function call for checking draw game
if check_draw(player_pos):
print_tic_tac_toe(values)
print("Game Drawn")
print("\n")
return 'D'
# Switch player moves
if cur_player == 'X':
cur_player = 'O'
else:
cur_player = 'X'
if __name__ == "__main__":
print("Player 1")
player1 = input("Enter the name : ")
print("\n")
print("Player 2")
player2 = input("Enter the name : ")
print("\n")
# Stores the player who chooses X and O
cur_player = player1
# Stores the choice of players
player_choice = {'X' : "", 'O' : ""}
# Stores the options
options = ['X', 'O']
# Stores the scoreboard
score_board = {player1: 0, player2: 0}
print_scoreboard(score_board)
# Game Loop for a series of Tic Tac Toe
# The loop runs until the players quit
while True:
# Player choice Menu
print("Turn to choose for", cur_player)
print("Enter 1 for X")
print("Enter 2 for O")
print("Enter 3 to Quit")
# Try exception for CHOICE input
try:
choice = int(input())
except ValueError:
print("Wrong Input!!! Try Again\n")
continue
# Conditions for player choice
if choice == 1:
player_choice['X'] = cur_player
if cur_player == player1:
player_choice['O'] = player2
else:
player_choice['O'] = player1
elif choice == 2:
player_choice['O'] = cur_player
if cur_player == player1:
player_choice['X'] = player2
else:
player_choice['X'] = player1
elif choice == 3:
print("Final Scores")
print_scoreboard(score_board)
break
else:
print("Wrong Choice!!!! Try Again\n")
# Stores the winner in a single game of Tic Tac Toe
winner = single_game(options[choice-1])
# Edits the scoreboard according to the winner
if winner != 'D' :
player_won = player_choice[winner]
score_board[player_won] = score_board[player_won] + 1
print_scoreboard(score_board)
# Switch player who chooses X or O
if cur_player == player1:
cur_player = player2
else:
cur_player = player1
Time for a Gameplay!
All the steps to create the game has been finished. Now is the time to play the game.
Player 1
Enter the name : Luffy
Player 2
Enter the name : Sanji
--------------------------------
SCOREBOARD
--------------------------------
Luffy 0
Sanji 0
--------------------------------
Turn to choose for Luffy
Enter 1 for X
Enter 2 for O
Enter 3 to Quit
1
| |
| |
_____|_____|_____
| |
| |
_____|_____|_____
| |
| |
| |
Player X turn. Which box? : 5
| |
| |
_____|_____|_____
| |
| X |
_____|_____|_____
| |
| |
| |
Player O turn. Which box? : 1
| |
O | |
_____|_____|_____
| |
| X |
_____|_____|_____
| |
| |
| |
Player X turn. Which box? : 9
| |
O | |
_____|_____|_____
| |
| X |
_____|_____|_____
| |
| | X
| |
Player O turn. Which box? : 2
| |
O | O |
_____|_____|_____
| |
| X |
_____|_____|_____
| |
| | X
| |
Player X turn. Which box? : 3
| |
O | O | X
_____|_____|_____
| |
| X |
_____|_____|_____
| |
| | X
| |
Player O turn. Which box? : 7
| |
O | O | X
_____|_____|_____
| |
| X |
_____|_____|_____
| |
O | | X
| |
Player X turn. Which box? : 6
| |
O | O | X
_____|_____|_____
| |
| X | X
_____|_____|_____
| |
O | | X
| |
Player X has won the game!!
--------------------------------
SCOREBOARD
--------------------------------
Luffy 1
Sanji 0
--------------------------------
Turn to choose for Sanji
Enter 1 for X
Enter 2 for O
Enter 3 to Quit
2
| |
| |
_____|_____|_____
| |
| |
_____|_____|_____
| |
| |
| |
Player O turn. Which box? : 5
| |
| |
_____|_____|_____
| |
| O |
_____|_____|_____
| |
| |
| |
Player X turn. Which box? : 3
| |
| | X
_____|_____|_____
| |
| O |
_____|_____|_____
| |
| |
| |
Player O turn. Which box? : 2
| |
| O | X
_____|_____|_____
| |
| O |
_____|_____|_____
| |
| |
| |
Player X turn. Which box? : 8
| |
| O | X
_____|_____|_____
| |
| O |
_____|_____|_____
| |
| X |
| |
Player O turn. Which box? : 1
| |
O | O | X
_____|_____|_____
| |
| O |
_____|_____|_____
| |
| X |
| |
Player X turn. Which box? : 9
| |
O | O | X
_____|_____|_____
| |
| O |
_____|_____|_____
| |
| X | X
| |
Player O turn. Which box? : 6
| |
O | O | X
_____|_____|_____
| |
| O | O
_____|_____|_____
| |
| X | X
| |
Player X turn. Which box? : 7
| |
O | O | X
_____|_____|_____
| |
| O | O
_____|_____|_____
| |
X | X | X
| |
Player X has won the game!!
--------------------------------
SCOREBOARD
--------------------------------
Luffy 2
Sanji 0
--------------------------------
Turn to choose for Luffy
Enter 1 for X
Enter 2 for O
Enter 3 to Quit
3
Final Scores
--------------------------------
SCOREBOARD
--------------------------------
Luffy 2
Sanji 0
--------------------------------
Conclusion
We hope that this article was fun as well as informative to the reader. I’ve also uploaded the code on Github. You can visit here for the code. If there are any suggestions for the game, feel free to comment.