Tic Tac Toe Game in Python

What's up HIVE Community, πŸ‘‹πŸ‘‹ For quite some time now I have been procrastinating getting started with sharing some different beginner programming projects.

I decided to start with sharing this small Python Project I was working on yesterday. It is pretty straightforward, probably some of you have tried the implementing the game out in different programming languages.

I will provide the image files at the end of the post for anyone interested in aggregating the different code blocks and giving the game a try.

I am currently working on my Python skills, will be sharing more projects in the community for anyone also learning Python.

image.png

Tic-Tac-Toe | Python

Steps To Build a Python Tic Tac Toe Game

  1. Create the display window for the game.
  2. Draw the grid on the canvas where to play the Tic Tac Toe Game.
  3. Draw a status bar below the canvas to show which player's turn it is and who wins the game.
  4. Reset the game when a person wins or both players draw.

1. Initializing the Game Components

  • Start by importing the pygame and time library.
import pygame as pg,sys
from pygame.locals import *
import time
  • The time library provides us with the time.sleep() method to pause the game.
  • Also initialize all the Global Variables to be used in the reset of the code.
#initialize the 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]
  • TTT is the main 3Γ—3 Tic Tac Toe board. The height and width of the canvas where to play the game are 400Γ—400.

2. Intializing the Display Window

  • Use pygame to initialize a new window on where to play the game.
pg.init()
fps = 30
CLOCK = pg.time.Clock()
  • The pg.display.set_mode() initializes the display, reference it with the
    screen variable. This screen variable will be used to draw something on the display.
screen = pg.display.set_mode((width, height+100),0,32)
  • The pg.display.set_caption() method is used to set a name that will appear at
    the top of the display window.
pg.display.set_caption("Tic Tac Toe")

3. Loading and Transforming images

The game requires 3 Images:

  • Opening Image which loads when the game start or resets.
  • X Image which will draw Xwhen mouse clicks on the game grid.
  • O Image which will draw O when mouse clicks on the game grid.
#Loading the Images
opening = pg.image.load('opening.png')
x_img = pg.image.load('x.png')
o_img = pg.image.load('o.png')

The images have to be resized to fit in the display window previously initialized.

#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))

4. Defining The Necessary Functions

game_opening() Function that Starts and Restarts the game

  • pygame provides the function blit() which allows drawing an image on top of another image.

  • Draw the opening image, after which the display has to be always updated using pg.display.update()

  • Using time.sleep(1) wait for 1 second and fill the screen with white color

  • To make the 3x3 grid, draw 2 horizonatal and vertical lines on the white background.

# Function that starts and restarts the game
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()

draw_status() Function

  • The function draws a black rectangle where the update status updates, showing which player’s turn is it and whether the game ends or draws.
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()

check_win() Function

  • The function checks 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 a mark β€˜X’ or
    β€˜O’ is drawn 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()

drawXO(row, col) Function

  • The function takes the row and column where the mouse is
    clicked and then it draws the β€˜X’ or β€˜O’ mark.
  • Calculate the x and y coordinates of the starting point from where to 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()

userClick() Function

  • The function is triggered every time the user presses the mouse button.
  • When the user clicks the mouse, first take the x and y coordinates of where the mouse is clicked on the display window and then if that place is not occupied draw the β€˜XO’ on the canvas.
  • Check if the player wins or not after drawing β€˜XO’ on the board.
def userClick():

    # Get coordinates of mouse click
    x,y = pg.mouse.get_pos()

    # Get column of mouse click
    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
    if (y < height/3):
        row = 1
    elif (y < height / 3 * 2):
        row = 2
    elif (y < height):
        row = 3
    else:
        row = None
    
    # Print row and 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()

reset_game() Function

The function restarts the game and also
resets all the variables to the beginning of 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()

5. The last part: Loop the game infinitely

  • To start the game, invoke game_opening() function.

  • Then, run an infinite loop and continuously check for any event made by the user.

    • If the user presses mouse button, the MOUSEBUTTONDOWN event will be captured and then trigger the userClick() function.
    • If the user wins or the game draws, reset the game by invoking reset_game() function.
  • In each iteration the display is updated, having set the frames per second to 30.

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)

πŸ₯³πŸ₯³ The game is complete and ready to play.

The expected output on running the code. πŸ‘‡

image.png

image.png

As promised here are the image files necessary for successful running of the game. πŸ‘‡πŸ‘‡

o.png

x.png

opening.png



Inspiration and Image files from Tech Vidvan


See you in my next update! Cheers ✌️




H2
H3
H4
3 columns
2 columns
1 column
5 Comments
Ecency