Chat
Ask me anything
Ithy Logo

Creating a Python Snake Game: Comprehensive Guide

Animated Snake | Liberated Pixel Cup

The Snake game is a classic and timeless game that serves as an excellent project for anyone looking to enhance their Python programming skills. In this guide, we'll explore multiple methods to create a Snake game using different Python libraries: tkinter, turtle, and pygame. Each approach offers unique features and complexities, allowing you to choose the one that best fits your preferences and skill level.

Table of Contents

1. Creating a Snake Game with tkinter

tkinter is Python's standard GUI library, making it a convenient choice for creating simple games. Below is a step-by-step guide to building a Snake game using tkinter.

a. Setting Up the Environment

Before diving into the code, ensure that Python is installed on your system. tkinter comes pre-installed with Python, so no additional installations are necessary.

b. The Complete Code


import tkinter as tk
import random

class SnakeGame:
    def __init__(self):
        self.window = tk.Tk()
        self.window.title("Snake Game")
        self.canvas = tk.Canvas(self.window, width=800, height=600, bg='black')
        self.canvas.pack()
        
        self.snake = [(200, 200), (220, 200), (240, 200)]
        self.direction = 'right'
        self.food = self.create_food()
        self.score = 0
        self.game_over = False
        
        self.draw_snake()
        self.draw_food()
        
        self.window.bind('', self.turn_up)
        self.window.bind('', self.turn_down)
        self.window.bind('', self.turn_left)
        self.window.bind('', self.turn_right)
        
        self.update()
        self.window.mainloop()
    
    def create_food(self):
        return (random.randint(0, 39) * 20, random.randint(0, 29) * 20)
    
    def draw_snake(self):
        self.canvas.delete('snake')
        for segment in self.snake:
            self.canvas.create_rectangle(segment[0], segment[1],
                                         segment[0]+20, segment[1]+20,
                                         fill='green', tag='snake')
    
    def draw_food(self):
        self.canvas.create_rectangle(self.food[0], self.food[1],
                                     self.food[0]+20, self.food[1]+20,
                                     fill='red', tag='food')
    
    def turn_up(self, event):
        if self.direction != 'down':
            self.direction = 'up'
    
    def turn_down(self, event):
        if self.direction != 'up':
            self.direction = 'down'
    
    def turn_left(self, event):
        if self.direction != 'right':
            self.direction = 'left'
    
    def turn_right(self, event):
        if self.direction != 'left':
            self.direction = 'right'
    
    def update(self):
        if self.game_over:
            self.canvas.create_text(400, 300, text="Game Over",
                                    fill="white", font=('Arial', 30))
            return
        
        head_x, head_y = self.snake[-1]
        if self.direction == 'up':
            new_head = (head_x, head_y - 20)
        elif self.direction == 'down':
            new_head = (head_x, head_y + 20)
        elif self.direction == 'left':
            new_head = (head_x - 20, head_y)
        elif self.direction == 'right':
            new_head = (head_x + 20, head_y)
        
        self.snake.append(new_head)
        
        if self.snake[-1] == self.food:
            self.score += 1
            self.food = self.create_food()
            self.draw_food()
        else:
            self.snake.pop(0)
        
        # Collision Detection
        if (self.snake[-1][0] < 0 or self.snake[-1][0] > 780 or
            self.snake[-1][1] < 0 or self.snake[-1][1] > 580 or
            self.snake[-1] in self.snake[:-1]):
            self.game_over = True
        
        self.draw_snake()
        self.window.after(100, self.update)
    
    def run(self):
        self.window.mainloop()

if __name__ == "__main__":
    game = SnakeGame()
    game.run()
  

c. Understanding the Code

The above script initializes a tkinter window and sets up the game elements, including the snake and food. Key aspects include:

  • Snake Initialization: The snake is represented as a list of tuples, each representing a segment's position.
  • Food Creation: Food is randomly placed on the canvas using the create_food method.
  • Movement: Key bindings handle direction changes, ensuring the snake cannot reverse directly.
  • Game Loop: The update method moves the snake, checks for collisions, and updates the canvas accordingly.

d. Running the Game

  1. Save the script as snake_tkinter.py.
  2. Open a terminal and navigate to the script's directory.
  3. Run the game using the command: python snake_tkinter.py

2. Creating a Snake Game with turtle

The turtle library is another built-in Python module that allows for unique and visually appealing graphics. Here's how to create a Snake game using turtle.

a. Setting Up the Environment

The turtle library is pre-installed with Python. Ensure you have Python installed to proceed.

b. The Complete Code


import turtle
import time
import random

delay = 0.1
score = 0
high_score = 0

# Set up the screen
screen = turtle.Screen()
screen.title("Snake Game")
screen.bgcolor("blue")
screen.setup(width=600, height=600)
screen.tracer(0)  # Turns off the screen updates

# Snake head
head = turtle.Turtle()
head.shape("square")
head.color("white")
head.penup()
head.goto(0, 0)
head.direction = "stop"

# Food
food = turtle.Turtle()
food.shape("circle")
food.color("red")
food.penup()
food.goto(0, 100)

# Score display
score_display = turtle.Turtle()
score_display.speed(0)
score_display.color("white")
score_display.penup()
score_display.hideturtle()
score_display.goto(0, 260)
score_display.write("Score: 0 High Score: 0", align="center", font=("Arial", 24, "normal"))

# Snake segments
segments = []

# Functions
def move_up():
    if head.direction != "down":
        head.direction = "up"

def move_down():
    if head.direction != "up":
        head.direction = "down"

def move_left():
    if head.direction != "right":
        head.direction = "left"

def move_right():
    if head.direction != "left":
        head.direction = "right"

def move():
    if head.direction == "up":
        y = head.ycor()
        head.sety(y + 20)
    if head.direction == "down":
        y = head.ycor()
        head.sety(y - 20)
    if head.direction == "left":
        x = head.xcor()
        head.setx(x - 20)
    if head.direction == "right":
        x = head.xcor()
        head.setx(x + 20)

# Keyboard bindings
screen.listen()
screen.onkey(move_up, "w")
screen.onkey(move_down, "s")
screen.onkey(move_left, "a")
screen.onkey(move_right, "d")

# Main game loop
while True:
    screen.update()
    
    # Check for collision with border
    if head.xcor()>290 or head.xcor()<-290 or head.ycor()>290 or head.ycor()<-290:
        time.sleep(1)
        head.goto(0,0)
        head.direction = "stop"
        
        # Hide the segments
        for segment in segments:
            segment.goto(1000, 1000)
        
        # Clear the segments list
        segments.clear()
        
        # Reset the score
        score = 0
        score_display.clear()
        score_display.write("Score: {} High Score: {}".format(score, high_score), align="center", font=("Arial", 24, "normal"))
    
    # Check for collision with food
    if head.distance(food) < 20:
        # Move the food to a random spot
        x = random.randint(-290, 290)
        y = random.randint(-290, 290)
        food.goto(x, y)
        
        # Add a segment
        new_segment = turtle.Turtle()
        new_segment.speed(0)
        new_segment.shape("square")
        new_segment.color("grey")
        new_segment.penup()
        segments.append(new_segment)
        
        # Increase the score
        score += 10
        if score > high_score:
            high_score = score
        score_display.clear()
        score_display.write("Score: {} High Score: {}".format(score, high_score), align="center", font=("Arial", 24, "normal"))
    
    # Move the end segments first in reverse order
    for index in range(len(segments)-1, 0, -1):
        x = segments[index-1].xcor()
        y = segments[index-1].ycor()
        segments[index].goto(x, y)
    
    # Move segment 0 to where the head is
    if len(segments) > 0:
        x = head.xcor()
        y = head.ycor()
        segments[0].goto(x, y)
    
    move()
    
    # Check for head collision with the body segments
    for segment in segments:
        if segment.distance(head) < 20:
            time.sleep(1)
            head.goto(0,0)
            head.direction = "stop"
            
            # Hide the segments
            for segment in segments:
                segment.goto(1000, 1000)
            
            segments.clear()
            score = 0
            score_display.clear()
            score_display.write("Score: {} High Score: {}".format(score, high_score), align="center", font=("Arial", 24, "normal"))
    
    time.sleep(delay)

c. Understanding the Code

This implementation leverages the turtle module to create a visually appealing Snake game. Key components include:

  • Screen Setup: Initializes the game window with specified dimensions and background color.
  • Snake and Food: The snake head and food are represented as Turtle objects with specific shapes and colors.
  • Score Management: Displays the current score and high score using a dedicated Turtle object.
  • Movement and Controls: Functions handle the direction changes based on user input, ensuring the snake moves smoothly.
  • Collision Detection: Checks for collisions with the borders and the snake's own body, resetting the game as needed.
  • Game Loop: Continuously updates the game state, including movement, collision checks, and score updates.

d. Running the Game

  1. Save the script as snake_turtle.py.
  2. Open a terminal and navigate to the script's directory.
  3. Run the game using the command: python snake_turtle.py
  4. Control the snake using the W (up), A (left), S (down), and D (right) keys.

3. Creating a Snake Game with pygame

pygame is a powerful library for creating games in Python, offering extensive functionalities for graphics, sound, and input handling. Below are multiple implementations of the Snake game using pygame, each building upon the previous to enhance features and complexity.

a. Basic Snake Game with Pygame

This implementation provides a foundational Snake game with essential features like movement, food consumption, and collision detection.


import pygame
import random

pygame.init()

# Window dimensions
window_width = 600
window_height = 400
window = pygame.display.set_mode((window_width, window_height))
pygame.display.set_caption("Snake Game")

# Colors
black = (0, 0, 0)
white = (255, 255, 255)
red = (255, 0, 0)
green = (0, 255, 0)

# Snake properties
snake_size = 10
snake_speed = 15

# Clock
clock = pygame.time.Clock()

# Fonts
font_style = pygame.font.SysFont(None, 30)
score_font = pygame.font.SysFont(None, 35)

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

def draw_snake(snake_size, snake_list):
    for x in snake_list:
        pygame.draw.rect(window, green, [x[0], x[1], snake_size, snake_size])

def game_loop():
    game_over = False
    game_close = False

    x = window_width / 2
    y = window_height / 2

    x_change = 0
    y_change = 0

    snake_list = []
    length_of_snake = 1

    food_x = round(random.randrange(0, window_width - snake_size) / 10.0) * 10.0
    food_y = round(random.randrange(0, window_height - snake_size) / 10.0) * 10.0

    score = 0

    while not game_over:

        while game_close:
            window.fill(black)
            message = font_style.render("You Lost! Press C-Play Again or Q-Quit", True, red)
            window.blit(message, [window_width / 6, window_height / 3])
            display_score(score)
            pygame.display.update()

            for event in pygame.event.get():
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_q:
                        game_over = True
                        game_close = False
                    if event.key == pygame.K_c:
                        game_loop()

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                game_over = True
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_LEFT:
                    x_change = -snake_size
                    y_change = 0
                elif event.key == pygame.K_RIGHT:
                    x_change = snake_size
                    y_change = 0
                elif event.key == pygame.K_UP:
                    y_change = -snake_size
                    x_change = 0
                elif event.key == pygame.K_DOWN:
                    y_change = snake_size
                    x_change = 0

        # Boundary conditions
        if x >= window_width or x < 0 or y >= window_height or y < 0:
            game_close = True

        x += x_change
        y += y_change
        window.fill(black)
        pygame.draw.rect(window, red, [food_x, food_y, snake_size, snake_size])
        snake_head = []
        snake_head.append(x)
        snake_head.append(y)
        snake_list.append(snake_head)

        if len(snake_list) > length_of_snake:
            del snake_list[0]

        # Self-collision
        for segment in snake_list[:-1]:
            if segment == snake_head:
                game_close = True

        draw_snake(snake_size, snake_list)
        display_score(score)

        pygame.display.update()

        # Food collision
        if x == food_x and y == food_y:
            food_x = round(random.randrange(0, window_width - snake_size) / 10.0) * 10.0
            food_y = round(random.randrange(0, window_height - snake_size) / 10.0) * 10.0
            length_of_snake += 1
            score += 1

        clock.tick(snake_speed)

    pygame.quit()
    quit()

game_loop()

b. Enhanced Snake Game with Pygame

This enhanced version incorporates additional features such as scorekeeping, adjustable speed, and improved graphics.


import pygame
import time
import random

pygame.init()

# Window dimensions
window_x = 720
window_y = 480
window = pygame.display.set_mode((window_x, window_y))
pygame.display.set_caption("Snake Game")

# Colors
black = pygame.Color(0, 0, 0)
white = pygame.Color(255, 255, 255)
red = pygame.Color(255, 0, 0)
green = pygame.Color(0, 255, 0)
blue = pygame.Color(0, 0, 255)

# FPS controller
fps = pygame.time.Clock()

# Snake properties
snake_position = [100, 50]
snake_body = [[100, 50], [90, 50], [80, 50], [70, 50]]
fruit_position = [random.randrange(1, (window_x//10)) * 10, random.randrange(1, (window_y//10)) * 10]
fruit_spawn = True

# Direction variables
direction = 'RIGHT'
change_to = direction

# Score
score = 0

# Font
def show_score(choice, color, font, size):
    score_font = pygame.font.SysFont(font, size)
    score_surface = score_font.render('Score : ' + str(score), True, color)
    score_rect = score_surface.get_rect()
    if choice == 1:
        score_rect.midtop = (window_x / 10, 15)
    else:
        score_rect.midtop = (window_x / 2, window_y / 1.25)
    window.blit(score_surface, score_rect)

# Game Over
def game_over_func():
    my_font = pygame.font.SysFont('times new roman', 50)
    game_over_surface = my_font.render('YOU DIED', True, red)
    game_over_rect = game_over_surface.get_rect()
    game_over_rect.midtop = (window_x / 2, window_y / 4)
    window.blit(game_over_surface, game_over_rect)
    show_score(0, red, 'times', 20)
    pygame.display.flip()
    time.sleep(2)
    pygame.quit()
    quit()

# Main Logic
while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            quit()
        # Whenever a key is pressed down
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_UP or event.key == ord('w'):
                change_to = 'UP'
            if event.key == pygame.K_DOWN or event.key == ord('s'):
                change_to = 'DOWN'
            if event.key == pygame.K_LEFT or event.key == ord('a'):
                change_to = 'LEFT'
            if event.key == pygame.K_RIGHT or event.key == ord('d'):
                change_to = 'RIGHT'
    
    # Validation of the direction
    if change_to == 'UP' and direction != 'DOWN':
        direction = 'UP'
    if change_to == 'DOWN' and direction != 'UP':
        direction = 'DOWN'
    if change_to == 'LEFT' and direction != 'RIGHT':
        direction = 'LEFT'
    if change_to == 'RIGHT' and direction != 'LEFT':
        direction = 'RIGHT'
    
    # Moving the snake
    if direction == 'UP':
        snake_position[1] -= 10
    if direction == 'DOWN':
        snake_position[1] += 10
    if direction == 'LEFT':
        snake_position[0] -= 10
    if direction == 'RIGHT':
        snake_position[0] += 10
    
    # Snake body growing mechanism
    snake_body.insert(0, list(snake_position))
    if snake_position[0] == fruit_position[0] and snake_position[1] == fruit_position[1]:
        score += 1
        fruit_spawn = False
    else:
        snake_body.pop()
        
    if not fruit_spawn:
        fruit_position = [random.randrange(1, (window_x//10)) * 10, random.randrange(1, (window_y//10)) * 10]
    fruit_spawn = True
    
    # Background
    window.fill(black)
    
    # Draw fruit
    pygame.draw.rect(window, green, pygame.Rect(fruit_position[0], fruit_position[1], 10, 10))
    
    # Draw snake
    for pos in snake_body:
        pygame.draw.rect(window, white, pygame.Rect(pos[0], pos[1], 10, 10))
    
    # Game Over conditions
    if snake_position[0] < 0 or snake_position[0] > window_x-10:
        game_over_func()
    if snake_position[1] < 0 or snake_position[1] > window_y-10:
        game_over_func()
    
    # Self collision
    for block in snake_body[1:]:
        if snake_position[0] == block[0] and snake_position[1] == block[1]:
            game_over_func()
    
    show_score(1, white, 'consolas', 20)
    
    pygame.display.update()
    fps.tick(snake_speed)

c. Advanced Snake Game with Pygame

This advanced version includes additional features such as levels, different speeds, and more sophisticated graphics to enhance gameplay.


import pygame
import time
import random

pygame.init()

# Initialize display
window_x = 800
window_y = 600
pygame.display.set_caption('Advanced Snake Game')
window = pygame.display.set_mode((window_x, window_y))

# Colors
black = pygame.Color(0, 0, 0)
white = pygame.Color(255, 255, 255)
red = pygame.Color(255, 0, 0)
green = pygame.Color(0, 255, 0)
blue = pygame.Color(0, 0, 255)
grey = pygame.Color(128, 128, 128)

# FPS (frames per second) controller
fps = pygame.time.Clock()

# Snake properties
snake_position = [100, 50]
snake_body = [[100, 50], [90, 50], [80, 50], [70, 50]]
fruit_position = [random.randrange(1, (window_x//10)) * 10, random.randrange(1, (window_y//10)) * 10]
fruit_spawn = True
direction = 'RIGHT'
change_to = direction

# Score
score = 0

# Level
level = 1
speed = 15

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

def show_score(choice, color, font, size):
    score_font_obj = pygame.font.SysFont(font, size)
    score_surface = score_font_obj.render('Score : ' + str(score), True, color)
    score_rect = score_surface.get_rect()
    if choice == 1:
        score_rect.midtop = (window_x / 10, 15)
    else:
        score_rect.midtop = (window_x / 2, window_y / 1.25)
    window.blit(score_surface, score_rect)

def draw_snake(snake_block, snake_list):
    for x in snake_list:
        pygame.draw.rect(window, green, pygame.Rect(x[0], x[1], snake_block, snake_block))

def message(msg, color):
    mesg = font_style.render(msg, True, color)
    window.blit(mesg, [window_x / 6, window_y / 3])

def gameLoop():
    global score, level, speed
    game_over = False
    game_close = False

    x1 = window_x / 2
    y1 = window_y / 2

    x1_change = 0
    y1_change = 0

    snake_List = []
    Length_of_snake = 1

    foodx = round(random.randrange(0, window_x - 10) / 10.0) * 10.0
    foody = round(random.randrange(0, window_y - 10) / 10.0) * 10.0

    while not game_over:

        while game_close:
            window.fill(black)
            message("You Lost! Press C-Play Again or Q-Quit", red)
            show_score(0, red, "comicsansms", 20)
            pygame.display.update()

            for event in pygame.event.get():
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_q:
                        game_over = True
                        game_close = False
                    if event.key == pygame.K_c:
                        score = 0
                        level = 1
                        speed = 15
                        gameLoop()

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                game_over = True
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_LEFT or event.key == ord('a'):
                    change_to = 'LEFT'
                elif event.key == pygame.K_RIGHT or event.key == ord('d'):
                    change_to = 'RIGHT'
                elif event.key == pygame.K_UP or event.key == ord('w'):
                    change_to = 'UP'
                elif event.key == pygame.K_DOWN or event.key == ord('s'):
                    change_to = 'DOWN'
                elif event.key == pygame.K_ESCAPE:
                    game_over = True

        # Validation of direction
        if change_to == 'UP' and direction != 'DOWN':
            direction = 'UP'
        if change_to == 'DOWN' and direction != 'UP':
            direction = 'DOWN'
        if change_to == 'LEFT' and direction != 'RIGHT':
            direction = 'LEFT'
        if change_to == 'RIGHT' and direction != 'LEFT':
            direction = 'RIGHT'

        # Moving the snake
        if direction == 'UP':
            y1 -= 10
        if direction == 'DOWN':
            y1 += 10
        if direction == 'LEFT':
            x1 -= 10
        if direction == 'RIGHT':
            x1 += 10

        # Snake body growing
        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]

        # Collision with self
        for x in snake_List[:-1]:
            if x == snake_Head:
                game_close = True

        # Boundary collision
        if x1 >= window_x or x1 < 0 or y1 >= window_y or y1 < 0:
            game_close = True

        window.fill(black)
        pygame.draw.rect(window, red, pygame.Rect(foodx, foody, 10, 10))
        draw_snake(10, snake_List)
        show_score(1, white, "comicsansms", 20)

        pygame.display.update()

        # Collision with food
        if x1 == foodx and y1 == foody:
            foodx = round(random.randrange(0, window_x - 10) / 10.0) * 10.0
            foody = round(random.randrange(0, window_y - 10) / 10.0) * 10.0
            Length_of_snake += 1
            score += 1
            if score % 5 == 0:
                level += 1
                speed += 5

        fps.tick(speed)

    pygame.quit()
    quit()

gameLoop()

d. Running the Game

  1. Ensure pygame is installed. If not, install it using: pip install pygame
  2. Save the desired implementation as snake_pygame.py.
  3. Open a terminal and navigate to the script's directory.
  4. Run the game using the command: python snake_pygame.py
  5. Control the snake using the arrow keys or W (up), A (left), S (down), and D (right) keys.

4. Enhancements and Features

Once you've mastered the basic Snake game, consider adding the following features to enhance gameplay and complexity:

a. Sound Effects

Add sound effects for actions like eating food or game over events to make the game more engaging.


# Initialize mixer
pygame.mixer.init()

# Load sounds
eat_sound = pygame.mixer.Sound('eat.wav')
game_over_sound = pygame.mixer.Sound('game_over.wav')

# Play sound on eating food
if x1 == foodx and y1 == foody:
    pygame.mixer.Sound.play(eat_sound)
    # Rest of the code...

# Play sound on game over
if collision_detected:
    pygame.mixer.Sound.play(game_over_sound)
    game_close = True

b. Increasing Difficulty Levels

Implementing levels that increase in difficulty can make the game more challenging. Increase the snake's speed or introduce obstacles as the player progresses.

c. Obstacles

Add obstacles within the game window that the snake must avoid. Collision with these obstacles should result in a game over.


# Define obstacle positions
obstacles = [
    pygame.Rect(200, 200, 10, 10),
    pygame.Rect(300, 300, 10, 10),
    # Add more obstacles as needed
]

# Draw obstacles
for obs in obstacles:
    pygame.draw.rect(window, grey, obs)

# Collision with obstacles
for obs in obstacles:
    if snake_head_rect.colliderect(obs):
        game_close = True

d. High Score Tracking

Persist the high score across game sessions by saving it to a file.


import os

def load_high_score():
    if os.path.exists("high_score.txt"):
        with open("high_score.txt", "r") as f:
            return int(f.read())
    return 0

def save_high_score(new_high_score):
    with open("high_score.txt", "w") as f:
        f.write(str(new_high_score))

# Initialize high score
high_score = load_high_score()

# Update high score
if score > high_score:
    high_score = score
    save_high_score(high_score)

# Display high score
def show_score(choice, color, font, size):
    ...
    if choice == 1:
        score_surface = score_font_obj.render('Score : ' + str(score) + '  High Score: ' + str(high_score), True, color)
    ...

e. Graphical Enhancements

Improve the game's visuals by adding images, animations, or more intricate designs for the snake and food.


# Load images
snake_img = pygame.image.load('snake.png')
food_img = pygame.image.load('food.png')

# Draw snake with image
for x in snake_list:
    window.blit(snake_img, (x[0], x[1]))

# Draw food with image
window.blit(food_img, (foodx, foody))

5. Conclusion

Creating a Snake game in Python is a rewarding project that helps solidify your understanding of programming concepts such as loops, conditionals, data structures, and event handling. By exploring different libraries like tkinter, turtle, and pygame, you can appreciate the versatility of Python in game development.

Start with a basic implementation and gradually introduce more features and complexities as your confidence grows. Happy coding and enjoy your Snake game!


Last updated January 3, 2025
Ask Ithy AI
Download Article
Delete Article