A Python game of Noughts and Crosses

In this blog post, we will focus on using a 2D array to create a 3×3 game of noughts and crosses. We will investigate the code using a step by step approach.

You can follow the steps described below and recreate the code yourself using your preferred IDE or using an online IDE such as trinket.io.

Step 1: Welcome Banner

Let’s start our game by creating a banner to introduce the game. To do so will create a procedure called displayBanner() that will output the banner on screen.

#A Procedure to display a welcome banner
def displayBanner():
  print("XOXOXOXOXOXOXOXOXOXOXOXOXOXOXOXO")
  print("O                              X")
  print("X     Noughts and Crosses      O")
  print("O                              X")
  print("XOXOXOXOXOXOXOXOXOXOXOXOXOXOXOXO")
  print("")
    

We will then create the main program by calling our subroutine.

#Main Program Starts Here
displayBanner()

Step 2: Defining the noughts and crosses grid as a 2D array

We will now use a 2D array to store the position of our noughts and crosses on a 3×3 grid. In Python, you can create a 2D array using a list of lists.

We will use a blank space ” ” for each value of the grid to indicate that each cell of the grid is empty to start with.

#Main Program Starts Here
displayBanner()
#Defining the grid a 2D array (3x3)!
grid = [[" "," "," "],[" "," "," "],[" "," "," "]]

Step 3: Displaying the grid on screen

At the top of our code, we are going to create a new subroutine used to display the 2D grid on screen using the following code:

#A procedure to display the noughts and crosses grid
def displayGrid(grid):
  print(" " + grid[0][0] + " | " + grid[0][1] + " | " + grid[0][2])
  print("-----------")
  print(" " + grid[1][0] + " | " + grid[1][1] + " | " + grid[1][2])
  print("-----------")
  print(" " + grid[2][0] + " | " + grid[2][1] + " | " + grid[2][2])

We will then edit our main program to call our new subroutine.

#Main Program Starts Here
displayBanner()
#Defining the grid a 2D array (3x3)!
grid = [[" "," "," "],[" "," "," "],[" "," "," "]]
displayGrid(grid)

Step 4: Player 1’s Turn

The game is now all set up and the next step is to ask for player 1 to decide where to place their cross “X”.

To do so, we will ask the user to input two values: the indexes of the row and of the column of the cell of their choice. Both indexes will be given as a number between 0 and 2:

Before deciding to place a token at the given row and column index, the program should check to see if the selected cell of the grid is empty. If not, it should ask the user to select a different row and column index.

The program will then set an “X” within the grid at the given row and column index before displaying the new grid on screen.

print("Player X: Your Turn!")
row = int(input("Enter the row index: 0 , 1 or 2"))
col = int(input("Enter the col index: 0 , 1 or 2"))

#Check if this cell is empty or not. If is is not empty the user will have to select another cell.
while grid[row][col]!=" ":
   print("Sorry this cell is already taken... Try again!")
   row = int(input("Enter the row index: 0 , 1 or 2"))
   col = int(input("Enter the col index: 0 , 1 or 2"))

#Place the cross on the grid using the chosen row and column index    
grid[row][col] = "X"

#Display the new grid on screen
displayGrid(grid)

Step 5: Player 2’s Turn

We are now going to ask player to input their row and column index to decide where to place their nought “O”.

To do so we can resuse the same code as used in step 4.

print("Player O: Your Turn!")
row = int(input("Enter the row index: 0 , 1 or 2"))
col = int(input("Enter the col index: 0 , 1 or 2"))

#Check if this cell is empty or not. If is is not empty the user will have to select another cell.
while grid[row][col]!=" ":
   print("Sorry this cell is already taken... Try again!")
   row = int(input("Enter the row index: 0 , 1 or 2"))
   col = int(input("Enter the col index: 0 , 1 or 2"))

#Place the cross on the grid using the chosen row and column index    
grid[row][col] = "O"

#Display the new grid on screen
displayGrid(grid)

Step 6: Playing against the computer

We can tweak the code from the above step, so that the computer decides where to place its token. To do so we will let the computer randomly select a row and a column index by generating two random values between 0 and 2.

To generate a random number you will first need to import the random library by adding the following line of code, at the very top of your program.

import random

Then we will replace the input statements to generate random values instead.

print("Player O: Your Turn!")
row = random.randint(0,2)
col = random.randint(0,2)

#Check if this cell is empty or not. If is is not empty the user will have to select another cell.
while grid[row][col]!=" ":
   print("Sorry this cell is already taken... Try again!")
   row = random.randint(0,2)
   col = random.randint(0,2)

#Place the cross on the grid using the chosen row and column index    
grid[row][col] = "O"

#Display the new grid on screen
displayGrid(grid)

Step 7: Using a loop to let each player place all their tokens

We now need to repeat steps 4 and 5 (or 6) several times to let the players complete the grid by placing 9 tokens in total. We will do so using a for loop that will be applied to all the code from step 4 and 5/6. (From line 25 to 55).

To alternate which turns it is to play we will use the counter of our for loop and if this counter is even we will let player 1 have a go, otherwise (if it is odd) player 2 will have a go.

for i in range(0,9):    
    if i%2 == 0: #check if i is an even number
       print("Player X: Your Turn!")
       ...
    else:
       print("Player O: Your Turn!")
       ...
       
 

Step 8: Checking if we have a winner!

We will now add a new subroutine to our code to check a grid and find out if the grid contains three consecutives “X” or “O”, in a row, a column or a diagonal. We can exit the game if it does.

def checkGrid(grid):
   #Checking the first row...
   if grid[0][0]=="X" and grid[0][1]=="X" and grid[0][2]=="X":
        print("Three Xs in a row.")
        exit()
   elif grid[0][0]=="O" and grid[0][1]=="O" and grid[0][2]=="O":
        print("Three Os in a row.")
        exit()
   #Checking the second row...
   if grid[1][0]=="X" and grid[1][1]=="X" and grid[1][2]=="X":
        print("Three Xs in a row.")
        exit()
   elif grid[1][0]=="O" and grid[1][1]=="O" and grid[1][2]=="O":
        print("Three Os in a row.")
        exit()
   #Checking the third row...
   if grid[2][0]=="X" and grid[2][1]=="X" and grid[2][2]=="X":
        print("Three Xs in a row.")
        exit()
   elif grid[2][0]=="O" and grid[2][1]=="O" and grid[2][2]=="O":
        print("Three Os in a row.")
        exit()
   
   #Checking the first column
   if grid[0][0]=="X" and grid[1][0]=="X" and grid[2][0]=="X":
        print("Three Xs in a column.")
        exit()
   elif grid[0][0]=="O" and grid[1][0]=="O" and grid[2][0]=="O":
        print("Three Os in a column.")
        exit()
      
   #Checking the second column
   #... complete the code here
   
   #Checking the third column
   #... complete the code here

   #Checking the first diagonale
   #... complete the code here

   #Checking the second diagonale
   #... complete the code here

We should then call this procedure as soon as a new nought or crosses is added to our grid. For instance, at the end of player 1’s turn, we can use the following lines:

grid[row][col] = "X"

#Display the new grid on screen
displayGrid(grid)

#Find out if there is a winner
checkGrid(grid)

And at the end of player 2’s turn:

grid[row][col] = "O"

#Display the new grid on screen
displayGrid(grid)

#Find out if there is a winner
checkGrid(grid)
unlock-access

Solution...

The solution for this challenge is available to full members!
Find out how to become a member:
➤ Members' Area

Did you like this challenge?

Click on a star to rate it!

Average rating 4.3 / 5. Vote count: 145

No votes so far! Be the first to rate this post.

As you found this challenge interesting...

Follow us on social media!

Tagged with: