Advanced — Day 4
Choose Your Game
Day Schedule
Online
| Time | Activity |
|---|---|
| 10:00 | Choose Your Game Intro the three options: Stick Man, Breakout, Space Shooter |
| 10:15 | Breakout Groups Build chosen game |
| 11:00 | Break |
| 11:10 | Breakout Groups Continue building + add one custom feature |
| 12:20 | Reminders |
In-Person
| Time | Activity |
|---|---|
| 8:00 | Choose Your Game Intro the three options: Stick Man, Breakout, Space Shooter |
| 8:15 | Breakout Groups Build chosen game |
| 9:40 | Break |
| 9:50 | Breakout Groups Continue building + add one custom feature |
| 11:00 | Break |
| 11:10 | Open Coding Polish and add extensions |
| 12:20 | Reminders |
Learning Objectives
- Build one of three game options to a playable state
- Add at least one custom feature or extension
Option A — Stick Man Runner
# INSTRUCTOR — Day 4: Option A: Stick Man Runner (Advanced)
# -----------------------------------------------
# TEACH: Students build a runner game with a jumping stick figure.
# The stick figure is drawn with Turtle lines and circles using a helper function.
# AI helps generate the drawing code — students describe what they want, not draw it pixel by pixel.
# Jump physics use a simple velocity + gravity model: velocity decrements each frame until zero.
#
# Check: space bar triggers a jump only when on the ground (man_y <= -150).
# Obstacle scrolling is a challenge extension — not required for the base version.
import turtle
import time
import random
screen = turtle.Screen()
screen.title("Stick Man Runner")
screen.bgcolor("sky blue")
screen.setup(width=800, height=400)
screen.tracer(0)
# --- Stick figure ---
man = turtle.Turtle()
man.penup()
man.hideturtle()
man.speed(0)
man_x = -250
man_y = -100 # ground level
velocity = 0
gravity = -1.5 # TEACH: gravity pulls velocity downward each frame
def draw_man(x, y):
"""Draw stick figure centered at (x, y)."""
man.clear()
man.color("black")
# Head
man.penup()
man.goto(x, y + 40)
man.pendown()
man.circle(15)
# Body
man.penup()
man.goto(x, y + 25)
man.pendown()
man.goto(x, y - 10)
# Arms
man.penup()
man.goto(x - 25, y + 10)
man.pendown()
man.goto(x + 25, y + 10)
# Left leg
man.penup()
man.goto(x, y - 10)
man.pendown()
man.goto(x - 18, y - 45)
# Right leg
man.penup()
man.goto(x, y - 10)
man.pendown()
man.goto(x + 18, y - 45)
def jump():
global velocity
if man_y <= -100: # only jump if on the ground
velocity = 18 # launch upward
screen.listen()
screen.onkeypress(jump, "space")
# --- Ground line ---
ground = turtle.Turtle()
ground.penup()
ground.hideturtle()
ground.goto(-400, -100)
ground.pendown()
ground.goto(400, -100)
# --- Obstacles ---
obstacles = []
def spawn_obstacle():
obs = turtle.Turtle()
obs.shape("square")
obs.shapesize(stretch_wid=2, stretch_len=1)
obs.color("dark red")
obs.penup()
obs.goto(420, -80) # start off-screen right
obstacles.append(obs)
spawn_obstacle() # spawn one to start
frame = 0
while True:
screen.update()
# TEACH: apply gravity to velocity each frame
velocity += gravity
man_y += velocity
# TEACH: clamp to ground so man doesn't fall through
if man_y <= -100:
man_y = -100
velocity = 0
draw_man(man_x, man_y)
# Scroll obstacles left
for obs in obstacles[:]:
obs.setx(obs.xcor() - 5)
# Remove obstacle when it leaves the screen
if obs.xcor() < -450:
obs.hideturtle()
obstacles.remove(obs)
# Collision detection (simple bounding box)
if abs(obs.xcor() - man_x) < 25 and abs(obs.ycor() - man_y) < 35:
pen = turtle.Turtle()
pen.penup()
pen.hideturtle()
pen.color("red")
pen.goto(0, 50)
pen.write("GAME OVER", align="center", font=("Courier", 28, "bold"))
screen.update()
time.sleep(2)
# Simple restart: rehide pen and respawn
pen.clear()
man_y = -100
velocity = 0
for o in obstacles:
o.hideturtle()
obstacles.clear()
spawn_obstacle()
# Spawn a new obstacle every ~80 frames
frame += 1
if frame % 80 == 0:
spawn_obstacle()
time.sleep(0.02)
Option B — Breakout
# INSTRUCTOR — Day 4: Option B: Breakout (Advanced)
# -----------------------------------------------
# TEACH: Students extend their Pong work from Day 3.
# New concept: nested for loops to create the brick grid.
# Each brick is a turtle object stored in a list.
# In the game loop, a for loop checks if the ball hit any brick.
# Removing items from a list while iterating: use bricks[:] (a copy) to iterate
# so removing from the original list doesn't skip items.
#
# Check: ball bounces off bricks and removes them; game ends when all bricks are gone;
# paddle keeps the ball in play.
import turtle
import time
import random
screen = turtle.Screen()
screen.title("Breakout")
screen.bgcolor("black")
screen.setup(width=640, height=600)
screen.tracer(0)
# --- Paddle ---
paddle = turtle.Turtle()
paddle.shape("square")
paddle.shapesize(stretch_wid=1, stretch_len=5)
paddle.color("white")
paddle.penup()
paddle.goto(0, -250)
def paddle_left():
x = paddle.xcor()
if x > -270:
paddle.setx(x - 30)
def paddle_right():
x = paddle.xcor()
if x < 270:
paddle.setx(x + 30)
screen.listen()
screen.onkeypress(paddle_left, "Left")
screen.onkeypress(paddle_right, "Right")
# --- Ball ---
ball = turtle.Turtle()
ball.shape("circle")
ball.color("white")
ball.penup()
ball.goto(0, -200)
ball.dx = random.choice([-3, 3])
ball.dy = 4
# --- Brick grid ---
# TEACH: nested for loops — outer loop = rows, inner loop = columns
ROW_COLORS = ["red", "orange", "yellow", "lime"]
bricks = []
for row in range(4):
for col in range(8):
brick = turtle.Turtle()
brick.shape("square")
brick.shapesize(stretch_wid=1, stretch_len=3)
brick.color(ROW_COLORS[row])
brick.penup()
brick.goto(-210 + col * 60, 220 - row * 40)
bricks.append(brick)
# --- Score ---
pen = turtle.Turtle()
pen.penup()
pen.hideturtle()
pen.color("white")
pen.goto(0, 270)
score = 0
def update_score():
pen.clear()
pen.write("Score: " + str(score), align="center", font=("Courier", 18, "bold"))
update_score()
while True:
screen.update()
# Move ball
ball.setx(ball.xcor() + ball.dx)
ball.sety(ball.ycor() + ball.dy)
# Top wall
if ball.ycor() > 280:
ball.dy *= -1
# Left/right walls
if ball.xcor() > 310 or ball.xcor() < -310:
ball.dx *= -1
# Bottom wall — ball lost
if ball.ycor() < -290:
ball.goto(0, -200)
ball.dx = random.choice([-3, 3])
ball.dy = 4
# Paddle collision
if (ball.ycor() > -265 and ball.ycor() < -240 and
abs(ball.xcor() - paddle.xcor()) < 55):
ball.sety(-240)
ball.dy *= -1
# TEACH: slight randomness on paddle bounce to prevent infinite loops
ball.dx += random.uniform(-0.5, 0.5)
# Brick collision
# TEACH: bricks[:] iterates a COPY so we can safely remove from bricks
for brick in bricks[:]:
if ball.distance(brick) < 40:
ball.dy *= -1
brick.hideturtle()
bricks.remove(brick)
score += 1
update_score()
# Win condition
if not bricks:
pen.clear()
pen.write("YOU WIN! Score: " + str(score),
align="center", font=("Courier", 24, "bold"))
screen.update()
time.sleep(3)
break
time.sleep(0.01)
Option C — Space Shooter
# INSTRUCTOR — Day 4: Option C: Space Shooter (Advanced)
# -----------------------------------------------
# TEACH: Students build a top-down space shooter.
# New concepts: a ship that fires bullets, enemies that fall downward,
# two lists (bullets and enemies) and nested iteration for collision.
# TEACH: when iterating and removing from the same list, use list[:] to copy.
# TEACH: bullets[:] inside the for loop is the safe pattern — show what goes wrong without it.
import turtle
import time
import random
screen = turtle.Screen()
screen.title("Space Shooter")
screen.bgcolor("black")
screen.setup(width=600, height=700)
screen.tracer(0)
# --- Ship ---
ship = turtle.Turtle()
ship.shape("triangle")
ship.color("cyan")
ship.penup()
ship.goto(0, -280)
ship.setheading(90) # TEACH: point the triangle upward
def ship_left():
x = ship.xcor()
if x > -270:
ship.setx(x - 20)
def ship_right():
x = ship.xcor()
if x < 270:
ship.setx(x + 20)
# --- Bullets ---
bullets = []
def fire():
b = turtle.Turtle()
b.shape("circle")
b.shapesize(stretch_wid=0.3, stretch_len=0.8)
b.color("yellow")
b.penup()
b.goto(ship.xcor(), ship.ycor() + 20)
b.setheading(90)
bullets.append(b)
screen.listen()
screen.onkeypress(ship_left, "Left")
screen.onkeypress(ship_right, "Right")
screen.onkeypress(fire, "space")
# --- Enemies ---
enemies = []
def spawn_enemy():
e = turtle.Turtle()
e.shape("square")
e.color("red")
e.penup()
e.goto(random.randint(-270, 270), 320) # start above screen
enemies.append(e)
for _ in range(5):
spawn_enemy() # start with 5 enemies
# --- Score ---
pen = turtle.Turtle()
pen.penup()
pen.hideturtle()
pen.color("white")
pen.goto(0, 310)
score = 0
lives = 3
def update_hud():
pen.clear()
pen.write("Score: " + str(score) + " Lives: " + str(lives),
align="center", font=("Courier", 16, "bold"))
update_hud()
frame = 0
while True:
screen.update()
# Move bullets upward
for b in bullets[:]:
b.forward(15)
if b.ycor() > 340: # off screen
b.hideturtle()
bullets.remove(b)
# Move enemies downward
for e in enemies[:]:
e.sety(e.ycor() - 3)
# Enemy reaches bottom
if e.ycor() < -340:
e.hideturtle()
enemies.remove(e)
lives -= 1
update_hud()
spawn_enemy()
if lives <= 0:
pen.clear()
pen.write("GAME OVER Score: " + str(score),
align="center", font=("Courier", 22, "bold"))
screen.update()
time.sleep(3)
break
# Bullet vs enemy collision
# TEACH: nested for loops — check every bullet against every enemy
for b in bullets[:]:
for e in enemies[:]:
if b.distance(e) < 25:
b.hideturtle()
bullets.remove(b)
e.hideturtle()
enemies.remove(e)
score += 1
update_hud()
spawn_enemy() # replace the destroyed enemy
break # bullet is gone — stop checking other enemies
# Spawn extra enemies every 120 frames (difficulty ramp)
frame += 1
if frame % 120 == 0 and len(enemies) < 12:
spawn_enemy()
time.sleep(0.02)