Tic Tac Toe Game using Python [GUI Based]

You all must have played the Tic Tac Toe game in your school days and there’s no doubt. We all love to play this game. In this blog, we build the same Tic Tac Toe game using a python programming language.

What is Tic Tac Toe?

Tic Tac Toe is one of the best and most played games of our childhood. We all have played this game using pen and paper. If you don’t know how to play Tic Tac To game don’t worry we will first understand how to play this game.

Tic Tac Toe Game

We need two people to play this game(Player1 and Player2). First, we draw a 3×3 table (3 rows and 3 columns). The first player draws ‘X’ in any of the blocks. Then the second player draws ‘O’ on the available block. Like this, the players draw ‘X’ and ‘O’ one by one in the empty block until all the blocks are filled by both players or a player draws 3 consecutive marks in the vertical, horizontal, or diagonal way. If any player draws these 3 consecutive marks then the player wins otherwise the game will be a tie.

Overview

In this project, we are using the pygame modules. pygame is a free and open-source cross-platform module that allows us to create a window. Using this module we can draw images and shapes on the window. I will explain all the pygame object methods that are used in this project.

You may also like : Virtual Assistant Using Python

Prerequisites

To implement this game you need basic concepts of python. As I told you we are using the pygame module to build our Tic Tac Toe game. So first, we need to install the pygame module. We can install it using pip installer as follows :

pip install pygame

Steps to Build a Python Tic Tac Toe Game

What will we do

  • Create and display 3×3 table using window
  • Display result bar that shows which player’s turn is it and who wins the game or the game is draw.
  • When any player wins the game or the game is a draw, we will reset the game and start again.

We need to run our game within an infinite loop. It will detect continuous events and when the user presses the mouse button on the grid we will first get the X and Y coordinates of the mouse. Then we will check which square the user clicked on. Then we draw the right ‘X’ or ‘O’ image on the canvas. So that’s what we’re going to do with the Python project idea.

So first, import the pygame module and the time modules because we will use the time.sleep() method to pause the game at certain positions. Then initialize all the variables that we will use in our Tic Tac Toe game

import pygame as pg,sys
from pygame.locals import *
import time

#initialize global variables
XO = 'x'
winner = None
draw = False
width = 400
height = 400
white = (255, 255, 255)
line_color = (10,10,10)

#TicTacToe 3x3 board
TTT = [[None]*3,[None]*3,[None]*3]

Here, the TTT is our 3×3 Tic Tac Toe table and at first, it will have 9 None values. The height and width of the window is 400×400.

Creating Pygame window

#initializing pygame window
pg.init()
fps = 30
CLOCK = pg.time.Clock()
screen = pg.display.set_mode((width, height+100),0,32)
pg.display.set_caption("Tic Tac Toe")

We are using pygame to create a new window where we’ll play our Tic Tac Toe game in this game. To initialize the pygame we use pg.init() and the window display will set with a width of 400 and a height of 500. We have added an extra 100-pixel space to display the status of the game.

The pg.display.set_mode() method initializes the display and references it with the screen variable. Using this screen variable we can draw on the display.

The pg.display.set_caption() method is used to set title of the display window.

Load images to Window

This project uses many images like splash images it will display when the game starts or resets. The “X” and “O” images that we will display whenever the user clicks on the particular block. We will load all the images and resize them so that they will fit in our window block.

#loading the images
opening = pg.image.load('tic tac opening.png')
x_img = pg.image.load('x.png')
o_img = pg.image.load('o.png')
#resizing images
x_img = pg.transform.scale(x_img, (80,80))
o_img = pg.transform.scale(o_img, (80,80))
opening = pg.transform.scale(opening, (width, height+100))

Defining the function

Now we define a function to start the game. We will also use this same function to restart the game. To draw an image on top of another image we will use the blit() function.

So we can draw the opening image and after drawing, we always need to update the display with pg.display.update(). When the opening image is drawn, we wait for 1 second. For that, we will use time.sleep(1) and fill the screen with white color.

Next, we need to draw 2 vertical and horizontal lines on the white background to make the 3×3 grid. In the end, we will call the draw_status() function.

def game_opening():
    screen.blit(opening,(0,0))
    pg.display.update()
    time.sleep(1)
    screen.fill(white)

    # Drawing vertical lines
    pg.draw.line(screen,line_color,(width/3,0),(width/3, height),7)
    pg.draw.line(screen,line_color,(width/3*2,0),(width/3*2, height),7)
    # Drawing horizontal lines
    pg.draw.line(screen,line_color,(0,height/3),(width, height/3),7)
    pg.draw.line(screen,line_color,(0,height/3*2),(width, height/3*2),7)
    draw_status()

The draw_status() function will draw a black rectangle where we update the status of the game to show which player’s turn is it and whether the game draws or ends.

def draw_status():
    global draw
    if winner is None:
        message = XO.upper() + "'s Turn"
    else:
        message = winner.upper() + " won!"
    if draw:
        message = 'Game Draw!'

    font = pg.font.Font(None, 30)
    text = font.render(message, 1, (255, 255, 255))
    # copy the rendered message onto the board
    screen.fill ((0, 0, 0), (0, 400, 500, 100))
    text_rect = text.get_rect(center=(width/2, 500-50))
    screen.blit(text, text_rect)
    pg.display.update()

We have defined the check_win() function to check the Tic Tac Toe board to see all the marks of ‘X’ and ‘O’. It calculates whether a player has won the game or not. They can either win when the player has marked 3 consecutive marks in a row, column, or diagonally. This function is called every time when we draw a mark ‘X’ or ‘O’ on the board.

def check_win():
    global TTT, winner,draw

    # check for winning rows
    for row in range (0,3):
        if ((TTT [row][0] == TTT[row][1] == TTT[row][2]) and(TTT [row][0] is not None)):
            # this row won
            winner = TTT[row][0]
            pg.draw.line(screen, (250,0,0), (0, (row + 1)*height/3 -height/6),\
                                (width, (row + 1)*height/3 - height/6 ), 4)
            break

    # check for winning columns
    for col in range (0, 3):
        if (TTT[0][col] == TTT[1][col] == TTT[2][col]) and (TTT[0][col] is not None):
            # this column won
            winner = TTT[0][col]
            #draw winning line
            pg.draw.line (screen, (250,0,0),((col + 1)* width/3 - width/6, 0),\
                            ((col + 1)* width/3 - width/6, height), 4)
            break

    # check for diagonal winners
    if (TTT[0][0] == TTT[1][1] == TTT[2][2]) and (TTT[0][0] is not None):
        # game won diagonally left to right
        winner = TTT[0][0]
        pg.draw.line (screen, (250,70,70), (50, 50), (350, 350), 4)

    if (TTT[0][2] == TTT[1][1] == TTT[2][0]) and (TTT[0][2] is not None):
        # game won diagonally right to left
        winner = TTT[0][2]
        pg.draw.line (screen, (250,70,70), (350, 50), (50, 350), 4)

    if(all([all(row) for row in TTT]) and winner is None ):
        draw = True
    draw_status()

The drawXO(row, col) function takes the row and column as parameters, where the user will click and then it draws the ‘X’ or ‘O’ mark. We need to calculate the x and y coordinates of the starting point from where we’ll draw the png image of the mark.

def drawXO(row,col):
    global TTT,XO
    if row==1:
        posx = 30
    if row==2:
        posx = width/3 + 30
    if row==3:
        posx = width/3*2 + 30

    if col==1:
        posy = 30
    if col==2:
        posy = height/3 + 30
    if col==3:
        posy = height/3*2 + 30
    TTT[row-1][col-1] = XO
    if(XO == 'x'):
        screen.blit(x_img,(posy,posx))
        XO= 'o'
    else:
        screen.blit(o_img,(posy,posx))
        XO= 'x'
    pg.display.update()
    #print(posx,posy)
    #print(TTT)

Every time the user presses the mouse button userClick() function is triggered. When the user clicks, we first take the x and y coordinates of where the mouse, and then if that place is not occupied we draw the ‘X’ or ‘O’ on the canvas. We also check if the player wins or not after drawing ‘X’ or ‘O’ on the board.

def userClick():
    #get coordinates of mouse click
    x,y = pg.mouse.get_pos()

    #get column of mouse click (1-3)
    if(x<width/3):
        col = 1
    elif (x<width/3*2):
        col = 2
    elif(x<width):
        col = 3
    else:
        col = None

    #get row of mouse click (1-3)
    if(y<height/3):
        row = 1
    elif (y<height/3*2):
        row = 2
    elif(y<height):
        row = 3
    else:
        row = None
    #print(row,col)

    if(row and col and TTT[row-1][col-1] is None):
        global XO

        #draw the x or o on screen
        drawXO(row,col)
        check_win()

The reset_game() will restart the game. And continue the game.

def reset_game():
    global TTT, winner,XO, draw
    time.sleep(3)
    XO = 'x'
    draw = False
    game_opening()
    winner=None
    TTT = [[None]*3,[None]*3,[None]*3]

    game_opening()

# run the game loop forever
while(True):
    for event in pg.event.get():
        if event.type == QUIT:
            pg.quit()
            sys.exit()
        elif event.type == MOUSEBUTTONDOWN:
            # the user clicked; place an X or O
            userClick()
            if(winner or draw):
                reset_game()

    pg.display.update()
    CLOCK.tick(fps)

Complete Code –

import pygame as pg,sys
from pygame.locals import *
import time


#initialize global variables
XO = 'x'
winner = None
draw = False
width = 400
height = 400
white = (255, 255, 255)
line_color = (10,10,10)

#TicTacToe 3x3 board
TTT = [[None]*3,[None]*3,[None]*3]

#initializing pygame window
pg.init()
fps = 30
CLOCK = pg.time.Clock()
screen = pg.display.set_mode((width, height+100),0,32)
pg.display.set_caption("Tic Tac Toe")

#loading the images
opening = pg.image.load('start game.png')
x_img = pg.image.load('x.png')
o_img = pg.image.load('o.png')

#resizing images
x_img = pg.transform.scale(x_img, (80,80))
o_img = pg.transform.scale(o_img, (80,80))
opening = pg.transform.scale(opening, (width, height+100))

def game_opening():
    screen.blit(opening,(0,0))
    pg.display.update()
    time.sleep(1)
    screen.fill(white)

    # Drawing vertical lines
    pg.draw.line(screen,line_color,(width/3,0),(width/3, height),7)
    pg.draw.line(screen,line_color,(width/3*2,0),(width/3*2, height),7)
    # Drawing horizontal lines
    pg.draw.line(screen,line_color,(0,height/3),(width, height/3),7)
    pg.draw.line(screen,line_color,(0,height/3*2),(width, height/3*2),7)
    draw_status()

def draw_status():
    global draw

    if winner is None:
        message = XO.upper() + "'s Turn"
    else:
        message = winner.upper() + " won!"
    if draw:
        message = 'Game Draw!'

    font = pg.font.Font(None, 30)
    text = font.render(message, 1, (255, 255, 255))

    # copy the rendered message onto the board
    screen.fill ((0, 0, 0), (0, 400, 500, 100))
    text_rect = text.get_rect(center=(width/2, 500-50))
    screen.blit(text, text_rect)
    pg.display.update()

def check_win():
    global TTT, winner,draw

    # check for winning rows
    for row in range (0,3):
        if ((TTT [row][0] == TTT[row][1] == TTT[row][2]) and(TTT [row][0] is not None)):
            # this row won
            winner = TTT[row][0]
            pg.draw.line(screen, (250,0,0), (0, (row + 1)*height/3 -height/6),\
                                (width, (row + 1)*height/3 - height/6 ), 4)
            break

    # check for winning columns
    for col in range (0, 3):
        if (TTT[0][col] == TTT[1][col] == TTT[2][col]) and (TTT[0][col] is not None):
            # this column won
            winner = TTT[0][col]
            #draw winning line
            pg.draw.line (screen, (250,0,0),((col + 1)* width/3 - width/6, 0),\
                            ((col + 1)* width/3 - width/6, height), 4)
            break

    # check for diagonal winners
    if (TTT[0][0] == TTT[1][1] == TTT[2][2]) and (TTT[0][0] is not None):
        # game won diagonally left to right
        winner = TTT[0][0]
        pg.draw.line (screen, (250,70,70), (50, 50), (350, 350), 4)

    if (TTT[0][2] == TTT[1][1] == TTT[2][0]) and (TTT[0][2] is not None):
        # game won diagonally right to left
        winner = TTT[0][2]
        pg.draw.line (screen, (250,70,70), (350, 50), (50, 350), 4)

    if(all([all(row) for row in TTT]) and winner is None ):
        draw = True
    draw_status()

def drawXO(row,col):
    global TTT,XO
    if row==1:
        posx = 30
    if row==2:
        posx = width/3 + 30
    if row==3:
        posx = width/3*2 + 30

    if col==1:
        posy = 30
    if col==2:
        posy = height/3 + 30
    if col==3:
        posy = height/3*2 + 30
    TTT[row-1][col-1] = XO
    if(XO == 'x'):
        screen.blit(x_img,(posy,posx))
        XO= 'o'
    else:
        screen.blit(o_img,(posy,posx))
        XO= 'x'
    pg.display.update()
    #print(posx,posy)
    #print(TTT)

def userClick():
    #get coordinates of mouse click
    x,y = pg.mouse.get_pos()

    #get column of mouse click (1-3)
    if(x<width/3):
        col = 1
    elif (x<width/3*2):
        col = 2
    elif(x<width):
        col = 3
    else:
        col = None

    #get row of mouse click (1-3)
    if(y<height/3):
        row = 1
    elif (y<height/3*2):
        row = 2
    elif(y<height):
        row = 3
    else:
        row = None

    if(row and col and TTT[row-1][col-1] is None):
        global XO

        #draw the x or o on screen
        drawXO(row,col)
        check_win()

def reset_game():
    global TTT, winner,XO, draw
    time.sleep(3)
    XO = 'x'
    draw = False
    game_opening()
    winner=None
    TTT = [[None]*3,[None]*3,[None]*3]

    game_opening()

# run the game loop forever
while(True):
    for event in pg.event.get():
        if event.type == QUIT:
            pg.quit()
            sys.exit()
        elif event.type == MOUSEBUTTONDOWN:
            # the user clicked; place an X or O
            userClick()
            if(winner or draw):
                reset_game()

    pg.display.update()
    CLOCK.tick(fps)

Output –

Start Tic Tac Toe Game
Final Output of Tic Tac Toe Game
nv-author-image

Kalpesh Khairnar

Hello Everyone, I'm Kalpesh. Energetic and passionate college student with the ability to work with open-source technologies. I spend most of my time in programming, blogging and helping other programming geeks.

Leave a Reply

Your email address will not be published. Required fields are marked *