How I implemented Snake and Apple Game using PyGame in Python

  1. Install PyGame

Pygame is a cross-platform set of Python modules which is used to create video games. It includes computer graphics and sound libraries designed to be used with the Python programming language.

You can install it by using following command,

pip install pygame

For more information, check official documentation here.

  1. Introduction to the Game

Building a snake and apple game in pygame and python. Snake and apple is a simple game where when a snake eats an apple it gets longer and your score increases. If a snake hits any part of its body it will die.

  1. Resources

Project complete media resources can be downloaded from master branch of my github repo here:

Project complete source code is available on my Github repo, you can check it here.

Note: Resources include all photos, videos, audio tracks and background music used in this project.

  1. Classes

Now we have talked about complete game setup, so it’s time to crack the main code into three major classes, that will help us understand and manage the code well and make it readable.

So we will have these three class:

  • Snake Class:

In snake class we will have an initial functional call __init__ that will let the screen ready, will do all other initialization for us like sound, screen size, snake length, snake initial direction etc. This class will also contain functions that will ensure the functionality of snake like, drawing snake, snake length increment, snake move, snake moving directions (left, right, up and down) and snake walk function. 

Code:

class Snake:
    def __init__(self, parent_screen, length):
        self.parent_screen = parent_screen
        self.length = length
        self.x = [SIZE]*length
        self.y = [SIZE]*length
        self.direction = 'down'
        self.block = pygame.image.load("resources/block.jpg").convert()

    # drawing snake with blocks
    def draw(self):

        for i in range(self.length):
            self.parent_screen.blit(self.block, (self.x[i], self.y[i]))
        pygame.display.flip()

    # increases the length as snake eats an apple
    def increase_length(self):
        self.length += 1
        self.x.append(-1)
        self.y.append(-1)

    # snake movement towards left
    def move_left(self):
        self.direction = 'left'

    # snake movement towards right
    def move_right(self):
        self.direction = 'right'

    # snake movement towards up
    def move_up(self):
        self.direction = 'up'

    # snake movement towards down
    def move_down(self):
        self.direction = 'down'

    # snake to continue walking unless movement direction is changed by    arrows
    def walk(self):

        # update body
        for i in range(self.length-1, 0, -1):
            self.x[i] = self.x[i-1]
            self.y[i] = self.y[i-1]

        # update head
        if self.direction == 'left':
            self.x[0] -= SIZE
        if self.direction == 'right':
            self.x[0] += SIZE
        if self.direction == 'up':
            self.y[0] -= SIZE
        if self.direction == 'down':
            self.y[0] += SIZE

        self.draw()

  • Apple Class:

In the context of the game, Apple is food that a snake eats. Now Apple class has some initial look (photo) and Apple size declared in __init__ function. It has draw and move functions that support its functionality in the game. 

Code:

class Apple:
    def __init__(self, parent_screen):
        self.image = pygame.image.load("resources/apple.jpg").convert()
        self.parent_screen = parent_screen
        self.x = SIZE*3
        self.y = SIZE*3

    # drawing apple on screen
    def draw(self):
        self.parent_screen.blit(self.image, (self.x, self.y))
        pygame.display.flip()

    # regenerating apple else where once snake eats an apple
    def move(self):
        self.x = random.randint(0, 24) * SIZE
        self.y = random.randint(0, 19) * SIZE
  • Game Class:

This is the main controller class of this game. It’s __init__ function will set up all the things required to start the game like all kinds of music/sounds, background image, snake and apple object, ready the surface to play game upon, and draw snake and apple on the surface. Some major functions are  calculate and rate the collisions (Snake-snake collision, snake-apple collision), rendering background function, play background music and sounds upon collision functions, play game function that is supposed to be a main handler, run the snake in all directions function, reset score, display score, show game over function. 

Code:

class Game:
    def __init__(self):
        pygame.init()
        # pygame.display.set_caption("Snake asnd Apple Game by .bullbat")

        pygame.mixer.init()
        self.play_bg_music()
        self.surface = pygame.display.set_mode((1000, 800))
        self.surface.fill((91, 180, 181))
        self.snake = Snake(self.surface, 1)
        self.snake.draw()
        self.apple = Apple(self.surface)
        self.apple.draw()

    # basic collision fun between two objects
    def is_collision(self, x1, y1, x2, y2):
        if x1 >= x2 and x1 < x2 + SIZE:
            if y1 >= y2 and y1 < y2 + SIZE:
                return True

        return False

    # provide backgroud music for game
    def play_bg_music(self):
        pygame.mixer.music.load("resources/bg_music_1.wav")
        pygame.mixer.music.play()

    # output sound on event occurance
    def play_sound(self, sound):
        sound = pygame.mixer.Sound(f"resources/{sound}.wav")
        pygame.mixer.Sound.play(sound)

    # background picture/ surface for the game
    def render_bg(self):
        bg = pygame.image.load("resources/background.jpg")
        self.surface.blit(bg, (0, 0))

    # @main game Handler/ Controller
    def play(self):
        self.render_bg()
        self.snake.walk()
        self.apple.draw()
        self.display_score()
        pygame.display.flip()

        # Snake eating Apple scenario
        if self.is_collision(self.snake.x[0], self.snake.y[0], self.apple.x, self.apple.y):
            self.play_sound("ding")
            self.snake.increase_length()
            self.apple.move()

        # Snake colliding its own body
        for i in range(3, self.snake.length):
            if self.is_collision(self.snake.x[0], self.snake.y[0], self.snake.x[i], self.snake.y[i]):
                self.play_sound("crash")
                raise "Collision Occured"

        # snake colliding with the boundries of the window
        if not (0 <= self.snake.x[0] <= 1000 and 0 <= self.snake.y[0] <= 800):
            self.play_sound('crash')
            raise "Hit the boundry error"

    # display score on screen
    def display_score(self):
        font = pygame.font.SysFont('arial', 30)
        score = font.render(
            f"Score: {self.snake.length}", True, (255, 255, 255))
        self.surface.blit(score, (800, 10))

    # show a scorecard and replay option once game is over
    def show_game_over(self):
        self.render_bg()
        font = pygame.font.SysFont('arial', 30)
        line1 = font.render(
            f"Game is over! Your Score is: {self.snake.length}", True, (255, 255, 255))
        self.surface.blit(line1, (200, 300))
        line2 = font.render(
            "To play again press Enter. To exit press Escape!", True, (255, 255, 255))
        self.surface.blit(line2, (200, 350))
        pygame.display.flip()

        pygame.mixer.music.pause()

    # reset the game state to initial state as user opt to replay the game
    def reset(self):
        self.snake = Snake(self.surface, 1)
        self.apple = Apple(self.surface)

    # @main Game Logic
    def run(self):
        running = True
        pause = False

        while running:
            for event in pygame.event.get():
                if event.type == KEYDOWN:

                    if event.key == K_ESCAPE:
                        running = False

                    if event.key == K_RETURN:
                        pygame.mixer.music.unpause()
                        pause = False

                    if not pause:
                        if event.key == K_UP:
                            self.snake.move_up()

                        if event.key == K_DOWN:
                            self.snake.move_down()

                        if event.key == K_LEFT:
                            self.snake.move_left()

                        if event.key == K_RIGHT:
                            self.snake.move_right()

                elif event.type == QUIT:
                    running = False

            try:
                if not pause:
                    self.play()

            except Exception as e:
                self.show_game_over()
                pause = True
                self.reset()

            time.sleep(0.3)
  • Main Class:

Code:

# place this code in the start of your project

import pygame
from pygame.locals import *
import time
import random

SIZE = 40
BACKGROUND_COLOR = (91, 180, 181)

# end here

# main functioin (may place at the end of the project)
if __name__ == "__main__":
    game = Game()
    game.run()
Snake and Apple Game
  1. Run-time Functionality:

This includes:

  • Score Count
  • Increment in snake length upon eating apple
  • Sounds on eating apple, hitting boundary and game over
  • Background music throughout the game
  • Replay option once game is over
  • Quit the game any time option  
Showing scorecard and replay option when game is over
  1. Conclusion:

So this is supposed to be an easy to implement game if and only if you break the logic into small modules that are manageable easily and also easily replicate the same logic into clean and well looking code. I hope you like this blog, and if you want to get complete source code, you can download it here.

Leave a comment