Saturday, July 19, 2025
HomeLanguagesSlide Puzzle using PyGame – Python

Slide Puzzle using PyGame – Python

Slide Puzzle game is a 2-dimensional game i.e the pieces can only be removed inside the grid and reconfigured by sliding them into an empty spot. The slide puzzle game developed here is a 3X3 grid game i.e 9 cells will be there from 1 to 8(9 is not here since a blank cell is needed for sliding the neighboring cells). In this article, we are going to see how to create a slide puzzle in Pygame using Python.

Slide_game Python

Importing necessary modules 

Python3




# Code for the sliding puzzle game in python using pygame.
import pygame
import random
import pyautogui
from pygame.locals import *


Call the main function 

  • The main() function is called to start the program i.e initialize the necessary variables

Python3




class Tiles:
    # main method for initializing different variables
    def __init__(self, screen, start_position_x,
                 start_position_y,
                 num, mat_pos_x,
                 mat_pos_y):
       
        self.color = (0, 255, 0)
        # complete screen
        self.screen = screen
        # screen(x)
        self.start_pos_x = start_position_x
        # screen(y)
        self.start_pos_y = start_position_y
        # total nums
        self.num = num
        # width of each tile
        self.width = tile_width
        # depth of each tile(shadow)
        self.depth = tile_depth
        # tile selected false
        self.selected = False
        # matrix alignment from screen w.r.t x coordinate
        self.position_x = mat_pos_x
        # matrix alignment from screen w.r.t y coordinate
        self.position_y = mat_pos_y
        # tile movable false in its initial state
        self.movable = False


draw_tyle() method for drawing the tiles in the grid :

  • Draw rectangles using .rect(). Pass length, breadth, width & height to make the rectangle 
  • blit() will take that rectangular Surface and put it on top of the screen

Python3




# Draw tiles
def draw_tyle(self):
    pygame.draw.rect(self.screen,
                     self.color,
                     pygame.Rect(
        self.start_pos_x, self.start_pos_y,
                       self.width, self.depth))
    numb = font.render(str(self.num), True,
                       (125, 55, 100))
    screen.blit(numb, (self.start_pos_x + 40,
                       self.start_pos_y + 10))


mouse_hover() method for changing the color of a tile: 

  • mouse_hover() method for changing the color of a tile to white when the mouse hovers over the tile

Python3




# Mouse hover chnage the color of tiles
def mouse_hover(self, x_m_motion, y_m_motion):
    if x_m_motion > self.start_pos_x and x_m_motion < self.start_pos_x + self.width and y_m_motion > self.start_pos_y and y_m_motion < self.start_pos_y + self.depth:
        self.color = (255, 255, 255)
    else:
        self.color = (255, 165, 0)


mouse_click() method for selecting the tile: 

  • when the mouse clicks on a tile, the tile changes position with some depthless causing it to go below the main grid

Python3




# when  mouse  clicks check if a tile is selected or not
def mouse_click(self, x_m_click, y_m_click):
    if x_m_click > self.start_pos_x and x_m_click < self.start_pos_x + self.width and y_m_click > self.start_pos_y and y_m_click < self.start_pos_y + self.depth:
        self.selected = True
    else:
        self.selected = False


mouse_click_release() for checking whether the tile is released or not: 

  • when the mouse click is released unselect the tile by setting selected as  False 

Python3




# when mouse click released unselect the tile by setting False
def mouse_click_release(self, x_m_click_rel, y_m_click_rel):
    if x_m_click_rel > 0 and y_m_click_rel > 0:
        self.selected = False


move_tyle() method for moving the tiles with appropriate co-ords:

Python3




# Move the tile(i.e hower)
def move_tyle(self, x_m_motion, y_m_motion):
    self.start_pos_x = x_m_motion
    self.start_pos_y = y_m_motion
 
# end of class


create_tyles() method for creating tiles in the matrix:

  • Create tiles w.r.t to no of tiles available i.e in a 3×3 matrix the no of tiles will be 9(blank tile included) 
  • For puzzle-making, create tiles at random positions in the matrix. Once the position is fixed append the tile_no to that tile from the available tiles
  • Print the tiles in the grid 

Python3




# Create tiles w.r.t to no of tiles available
def create_tyles():
    i = 1
    # create tiles at random positions
    while i <= tile_count:
        r = random.randint(1, tile_count)
        if r not in tile_no:
            tile_no.append(r)
            i += 1
    tile_no.append("")
    k = 0
    # print the tiles in the grid
    for i in range(0, rows):
        for j in range(0, cols):
            if (i == rows - 1) and (j == cols - 1):
                pass
            else:
                t = Tiles(screen, tile_print_position[(
                    i, j)][0], tile_print_position[(i, j)][1],
                          tile_no[k], i, j)
                tiles.append(t)
            matrix[i][j] = tile_no[k]
            k += 1
    check_mobility()


check_mobility() method for validating  positions: 

  • check if the tile can be placed in the required position where the player is trying to move the tile

Python3




# check if the tile can be placed
# in the required position where
# the player is trying to move the tile
 
 
def check_mobility():
   
    for i in range(tile_count):
        tile = tiles[i]
        row_index = tile.position_x
        col_index = tile.position_y
        adjacent_cells = []
        adjacent_cells.append([row_index-1, col_index, False])  # up
        adjacent_cells.append([row_index+1, col_index, False])  # down
        adjacent_cells.append([row_index, col_index-1, False])  # right
        adjacent_cells.append([row_index, col_index+1, False])  # left
         
        for i in range(len(adjacent_cells)):
            if (adjacent_cells[i][0] >= 0 and adjacent_cells[i][0] < rows)
            and (adjacent_cells[i][1] >= 0 and adjacent_cells[i][1] < cols):
                adjacent_cells[i][2] = True
 
        for j in range(len(adjacent_cells)):
            if adjacent_cells[j][2]:
                adj_cell_row = adjacent_cells[j][0]
                adj_cell_col = adjacent_cells[j][1]
                for k in range(tile_count):
                    if adj_cell_row == tiles[k].position_x
                    and adj_cell_col == tiles[k].position_y:
                        adjacent_cells[j][2] = False
 
                false_count = 0
 
                for m in range(len(adjacent_cells)):
                    if adjacent_cells[m][2]:
                        tile.movable = True
                        break
                    else:
                        false_count += 1
 
                if false_count == 4:
                    tile.movable = False


isGameOver() method for checking whether the game is over or not:  

  • If after iterating the matrix the string we get is 12345678_ then the player has won(“Game Over”) and lock the tiles at that position

Python3




# if after iterating the matrix
# the string we get is 12345678_
# then the player has won("Game Over")
 
 
def isGameOver():
    global game_over, game_over_banner
    allcelldata = ""
    for i in range(rows):
        for j in range(cols):
            allcelldata = allcelldata + str(matrix[i][j])
 
    if allcelldata == "12345678 ":
        game_over = True
        game_over_banner = "Game Over"
 
        print("Game Over")
        # lock the tiles at that position
        for i in range(tile_count):
            tiles[i].movable = False
            tiles[i].selected = False


Define the matrix with its size and some initial variables: 

  • Define the window screen dimension with the help of pyautogui.size() function.   
  • pyautogui.size() returns two integers in tuple(width, height) of the screen size, in pixels.
  • Define no of rows and columns of the matrix & print the tiles at appropriate positions
  • Then set the initial values for mouse_press , x_m_click, y_m_click, x_m_click_rel, y_m_click_rel, game_over, game_over_banner.

Python3




# Window dimension
page_width, page_depth = pyautogui.size()
page_width = int(page_width * .95)
page_depth = int(page_depth * .95)
 
# tile dimensions
tiles = []
tile_width = 200
tile_depth = 200
 
# no of rows & column i.e puzzle size
rows, cols = (3, 3)
tile_count = rows * cols - 1  # how many tiles should be created
matrix = [["" for i in range(cols)] for j in range(rows)]
tile_no = []
tile_print_position = {(0, 0): (100, 50),
                       (0, 1): (305, 50),
                       (0, 2): (510, 50),
                       (1, 0): (100, 255),
                       (1, 1): (305, 255),
                       (1, 2): (510, 255),
                       (2, 0): (100, 460),
                       (2, 1): (305, 460),
                       (2, 2): (510, 460)}
 
# initial values of variables
mouse_press = False
x_m_click, y_m_click = 0, 0
x_m_click_rel, y_m_click_rel = 0, 0
game_over = False
game_over_banner = ""


Initialize pygame module &  set the caption:

  • Initialize all the pygame modules with the help of pygame.init()
  • Now set the captions for texts and counter of counting the total number of moves.

Python3




# initialize pygame and set the caption
pygame.init()
game_over_font = pygame.font.Font('freesansbold.ttf', 70)
move_count = 0
move_count_banner = "Moves : "
move_count_font = pygame.font.Font('freesansbold.ttf', 40)
screen = pygame.display.set_mode((page_width, page_depth))
pygame.display.set_caption("Slide Game")
font = pygame.font.Font('freesansbold.ttf', 200)
 
# creation of tiles in the puzzle
create_tyles()


Making the puzzle by doing random moves: 

  • Set the running = True, it indicates that the player is playing the game 
  • Fill the window screen with black color then start drawing the GUI board and print the tiles at the GUI
  • Now render the total no of counts each time a new move is played
  • Now get the events triggered by the player with the help of pygame.event.get()
    • If the event is quit operation then terminate the program
    • If mouse clicks are detected then find (x,y) and then pass them to the mouse_hover method
    • If the tile is selected & mouse is pressed then pass the coords to the move_tyle method and it will move the tile to the desired location
      • If the desired location is down pass coords to mouse_click and settle the tile there if the conditions are satisfying. similarly for other conditions.

Python3




# main loop
running = True
 
while running:
   
  # fill with black color
    screen.fill((0, 0, 0))
     
    # start drawing the gui board of sliding puzzle
    pygame.draw.rect(screen, (165, 42, 42),
                     pygame.Rect(95, 45, 620, 620))
    game_over_print = game_over_font.render(
        game_over_banner, True, (255, 255, 0))
     
    # blit() will take that rectangular
    # Surface and put it on top of the screen.
    screen.blit(game_over_print, (950, 100))
 
    # render the move_count with the use of str
    if move_count == 0:
        move_count_render = move_count_font.render(
            move_count_banner, True, (0, 255, 0))
    else:
        move_count_render = move_count_font.render(
            move_count_banner + str(move_count), True, (0, 255, 0))
    screen.blit(move_count_render, (1050, 200))
 
    # Get events from the queue.
    for event in pygame.event.get():
        # if its quite operation then exit the while loop
        if event.type == pygame.QUIT:
            running = False
         
        # if mouse click are detected
        # then find (x,y) and then pass
        # them to mouse_hover method
        if event.type == pygame.MOUSEMOTION:
            x_m_motion, y_m_motion = pygame.mouse.get_pos()
            for i in range(tile_count):
                tiles[i].mouse_hover(x_m_motion, y_m_motion)
             
            # if the tile is selected &
            # mouse is pressed then pass
            # the coords to move_tyle method
            for i in range(tile_count):
                if tiles[i].selected and mouse_press:
                    tiles[i].move_tyle(x_m_motion, y_m_motion)
        # Moving tile downwards
        if event.type == pygame.MOUSEBUTTONDOWN:
            mouse_press = True
            x_m_click, y_m_click = pygame.mouse.get_pos()
            for i in range(tile_count):
                tiles[i].mouse_click(x_m_click, y_m_click)
         
        # Moving tile upwards
        if event.type == pygame.MOUSEBUTTONUP:
            mouse_press = False
            x_m_click_rel, y_m_click_rel = pygame.mouse.get_pos()
            x_m_click, y_m_click = 0, 0
            cell_found = False
            for i in range(0, rows):
                for j in range(0, cols):
                    tile_start_pos_x = tile_print_position[(i, j)][0]
                    tile_start_pos_y = tile_print_position[(i, j)][1]
 
                    if (x_m_click_rel > tile_start_pos_x
                        and x_m_click_rel < tile_start_pos_x + tile_width)
                    and (y_m_click_rel > tile_start_pos_y
                         and y_m_click_rel < tile_start_pos_y + tile_depth):
                        if matrix[i][j] == "":
                            for k in range(tile_count):
                                if game_over == False:
                                    if tiles[k].selected:
                                        if tiles[k].movable:
                                            cell_found = True
                                            dummy = matrix[tiles[k].position_x][tiles[k].position_y]
                                            matrix[tiles[k].position_x][tiles[k].position_y] = matrix[i][j]
                                            matrix[i][j] = dummy
                                            tiles[k].position_x = i
                                            tiles[k].position_y = j
                                            tiles[k].start_pos_x = tile_print_position[(
                                                i, j)][0]
                                            tiles[k].start_pos_y = tile_print_position[(
                                                i, j)][1]
                                            move_count += 1
                                            isGameOver()
                                            check_mobility()
 
                    if cell_found == False:
                        for k in range(tile_count):
                            if tiles[k].selected:
                                mat_pos_x = tiles[k].position_x
                                mat_pos_y = tiles[k].position_y
                                tiles[k].start_pos_x = tile_print_position[(
                                    mat_pos_x, mat_pos_y)][0]
                                tiles[k].start_pos_y = tile_print_position[(
                                    mat_pos_x, mat_pos_y)][1]
                                break


Traversing & updating of tiles :

  • Traverse all the tiles and draw them
  • After drawing update them on the screen with the help of pygame.display.flip() 
  • pygame.display.flip(): Allows only a portion of the screen to update, instead of the entire area, 

Python3




for i in range(tile_count):
  tiles[i].draw_tyle()
  pygame.display.flip()


Update the window of game: 

  • Now update the whole window of the game using pygame.display.update()
  • pygame.display.update(): If no argument is passed it updates the entire window area(This is the case of the below code).

Python3




# Update the whole screen
pygame.display.update()


Complete source code: 

Python3




import pygame
import random
import pyautogui
from pygame.locals import *
 
 
class Tiles:
    # main method for initializing different variables
    def __init__(self, screen, start_position_x,
                 start_position_y, num, mat_pos_x, mat_pos_y):
        self.color = (0, 255, 0)
        self.screen = screen
        self.start_pos_x = start_position_x
        self.start_pos_y = start_position_y
        self.num = num
        self.width = tile_width
        self.depth = tile_depth
        self.selected = False
        self.position_x = mat_pos_x
        self.position_y = mat_pos_y
        self.movable = False
 
        # Draw tiles
    def draw_tyle(self):
        pygame.draw.rect(self.screen, self.color, pygame.Rect(
            self.start_pos_x, self.start_pos_y, self.width, self.depth))
        numb = font.render(str(self.num), True, (125, 55, 100))
        screen.blit(numb, (self.start_pos_x + 40, self.start_pos_y + 10))
 
        # Mouse hover chnage the color of tiles
    def mouse_hover(self, x_m_motion, y_m_motion):
        if x_m_motion > self.start_pos_x and x_m_motion < self.start_pos_x + self.width and y_m_motion > self.start_pos_y and y_m_motion < self.start_pos_y + self.depth:
            self.color = (255, 255, 255)
        else:
            self.color = (255, 165, 0)
 
        # when  mouse  clicks check if a tile is selected or not
    def mouse_click(self, x_m_click, y_m_click):
        if x_m_click > self.start_pos_x and x_m_click < self.start_pos_x + self.width and y_m_click > self.start_pos_y and y_m_click < self.start_pos_y + self.depth:
            self.selected = True
        else:
            self.selected = False
 
        # when mouse click released unselect the tile by setting False
    def mouse_click_release(self, x_m_click_rel, y_m_click_rel):
        if x_m_click_rel > 0 and y_m_click_rel > 0:
            self.selected = False
 
        # Move the tile(i.e hower)
    def move_tyle(self, x_m_motion, y_m_motion):
        self.start_pos_x = x_m_motion
        self.start_pos_y = y_m_motion
 
# Create tiles w.r.t to no of tiles available
 
 
def create_tyles():
    i = 1
    while i <= tile_count:
        r = random.randint(1, tile_count)
        if r not in tile_no:
            tile_no.append(r)
            i += 1
    tile_no.append("")
    k = 0
    for i in range(0, rows):
        for j in range(0, cols):
            if (i == rows - 1) and (j == cols - 1):
                pass
            else:
                t = Tiles(screen, tile_print_position[(
                    i, j)][0], tile_print_position[(i, j)][1],
                          tile_no[k], i, j)
                tiles.append(t)
            matrix[i][j] = tile_no[k]
            k += 1
    check_mobility()
 
# check if the tile can be places on the
# required position where the
# player is trying to move the tile
 
 
def check_mobility():
    for i in range(tile_count):
        tile = tiles[i]
        row_index = tile.position_x
        col_index = tile.position_y
        adjacent_cells = []
        adjacent_cells.append([row_index-1, col_index, False])  # up
        adjacent_cells.append([row_index+1, col_index, False])  # down
        adjacent_cells.append([row_index, col_index-1, False])  # right
        adjacent_cells.append([row_index, col_index+1, False])  # left
        for i in range(len(adjacent_cells)):
            if (adjacent_cells[i][0] >= 0 and adjacent_cells[i][0] < rows) and (adjacent_cells[i][1] >= 0 and adjacent_cells[i][1] < cols):
                adjacent_cells[i][2] = True
 
        for j in range(len(adjacent_cells)):
            if adjacent_cells[j][2]:
                adj_cell_row = adjacent_cells[j][0]
                adj_cell_col = adjacent_cells[j][1]
                for k in range(tile_count):
                    if adj_cell_row == tiles[k].position_x and adj_cell_col == tiles[k].position_y:
                        adjacent_cells[j][2] = False
 
                false_count = 0
 
                for m in range(len(adjacent_cells)):
                    if adjacent_cells[m][2]:
                        tile.movable = True
                        break
                    else:
                        false_count += 1
 
                if false_count == 4:
                    tile.movable = False
 
# if after iterating the matrix the
# string we get is 12345678_ then
# the player has won("Game Over")
 
 
def isGameOver():
    global game_over, game_over_banner
    allcelldata = ""
    for i in range(rows):
        for j in range(cols):
            allcelldata = allcelldata + str(matrix[i][j])
 
    if allcelldata == "12345678 ":
        game_over = True
        game_over_banner = "Game Over"
 
        print("Game Over")
 
        for i in range(tile_count):
            tiles[i].movable = False
            tiles[i].selected = False
 
 
# Window dimension
page_width, page_depth = pyautogui.size()
page_width = int(page_width * .95)
page_depth = int(page_depth * .95)
 
# tile dimensions
tiles = []
tile_width = 200
tile_depth = 200
 
# no of rows & column i.e puzzle size
rows, cols = (3, 3)
tile_count = rows * cols - 1  # how many tiles should be created
matrix = [["" for i in range(cols)] for j in range(rows)]
tile_no = []
tile_print_position = {(0, 0): (100, 50),
                       (0, 1): (305, 50),
                       (0, 2): (510, 50),
                       (1, 0): (100, 255),
                       (1, 1): (305, 255),
                       (1, 2): (510, 255),
                       (2, 0): (100, 460),
                       (2, 1): (305, 460),
                       (2, 2): (510, 460)}
 
# initial values of variables
mouse_press = False
x_m_click, y_m_click = 0, 0
x_m_click_rel, y_m_click_rel = 0, 0
game_over = False
game_over_banner = ""
 
# initialize pygame and set the caption
pygame.init()
game_over_font = pygame.font.Font('freesansbold.ttf', 70)
move_count = 0
move_count_banner = "Moves : "
move_count_font = pygame.font.Font('freesansbold.ttf', 40)
screen = pygame.display.set_mode((page_width, page_depth))
pygame.display.set_caption("Slide Game")
font = pygame.font.Font('freesansbold.ttf', 200)
 
# creation of tiles in the puzzle
create_tyles()
 
running = True
while running:
    screen.fill((0, 0, 0))  # fill with black color
    # start drawing the gui board of sliding puzzle
    pygame.draw.rect(screen, (165, 42, 42), pygame.Rect(95, 45, 620, 620))
    game_over_print = game_over_font.render(
        game_over_banner, True, (255, 255, 0))
     
    # blit() will take that rectangular Surface
    # and put it on top of the screen.
    screen.blit(game_over_print, (950, 100))
 
    # render the move_count with the use of str
    if move_count == 0:
        move_count_render = move_count_font.render(
            move_count_banner, True, (0, 255, 0))
    else:
        move_count_render = move_count_font.render(
            move_count_banner + str(move_count), True, (0, 255, 0))
    screen.blit(move_count_render, (1050, 200))
 
    # Get events from the queue.
    for event in pygame.event.get():
        # if its quite operation then exit the while loop
        if event.type == pygame.QUIT:
            running = False
        # if mouse click are detected then find (x,y)
        # and then pass them to mouse_hover method
        if event.type == pygame.MOUSEMOTION:
            x_m_motion, y_m_motion = pygame.mouse.get_pos()
            for i in range(tile_count):
                tiles[i].mouse_hover(x_m_motion, y_m_motion)
            # if the tile is selected & mouse is pressed
            # then pass the coords to move_tyle method
            for i in range(tile_count):
                if tiles[i].selected and mouse_press:
                    tiles[i].move_tyle(x_m_motion, y_m_motion)
        # Moving tile downwards
        if event.type == pygame.MOUSEBUTTONDOWN:
            mouse_press = True
            x_m_click, y_m_click = pygame.mouse.get_pos()
            for i in range(tile_count):
                tiles[i].mouse_click(x_m_click, y_m_click)
        # Moving tile upwards
        if event.type == pygame.MOUSEBUTTONUP:
            mouse_press = False
            x_m_click_rel, y_m_click_rel = pygame.mouse.get_pos()
            x_m_click, y_m_click = 0, 0
            cell_found = False
            for i in range(0, rows):
                for j in range(0, cols):
                    tile_start_pos_x = tile_print_position[(i, j)][0]
                    tile_start_pos_y = tile_print_position[(i, j)][1]
 
                    if (x_m_click_rel > tile_start_pos_x and x_m_click_rel < tile_start_pos_x + tile_width) and (y_m_click_rel > tile_start_pos_y and y_m_click_rel < tile_start_pos_y + tile_depth):
                        if matrix[i][j] == "":
                            for k in range(tile_count):
                                if game_over == False:
                                    if tiles[k].selected:
                                        if tiles[k].movable:
                                            cell_found = True
                                            dummy = matrix[tiles[k].position_x][tiles[k].position_y]
                                            matrix[tiles[k].position_x][tiles[k].position_y] = matrix[i][j]
                                            matrix[i][j] = dummy
                                            tiles[k].position_x = i
                                            tiles[k].position_y = j
                                            tiles[k].start_pos_x = tile_print_position[(
                                                i, j)][0]
                                            tiles[k].start_pos_y = tile_print_position[(
                                                i, j)][1]
                                            move_count += 1
                                            isGameOver()
                                            check_mobility()
 
                    if cell_found == False:
                        for k in range(tile_count):
                            if tiles[k].selected:
                                mat_pos_x = tiles[k].position_x
                                mat_pos_y = tiles[k].position_y
                                tiles[k].start_pos_x = tile_print_position[(
                                    mat_pos_x, mat_pos_y)][0]
                                tiles[k].start_pos_y = tile_print_position[(
                                    mat_pos_x, mat_pos_y)][1]
                                break
 
    for i in range(tile_count):
        tiles[i].draw_tyle()
    # allows only a portion of the screen to updated,
    # instead of the entire area,
    # If no argument is passed it
    # updates the entire Surface area like pygame.
    pygame.display.flip()
# Update the whole screen
pygame.display.update()


Output : 

Dominic
Dominichttp://wardslaus.com
infosec,malicious & dos attacks generator, boot rom exploit philanthropist , wild hacker , game developer,
RELATED ARTICLES

Most Popular

Dominic
32154 POSTS0 COMMENTS
Milvus
67 POSTS0 COMMENTS
Nango Kala
6529 POSTS0 COMMENTS
Nicole Veronica
11677 POSTS0 COMMENTS
Nokonwaba Nkukhwana
11737 POSTS0 COMMENTS
Shaida Kate Naidoo
6623 POSTS0 COMMENTS
Ted Musemwa
6899 POSTS0 COMMENTS
Thapelo Manthata
6590 POSTS0 COMMENTS
Umr Jansen
6583 POSTS0 COMMENTS