Intermediate — Day 4
Body Segments & Food
Day Schedule
Online
| Time | Activity |
|---|---|
| 10:00 | Food Setup food turtle, random position with random.randint() |
| 10:15 | Body Segments bodysegments list, head.clone(), got_food() |
| 10:35 | Backward Shift Loop Draw on whiteboard before coding; explain why backwards |
| 11:00 | Break |
| 11:10 | Breakout Groups Guided coding: complete playable snake |
| 12:20 | Reminders |
In-Person
| Time | Activity |
|---|---|
| 8:00 | Warm-Up Review Day 3: wall collision working for everyone? |
| 8:20 | Food Setup food turtle, random position with random.randint() |
| 8:45 | Body Segments bodysegments list, head.clone(), got_food() |
| 9:10 | Backward Shift Loop Draw on whiteboard before coding; explain why backwards |
| 9:40 | Break |
| 9:50 | Breakout Groups Guided coding: complete playable snake |
| 11:00 | Break |
| 11:10 | Extra Activity Body collision detection + score display with a pen turtle |
| 11:50 | Open Coding Catch up or add extra features |
| 12:20 | Reminders |
Learning Objectives
- Understand why the body shift loop iterates backwards (not forwards)
- Use head.clone() to create body segments
- Detect food collision with head.distance()
- Clear body segments in reset_game() using hideturtle() and clear()
Main Topic
# INSTRUCTOR — Day 4: Lists, For Loops & Intro to AI (Intermediate)
# -----------------------------------------------
# TEACH (first half): The body segment system is the most complex logic of the week.
# Walk through the backward shift loop carefully on a whiteboard or diagram first.
# Key insight: we go BACKWARDS through the list so each segment gets the position
# of the one AHEAD of it — if we went forwards, every segment would pile onto index 0.
#
# TEACH (second half): AI as a coding partner.
# Frame: "You've built most of this game. AI can now help you push further."
# Demo: paste the snake code into AI and ask it to add a score display.
# Have students read every line of the result before using it.
import turtle
import time
import random
# TEACH: Introduce random before using it.
# "random is a Python library for generating unpredictable values."
# random.randint(a, b) returns a random whole number between a and b (inclusive).
# Demo: run random.randint(-270, 270) several times and show different results each time.
# Ask: why -270 to 270 and not -300 to 300? (food would appear outside the playable border)
screen = turtle.Screen()
screen.title("Snake Game")
screen.bgcolor("black")
screen.setup(width=600, height=600)
screen.tracer(0)
head = turtle.Turtle()
head.shape("square")
head.color("white")
head.penup()
head.goto(0, 0)
# TEACH: bodysegments is a list of turtle objects — each one is a visible body square
bodysegments = []
def go_up():
if head.heading() != 270:
head.setheading(90)
def go_down():
if head.heading() != 90:
head.setheading(270)
def go_left():
if head.heading() != 0:
head.setheading(180)
def go_right():
if head.heading() != 180:
head.setheading(0)
screen.listen()
screen.onkeypress(go_up, "Up")
screen.onkeypress(go_down, "Down")
screen.onkeypress(go_left, "Left")
screen.onkeypress(go_right, "Right")
# TEACH: the backward shift loop
# range(len-1, 0, -1) counts from last index DOWN to index 1 (not 0).
# We stop at 1 because index 0 is handled separately (it follows the head).
for i in range(len(bodysegments) - 1, 0, -1):
bodysegments[i].goto(bodysegments[i - 1].pos())
bodysegments[i].setheading(bodysegments[i - 1].heading())
if len(bodysegments) > 0:
bodysegments[0].goto(head.pos())
bodysegments[0].setheading(head.heading())
# TEACH: clone() copies the turtle's shape, color, size — no need to set those again
new_seg = head.clone()
bodysegments.append(new_seg)
Project — Complete Snake
# INSTRUCTOR — Day 4: Project: Snake: Body Segments & Food (Intermediate)
# -----------------------------------------------
# TEACH: Cumulative from Day 3. This is the fully playable snake game.
# New additions: food turtle, got_food() function, body shift loop, food collision.
# Check: eating food grows the snake; body follows head; wall collision still works.
# Common issue: snake "teleports" body — likely body shift loop placed AFTER head.forward().
# The shift loop must run BEFORE head moves so segments copy positions from this frame.
import turtle
import time
import random
screen = turtle.Screen()
screen.title("Snake Game")
screen.bgcolor("black")
screen.setup(width=600, height=600)
screen.tracer(0)
head = turtle.Turtle()
head.shape("square")
head.color("white")
head.penup()
head.goto(0, 0)
# --- Food ---
food = turtle.Turtle()
food.shape("circle")
food.color("green")
food.penup()
food.goto(random.randint(-270, 270), random.randint(-270, 270))
# TEACH: bodysegments grows by one each time the snake eats
bodysegments = []
def got_food():
food.goto(random.randint(-270, 270), random.randint(-270, 270))
new_seg = head.clone() # clone copies shape + color from head
bodysegments.append(new_seg)
def go_up():
if head.heading() != 270:
head.setheading(90)
def go_down():
if head.heading() != 90:
head.setheading(270)
def go_left():
if head.heading() != 0:
head.setheading(180)
def go_right():
if head.heading() != 180:
head.setheading(0)
screen.listen()
screen.onkeypress(go_up, "Up")
screen.onkeypress(go_down, "Down")
screen.onkeypress(go_left, "Left")
screen.onkeypress(go_right, "Right")
halfSize = 290
def reset_game():
head.goto(0, 0)
head.setheading(0)
for seg in bodysegments:
seg.hideturtle() # TEACH: hide so segments disappear visually
bodysegments.clear() # TEACH: clear empties the list — segments are gone
while True:
screen.update()
# TEACH: body shift must happen BEFORE head moves
for i in range(len(bodysegments) - 1, 0, -1):
bodysegments[i].goto(bodysegments[i - 1].pos())
bodysegments[i].setheading(bodysegments[i - 1].heading())
if len(bodysegments) > 0:
bodysegments[0].goto(head.pos())
bodysegments[0].setheading(head.heading())
head.forward(20)
# Food collision
if head.distance(food) < 20: # distance() measures pixel distance between two turtles
got_food()
# Wall collision
if (head.xcor() > halfSize or head.xcor() < -halfSize or
head.ycor() > halfSize or head.ycor() < -halfSize):
reset_game()
time.sleep(0.1)
Extra — Body Collision
# INSTRUCTOR — Day 4: Extra Activity: Body Collision (Intermediate)
# -----------------------------------------------
# TEACH: Add self-collision — the game resets if the head touches any body segment.
# This is a for loop over the bodysegments list with a distance check.
# It slots into the game loop after the body shift loop and food check.
# Threshold of 15px (not 20) avoids false triggers on adjacent segments.
# REFERENCE: the body collision block (added inside the game loop)
for seg in bodysegments:
if head.distance(seg) < 15:
reset_game()
break # TEACH: break exits the for loop immediately — no need to check remaining segments
# TEACH: why 15 and not 20? Each square is 20px wide.
# At distance 20 the squares are just touching edge-to-edge.
# At distance 15 they are overlapping — that's a real collision, not just adjacency.
# TEACH challenge: add a second food item
food2 = turtle.Turtle()
food2.shape("circle")
food2.color("yellow") # different color so students can tell them apart
food2.penup()
food2.goto(random.randint(-270, 270), random.randint(-270, 270))
# In the game loop, add another food collision check:
if head.distance(food2) < 20:
food2.goto(random.randint(-270, 270), random.randint(-270, 270))
new_seg = head.clone()
bodysegments.append(new_seg)