Snake Game Tutorial

Let’s Code A Snake Game in Python EASILY: How to Build a Simple Snake Game in Python with Turtle Graphics

In this tutorial, we’ll walk you through the process of creating a simple Snake game using Python’s turtle module. This classic game is not only a fun project but also a great way to practice your Python programming skills, especially if you’re new to game development.

Getting Started

Before we dive into the code, make sure you have Python installed on your machine. The turtle module comes pre-installed with Python, so you don’t need to install any external libraries.

Step 1: Setting Up the Screen

First, we need to set up the screen where our game will take place. We’ll use the turtle.Screen() function to create a window and configure its size, background color, and title.

import turtle
#We will make use of these below modules later on
import time 
import random

score = 0
high_score = 0
delay = 0.1

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

Step 2: Creating the Snake

Next, we’ll create the snake’s head, which will be controlled by the player. We’ll use the turtle.Turtle() function to create a turtle object that represents the snake’s head.

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

Step 3: Adding Food for the Snake

In the game, the snake needs to eat food to grow. Let’s create the food using another turtle object, this time with a circular shape and a red color.

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

Step 4: Displaying the Score

To make the game more interactive, we’ll display the player’s score and the high score at the top of the screen.

# Pen for scoring
pen = turtle.Turtle()
pen.speed(0)
pen.shape("square")
pen.color("white")
pen.penup()
pen.hideturtle()
pen.goto(0, 260)
pen.write("Score: 0 High Score: 0", align="center", font=("Courier", 24, "normal"))

Step 5: Moving the Snake

We’ll define functions to change the direction of the snake’s movement. These functions will be bound to the keyboard arrow keys, allowing the player to control the snake.

# Functions to change direction
def go_up():
    if head.direction != "down":
        head.direction = "up"

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

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

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

# Function to move the snake
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)

Step 6: Handling Keyboard Input

We’ll bind the movement functions to the w, s, a, and d keys, which will control the snake’s movement in the game.

# Keyboard bindings
wn.listen()
wn.onkey(go_up, "w")
wn.onkey(go_down, "s")
wn.onkey(go_left, "a")
wn.onkey(go_right, "d")

Step 7: Main Game Loop

Now, we’ll set up the main game loop where all the action happens. This loop will continuously update the screen, check for collisions, move the snake, and update the score.

# Main game loop
while True:
    wn.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 and reset the game
        for segment in segments:
            segment.goto(1000, 1000)
        segments.clear()

        # Reset the score
        score = 0

        pen.clear()
        pen.write("Score: {} High Score: {}".format(score, high_score), align="center", font=("Courier", 24, "normal"))

    # Check for collision with food
    if head.distance(food) < 20:
        x = random.randint(-290, 290)
        y = random.randint(-290, 290)
        food.goto(x, y)

        # Add a segment to the snake
        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

        pen.clear()
        pen.write("Score: {} High Score: {}".format(score, high_score), align="center", font=("Courier", 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 collision with the body
    for segment in segments:
        if segment.distance(head) < 20:
            time.sleep(1)
            head.goto(0, 0)
            head.direction = "stop"

            # Hide the segments and reset the game
            for segment in segments:
                segment.goto(1000, 1000)
            segments.clear()

            # Reset the score
            score = 0

            pen.clear()
            pen.write("Score: {} High Score: {}".format(score, high_score), align="center", font=("Courier", 24, "normal"))

    time.sleep(delay)

wn.mainloop()

Don’t worry, I will break the main game loop in pieces.

The main game loop is the heart of the game, where the game logic continuously runs. It updates the screen, checks for collisions, moves the snake, and updates the score. Let’s go through this step by step.

7.1: Update the Screen

The wn.update() call updates the screen with the latest changes. Since we turned off automatic screen updates with wn.tracer(0), this line is necessary to manually refresh the screen. This ensures that the screen displays the current state of the game, including the position of the snake and food.

while True:
    wn.update()

7.2: Check for Border Collision

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"

This checks if the snake’s head has collided with the border of the screen. head.xcor() and head.ycor() get the current x and y coordinates of the snake’s head. If the head moves beyond the 290-pixel boundary (which is close to the screen edge), the snake is considered to have collided with the border. time.sleep(1) pauses the game for a moment to indicate the collision. head.goto(0, 0) resets the snake’s head position to the center of the screen. head.direction = "stop" stops the snake from moving after a collision.

7.3: Reset the Snake After Collision

        for segment in segments:
            segment.goto(1000, 1000)
        segments.clear()

        score = 0

        pen.clear()
        pen.write("Score: {} High Score: {}".format(score, high_score), align="center", font=("Courier", 24, "normal"))

This code resets the snake and clears the screen after a collision with the border. for segment in segments: loops through all segments of the snake’s body. segment.goto(1000, 1000) moves each segment off the screen to effectively “hide” them. segments.clear() removes all segments from the list, effectively resetting the snake to just the head. score = 0 resets the current score to zero. pen.clear() clears the current score display from the screen. pen.write(...) rewrites the score, showing the reset score and the high score.

7.4: Check for Collision with Food

    if head.distance(food) < 20:
        x = random.randint(-290, 290)
        y = random.randint(-290, 290)
        food.goto(x, y)

This checks if the snake has collided with the food. head.distance(food) < 20 checks the distance between the snake’s head and the food. If it’s less than 20 pixels, it means the snake has “eaten” the food. If a collision is detected, new random coordinates are generated using random.randint(-290, 290) for both x and y positions to move the food to a new location.

7.5: Add a Segment to the Snake

        new_segment = turtle.Turtle()
        new_segment.speed(0)
        new_segment.shape("square")
        new_segment.color("grey")
        new_segment.penup()
        segments.append(new_segment)

This code adds a new segment to the snake’s body each time it eats the food. A new turtle.Turtle() object is created, representing a new body segment. The segment is styled to match the rest of the snake with shape("square") and color("grey"). segments.append(new_segment) adds this new segment to the segments list.

7.6: Update the Score

        score += 10
        if score > high_score:
            high_score = score

        pen.clear()
        pen.write("Score: {} High Score: {}".format(score, high_score), align="center", font=("Courier", 24, "normal"))

This updates the current score each time the snake eats food and checks if the current score exceeds the high score. score += 10 increases the score by 10 points each time the snake eats food. If the current score exceeds the high_score, the high score is updated. pen.clear() and pen.write(...) update the displayed score on the screen.

7.7: Move the Snake’s Body Segments

    for index in range(len(segments) - 1, 0, -1):
        x = segments[index - 1].xcor()
        y = segments[index - 1].ycor()
        segments[index].goto(x, y)

This moves the snake’s body segments to follow the head as it moves. The loop for index in range(len(segments) - 1, 0, -1): starts from the last segment and works backwards. Each segment takes the position of the segment before it, creating the appearance of the snake slithering.

7.8: Move the First Segment to Follow the Head

    if len(segments) > 0:
        x = head.xcor()
        y = head.ycor()
        segments[0].goto(x, y)

This ensures that the first segment of the snake follows the head. The first segment (if it exists) is moved to the current position of the snake’s head, keeping the snake connected.

7.9: Move the Snake

    move()

This simply calls the move() function to update the position of the snake’s head based on its current direction.

7.10: Check for Collision with the Snake’s Own Body

    for segment in segments:
        if segment.distance(head) < 20:
            time.sleep(1)
            head.goto(0, 0)
            head.direction = "stop"
            
            for segment in segments:
                segment.goto(1000, 1000)
            segments.clear()

            score = 0

            pen.clear()
            pen.write("Score: {} High Score: {}".format(score, high_score), align="center", font=("Courier", 24, "normal"))

This code checks if the snake has collided with its own body, which would end the game. The loop checks if any segment of the snake’s body is within 20 pixels of the head. If a collision is detected, the game pauses for a moment, resets the head to the center, hides the segments, and resets the score—similar to the border collision logic.

7.11: Control the Game Speed

    time.sleep(delay)

This controls the speed of the game by pausing the loop for a short time on each iteration. time.sleep(delay) pauses the game loop for the duration specified by delay (0.1 seconds), controlling the speed at which the game runs.

7.12: Keep the Game Running

wn.mainloop()

This keeps the turtle graphics window open and the game running indefinitely. The mainloop() method ensures the program doesn’t terminate after the initial loop and keeps the game window active.

Conclusion

Congratulations! You’ve just built a simple Snake game in Python using the turtle module. You can enhance this game by adding new features, like levels, power-ups, or even a two-player mode. The possibilities are endless!

Leave a Reply

Your email address will not be published. Required fields are marked *