๐ŸŽ“ A1Learner
๐Ÿ”
0 of 18 lessons completed

Python Snake Obstacles

Obstacles are fixed grey blocks on the board that end the run when the head runs into them. This challenge adds three blocks to the complete game.

A list to hold the blocks

The obstacles list starts empty. The loop below fills it with one block for each spot.

# Three grey blocks sit on the board as obstacles
obstacles = []

One turtle per spot

The loop builds one grey square Turtle for each spot. goto takes the pair itself and places the block.

for spot in [(-100, 100), (100, -100), (120, 120)]:
    block = turtle.Turtle()
    block.speed(0)
    block.shape("square")
    block.color("grey")
    block.penup()
    block.goto(spot)
    obstacles.append(block)

The checks before eating

The wall check and the self check keep their places in the loop. The obstacle check runs after them and before the eat check.

        # Obstacles: the head ran into a block, so the run ends
        for block in obstacles:
            if head.distance(block) < 20:
                reset_game()
                break

Example

# Snake - the classic arcade game, built lesson by lesson.
# Run it with: python snake.py
# Steer with the arrow keys. Press P to pause and R to restart.

import turtle
import time
import random

# The game window
screen = turtle.Screen()
screen.setup(width=600, height=600)
screen.bgcolor("darkgreen")
screen.title("Snake")
screen.tracer(0)

# The border marks the edge of the board
border = turtle.Turtle()
border.speed(0)
border.color("white")
border.penup()
border.goto(-290, -290)
border.pendown()
for side in range(4):
    border.forward(580)
    border.left(90)
border.hideturtle()

# The snake head
head = turtle.Turtle()
head.speed(0)
head.shape("square")
head.color("lime")
head.penup()
head.goto(0, 0)
head.direction = "right"

# The food: one red circle on the board
food = turtle.Turtle()
food.speed(0)
food.shape("circle")
food.color("red")
food.penup()

# Three grey blocks sit on the board as obstacles
obstacles = []
for spot in [(-100, 100), (100, -100), (120, 120)]:
    block = turtle.Turtle()
    block.speed(0)
    block.shape("square")
    block.color("grey")
    block.penup()
    block.goto(spot)
    obstacles.append(block)

# The body starts empty; the scores start at zero
segments = []
score = 0
high_score = 0
delay = 0.1
paused = False

# The pen writes the score at the top of the board
pen = turtle.Turtle()
pen.speed(0)
pen.color("white")
pen.penup()
pen.hideturtle()
pen.goto(0, 260)


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


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"


def new_food_spot():
    x = random.randint(-14, 14) * 20
    y = random.randint(-14, 14) * 20
    food.goto(x, y)


def show_score():
    pen.clear()
    pen.write(f"Score: {score}  High Score: {high_score}", align="center", font=("Courier", 20, "bold"))


def toggle_pause():
    global paused
    paused = not paused


def reset_game():
    global score, high_score, delay
    time.sleep(0.5)
    head.goto(0, 0)
    head.direction = "stop"
    for segment in segments:
        segment.hideturtle()
    segments.clear()
    if score > high_score:
        high_score = score
    score = 0
    delay = 0.1
    show_score()


new_food_spot()
show_score()

# The keyboard steers the snake
screen.listen()
screen.onkey(go_up, "Up")
screen.onkey(go_down, "Down")
screen.onkey(go_left, "Left")
screen.onkey(go_right, "Right")
screen.onkey(toggle_pause, "p")
screen.onkey(reset_game, "r")

# The game loop: redraw, step the game unless paused, wait, repeat
while True:
    screen.update()

    if not paused:
        # The body follows: each segment takes the place of the one ahead
        for index in range(len(segments) - 1, 0, -1):
            x = segments[index - 1].xcor()
            y = segments[index - 1].ycor()
            segments[index].goto(x, y)
        if len(segments) > 0:
            segments[0].goto(head.xcor(), head.ycor())

        move()

        # Walls: the head left the board, so the run ends
        if head.xcor() > 290 or head.xcor() < -290 or head.ycor() > 290 or head.ycor() < -290:
            reset_game()

        # Self: the head ran into the body, so the run ends
        for segment in segments:
            if segment.distance(head) < 20:
                reset_game()
                break

        # Obstacles: the head ran into a block, so the run ends
        for block in obstacles:
            if head.distance(block) < 20:
                reset_game()
                break

        # Eat: the head reached the food
        if head.distance(food) < 20:
            new_food_spot()
            new_segment = turtle.Turtle()
            new_segment.speed(0)
            new_segment.shape("square")
            new_segment.color("lightgreen")
            new_segment.penup()
            segments.append(new_segment)
            score = score + 10
            if delay > 0.05:
                delay = delay - 0.005
            show_score()

    time.sleep(delay)

Key points

  • The obstacles are kept together in a list named obstacles
  • The spots come from a list of (x, y) pairs
  • goto(spot) takes a pair directly, with no separate x and y
  • The three spots are (-100, 100), (100, -100) and (120, 120)
  • The obstacle check joins the wall check and the self check before the eat check
  • The check uses the same distance rule as the food and the body
  • break stops the check at the first block the head hits

In this example, the program is the complete game with one new section and one new check. The new section sits after the food setup. It runs through the list of three spots and builds one Turtle per spot. Each block is a grey square with its pen up. goto places each block at its pair of coordinates, and the block joins the obstacles list. The rest of the setup and the functions match the complete game. The loop still follows, moves and runs the wall check and the self check. A new obstacle check then tests every block with the same distance rule as the other checks. A head closer than 20 to a block calls reset_game, and break leaves the check at that first hit. Only a head that clears every block reaches the eat check below it.

Try it Yourself

Edit the code, then press Run.

Running is simulated

Exercise

What happens when the head comes within 20 of a grey block?