World of Electronics and Cyber Consultancy

Building a Maze Game in Python: Full Tutorial & Showcase

Source Code:

import pygame
import random
import sys
import os
from datetime import datetime
def resource_path(relative_path):
   
    try:
        base_path = sys._MEIPASS
    except Exception:
        base_path = os.path.abspath(".")

    return os.path.join(base_path, relative_path)


CELL = 30        
MAZE_W = 25       
MAZE_H = 25       
TIME_LIMIT = 40   
BG_COLOR     = (255, 255, 255) 
WALL_COLOR   = (0, 0, 0)    
PLAYER_COLOR = (0, 0, 255)    
EXIT_COLOR   = (0, 0, 255)      
TEXT_COLOR   = (0, 0, 0)      

def generate_maze(width, height):
    if width % 2 == 0:
        width += 1
    if height % 2 == 0:
        height += 1

    maze = [[1 for _ in range(width)] for _ in range(height)]

    def carve(x, y):
        dirs = [(2, 0), (-2, 0), (0, 2), (0, -2)]
        random.shuffle(dirs)
        for dx, dy in dirs:
            nx, ny = x + dx, y + dy
            if 1 <= nx < width - 1 and 1 <= ny < height - 1:
                if maze[ny][nx] == 1:
                    maze[ny][nx] = 0
                    maze[y + dy // 2][x + dx // 2] = 0
                    carve(nx, ny)

    maze[1][1] = 0
    carve(1, 1)

    return maze


def save_maze_image(maze, exit_x, exit_y):
    rows = len(maze)
    cols = len(maze[0])

    surface = pygame.Surface((cols * CELL, rows * CELL))
    surface.fill(BG_COLOR)

    # Draw walls
    for y in range(rows):
        for x in range(cols):
            if maze[y][x] == 1:
                pygame.draw.rect(
                    surface,
                    WALL_COLOR,
                    (x * CELL, y * CELL, CELL, CELL)
                )

    # Draw exit
    pygame.draw.rect(
        surface,
        EXIT_COLOR,
        (exit_x * CELL, exit_y * CELL, CELL, CELL)
    )

    # Ensure Images folder exists
    images_dir = os.path.join(os.path.dirname(__file__), "Images")
    os.makedirs(images_dir, exist_ok=True)

    filename = f"maze_{datetime.now().strftime('%Y%m%d_%H%M%S')}.png"
    full_path = os.path.join(images_dir, filename)

    pygame.image.save(surface, full_path)
    print(f"Saved maze image: {full_path}")


def load_sound(name):
    try:
        return pygame.mixer.Sound(name)
    except Exception as e:
        print(f"Warning: could not load sound '{name}': {e}")
        return None


pygame.mixer.init(44100,-16,1,512)
pygame.init()

SCREEN_W = MAZE_W * CELL
SCREEN_H = MAZE_H * CELL
screen = pygame.display.set_mode((SCREEN_W, SCREEN_H))
pygame.display.set_caption("Random Maze Game — by Jean-Marie (www.cb-electronics.com)")

font = pygame.font.SysFont(None, 32)

move_sound = load_sound(resource_path("Sounds/moveWav.wav"))
win_sound = load_sound(resource_path("Sounds/win.wav"))
lose_sound = load_sound(resource_path("Sounds/lose.wav"))

clock = pygame.time.Clock()

exit_x, exit_y = MAZE_W - 2, MAZE_H - 2


maze = generate_maze(MAZE_W, MAZE_H)

player_cell_x = 1
player_cell_y = 1

player_px = player_cell_x * CELL
player_py = player_cell_y * CELL

moving = False        # currently sliding between cells?
move_dx = 0           # movement direction in cell grid
move_dy = 0
pixels_remaining = 0  # how many pixels left to slide to next cell
speed = 5             # pixels per frame

current_dir = (0, 0)

start_ticks = pygame.time.get_ticks()


def start_new_maze():
    global maze, player_cell_x, player_cell_y
    global player_px, player_py, moving, move_dx, move_dy, pixels_remaining
    global start_ticks

    maze = generate_maze(MAZE_W, MAZE_H)
    player_cell_x, player_cell_y = 1, 1
    player_px = player_cell_x * CELL
    player_py = player_cell_y * CELL
    moving = False
    move_dx = move_dy = 0
    pixels_remaining = 0

    start_ticks = pygame.time.get_ticks()
    save_maze_image(maze, exit_x, exit_y)


start_new_maze()

while True:
    clock.tick(60)  # FPS

    elapsed_ms = pygame.time.get_ticks() - start_ticks
    remaining = TIME_LIMIT - elapsed_ms / 1000.0

    if remaining <= 0:
        if lose_sound:
            lose_sound.play()
        print("⏰ Time's up! New maze.")
        start_new_maze()
        start_ticks = pygame.time.get_ticks()
        continue

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()

        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_UP:
                current_dir = (0, -1)
            elif event.key == pygame.K_DOWN:
                current_dir = (0, 1)
            elif event.key == pygame.K_LEFT:
                current_dir = (-1, 0)
            elif event.key == pygame.K_RIGHT:
                current_dir = (1, 0)
            elif event.key == pygame.K_SPACE: #Hayde 7tayta l 2etsala
                start_new_maze()

        if event.type == pygame.KEYUP:
            if event.key == pygame.K_UP and current_dir == (0, -1):
                current_dir = (0, 0)
            elif event.key == pygame.K_DOWN and current_dir == (0, 1):
                current_dir = (0, 0)
            elif event.key == pygame.K_LEFT and current_dir == (-1, 0):
                current_dir = (0, 0)
            elif event.key == pygame.K_RIGHT and current_dir == (1, 0):
                current_dir = (0, 0)

    if moving:
        step = min(speed, pixels_remaining)
        player_px += move_dx * step
        player_py += move_dy * step
        pixels_remaining -= step

        if pixels_remaining <= 0:
            moving = False
            player_cell_x += move_dx
            player_cell_y += move_dy

    if not moving and current_dir != (0, 0):
        dx, dy = current_dir
        nx = player_cell_x + dx
        ny = player_cell_y + dy

        if 0 <= nx < MAZE_W and 0 <= ny < MAZE_H and maze[ny][nx] == 0:
            # Start sliding towards next cell
            moving = True
            move_dx, move_dy = dx, dy
            pixels_remaining = CELL
            if move_sound:
                move_sound.play()

    if player_cell_x == exit_x and player_cell_y == exit_y:
        if win_sound:
            win_sound.play()
        print("🎉 YOU WON! New maze.")
        start_new_maze()

   
    screen.fill(BG_COLOR)

    for y in range(MAZE_H):
        for x in range(MAZE_W):
            if maze[y][x] == 1:
                pygame.draw.rect(
                    screen,
                    WALL_COLOR,
                    (x * CELL, y * CELL, CELL, CELL)
                )

    pygame.draw.rect(
        screen,
        EXIT_COLOR,
        (exit_x * CELL, exit_y * CELL, CELL, CELL)
    )

    pygame.draw.rect(
        screen,
        PLAYER_COLOR,
        (int(player_px), int(player_py), CELL, CELL)
    )

    timer_text = font.render(f"Time: {int(remaining)}", True, (0,0,0))
    screen.blit(timer_text, (10, 10))

    pygame.draw.rect(screen, (255, 255, 0), (0, 0, 50, 25))  
    timer_text = font.render(f"{int(remaining)}", True, (0,0,0))
    screen.blit(timer_text, (10, 5))


    pygame.display.flip()

Guys Don’t forget to download the sound files.

Sound Files (Compressed Folder)

Ready to use app for MAC

Ready to use app for Windows