Games
Memory Game in Python

Memory Game in Python

We will learn to create a memory Game in Python.

Table of Contents

Part I: Displaying the basic screen

import pygame, random,sys
from pygame.locals import *


#frames per second, the general speed of the program
FPS = 30
#window width in pixels
WINDOWWIDTH = 640
#Window height in pixels
WINDOWHEIGHT = 480
#Boxsize
BOXSIZE = 80
#GAPsize
GAPSIZE = 20
#Number of columns of icons 
BOARDWIDTH = 4
#Number of rows of icons
BOARDHEIGHT = 4

#Board need to have an even numberof boxes for pairs of matches
assert(BOARDWIDTH*BOARDHEIGHT)%2 == 0, "Board needs to have even number of Boxes"

XMARGIN = int((WINDOWWIDTH - (BOARDWIDTH*(BOXSIZE+GAPSIZE)))/2)
YMARGIN = int((WINDOWHEIGHT - (BOARDHEIGHT * (BOXSIZE+GAPSIZE)))/2)

#            R   G   B
GRAY     = (100,100,100)
NAVYBLUE = (60,60,100)
WHITE    = (255,255,255)
RED      = (255,0,0)
GREEN    = (0,255,0)
BLUE     = (0,0,255)
YELLOW   = (255,255,0)
ORANGE   = (255,128,0)
PURPLE   = (255,0,255)
CYAN     = (0,255,255)


BGCOLOR = NAVYBLUE
LIGHTBGCOLOR = GRAY
BOXCOLOR = WHITE
HIGHLIGHTCOLOR = BLUE



DONUT = 'donut'
SQUARE= ' square'
DIAMOND = 'diamond'
LINES = 'lines'
OVAL = 'oval'

ALLCOLORS = (RED,GREEN,BLUE,YELLOW,ORANGE,PURPLE,CYAN)
ALLSHAPES = (DONUT,SQUARE,DIAMOND,LINES,OVAL)

assert len(ALLCOLORS) * len(ALLSHAPES) * 2 >= BOARDHEIGHT*BOARDWIDTH, "Board is too big for the number of shapes/colors defined"


def main():
    global FPSCLOCK,DISPLAYSURF
    pygame.init()
    
    FPSCLOCK = pygame.time.Clock() #this will allow us to track time since last time the screen has been refreshed
    DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH,WINDOWHEIGHT))


    pygame.display.set_caption("Memory Game")


    while True:

        #Drawing the window
        DISPLAYSURF.fill(BGCOLOR)

        for event in pygame.event.get():
            if event.type == QUIT or (event.type == KEYUP and event.key == K_ESCAPE):
                pygame.quit()
                sys.exit()


        #Redraw the screen and wait a clock tick
        pygame.display.flip()
        FPSCLOCK.tick(FPS)


if __name__ == "__main__":
    main()
Game in Python

Part II: Adding Boxes

import pygame, random,sys
from pygame.locals import *


#frames per second, the general speed of the program
FPS = 30
#window width in pixels
WINDOWWIDTH = 640
#Window height in pixels
WINDOWHEIGHT = 480
#Boxsize
BOXSIZE = 90
#GAPsize
GAPSIZE = 20
#Number of columns of icons 
BOARDWIDTH = 4
#Number of rows of icons
BOARDHEIGHT = 4

#Board need to have an even numberof boxes for pairs of matches
assert(BOARDWIDTH*BOARDHEIGHT)%2 == 0, "Board needs to have even number of Boxes"

XMARGIN = int((WINDOWWIDTH - (BOARDWIDTH*(BOXSIZE+GAPSIZE)))/2)
YMARGIN = int((WINDOWHEIGHT - (BOARDHEIGHT * (BOXSIZE+GAPSIZE)))/2)
print("Xmargin",XMARGIN)
print("Ymargin",YMARGIN)


#            R   G   B
GRAY     = (100,100,100)
NAVYBLUE = (60,60,100)
WHITE    = (255,255,255)
RED      = (255,0,0)
GREEN    = (0,255,0)
BLUE     = (0,0,255)
YELLOW   = (255,255,0)
ORANGE   = (255,128,0)
PURPLE   = (255,0,255)
CYAN     = (0,255,255)


BGCOLOR = NAVYBLUE
LIGHTBGCOLOR = GRAY
BOXCOLOR = WHITE
HIGHLIGHTCOLOR = BLUE



DONUT = 'donut'
SQUARE= ' square'
DIAMOND = 'diamond'
LINES = 'lines'
OVAL = 'oval'

ALLCOLORS = (RED,GREEN,BLUE,YELLOW,ORANGE,PURPLE,CYAN)
ALLSHAPES = (DONUT,SQUARE,DIAMOND,LINES,OVAL)

assert len(ALLCOLORS) * len(ALLSHAPES) * 2 >= BOARDHEIGHT*BOARDWIDTH, "Board is too big for the number of shapes/colors defined"


def main():
    global FPSCLOCK,DISPLAYSURF
    pygame.init()
    
    FPSCLOCK = pygame.time.Clock() #this will allow us to track time since last time the screen has been refreshed
    DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH,WINDOWHEIGHT))


    pygame.display.set_caption("Memory Game")




    while True:

        #Drawing the window
        DISPLAYSURF.fill(BGCOLOR)

        drawBoard() #part 2

        for event in pygame.event.get():
            if event.type == QUIT or (event.type == KEYUP and event.key == K_ESCAPE):
                pygame.quit()
                sys.exit()


        #Redraw the screen and wait a clock tick
        pygame.display.flip()
        FPSCLOCK.tick(FPS)



def leftTopCoordsOfBox(boxx,boxy): #part 2
    #convert the board coordinates to pixel coordinates
    left = boxx * (BOXSIZE + GAPSIZE) + XMARGIN
    top = boxy * (BOXSIZE + GAPSIZE)+YMARGIN

    return (left,top)

def drawBoard(): #part 2
    #Draw all the boxes
    for boxx in range(BOARDWIDTH):
        for boxy in range(BOARDHEIGHT):
            left,top = leftTopCoordsOfBox(boxx,boxy)
           

            pygame.draw.rect(DISPLAYSURF,BOXCOLOR,(left,top,BOXSIZE,BOXSIZE))


if __name__ == "__main__":
    main()
Game in Python

Part III: Highlight Box when there is cursor over it.

#Inside main function
#Part 3
mousex = 0 #used to store x coordinate of mouse event
mousey = 0 #used to store y coordinate of mouse event
#changes inside the game loop
    while True:

        #Drawing the window
        DISPLAYSURF.fill(BGCOLOR)

        drawBoard() #part 2

        for event in pygame.event.get():
            if event.type == QUIT or (event.type == KEYUP and event.key == K_ESCAPE):
                pygame.quit()
                sys.exit()

            #part 3
            elif event.type == MOUSEMOTION:
                mousex,mousey = event.pos
                mouseClicked = True

        boxx,boxy = getBoxAtPixel(mousex,mousey)
        if boxx != None and boxy != None:
            drawHighlightBox(boxx,boxy)
#This function returns the column and row of the box according to 
#position of cursor
#Part 3
def getBoxAtPixel(x,y):
    for boxx in range(BOARDWIDTH):
        for boxy in range(BOARDHEIGHT):
            left,top = leftTopCoordsOfBox(boxx,boxy)
            boxRect = pygame.Rect(left,top,BOXSIZE,BOXSIZE)

            if boxRect.collidepoint(x,y):
                return(boxx,boxy)
            
    return (None,None)

#Highlights the given box by drawing a blue rectangle around it
def drawHighlightBox(boxx,boxy):
    left,top = leftTopCoordsOfBox(boxx,boxy)
    pygame.draw.rect(DISPLAYSURF,HIGHLIGHTCOLOR,(left-5,top-5,BOXSIZE+10,BOXSIZE+10),4)

The whole code.

#Part 3 highlighting the box with a cursor over it

import pygame, random,sys
from pygame.locals import *


#frames per second, the general speed of the program
FPS = 30
#window width in pixels
WINDOWWIDTH = 640
#Window height in pixels
WINDOWHEIGHT = 480
#Boxsize
BOXSIZE = 90
#GAPsize
GAPSIZE = 20
#Number of columns of icons 
BOARDWIDTH = 4
#Number of rows of icons
BOARDHEIGHT = 4

#Board need to have an even numberof boxes for pairs of matches
assert(BOARDWIDTH*BOARDHEIGHT)%2 == 0, "Board needs to have even number of Boxes"

XMARGIN = int((WINDOWWIDTH - (BOARDWIDTH*(BOXSIZE+GAPSIZE)))/2)
YMARGIN = int((WINDOWHEIGHT - (BOARDHEIGHT * (BOXSIZE+GAPSIZE)))/2)


#            R   G   B
GRAY     = (100,100,100)
NAVYBLUE = (60,60,100)
WHITE    = (255,255,255)
RED      = (255,0,0)
GREEN    = (0,255,0)
BLUE     = (0,0,255)
YELLOW   = (255,255,0)
ORANGE   = (255,128,0)
PURPLE   = (255,0,255)
CYAN     = (0,255,255)


BGCOLOR = NAVYBLUE
LIGHTBGCOLOR = GRAY
BOXCOLOR = WHITE
HIGHLIGHTCOLOR = BLUE



DONUT = 'donut'
SQUARE= ' square'
DIAMOND = 'diamond'
LINES = 'lines'
OVAL = 'oval'

ALLCOLORS = (RED,GREEN,BLUE,YELLOW,ORANGE,PURPLE,CYAN)
ALLSHAPES = (DONUT,SQUARE,DIAMOND,LINES,OVAL)

assert len(ALLCOLORS) * len(ALLSHAPES) * 2 >= BOARDHEIGHT*BOARDWIDTH, "Board is too big for the number of shapes/colors defined"


def main():
    global FPSCLOCK,DISPLAYSURF
    pygame.init()
    
    FPSCLOCK = pygame.time.Clock() #this will allow us to track time since last time the screen has been refreshed
    DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH,WINDOWHEIGHT))


    pygame.display.set_caption("Memory Game")

    #Part 3
    mousex = 0 #used to store x coordinate of mouse event
    mousey = 0 #used to store y coordinate of mouse event




    while True:

        #Drawing the window
        DISPLAYSURF.fill(BGCOLOR)

        drawBoard() #part 2

        for event in pygame.event.get():
            if event.type == QUIT or (event.type == KEYUP and event.key == K_ESCAPE):
                pygame.quit()
                sys.exit()

            #part 3
            elif event.type == MOUSEMOTION:
                mousex,mousey = event.pos
                mouseClicked = True

        boxx,boxy = getBoxAtPixel(mousex,mousey)
        if boxx != None and boxy != None:
            drawHighlightBox(boxx,boxy)

            


        #Redraw the screen and wait a clock tick
        pygame.display.flip()
        FPSCLOCK.tick(FPS)



def leftTopCoordsOfBox(boxx,boxy): #part 2
    #convert the board coordinates to pixel coordinates
    left = boxx * (BOXSIZE + GAPSIZE) + XMARGIN
    top = boxy * (BOXSIZE + GAPSIZE)+YMARGIN

    return (left,top)

#Part 3
def getBoxAtPixel(x,y):
    for boxx in range(BOARDWIDTH):
        for boxy in range(BOARDHEIGHT):
            left,top = leftTopCoordsOfBox(boxx,boxy)
            boxRect = pygame.Rect(left,top,BOXSIZE,BOXSIZE)

            if boxRect.collidepoint(x,y):
                return(boxx,boxy)
            
    return (None,None)

def drawHighlightBox(boxx,boxy):
    left,top = leftTopCoordsOfBox(boxx,boxy)
    pygame.draw.rect(DISPLAYSURF,HIGHLIGHTCOLOR,(left-5,top-5,BOXSIZE+10,BOXSIZE+10),4)

def drawBoard(): #part 2
    #Draw all the boxes
    for boxx in range(BOARDWIDTH):
        for boxy in range(BOARDHEIGHT):
            left,top = leftTopCoordsOfBox(boxx,boxy)
           

            pygame.draw.rect(DISPLAYSURF,BOXCOLOR,(left,top,BOXSIZE,BOXSIZE))


if __name__ == "__main__":
    main()

Part IV: Generating a random board

#function to generate a random board according to colors and shapes #defined
#part 4
def getRandomizedBoard():
    #Get a list of every possible shape in every possible color
    icons = []
    for color in ALLCOLORS:
        for shape in ALLSHAPES:
            icons.append((shape,color))

    random.shuffle(icons) #randomize the order of the icons list
    numIconsUsed = int(BOARDWIDTH*BOARDHEIGHT/2) #Calculate how many icons are needed
    icons = icons[:numIconsUsed]*2 #make two of each
    
    #Create the board data structure, with randomly placed icons
    board = []
    for x in range(BOARDWIDTH):
        column = []
        for y in range(BOARDHEIGHT):
            column.append(icons[0])
            del icons[0] #remove the icons as we assign them

        board.append(column)

    return board

Call this function from main function.

#Generating a random board
#Part 3 highlighting the box with a cursor over it

import pygame, random,sys
from pygame.locals import *


#frames per second, the general speed of the program
FPS = 30
#window width in pixels
WINDOWWIDTH = 640
#Window height in pixels
WINDOWHEIGHT = 480
#Boxsize
BOXSIZE = 90
#GAPsize
GAPSIZE = 20
#Number of columns of icons 
BOARDWIDTH = 4
#Number of rows of icons
BOARDHEIGHT = 4

#Board need to have an even numberof boxes for pairs of matches
assert(BOARDWIDTH*BOARDHEIGHT)%2 == 0, "Board needs to have even number of Boxes"

XMARGIN = int((WINDOWWIDTH - (BOARDWIDTH*(BOXSIZE+GAPSIZE)))/2)
YMARGIN = int((WINDOWHEIGHT - (BOARDHEIGHT * (BOXSIZE+GAPSIZE)))/2)


#            R   G   B
GRAY     = (100,100,100)
NAVYBLUE = (60,60,100)
WHITE    = (255,255,255)
RED      = (255,0,0)
GREEN    = (0,255,0)
BLUE     = (0,0,255)
YELLOW   = (255,255,0)
ORANGE   = (255,128,0)
PURPLE   = (255,0,255)
CYAN     = (0,255,255)


BGCOLOR = NAVYBLUE
LIGHTBGCOLOR = GRAY
BOXCOLOR = WHITE
HIGHLIGHTCOLOR = BLUE



DONUT = 'donut'
SQUARE= ' square'
DIAMOND = 'diamond'
LINES = 'lines'
OVAL = 'oval'

ALLCOLORS = (RED,GREEN,BLUE,YELLOW,ORANGE,PURPLE,CYAN)
ALLSHAPES = (DONUT,SQUARE,DIAMOND,LINES,OVAL)

assert len(ALLCOLORS) * len(ALLSHAPES) * 2 >= BOARDHEIGHT*BOARDWIDTH, "Board is too big for the number of shapes/colors defined"


def main():
    global FPSCLOCK,DISPLAYSURF
    pygame.init()
    
    FPSCLOCK = pygame.time.Clock() #this will allow us to track time since last time the screen has been refreshed
    DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH,WINDOWHEIGHT))


    pygame.display.set_caption("Memory Game")

    #Part 3
    mousex = 0 #used to store x coordinate of mouse event
    mousey = 0 #used to store y coordinate of mouse event

    #Part 4 
    #The function to generate a random board
    mainBoard = getRandomizedBoard()
    print("\n\n\n")
    print(mainBoard)




    while True:

        #Drawing the window
        DISPLAYSURF.fill(BGCOLOR)

        drawBoard() #part 2

        for event in pygame.event.get():
            if event.type == QUIT or (event.type == KEYUP and event.key == K_ESCAPE):
                pygame.quit()
                sys.exit()

            #part 3
            elif event.type == MOUSEMOTION:
                mousex,mousey = event.pos
                mouseClicked = True

        boxx,boxy = getBoxAtPixel(mousex,mousey)
        if boxx != None and boxy != None:
            drawHighlightBox(boxx,boxy)

            


        #Redraw the screen and wait a clock tick
        pygame.display.flip()
        FPSCLOCK.tick(FPS)


#part 4
def getRandomizedBoard():
    #Get a list of every possible shape in every possible color
    icons = []
    for color in ALLCOLORS:
        for shape in ALLSHAPES:
            icons.append((shape,color))

    random.shuffle(icons) #randomize the order of the icons list
    numIconsUsed = int(BOARDWIDTH*BOARDHEIGHT/2) #Calculate how many icons are needed
    icons = icons[:numIconsUsed]*2 #make two of each
    
    #Create the board data structure, with randomly placed icons
    board = []
    for x in range(BOARDWIDTH):
        column = []
        for y in range(BOARDHEIGHT):
            column.append(icons[0])
            del icons[0] #remove the icons as we assign them

        board.append(column)

    return board



def leftTopCoordsOfBox(boxx,boxy): #part 2
    #convert the board coordinates to pixel coordinates
    left = boxx * (BOXSIZE + GAPSIZE) + XMARGIN
    top = boxy * (BOXSIZE + GAPSIZE)+YMARGIN

    return (left,top)

#Part 3
def getBoxAtPixel(x,y):
    for boxx in range(BOARDWIDTH):
        for boxy in range(BOARDHEIGHT):
            left,top = leftTopCoordsOfBox(boxx,boxy)
            boxRect = pygame.Rect(left,top,BOXSIZE,BOXSIZE)

            if boxRect.collidepoint(x,y):
                return(boxx,boxy)
            
    return (None,None)

def drawHighlightBox(boxx,boxy):
    left,top = leftTopCoordsOfBox(boxx,boxy)
    pygame.draw.rect(DISPLAYSURF,HIGHLIGHTCOLOR,(left-5,top-5,BOXSIZE+10,BOXSIZE+10),4)

def drawBoard(): #part 2
    #Draw all the boxes
    for boxx in range(BOARDWIDTH):
        for boxy in range(BOARDHEIGHT):
            left,top = leftTopCoordsOfBox(boxx,boxy)
           

            pygame.draw.rect(DISPLAYSURF,BOXCOLOR,(left,top,BOXSIZE,BOXSIZE))


if __name__ == "__main__":
    main()

Part V: Organizing

Moving functions and constants to different file.

#memory_functions.py
import pygame,random
from memory_constants import *

#part 4
def getRandomizedBoard():
    #Get a list of every possible shape in every possible color
    icons = []
    for color in ALLCOLORS:
        for shape in ALLSHAPES:
            icons.append((shape,color))

    random.shuffle(icons) #randomize the order of the icons list
    numIconsUsed = int(BOARDWIDTH*BOARDHEIGHT/2) #Calculate how many icons are needed
    icons = icons[:numIconsUsed]*2 #make two of each
    
    #Create the board data structure, with randomly placed icons
    board = []
    for x in range(BOARDWIDTH):
        column = []
        for y in range(BOARDHEIGHT):
            column.append(icons[0])
            del icons[0] #remove the icons as we assign them

        board.append(column)

    return board



def leftTopCoordsOfBox(boxx,boxy): #part 2
    #convert the board coordinates to pixel coordinates
    left = boxx * (BOXSIZE + GAPSIZE) + XMARGIN
    top = boxy * (BOXSIZE + GAPSIZE)+YMARGIN

    return (left,top)

#Part 3
def getBoxAtPixel(x,y):
    for boxx in range(BOARDWIDTH):
        for boxy in range(BOARDHEIGHT):
            left,top = leftTopCoordsOfBox(boxx,boxy)
            boxRect = pygame.Rect(left,top,BOXSIZE,BOXSIZE)

            if boxRect.collidepoint(x,y):
                return(boxx,boxy)
            
    return (None,None)

def drawHighlightBox(boxx,boxy):
    left,top = leftTopCoordsOfBox(boxx,boxy)
    pygame.draw.rect(DISPLAYSURF,HIGHLIGHTCOLOR,(left-5,top-5,BOXSIZE+10,BOXSIZE+10),4)

def drawBoard(): #part 2
    #Draw all the boxes
    for boxx in range(BOARDWIDTH):
        for boxy in range(BOARDHEIGHT):
            left,top = leftTopCoordsOfBox(boxx,boxy)
           

            pygame.draw.rect(DISPLAYSURF,BOXCOLOR,(left,top,BOXSIZE,BOXSIZE))

#memory_constant.py
import pygame
from pygame.locals import *


#frames per second, the general speed of the program
FPS = 30
#window width in pixels
WINDOWWIDTH = 640
#Window height in pixels
WINDOWHEIGHT = 480
#Boxsize
BOXSIZE = 90
#GAPsize
GAPSIZE = 20
#Number of columns of icons 
BOARDWIDTH = 4
#Number of rows of icons
BOARDHEIGHT = 4

#Board need to have an even numberof boxes for pairs of matches
assert(BOARDWIDTH*BOARDHEIGHT)%2 == 0, "Board needs to have even number of Boxes"

XMARGIN = int((WINDOWWIDTH - (BOARDWIDTH*(BOXSIZE+GAPSIZE)))/2)
YMARGIN = int((WINDOWHEIGHT - (BOARDHEIGHT * (BOXSIZE+GAPSIZE)))/2)


#            R   G   B
GRAY     = (100,100,100)
NAVYBLUE = (60,60,100)
WHITE    = (255,255,255)
RED      = (255,0,0)
GREEN    = (0,255,0)
BLUE     = (0,0,255)
YELLOW   = (255,255,0)
ORANGE   = (255,128,0)
PURPLE   = (255,0,255)
CYAN     = (0,255,255)


BGCOLOR = NAVYBLUE
LIGHTBGCOLOR = GRAY
BOXCOLOR = WHITE
HIGHLIGHTCOLOR = BLUE



DONUT = 'donut'
SQUARE= ' square'
DIAMOND = 'diamond'
LINES = 'lines'
OVAL = 'oval'

ALLCOLORS = (RED,GREEN,BLUE,YELLOW,ORANGE,PURPLE,CYAN)
ALLSHAPES = (DONUT,SQUARE,DIAMOND,LINES,OVAL)

DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH,WINDOWHEIGHT))
assert len(ALLCOLORS) * len(ALLSHAPES) * 2 >= BOARDHEIGHT*BOARDWIDTH, "Board is too big for the number of shapes/colors defined"

#Main program for game
import pygame, random,sys
from pygame.locals import *

from memory_functions import *
from memory_constants import *

def main():
    global FPSCLOCK,DISPLAYSURF
    pygame.init()
    
    FPSCLOCK = pygame.time.Clock() #this will allow us to track time since last time the screen has been refreshed
    
    pygame.display.set_caption("Memory Game")
  
    mousex = 0 #used to store x coordinate of mouse event
    mousey = 0 #used to store y coordinate of mouse event

    #The function to generate a random board
    mainBoard = getRandomizedBoard()
    print("\n\n\n")
    print(mainBoard)

    while True:

        #Drawing the window
        DISPLAYSURF.fill(BGCOLOR)

        drawBoard() 

        for event in pygame.event.get():
            if event.type == QUIT or (event.type == KEYUP and event.key == K_ESCAPE):
                pygame.quit()
                sys.exit()

         
            elif event.type == MOUSEMOTION:
                mousex,mousey = event.pos
                mouseClicked = True

        boxx,boxy = getBoxAtPixel(mousex,mousey)
        if boxx != None and boxy != None:
            drawHighlightBox(boxx,boxy)

        
        #Redraw the screen and wait a clock tick
        pygame.display.flip()
        FPSCLOCK.tick(FPS)


if __name__ == "__main__":
    main()

Part VI: Drawing Icons on the Board

#Functions to draw icons
#drawing Icons
def drawIcon(shape, color, boxx,boxy):

    quater = int(BOXSIZE * 0.25)
    half = int(BOXSIZE * 0.5)

    left, top = leftTopCoordsOfBox(boxx,boxy)

    #draw the shapes
    if shape == DONUT:
        pygame.draw.circle(DISPLAYSURF,color,(left+half,top+half),half-5)
        pygame.draw.circle(DISPLAYSURF,BGCOLOR,(left+half,top+half),quater-5)

    elif shape == SQUARE:
        pygame.draw.rect(DISPLAYSURF,color,(left+quater,top+quater,BOXSIZE-half,BOXSIZE-half))

    elif shape == DIAMOND:
        pygame.draw.polygon(DISPLAYSURF,color,((left+half,top),(left+BOXSIZE-1,top+half),(left+half,top+BOXSIZE-1),(left,top+half)))

    elif shape == LINES:
        for i in range(0,BOXSIZE,4):
            pygame.draw.line(DISPLAYSURF,color,(left,top+i),(left+i,top))
            pygame.draw.line(DISPLAYSURF,color,(left+i,top+BOXSIZE-1),(left+BOXSIZE-1,top+i))

    elif shape == OVAL:
        pygame.draw.ellipse(DISPLAYSURF,color,(left,top+quater,BOXSIZE,half))
#Function to generate the shape and color of box

def getShapeAndColor(board,boxx,boxy):
    return board[boxx][boxy][0],board[boxx][boxy][1]
#Function to draw the icons on the board 

#drawRevealed Board
def drawRevealedBoard(board):
    for boxx in range(BOARDWIDTH):
        for boxy in range(BOARDHEIGHT):
            
            shape,color = getShapeAndColor(board,boxx,boxy)
            drawIcon(shape,color,boxx,boxy)

Current Board Data

Current Board

Part VII: Reveal with a click

Start by combining the two functions: drawRevelBoard() and drawBoard()

def drawBoard(board,revealed):
 
    #Draw all the boxes in their covered or revealed state
    for boxx in range(BOARDWIDTH):
        for boxy in range(BOARDHEIGHT):
            left,top = leftTopCoordsOfBox(boxx,boxy)
            if not revealed[boxx][boxy]:
                #Draw a covered box
                pygame.draw.rect(DISPLAYSURF,BOXCOLOR,(left,top,BOXSIZE,BOXSIZE))

            else:
                #Draw the (revealed icon)
                shape, color = getShapeAndColor(board,boxx,boxy)
                drawIcon(shape,color,boxx,boxy)

The =n following two functions will help us to animate the revealing of icons

def drawBoxCovers(board,boxes,coverage):
    for box in boxes:
        left, top = leftTopCoordsOfBox(box[0],box[1])
        pygame.draw.rect(DISPLAYSURF,BGCOLOR,(left,top,BOXSIZE,BOXSIZE))
        shape,color = getShapeAndColor(board,box[0],box[1])
        drawIcon(shape,color,box[0],box[1])

        if coverage > 0:
            pygame.draw.rect(DISPLAYSURF,BOXCOLOR,(left,top,coverage,BOXSIZE))


    pygame.display.update()
    FPSCLOCK.tick(FPS)


#Animating the revealbox 
def revealBoxesAnimation(board,boxesToReveal):
    #do the box reveal animation
    for coverage in range(BOXSIZE,(-REVEALSPEED)-1,-REVEALSPEED):
        drawBoxCovers(board,boxesToReveal,coverage)

The main code:For now we will set all the boxes as not revealed with

revealedBoxes = generateRevealedBoxesData(False) . We will check whether the current box has been clicked or not.

After clicking we call the revealBoxesAnimation(mainBoard,[(boxx,boxy)]) function.

import pygame, random,sys
from pygame.locals import *

from memory_functions import *
from memory_constants import *

def main():
    
    pygame.init()
    
     #this will allow us to track time since last time the screen has been refreshed
    
    pygame.display.set_caption("Memory Game")
  
    mousex = 0 #used to store x coordinate of mouse event
    mousey = 0 #used to store y coordinate of mouse event

    #The function to generate a random board
    mainBoard = getRandomizedBoard()
    
    #part 7
    revealedBoxes = generateRevealedBoxesData(False) 

    while True:

        #Drawing the window
        DISPLAYSURF.fill(BGCOLOR)
        

        #drawRevealedBoard(mainBoard)
        drawBoard(mainBoard,revealedBoxes)
         

        for event in pygame.event.get():
            if event.type == QUIT or (event.type == KEYUP and event.key == K_ESCAPE):
                pygame.quit()
                sys.exit()

            
            elif event.type == MOUSEBUTTONUP:
                mousex,mousey = event.pos
                mouseClicked = True

        boxx,boxy = getBoxAtPixel(mousex,mousey)
        if boxx != None and boxy != None:

            #part 7
            if not revealedBoxes[boxx][boxy]:
                drawHighlightBox(boxx,boxy)

            if not revealedBoxes[boxx][boxy] and mouseClicked:
                revealBoxesAnimation(mainBoard,[(boxx,boxy)])


        
        #Redraw the screen and wait a clock tick
        pygame.display.flip()
        FPSCLOCK.tick(FPS)


if __name__ == "__main__":
    main()

Part VIII: Comparing the icons of two boxes

The first clicked box will be revealed. The second clicked box will also be revealed. We will compare the the shape and colors of icons under first and second boxes. If the match, we will leave them in revealed condition, else we will call coverBoxesAnimation(board,boxesToCover) to cover them.

Changes to main.py

................
def main():
    
    pygame.init()
    pygame.display.set_caption("Memory Game")
  
    mousex = 0 #used to store x coordinate of mouse event
    mousey = 0 #used to store y coordinate of mouse event

    #The function to generate a random board
    mainBoard = getRandomizedBoard()
    
    #part 7
    revealedBoxes = generateRevealedBoxesData(False) 

    #part 8
    #store the (x,y) of the first box clicked
    firstSelection = None


    while True:

        #part 8
        mouseClicked = False
        #Drawing the window
        DISPLAYSURF.fill(BGCOLOR)
        drawBoard(mainBoard,revealedBoxes)
         

        for event in pygame.event.get():
            if event.type == QUIT or (event.type == KEYUP and event.key == K_ESCAPE):
                pygame.quit()
                sys.exit()

            
            elif event.type == MOUSEMOTION:
                mousex,mousey = event.pos
            elif event.type == MOUSEBUTTONUP:
                mousex,mousey = event.pos
                mouseClicked = True

        boxx,boxy = getBoxAtPixel(mousex,mousey)
        if boxx != None and boxy != None:

            #part 7
            if not revealedBoxes[boxx][boxy]:
                drawHighlightBox(boxx,boxy)

            if not revealedBoxes[boxx][boxy] and mouseClicked:
                revealBoxesAnimation(mainBoard,[(boxx,boxy)])

                #part 8
                #set the box as revealed
                revealedBoxes[boxx][boxy]=True

                if firstSelection == None:#the current box was the first box clicked
                    firstSelection = (boxx,boxy)

                else:#the current box was the second box clicked
                    #check if there is a match between the two icons
                    icon1Shape, icon1Color = getShapeAndColor(mainBoard,firstSelection[0],firstSelection[1])
                    icon2Shape, icon2Color = getShapeAndColor(mainBoard,boxx,boxy)

                    if icon1Shape != icon2Shape or icon1Color != icon2Color:
                        #icons do not match recover up both selections
                        coverBoxesAnimation(mainBoard,[(firstSelection[0],firstSelection[1]),(boxx,boxy)])

                        revealedBoxes[firstSelection[0]][firstSelection[1]] = False
                        revealedBoxes[boxx][boxy] = False

                    firstSelection = None

...........................

Defining coverBoxesAnimation(board,boxesToCover):


def coverBoxesAnimation(board,boxesToCover):
    for coverage in range(BOXSIZE,(-REVEALSPEED)-1,-REVEALSPEED):
        drawBoxCovers(board,boxesToCover,coverage)

Part IX: Game Completion

We import tkinter to display message.

.................................................
from tkinter import *
from tkinter import messagebox
Tk().wm_withdraw() #to hide the main window
...............................................

elif hasWon(revealedBoxes):
       print("You did it")
                        
       messagebox.showinfo('Winner','You did it')

..................................
#has won function
def hasWon(revealedBoxes):
    #Return True if all the boxes have been revealed
    for i in revealedBoxes:
        if False in i:
            return False

    return True 

Part X: Displaying Time of Completion

Add tic before while and toc after winning.

 import time
......................................
tic = time.time()
..............................................................
elif hasWon(revealedBoxes):
                        toc = time.time()
                        print(toc)
                        print("You did it")
                        string = "You completed in " + str(math.floor(toc-tic))+"seconds"
                        
                        messagebox.showinfo('Winner',string)
                        #wait for a second and end the program
                        pygame.time.wait(1000)
                        pygame.quit()
                        sys.exit()

Edit gg and add one more shuffle

def getRandomizedBoard():
    #Get a list of every possible shape in every possible color
    icons = []
    for color in ALLCOLORS:
        for shape in ALLSHAPES:
            icons.append((shape,color))

    random.shuffle(icons) #randomize the order of the icons list
    numIconsUsed = int(BOARDWIDTH*BOARDHEIGHT/2) #Calculate how many icons are needed
    icons = icons[:numIconsUsed]*2 #make two of each
    random.shuffle(icons)#added later
    
    #Create the board data structure, with randomly placed icons
    board = []
    for x in range(BOARDWIDTH):
        column = []
        for y in range(BOARDHEIGHT):
            column.append(icons[0])
            del icons[0] #remove the icons as we assign them

        board.append(column)

    return board

Final Output

Part XI: Final Complete Code

#memory_constants.py
import pygame
from pygame.locals import *


#frames per second, the general speed of the program
FPS = 30
#window width in pixels
WINDOWWIDTH = 640
#Window height in pixels
WINDOWHEIGHT = 480
#Boxsize
BOXSIZE = 90
#GAPsize
GAPSIZE = 20
#Number of columns of icons 
BOARDWIDTH = 4
#Number of rows of icons
BOARDHEIGHT = 4

#Board need to have an even numberof boxes for pairs of matches
assert(BOARDWIDTH*BOARDHEIGHT)%2 == 0, "Board needs to have even number of Boxes"

XMARGIN = int((WINDOWWIDTH - (BOARDWIDTH*(BOXSIZE+GAPSIZE)))/2)
YMARGIN = int((WINDOWHEIGHT - (BOARDHEIGHT * (BOXSIZE+GAPSIZE)))/2)


#            R   G   B
GRAY     = (100,100,100)
NAVYBLUE = (60,60,100)
WHITE    = (255,255,255)
RED      = (255,0,0)
GREEN    = (0,255,0)
BLUE     = (0,0,255)
YELLOW   = (255,255,0)
ORANGE   = (255,128,0)
PURPLE   = (255,0,255)
CYAN     = (0,255,255)


BGCOLOR = NAVYBLUE
LIGHTBGCOLOR = GRAY
BOXCOLOR = WHITE
HIGHLIGHTCOLOR = BLUE



DONUT = 'donut'
SQUARE= ' square'
DIAMOND = 'diamond'
LINES = 'lines'
OVAL = 'oval'

ALLCOLORS = (RED,GREEN,BLUE,YELLOW,ORANGE,PURPLE,CYAN)
ALLSHAPES = (DONUT,SQUARE,DIAMOND,LINES,OVAL)

DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH,WINDOWHEIGHT))
FPSCLOCK = pygame.time.Clock()

#speed of boxes sliding reveals and covers
REVEALSPEED = 8

assert len(ALLCOLORS) * len(ALLSHAPES) * 2 >= BOARDHEIGHT*BOARDWIDTH, "Board is too big for the number of shapes/colors defined"

#memory_functions.py
import pygame,random
from memory_constants import *

#part 4
def getRandomizedBoard():
    #Get a list of every possible shape in every possible color
    icons = []
    for color in ALLCOLORS:
        for shape in ALLSHAPES:
            icons.append((shape,color))

    random.shuffle(icons) #randomize the order of the icons list
    numIconsUsed = int(BOARDWIDTH*BOARDHEIGHT/2) #Calculate how many icons are needed
    icons = icons[:numIconsUsed]*2 #make two of each
    random.shuffle(icons)
    
    #Create the board data structure, with randomly placed icons
    board = []
    for x in range(BOARDWIDTH):
        column = []
        for y in range(BOARDHEIGHT):
            column.append(icons[0])
            del icons[0] #remove the icons as we assign them

        board.append(column)

    return board



def leftTopCoordsOfBox(boxx,boxy): #part 2
    #convert the board coordinates to pixel coordinates
    left = boxx * (BOXSIZE + GAPSIZE) + XMARGIN
    top = boxy * (BOXSIZE + GAPSIZE)+YMARGIN

    return (left,top)

#Part 3
def getBoxAtPixel(x,y):
    for boxx in range(BOARDWIDTH):
        for boxy in range(BOARDHEIGHT):
            left,top = leftTopCoordsOfBox(boxx,boxy)
            boxRect = pygame.Rect(left,top,BOXSIZE,BOXSIZE)

            if boxRect.collidepoint(x,y):
                return(boxx,boxy)
            
    return (None,None)

def drawHighlightBox(boxx,boxy):
    left,top = leftTopCoordsOfBox(boxx,boxy)
    pygame.draw.rect(DISPLAYSURF,HIGHLIGHTCOLOR,(left-5,top-5,BOXSIZE+10,BOXSIZE+10),4)



#drawing Icons
def drawIcon(shape, color, boxx,boxy):

    quater = int(BOXSIZE * 0.25)
    half = int(BOXSIZE * 0.5)

    left, top = leftTopCoordsOfBox(boxx,boxy)

    #draw the shapes
    if shape == DONUT:
        pygame.draw.circle(DISPLAYSURF,color,(left+half,top+half),half-5)
        pygame.draw.circle(DISPLAYSURF,BGCOLOR,(left+half,top+half),quater-5)

    elif shape == SQUARE:
        pygame.draw.rect(DISPLAYSURF,color,(left+quater,top+quater,BOXSIZE-half,BOXSIZE-half))

    elif shape == DIAMOND:
        pygame.draw.polygon(DISPLAYSURF,color,((left+half,top),(left+BOXSIZE-1,top+half),(left+half,top+BOXSIZE-1),(left,top+half)))

    elif shape == LINES:
        for i in range(0,BOXSIZE,4):
            pygame.draw.line(DISPLAYSURF,color,(left,top+i),(left+i,top))
            pygame.draw.line(DISPLAYSURF,color,(left+i,top+BOXSIZE-1),(left+BOXSIZE-1,top+i))

    elif shape == OVAL:
        pygame.draw.ellipse(DISPLAYSURF,color,(left,top+quater,BOXSIZE,half))


def getShapeAndColor(board,boxx,boxy):
    return board[boxx][boxy][0],board[boxx][boxy][1]




#generate revealedBox data
def generateRevealedBoxesData(val):
    revealedBoxes = []
    for i in range(BOARDWIDTH):
        revealedBoxes.append([val]*BOARDHEIGHT)

    return revealedBoxes



#part 7
def drawBoard(board,revealed):
 
    #Draw all the boxes in their covered or revealed state
    for boxx in range(BOARDWIDTH):
        for boxy in range(BOARDHEIGHT):
            left,top = leftTopCoordsOfBox(boxx,boxy)
            if not revealed[boxx][boxy]:
                #Draw a covered box
                pygame.draw.rect(DISPLAYSURF,BOXCOLOR,(left,top,BOXSIZE,BOXSIZE))

            else:
                #Drwa the (revealed icon)
                shape, color = getShapeAndColor(board,boxx,boxy)
                drawIcon(shape,color,boxx,boxy)



def drawBoxCovers(board,boxes,coverage):
    for box in boxes:
        left, top = leftTopCoordsOfBox(box[0],box[1])
        pygame.draw.rect(DISPLAYSURF,BGCOLOR,(left,top,BOXSIZE,BOXSIZE))
        shape,color = getShapeAndColor(board,box[0],box[1])
        drawIcon(shape,color,box[0],box[1])

        if coverage > 0:
            pygame.draw.rect(DISPLAYSURF,BOXCOLOR,(left,top,coverage,BOXSIZE))


    pygame.display.update()
    FPSCLOCK.tick(FPS)


#Animating the revealbox 
def revealBoxesAnimation(board,boxesToReveal):
    #do the box reveal animation
    for coverage in range(BOXSIZE,(-REVEALSPEED)-1,-REVEALSPEED):
        drawBoxCovers(board,boxesToReveal,coverage)



def coverBoxesAnimation(board,boxesToCover):
    for coverage in range(BOXSIZE,(-REVEALSPEED)-1,-REVEALSPEED):
        drawBoxCovers(board,boxesToCover,coverage)

#Part 9
def hasWon(revealedBoxes):
    #Return True if all the boxes have been revealed
    for i in revealedBoxes:
        if False in i:
            return False

    return True 
#main.py
import pygame, random,sys
from pygame.locals import *
from tkinter import *
from tkinter import messagebox
Tk().wm_withdraw() #to hide the main window
import time
import math


from memory_functions import *
from memory_constants import *

def main():
    
    pygame.init()
    pygame.display.set_caption("Memory Game")
  
    mousex = 0 #used to store x coordinate of mouse event
    mousey = 0 #used to store y coordinate of mouse event

    #The function to generate a random board
    mainBoard = getRandomizedBoard()
    
    #part 7
    revealedBoxes = generateRevealedBoxesData(False) 

    #part 8
    #store the (x,y) of the first box clicked
    firstSelection = None
    tic = time.time()


    while True:
        
        #part 8
        mouseClicked = False
        #Drawing the window
        DISPLAYSURF.fill(BGCOLOR)
        drawBoard(mainBoard,revealedBoxes)
         

        for event in pygame.event.get():
            if event.type == QUIT or (event.type == KEYUP and event.key == K_ESCAPE):
                pygame.quit()
                sys.exit()

            
            elif event.type == MOUSEMOTION:
                mousex,mousey = event.pos
            elif event.type == MOUSEBUTTONUP:
                mousex,mousey = event.pos
                mouseClicked = True

        boxx,boxy = getBoxAtPixel(mousex,mousey)
        if boxx != None and boxy != None:

            #part 7
            if not revealedBoxes[boxx][boxy]:
                drawHighlightBox(boxx,boxy)

            if not revealedBoxes[boxx][boxy] and mouseClicked:
                revealBoxesAnimation(mainBoard,[(boxx,boxy)])

                #part 8
                #set the box as revealed
                revealedBoxes[boxx][boxy]=True

                if firstSelection == None:#the current box was the first box clicked
                    firstSelection = (boxx,boxy)

                else:#the current box was the second box clicked
                    #check if there is a match between the two icons
                    icon1Shape, icon1Color = getShapeAndColor(mainBoard,firstSelection[0],firstSelection[1])
                    icon2Shape, icon2Color = getShapeAndColor(mainBoard,boxx,boxy)
                    

                    if icon1Shape != icon2Shape or icon1Color != icon2Color:
                        #icons do not match recover up both selections
                        pygame.time.wait(1000)
                        coverBoxesAnimation(mainBoard,[(firstSelection[0],firstSelection[1]),(boxx,boxy)])

                        revealedBoxes[firstSelection[0]][firstSelection[1]] = False
                        revealedBoxes[boxx][boxy] = False

                    #part 9
                        
                    elif hasWon(revealedBoxes):
                        toc = time.time()
                        print(toc)
                        print("You did it")
                        string = "You completed in " + str(math.floor(toc-tic))+"seconds"
                        
                        messagebox.showinfo('Winner',string)
                        #wait for a second and end the program
                        pygame.time.wait(1000)
                        pygame.quit()
                        sys.exit()
                        
                    firstSelection = None

                     


        
        #Redraw the screen and wait a clock tick
        pygame.display.flip()
        FPSCLOCK.tick(FPS)


if __name__ == "__main__":
    main()