Snake Game With Joystick

This code integrates a joystick with a simple Snake Game using the pygame library. It also utilizes the ACROME SMD platform to receive joystick inputs and provide feedback, like turning on a buzzer when food is consumed.

Key Component:

  1. Buzzer Module: Every time the snake eats the food Buzzer Module makes a sound.

  2. Joystick Module: Handles snake movement.

  3. pygame: Handles the graphics and user interface for the snake game. It displays the snake, food, score, and manages input events.

  4. ACROME SMD: Communicates with the joystick and handles joystick input as well as buzzer feedback.

Project Key Features:

  1. Joystick Input: The joystick connected via the ACROME SMD platform is used to control the snake's direction. The snake moves left, right, up, or down based on the joystick's X and Y-axis values. A button on the joystick can be used to pause and resume the game.

  2. Start and Game Over Menus: The game starts with a simple menu where you can choose to start the game or quit. If the game ends (snake hits a wall or itself), a game over menu is displayed, with options to retry or quit.

  3. Game Logic: The snake grows when it eats food, and the length is increased. The snake's movement speed is fixed but can be controlled by the joystick. A buzzer sound is triggered when the snake eats the food.

  4. Pause Functionality: A button on the joystick allows pausing and resuming the game.

Project Wiring Diagram

Workflow:

Start Menu: When the program is run, the start menu is displayed. The user can click on "Start" to begin the game or "Quit" to exit.

Snake Movement: During the game, the joystick's position controls the snake's movement. Moving the joystick in any direction updates the snake’s position.

Eating Food: When the snake's head collides with the food, the snake grows, and a buzzer sound is triggered.

Game Over: The game ends when the snake hits the boundaries or itself. A "Game Over" menu is displayed with options to retry or quit.

Pausing the Game: The game can be paused and resumed using the joystick's button

import pygame
import time
import random
from smd.red import *
from serial.tools.list_ports import comports
from platform import system
def USB_Port():
    if system() == "Windows":
        ports = list(comports())
        if ports:
            for port, desc, hwid in sorted(ports):
                if 'USB Serial Port' in desc:
                    SMD_Port = port
                    return SMD_Port
        else:
            SMD_Port = None
            return SMD_Port

    elif system() == "Linux":
        ports = list(serial.tools.list_ports.comports())
        if ports:
            for port, desc, hwid in sorted(ports):
                if '/dev/ttyUSB' in port:
                    SMD_Port = port
                    return SMD_Port

    elif system() == "Darwin":  # macOS
        ports = list(serial.tools.list_ports.comports())
        if ports:
            for port, desc, hwid in sorted(ports):
                if '/dev/tty.usbserial' in port or '/dev/tty.usbmodem' in port:
                    SMD_Port = port
                    return SMD_Port

        else:
            SMD_Port = None
            return SMD_Port

port = USB_Port()
m = Master(port)
m.attach(Red(0))  # Attach the Red device

# Initialize the game
pygame.init()

# Define colors
white = (255, 255, 255)
yellow = (255, 255, 102)
black = (0, 0, 0)
red = (213, 50, 80)
green = (0, 255, 0)
blue = (50, 153, 213)

# Set display dimensions
width, height = 600, 400
display = pygame.display.set_mode((width, height))
pygame.display.set_caption('Snake Game')

# Clock to control game speed
clock = pygame.time.Clock()

# Snake settings
snake_block = 10
snake_speed = 10

# Font styles
font_style = pygame.font.SysFont("bahnschrift", 25)
score_font = pygame.font.SysFont("comicsansms", 35)


def display_score(score):
    value = score_font.render("Score: " + str(score), True, black)
    display.blit(value, [0, 0])


def draw_snake(snake_block, snake_list):
    for x in snake_list:
        pygame.draw.rect(display, black, [x[0], x[1], snake_block, snake_block])


def message(msg, color, y_displace=0):
    mesg = font_style.render(msg, True, color)
    display.blit(mesg, [width / 6, height / 3 + y_displace])


# Button function to create clickable buttons
def button(text, x, y, w, h, inactive_color, active_color, action=None):
    mouse = pygame.mouse.get_pos()
    click = pygame.mouse.get_pressed()

    # Change color if mouse is over the button
    if x + w > mouse[0] > x and y + h > mouse[1] > y:
        pygame.draw.rect(display, active_color, (x, y, w, h))
        if click[0] == 1 and action != None:
            action()
    else:
        pygame.draw.rect(display, inactive_color, (x, y, w, h))

    # Render the button text
    text_surf = font_style.render(text, True, black)
    display.blit(text_surf, [x + (w / 4), y + (h / 4)])


# Start menu screen
def start_menu():
    start_game = False
    while not start_game:
        display.fill(blue)
        message("Welcome to Snake Game!", white, -50)
        
        # Create Start and Quit buttons
        button("Start", 150, 200, 100, 50, green, yellow, game_loop)  # Calls game_loop() when clicked
        button("Quit", 350, 200, 100, 50, red, yellow, pygame.quit)   # Calls pygame.quit() when clicked

        pygame.display.update()

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


# Game over menu with Retry and Quit options
def game_over_menu():
    game_over_screen = True
    while game_over_screen:
        display.fill(blue)
        message("You Lost!", red, -50)
        message("Score: " + str(length_of_snake - 1), white, 0)
        
        # Create Retry and Quit buttons
        button("Retry", 150, 200, 100, 50, green, yellow, game_loop)  # Restart the game
        button("Quit", 350, 200, 100, 50, red, yellow, pygame.quit)   # Quit the game

        pygame.display.update()

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


def game_loop():
    global length_of_snake
    paused = False
    game_over = False
    game_close = False

    # Initial snake settings
    x1 = width / 2
    y1 = height / 2
    x1_change = 0
    y1_change = 0

    snake_list = []
    length_of_snake = 1

    # Food position
    foodx = round(random.randrange(0, width - snake_block) / 10.0) * 10.0
    foody = round(random.randrange(0, height - snake_block) / 10.0) * 10.0

    while not game_over:
        joystick = m.get_joystick(0, 1)
        while game_close == True:
            game_over_menu()

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                game_over = True

        # Read joystick values
        x_val = joystick[0]
        y_val = joystick[1]
        y_val = -y_val
        
        # Check if the pause button (e.g., button 1) is pressed
        if m.get_button(0, 1):  # Assuming button 1 is used for pause (check your joystick mapping)
            paused = not paused  # Toggle pause state

        # If the game is paused, display the pause message
        if paused:
            display.fill(blue)
            message("Paused! Press the button to Resume", yellow)
            pygame.display.update()
            continue  # Skip the rest of the loop while paused
        
        # Control snake direction based on joystick position
        if x_val < -20:  # Left
            x1_change = -snake_block
            y1_change = 0
        elif x_val > 20:  # Right
            x1_change = snake_block
            y1_change = 0
        elif y_val < -20:  # Up
            y1_change = -snake_block
            x1_change = 0
        elif y_val > 20:  # Down
            y1_change = snake_block
            x1_change = 0

        # Check for wall collisions
        if x1 >= width or x1 < 0 or y1 >= height or y1 < 0:
            game_close = True

        x1 += x1_change
        y1 += y1_change
        display.fill(blue)
        pygame.draw.rect(display, green, [foodx, foody, snake_block, snake_block])
        snake_head = []
        snake_head.append(x1)
        snake_head.append(y1)
        snake_list.append(snake_head)
        if len(snake_list) > length_of_snake:
            del snake_list[0]

        for x in snake_list[:-1]:
            if x == snake_head:
                game_close = True

        draw_snake(snake_block, snake_list)
        display_score(length_of_snake - 1)

        pygame.display.update()

        if x1 == foodx and y1 == foody:
            foodx = round(random.randrange(0, width - snake_block) / 10.0) * 10.0
            foody = round(random.randrange(0, height - snake_block) / 10.0) * 10.0
            length_of_snake += 1
            m.set_buzzer(0, 1, 10)  # Buzzer on collision
            time.sleep(0.1)
            m.set_buzzer(0, 1, 0)  # Turn off buzzer

        clock.tick(snake_speed)

    pygame.quit()
    m.detach(0)  # Detach the Red device using the correct integer ID
    print("Disconnected from device.")


# Start the game
start_menu()  # Display the start menu first

Conclusion: This is a fun and interactive way to use the ACROME SMD joystick to control the classic Snake game, providing a hardware-based gaming experience.

Last updated