Open In App

Create a Memory Puzzle Game Using Pygame

Last Updated : 19 Mar, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

Playing memory puzzle games provides entertainment and boosts mental abilities such as focus and memory recall. This article guides you through the steps to create a straightforward memory puzzle game using the Python Pygame Library. After completing the article, you will have a fully functioning game where players try to match pairs of game pieces.

Create a Memory Puzzle Game in Python

Below is the implementation of the memory puzzle game in Python:

Create a Virtual Environment

First, create the virtual environment using the below commands

python -m venv env 
.\env\Scripts\activate.ps1

Install Necessary Library

First, install PyGame, Requests, and Pillow libraries for multimedia, HTTP requests, and image processing respectively using the command below.

pip install pygame
pip install requests
pip install pillow

Writing PyGame Code

Here is the code of main.py that we are using to create our game using PyGame. Below is a step-by-step explanation of this code.

Step 1: Library Imports

below code imports essential modules for building a game with Pygame, incorporating functionalities for graphics, randomization, time management, HTTP requests, binary data handling, and image processing.

Python3
import pygame
import random
import time
import requests
from io import BytesIO
from PIL import Image

Step 2: Initialization and Setup

below code initializes the Pygame library, defines constants for screen dimensions, card size, grid size, colors, flip delay, button dimensions, and timer limit. Additionally, it provides a list of image URLs for the game. Finally, it creates a game window with specified dimensions and sets the window’s caption to “Memory Puzzle Game” using Pygame functions.

Python3
# Initialize pygame
pygame.init()

# Constants
SCREEN_WIDTH = 700
SCREEN_HEIGHT = 700
CARD_SIZE = 175
GRID_SIZE = 4
WHITE = (255, 255, 255)
FLIP_DELAY = 0.5
BUTTON_WIDTH = 140
BLACK = (0, 0, 0)
BUTTON_HEIGHT = 40
TIMER_LIMIT = 15

# URLs for images
image_urls = [
    "https://media.geeksforgeeks.org/wp-content/uploads/20240311120810/f.webp",
    "https://media.geeksforgeeks.org/wp-content/uploads/20240311121011/d.webp",
    "https://media.geeksforgeeks.org/wp-content/uploads/20240311120802/a.webp",
    "https://media.geeksforgeeks.org/wp-content/uploads/20240311120802/b.webp",
    "https://media.geeksforgeeks.org/wp-content/uploads/20240311120801/c.webp",
    "https://media.geeksforgeeks.org/wp-content/uploads/20240311122347/z.webp",
    "https://media.geeksforgeeks.org/wp-content/uploads/20240311122913/y.webp",
    "https://media.geeksforgeeks.org/wp-content/uploads/20240311122913/x.webp"
]

# Create the game window
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Memory Puzzle Game")

Step 3: Loading and Preprocessing Images

below, code uses the `requests` library to download an image from a specified URL, convert it to PNG format using the `PIL` (Pillow) library, and then load it as a Pygame image. The process is applied to both the card back image and a list of card images obtained from a set of URLs.

Python3
# Load card back image from URL and convert to PNG format
response = requests.get(
    "https://media.geeksforgeeks.org/wp-content/uploads/20240311145552/geeksforgeeks.png")
image = Image.open(BytesIO(response.content))
image = image.convert("RGB")
with BytesIO() as img_bytes:
    image.save(img_bytes, "PNG")
    img_bytes.seek(0)
    card_back = pygame.image.load(img_bytes)

# Load card images from URLs and convert to PNG format
card_images = []
for url in image_urls:
    response = requests.get(url)
    image = Image.open(BytesIO(response.content))
    image = image.convert("RGB")
    with BytesIO() as img_bytes:
        image.save(img_bytes, "PNG")
        img_bytes.seek(0)
        card_images.append(pygame.image.load(img_bytes))

Step 4: Game Setup – Shuffling and Duplicating Cards

In below code duplicates the card images to create pairs, shuffles them randomly, and initializes a list (`card_state`) to keep track of the state of each card in the game. Each element of `card_state` corresponds to a card on the grid, where `True` indicates the card is face-up and `False` indicates it’s face-down.

Python3
# Duplicate card images to create pairs
card_images *= 2

# Shuffle the cards
random.shuffle(card_images)

# Create a list to store the state of each card (True: face-up, False: face-down)
card_state = [False] * (GRID_SIZE ** 2)

Step 5: Game Loop and Input Handling

below code segment represents the main game loop, which continuously checks for user input events, such as quitting the game or clicking the mouse. If the mouse is clicked, the code determines if the click occurred within the restart button’s rectangle and, if so, shuffles the cards and resets relevant game variables. The code also handles flipping cards, updating move counts, and rendering the game grid with card images.

Python3
# Main game loop
running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.MOUSEBUTTONDOWN:
            mouse_x, mouse_y = pygame.mouse.get_pos()
            restart_button_rect = (
                SCREEN_WIDTH - BUTTON_WIDTH - 20, 20, BUTTON_WIDTH, BUTTON_HEIGHT)
            if point_in_rect((mouse_x, mouse_y), restart_button_rect):
                random.shuffle(card_images)
                card_state = [False] * (GRID_SIZE ** 2)
                flipped_cards = []
                matched_pairs = 0
                moves = 0
                timer_start_time = time.time()  # Restart the timer
            else:
                col = mouse_x // CARD_SIZE
                row = mouse_y // CARD_SIZE
                index = row * GRID_SIZE + col
                if not card_state[index] and len(flipped_cards) < 2:
                    card_state[index] = True
                    flipped_cards.append(index)
                    moves += 1

    screen.fill(WHITE)

    # Draw grid of cards
    for i in range(GRID_SIZE):
        for j in range(GRID_SIZE):
            index = i * GRID_SIZE + j
            pygame.draw.rect(screen, WHITE, (j * CARD_SIZE,
                                             i * CARD_SIZE, CARD_SIZE, CARD_SIZE))
            if card_state[index] or index in flipped_cards:
                card = card_images[index]
            else:
                card = card_back
            card = pygame.transform.scale(card, (CARD_SIZE - 8, CARD_SIZE - 8))
            screen.blit(card, (j * CARD_SIZE + 4, i * CARD_SIZE + 4))

    # Render moves counter
    moves_text = font.render(f"Moves: {moves}", True, WHITE)
    screen.blit(moves_text, (10, 10))

    # Draw restart game button
    draw_restart_button()

    # Draw timer
    draw_timer()

Step 6: Drawing Functions

below, code initializes a memory puzzle game using Pygame, defining constants, loading card images from URLs, and creating a game window. It employs a main game loop that handles user input, updates the game state, and continuously renders the grid of cards, move counts, restart button, and timer.

Python3
# Function to draw restart game button
def draw_restart_button():
    restart_button_rect = (SCREEN_WIDTH - BUTTON_WIDTH -
                           20, 20, BUTTON_WIDTH, BUTTON_HEIGHT)
    pygame.draw.rect(screen, WHITE, restart_button_rect)
    # Specify text color (black)
    restart_text = font.render("Restart Game", True, (0, 0, 0))
    text_rect = restart_text.get_rect(center=(
        restart_button_rect[0] + BUTTON_WIDTH / 2, restart_button_rect[1] + BUTTON_HEIGHT / 2))
    screen.blit(restart_text, text_rect)

# Function to draw timer
def draw_timer():
    elapsed_time = max(0, int(time.time() - timer_start_time))
    remaining_time = max(0, TIMER_LIMIT - elapsed_time)
    timer_text = font.render(f"Time: {remaining_time}s", True, BLACK)
    screen.blit(timer_text, (SCREEN_WIDTH - 150, 10))

# Function to display message on the window
def display_message(message):
    message_text = font.render(message, True, BLACK)
    text_rect = message_text.get_rect(
        center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2))
    screen.blit(message_text, text_rect)

Step 7: Game Logic and End Conditions

Below, code checks for matched pairs when two cards are flipped, introducing a brief delay for visibility. If a pair is found, the counter increments; otherwise, the card states are reset. It verifies if all pairs are matched or if the time limit is reached, displaying corresponding messages and concluding the game.

Python3
# Check for matched pairs
   if len(flipped_cards) == 2:
        time.sleep(FLIP_DELAY)
        if card_images[flipped_cards[0]] == card_images[flipped_cards[1]]:
            matched_pairs += 1
            flipped_cards = []
        else:
            card_state[flipped_cards[0]] = False
            card_state[flipped_cards[1]] = False
            flipped_cards = []

    # Check for game over
    if matched_pairs == GRID_SIZE ** 2 // 2:
        display_message("Congratulations! You found all the pairs!")
        pygame.display.flip()
        time.sleep(2)  # Display the message for 2 seconds
        running = False

    # Check for time limit reached
    elapsed_time = time.time() - timer_start_time
    if elapsed_time >= TIMER_LIMIT:
        display_message("Time's up! You lost the game.")
        pygame.display.flip()
        time.sleep(2)  # Display the message for 2 seconds
        running = False

    pygame.display.flip()

pygame.quit()

Complete Code

This is the complete code that we have explained in the above steps that we have used to create memory puzzle game using Pygame.

main.py

Python3
import pygame
import random
import time
import requests
from io import BytesIO
from PIL import Image

# Initialize pygame
pygame.init()

# Constants
SCREEN_WIDTH = 700
SCREEN_HEIGHT = 700
CARD_SIZE = 175
GRID_SIZE = 4
WHITE = (255, 255, 255)
FLIP_DELAY = 0.5
BUTTON_WIDTH = 140
BLACK = (0, 0, 0)
BUTTON_HEIGHT = 40
TIMER_LIMIT = 15

# URLs for images
image_urls = [
    "https://media.geeksforgeeks.org/wp-content/uploads/20240311120810/f.webp",
    "https://media.geeksforgeeks.org/wp-content/uploads/20240311121011/d.webp",
    "https://media.geeksforgeeks.org/wp-content/uploads/20240311120802/a.webp",
    "https://media.geeksforgeeks.org/wp-content/uploads/20240311120802/b.webp",
    "https://media.geeksforgeeks.org/wp-content/uploads/20240311120801/c.webp",
    "https://media.geeksforgeeks.org/wp-content/uploads/20240311122347/z.webp",
    "https://media.geeksforgeeks.org/wp-content/uploads/20240311122913/y.webp",
    "https://media.geeksforgeeks.org/wp-content/uploads/20240311122913/x.webp"
]

# Create the game window
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Memory Puzzle Game")

# Load card back image from URL and convert to PNG format
response = requests.get(
    "https://media.geeksforgeeks.org/wp-content/uploads/20240311145552/geeksforgeeks.png")
image = Image.open(BytesIO(response.content))
image = image.convert("RGB")
with BytesIO() as img_bytes:
    image.save(img_bytes, "PNG")
    img_bytes.seek(0)
    card_back = pygame.image.load(img_bytes)

# Load card images from URLs and convert to PNG format
card_images = []
for url in image_urls:
    response = requests.get(url)
    image = Image.open(BytesIO(response.content))
    image = image.convert("RGB")
    with BytesIO() as img_bytes:
        image.save(img_bytes, "PNG")
        img_bytes.seek(0)
        card_images.append(pygame.image.load(img_bytes))

# Duplicate card images to create pairs
card_images *= 2

# Shuffle the cards
random.shuffle(card_images)

# Create a list to store the state of each card (True: face-up, False: face-down)
card_state = [False] * (GRID_SIZE ** 2)

# Variables to keep track of flipped cards, matched pairs, moves, and timer
flipped_cards = []
matched_pairs = 0
moves = 0
timer_start_time = time.time()

# Font for displaying text
font = pygame.font.Font(None, 36)

# Function to check if a point is within a rectangle


def point_in_rect(point, rect):
    x, y = point
    rx, ry, rw, rh = rect
    return rx < x < rx + rw and ry < y < ry + rh

# Function to draw restart game button


def draw_restart_button():
    restart_button_rect = (SCREEN_WIDTH - BUTTON_WIDTH -
                           20, 20, BUTTON_WIDTH, BUTTON_HEIGHT)
    pygame.draw.rect(screen, WHITE, restart_button_rect)
    # Specify text color (black)
    restart_text = font.render("Restart Game", True, (0, 0, 0))
    text_rect = restart_text.get_rect(center=(
        restart_button_rect[0] + BUTTON_WIDTH / 2, restart_button_rect[1] + BUTTON_HEIGHT / 2))
    screen.blit(restart_text, text_rect)

# Function to draw timer


def draw_timer():
    elapsed_time = max(0, int(time.time() - timer_start_time))
    remaining_time = max(0, TIMER_LIMIT - elapsed_time)
    timer_text = font.render(f"Time: {remaining_time}s", True, BLACK)
    screen.blit(timer_text, (SCREEN_WIDTH - 150, 10))

# Function to display message on the window


def display_message(message):
    message_text = font.render(message, True, BLACK)
    text_rect = message_text.get_rect(
        center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2))
    screen.blit(message_text, text_rect)


# Main game loop
running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.MOUSEBUTTONDOWN:
            mouse_x, mouse_y = pygame.mouse.get_pos()
            restart_button_rect = (
                SCREEN_WIDTH - BUTTON_WIDTH - 20, 20, BUTTON_WIDTH, BUTTON_HEIGHT)
            if point_in_rect((mouse_x, mouse_y), restart_button_rect):
                random.shuffle(card_images)
                card_state = [False] * (GRID_SIZE ** 2)
                flipped_cards = []
                matched_pairs = 0
                moves = 0
                timer_start_time = time.time()  # Restart the timer
            else:
                col = mouse_x // CARD_SIZE
                row = mouse_y // CARD_SIZE
                index = row * GRID_SIZE + col
                if not card_state[index] and len(flipped_cards) < 2:
                    card_state[index] = True
                    flipped_cards.append(index)
                    moves += 1

    screen.fill(WHITE)

    # Draw grid of cards
    for i in range(GRID_SIZE):
        for j in range(GRID_SIZE):
            index = i * GRID_SIZE + j
            pygame.draw.rect(screen, WHITE, (j * CARD_SIZE,
                                             i * CARD_SIZE, CARD_SIZE, CARD_SIZE))
            if card_state[index] or index in flipped_cards:
                card = card_images[index]
            else:
                card = card_back
            card = pygame.transform.scale(card, (CARD_SIZE - 8, CARD_SIZE - 8))
            screen.blit(card, (j * CARD_SIZE + 4, i * CARD_SIZE + 4))

    # Render moves counter
    moves_text = font.render(f"Moves: {moves}", True, WHITE)
    screen.blit(moves_text, (10, 10))

    # Draw restart game button
    draw_restart_button()

    # Draw timer
    draw_timer()

    # Check for matched pairs
    if len(flipped_cards) == 2:
        time.sleep(FLIP_DELAY)
        if card_images[flipped_cards[0]] == card_images[flipped_cards[1]]:
            matched_pairs += 1
            flipped_cards = []
        else:
            card_state[flipped_cards[0]] = False
            card_state[flipped_cards[1]] = False
            flipped_cards = []

    # Check for game over
    if matched_pairs == GRID_SIZE ** 2 // 2:
        display_message("Congratulations! You found all the pairs!")
        pygame.display.flip()
        time.sleep(2)  # Display the message for 2 seconds
        running = False

    # Check for time limit reached
    elapsed_time = time.time() - timer_start_time
    if elapsed_time >= TIMER_LIMIT:
        display_message("Time's up! You lost the game.")
        pygame.display.flip()
        time.sleep(2)  # Display the message for 2 seconds
        running = False

    pygame.display.flip()

pygame.quit()

Output

giii

Create a Memory Puzzle Game in Python



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads