Advanced — Day 2

Complete Snake Game

Day Schedule
Online
TimeActivity
10:00Backward Shift Loop
Whiteboard diagram before any code; explain why backwards
10:25Food + Body Growth
got_food(), head.clone(), bodysegments list
10:45All Collisions
Wall, self-collision, and food in the game loop
11:00Break
11:10Breakout Groups
Fully playable snake game
12:20Reminders
In-Person
TimeActivity
8:00Warm-Up
Review Day 1: arrow keys and wall collision working?
8:20Backward Shift Loop
Whiteboard diagram before any code; explain why backwards
8:45Food + Body Growth
got_food(), head.clone(), bodysegments list
9:10All Collisions
Wall, self-collision, and food in the game loop
9:40Break
9:50Breakout Groups
Fully playable snake game
11:00Break
11:10Open Coding
Score display, speed scaling, or other extensions
12:20Reminders
Learning Objectives
  1. Implement the backward shift loop correctly
  2. Add food and body growth with head.clone()
  3. Handle wall collision and self-collision
# 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)
# 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)