Building a small game is a useful way to learn Python because the program has to respond to input, update state, apply rules, and render a result repeatedly. A Snake game is a compact project that brings these ideas together without requiring a large framework.

This tutorial builds a simple Snake game with Python’s built-in turtle module. The focus is on the game loop, state management, event handling, and collision logic.
What You Will Build
- A 600 × 600 game window
- A controllable snake head
- Randomly positioned food
- A growing body stored as a list of segments
- Wall and self-collision handling
- Keyboard controls and a simple score counter
Requirements
You need Python 3 and a desktop environment that supports Tk-based Turtle graphics. The example uses only the standard-library modules turtle, time, and random, so there is no separate package installation.
Understand the Game State
- Direction: the current movement direction.
- Head position: the snake’s current coordinates.
- Food position: the target the snake is trying to reach.
- Body segments: the positions that follow the head.
- Score and game status: values that change during play.
Step 1: Import the Modules
import turtle import time import random
turtle draws the game, time controls the update delay, and random generates new food positions.
Step 2: Create the Game Window
screen = turtle.Screen()
screen.title("Snake Game in Python")
screen.bgcolor("black")
screen.setup(width=600, height=600)
screen.tracer(0)
Calling tracer(0) gives the program control over screen refreshes. We will call screen.update() once per loop iteration.
Step 3: Create the Snake Head
head = turtle.Turtle()
head.shape("square")
head.color("green")
head.penup()
head.goto(0, 0)
head.direction = "stop"
The custom direction attribute stores movement state, while penup() prevents drawing a line as the head moves.
Step 4: Create the Food
food = turtle.Turtle()
food.shape("circle")
food.color("red")
food.penup()
food.goto(100, 0)
Step 5: Add Direction Controls
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"
The checks prevent an immediate 180-degree turn, which would make the snake move directly into its existing body.
Step 6: Register Keyboard Events
screen.listen() screen.onkeypress(go_up, "Up") screen.onkeypress(go_down, "Down") screen.onkeypress(go_left, "Left") screen.onkeypress(go_right, "Right")
Step 7: Move the Head
def move():
step = 20
if head.direction == "up":
head.sety(head.ycor() + step)
elif head.direction == "down":
head.sety(head.ycor() - step)
elif head.direction == "left":
head.setx(head.xcor() - step)
elif head.direction == "right":
head.setx(head.xcor() + step)
Step 8: Move the Snake Body
Store the body segments in a list. Update the list from the tail toward the head so each segment receives the position that was previously occupied by the segment ahead of it.
segments = []
def move_body():
for index in range(len(segments) - 1, 0, -1):
segments[index].goto(
segments[index - 1].xcor(),
segments[index - 1].ycor()
)
if segments:
segments[0].goto(head.xcor(), head.ycor())
Step 9: Add a New Segment
def add_segment():
segment = turtle.Turtle()
segment.shape("square")
segment.color("green")
segment.penup()
if segments:
segment.goto(segments[-1].xcor(), segments[-1].ycor())
else:
segment.goto(head.xcor(), head.ycor())
segments.append(segment)
Step 10: Build the Main Game Loop
score = 0
while True:
screen.update()
move_body()
move()
if head.distance(food) < 20:
score += 10
food.goto(
random.randint(-280, 280),
random.randint(-280, 280)
)
add_segment()
if (head.xcor() > 290 or head.xcor() < -290 or
head.ycor() > 290 or head.ycor() < -290):
break
for segment in segments:
if segment.distance(head) < 20:
break
time.sleep(0.1)
screen.bye()
The loop is the game engine: it updates the display, moves the body and head, handles food, checks boundaries, checks the body, and waits briefly before the next frame.
Collision Handling
Food collision increases the score and adds a segment. Wall collision ends the basic game. For a complete game-over implementation, track a game_over flag and exit the loop when the head touches a segment.
Common Bugs to Watch For
- Updating body segments from the head toward the tail instead of backward.
- Allowing an immediate reverse direction.
- Generating food outside the playable area.
- Forgetting
screen.update()when using manual tracing. - Checking self-collision against stale segment positions.
How to Turn This Into a Better Project
- Add a proper game-over screen and restart control.
- Display the score inside the game window.
- Increase speed as the score grows.
- Prevent food from spawning on the snake body.
- Separate input, update, collision, and rendering into independent functions.
- Refactor the game into classes once the procedural version is understood.
What This Project Teaches
| Concept | Where It Appears |
|---|---|
| Functions | Movement, input, body updates, collisions |
| Lists | Storing snake segments |
| Loops | Game updates and body movement |
| Conditions | Direction and collision rules |
| Events | Keyboard input |
| State | Direction, food, score, and game status |
Conclusion
A Snake game is small enough to understand end to end, yet rich enough to demonstrate application state, event handling, control flow, collections, and collision logic. Build the simplest version first, test each rule, and then add features incrementally. That workflow teaches more than copying a large finished project.
2 thoughts on “Creating a Snake Game in Python”