Skip to main content
This page walks through the entire SnakeGame.py source file section by section. Each accordion covers a distinct part of the program, with the actual code and an explanation of what it does.
The file begins with three standard library imports and three global variables that track game state.
import turtle
import time
import random

retraso = 0.1
puntaje = 0
puntajeAlto = 0
NameTypePurpose
turtlemoduleProvides the graphics window, drawing primitives, and Turtle objects for the snake, food, and score display.
timemoduleUsed for time.sleep() to introduce a fixed delay between each frame.
randommoduleUsed for random.randint() to place food at a random position after it is eaten.
retrasofloatFrame delay in seconds. Set to 0.1, meaning the game targets roughly 10 frames per second.
puntajeintCurrent score. Starts at 0, increments by 10 each time food is eaten, and resets to 0 on a collision.
puntajeAltointSession high score. Starts at 0 and is only updated upward — it never resets during a session.
The game window is created and configured before any game objects are drawn.
screen = turtle.Screen()
screen.setup(650, 650)
screen.bgcolor('black')
screen.title('Snake Game "La culebrita" - Proyecto Programacion III (Grupo 5)')
screen.tracer(0)
CallEffect
turtle.Screen()Creates the single game window and returns a reference stored in screen.
screen.setup(650, 650)Sets the canvas to 650×650 pixels. The playable area is the inner ±300 pixel range on each axis.
screen.bgcolor('black')Sets the background color to black, which provides contrast for the light green snake and red food.
screen.title(...)Sets the text shown in the window title bar.
screen.tracer(0)Disables automatic screen updates. With tracer(0), nothing is drawn to the window until screen.update() is called explicitly. This prevents flickering during a frame and is required for smooth animation.
Because screen.tracer(0) is set here, every iteration of the main game loop must call screen.update() manually at the start of the loop to render the latest frame.
The snake’s head is a single Turtle object. The body segments are separate objects added later.
serpiente = turtle.Turtle()
serpiente.speed(1)
serpiente.shape('square')
serpiente.penup()
serpiente.goto(0, 0)
serpiente.direction = 'stop'
serpiente.color('lightgreen')
Attribute / CallPurpose
serpiente.shape('square')Renders the head as a filled square, matching the grid-aligned 20-pixel movement steps.
serpiente.color('lightgreen')Light green color distinguishes the head from the darker green body segments.
serpiente.penup()Prevents the turtle from drawing lines as it moves.
serpiente.goto(0, 0)Places the head at the center of the canvas at startup.
serpiente.direction = 'stop'A custom attribute (not built-in to Turtle) used by the direction functions and movimiento() to track which way the snake is heading. Initial value 'stop' means the snake does not move until the player presses an arrow key.
The food is a separate Turtle object styled as a red circle.
comida = turtle.Turtle()
comida.shape('circle')
comida.color('red')
comida.penup()
comida.goto(0, 100)
comida.speed(0)
Attribute / CallPurpose
comida.shape('circle')Circle shape visually differentiates the food from the square snake segments.
comida.color('red')High-contrast red makes the food easy to spot on the black background.
comida.goto(0, 100)Places the food slightly above center at startup so it is not on top of the snake head.
comida.speed(0)Sets the turtle animation speed to maximum, so repositioning the food after it is eaten appears instantaneous.
When the snake eats the food, the game loop calls comida.goto(x, y) with random coordinates to move it to a new position.
The snake’s body is tracked as a plain Python list, and a dedicated Turtle object renders the score text.
cuerpo = []

texto = turtle.Turtle()
texto.speed(0)
texto.color('white')
texto.penup()
texto.hideturtle()
texto.goto(0, -260)
texto.write("Puntaje: 0\tPuntaje más alto: 0", align="center", font=("verdana", 12, "normal"))
cuerpo is a list of turtle.Turtle objects. Each time the snake eats food, a new square Turtle (colored 'green') is appended. The game loop propagates positions through this list each frame so segments follow the head.
texto callEffect
texto.hideturtle()Hides the turtle cursor so only the written text is visible.
texto.goto(0, -260)Positions the score text below the 300-pixel play boundary, in the lower margin of the 650-pixel canvas.
texto.write(...)Renders the score string. The align="center" argument centers the text at the turtle’s position. texto.clear() is called before each update to erase the previous text before writing the new value.
Four functions handle keyboard input. Each sets serpiente.direction but includes a guard to prevent reversing direction (which would cause an immediate self-collision).
def arriba():
    if serpiente.direction != 'down':
        serpiente.direction = 'up'

def abajo():
    if serpiente.direction != 'up':
        serpiente.direction = 'down'

def derecha():
    if serpiente.direction != 'left':
        serpiente.direction = 'right'

def izquierda():
    if serpiente.direction != 'right':
        serpiente.direction = 'left'
Each function follows the same pattern:
  1. Check whether the snake is currently moving in the directly opposite direction.
  2. Only update serpiente.direction if the guard condition passes.
For example, calling abajo() while the snake is moving 'up' does nothing — the guard serpiente.direction != 'up' is False, so the assignment is skipped. This prevents the player from instantly reversing into the snake’s own body.
The guard conditions also allow the snake to change direction when serpiente.direction == 'stop' (the initial state), so the first key press the player makes will always work.
movimiento() translates the current direction string into a pixel offset applied to the snake head.
def movimiento():
    if serpiente.direction == 'up':
        y = serpiente.ycor()
        serpiente.sety(y + 20)
    if serpiente.direction == 'down':
        y = serpiente.ycor()
        serpiente.sety(y - 20)
    if serpiente.direction == 'right':
        x = serpiente.xcor()
        serpiente.setx(x + 20)
    if serpiente.direction == 'left':
        x = serpiente.xcor()
        serpiente.setx(x - 20)
The snake moves in 20-pixel steps to stay on a grid aligned with the square turtle size. Each call to movimiento() moves the head exactly once, so calling it once per game loop iteration produces consistent speed controlled by time.sleep(retraso).When serpiente.direction == 'stop', none of the four if branches match and the function returns without moving the head.
Keyboard input is wired up after the functions are defined.
screen.listen()
screen.onkeypress(arriba, "Up")
screen.onkeypress(abajo, "Down")
screen.onkeypress(derecha, "Right")
screen.onkeypress(izquierda, "Left")
CallEffect
screen.listen()Activates keyboard event detection on the game window. Without this call, onkeypress bindings have no effect.
screen.onkeypress(arriba, "Up")Calls arriba() each time the Up arrow key is pressed.
screen.onkeypress(abajo, "Down")Calls abajo() each time the Down arrow key is pressed.
screen.onkeypress(derecha, "Right")Calls derecha() each time the Right arrow key is pressed.
screen.onkeypress(izquierda, "Left")Calls izquierda() each time the Left arrow key is pressed.
The key strings "Up", "Down", "Right", and "Left" are the Tkinter key names for the arrow keys. turtle uses Tkinter under the hood for event handling.
The game runs in an infinite while True: loop. Each iteration represents one frame.
while True:
    screen.update()

    # Wall collision
    if (serpiente.xcor() > 300 or
    serpiente.xcor() < -300 or
    serpiente.ycor() > 300 or
    serpiente.ycor() < -300):
        time.sleep(2)
        for i in cuerpo:
            i.clear()
            i.hideturtle()
        serpiente.home()
        serpiente.direction = 'stop'
        cuerpo.clear()
        puntaje = 0
        texto.clear()
        texto.write(f"Puntaje: {puntaje}\tPuntaje más alto: {puntajeAlto}",
        align="center",
        font=("verdana", 12, "normal"))

    # Food collision
    if serpiente.distance(comida) < 20:
        x = random.randint(-250, 250)
        y = random.randint(-250, 250)
        comida.goto(x, y)
        nuevoCuerpo = turtle.Turtle()
        nuevoCuerpo.shape('square')
        nuevoCuerpo.color('green')
        nuevoCuerpo.penup()
        nuevoCuerpo.goto(0, 0)
        nuevoCuerpo.speed(0)
        cuerpo.append(nuevoCuerpo)
        puntaje += 10
        if puntaje > puntajeAlto:
            puntajeAlto = puntaje
            texto.clear()
            texto.write(f"Puntaje: {puntaje}\tPuntaje más alto: {puntajeAlto}",
            align="center",
            font=("verdana", 12, "normal"))
        if puntaje < puntajeAlto:
            texto.clear()
            texto.write(f"Puntaje: {puntaje}\tPuntaje más alto: {puntajeAlto}",
            align="center",
            font=("verdana", 12, "normal"))

    # Body segment propagation
    total = len(cuerpo)
    for i in range(total - 1, 0, -1):
        x = cuerpo[i - 1].xcor()
        y = cuerpo[i - 1].ycor()
        cuerpo[i].goto(x, y)
    if total > 0:
        x = serpiente.xcor()
        y = serpiente.ycor()
        cuerpo[0].goto(x, y)

    movimiento()

    # Self-collision
    for i in cuerpo:
        if i.distance(serpiente) < 20:
            for i in cuerpo:
                i.clear()
                i.hideturtle()
            serpiente.home()
            cuerpo.clear()
            serpiente.direction = 'stop'
            puntaje = 0
            texto.clear()
            texto.write(f"Puntaje: {puntaje}\tPuntaje más alto: {puntajeAlto}",
            align="center",
            font=("verdana", 12, "normal"))

    time.sleep(retraso)
Each frame executes in this order:
  1. screen.update() — Flushes all pending drawing operations to the window. Required because screen.tracer(0) is set.
  2. Wall collision check — If xcor() or ycor() is outside ±300, the game pauses for 2 seconds, hides and removes all body segments, returns the head to the origin with serpiente.home(), resets serpiente.direction to 'stop', clears cuerpo, resets puntaje to 0, and updates the score display. puntajeAlto is not reset.
  3. Food collision — If the Euclidean distance between the snake head and comida is less than 20 pixels, food is relocated randomly within ±250 pixels, a new body segment Turtle is created (dark green square) and appended to cuerpo, puntaje increases by 10, and puntajeAlto is updated if the new score exceeds it.
  4. Body propagation — Iterates cuerpo from the tail forward, moving each segment to the previous segment’s position. The first segment moves to the head’s current (pre-move) position. This runs before movimiento() so the head’s old position is captured correctly.
  5. movimiento() — Advances the snake head one step.
  6. Self-collision check — Iterates all body segments and checks if any is within 20 pixels of the head. If so, the game resets identically to a wall collision (minus the 2-second pause).
  7. time.sleep(retraso) — Pauses for 0.1 seconds, throttling the game to approximately 10 frames per second.

Build docs developers (and LLMs) love