Advanced — Day 2
Complete Snake Game
Day Schedule
Online
| Time | Activity |
|---|---|
| 10:00 | Backward Shift Loop Whiteboard diagram before any code; explain why backwards |
| 10:25 | Food + Body Growth got_food(), head.clone(), bodysegments list |
| 10:45 | All Collisions Wall, self-collision, and food in the game loop |
| 11:00 | Break |
| 11:10 | Breakout Groups Fully playable snake game |
| 12:20 | Reminders |
In-Person
| Time | Activity |
|---|---|
| 8:00 | Warm-Up Review Day 1: arrow keys and wall collision working? |
| 8:20 | Backward Shift Loop Whiteboard diagram before any code; explain why backwards |
| 8:45 | Food + Body Growth got_food(), head.clone(), bodysegments list |
| 9:10 | All Collisions Wall, self-collision, and food in the game loop |
| 9:40 | Break |
| 9:50 | Breakout Groups Fully playable snake game |
| 11:00 | Break |
| 11:10 | Open Coding Score display, speed scaling, or other extensions |
| 12:20 | Reminders |
Learning Objectives
- Implement the backward shift loop correctly
- Add food and body growth with head.clone()
- Handle wall collision and self-collision
Project
# INSTRUCTOR — Day 2: Snake — Complete Game (Advanced)
# -----------------------------------------------
# TEACH: Three systems added today — body growth, food, and all collision detection.
# By end of Day 2 the game is fully playable.
#
# BACKWARD SHIFT LOOP — draw this on the whiteboard before coding:
# bodysegments = [seg0, seg1, seg2] (seg0 is closest to head)
# Frame N: head=(100,0) seg0=(80,0) seg1=(60,0) seg2=(40,0)
# We want: seg2 → where seg1 was, seg1 → where seg0 was, seg0 → where head was
# Going FORWARDS (0→1→2): seg1 overwrites seg0's old position before seg2 can copy it.
# Going BACKWARDS (2→1→0): each position is preserved before it gets overwritten.
#
# CHECK: eat 3+ pieces of food — body grows each time.
# Run into a wall — resets to center.
# Run into your own body — also resets.
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 = turtle.Turtle()
food.shape("circle")
food.color("green")
food.penup()
food.goto(random.randint(-270, 270), random.randint(-270, 270))
bodysegments = []
score = 0
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():
global score
head.goto(0, 0)
head.setheading(0)
score = 0
for seg in bodysegments:
seg.hideturtle()
bodysegments.clear()
while True:
screen.update()
# Backward shift — runs BEFORE head moves so positions are still accurate
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:
food.goto(random.randint(-270, 270), random.randint(-270, 270))
score += 1
new_seg = head.clone() # TEACH: clone() copies shape + color automatically
bodysegments.append(new_seg)
# Body collision (self-collision)
for seg in bodysegments:
if head.distance(seg) < 15:
reset_game()
break
# 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 — Score Display
# INSTRUCTOR — Day 2: Extra Activity: Score Display (Advanced)
# -----------------------------------------------
# TEACH: A pen turtle writes text on screen. It clears and rewrites each time the score changes.
# This is a common pattern for any on-screen HUD element (score, lives, timer).
# Students fill in: (1) where to position the pen, (2) which variable to display.
# Answers: pen at (0, 270) — top center; display the score variable.
import turtle
import random
import time
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: pen turtle is invisible but writes text at its position
pen = turtle.Turtle()
pen.penup()
pen.hideturtle()
pen.color("white")
pen.goto(0, 270) # TEACH: top-center of a 600px screen
score = 0
high_score = 0
def update_score():
pen.clear() # TEACH: clear() wipes previous text — must call before rewriting
pen.write("Score: " + str(score) + " Best: " + str(high_score),
align="center", font=("Courier", 20, "bold"))
update_score() # call once at start to show 0
food = turtle.Turtle()
food.shape("circle")
food.color("green")
food.penup()
food.goto(random.randint(-270, 270), random.randint(-270, 270))
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")
halfSize = 290
def reset_game():
global score, high_score
if score > high_score:
high_score = score # TEACH challenge: high score persists through resets
score = 0
head.goto(0, 0)
head.setheading(0)
for seg in bodysegments:
seg.hideturtle()
bodysegments.clear()
update_score()
while True:
screen.update()
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)
if head.distance(food) < 20:
food.goto(random.randint(-270, 270), random.randint(-270, 270))
score += 1
new_seg = head.clone()
bodysegments.append(new_seg)
update_score() # TEACH: must call update_score() here so display reflects new score
for seg in bodysegments:
if head.distance(seg) < 15:
reset_game()
break
if (head.xcor() > halfSize or head.xcor() < -halfSize or
head.ycor() > halfSize or head.ycor() < -halfSize):
reset_game()
time.sleep(0.1)