An optional lesson in the user interface direction. Students turn the game into an app with rows of buttons built from lists, so adding a control means adding to a list.
Load these into Waypoints Prepare: choose Load URL and paste the copied address, or download the file and import it. In Prepare, the worksheet's Teacher edition shows the answers; the student copy hides them.
Predict, Run and Investigate 15 min · Difficulty row 15 min · Your own controls 15 min
The big idea: buttons are data. Three lists (the names, the positions, the values) and two loops (one draws the buttons, one checks which was clicked) replace a pile of copied if statements. "New game" waits 1.1 seconds before starting: that lets any timer still running from the last game stop first, so two games never run at once.
Controls stored as lists that must stay the same length. Ask them to count each list's items when an IndexError appears.
| What you will see | A question to ask | The fix |
|---|---|---|
| An IndexError when clicking or drawing. | Do all the lists that go together have the same number of items? | Lists used side by side (names and bxs) must be the same length. |
| Two rows of buttons overlap. | What y value does each row use? | Give each row its own y, at least 50 apart for 40-high buttons. |
| A click changes the difficulty but the turtle still moves at the old speed. | Is delay in the global line of clicked? | Add delay to clicked's global line. |
One possible solution for each task. Other correct programs are fine.
from turtle import *
from ui import inside, draw_button
speed(0)
penup()
hideturtle()
xs = [-120, 0, 120, 120, 0, -120]
ys = [70, 110, 70, -20, -50, -20]
place = 0
tx = 0
ty = 0
score = 0
showing = False
time_left = 30
playing = False
delay = 1000
names = ["New game", "Pause", "Go"]
bxs = [-190, -60, 70]
levels = ["Easy", "Medium", "Hard"]
lxs = [-190, -60, 70]
delays = [1300, 1000, 700]
def draw_scene():
clear()
goto(-190, 160)
write(score, False, "left", ("Arial", 20, "normal"))
goto(150, 160)
write(time_left, False, "left", ("Arial", 20, "normal"))
for i in range(len(names)):
draw_button(bxs[i], -190, 120, 40, names[i])
for i in range(len(levels)):
draw_button(lxs[i], -140, 120, 40, levels[i])
goto(tx, ty)
def hop():
global place, tx, ty, showing
if playing:
tx = xs[place]
ty = ys[place]
place = (place + 1) % len(xs)
goto(tx, ty)
showturtle()
showing = True
ontimer(hide, delay - 300)
ontimer(hop, delay)
def hide():
global showing
if showing:
showing = False
hideturtle()
def tick():
global time_left, playing
if playing:
time_left = time_left - 1
if time_left == 0:
playing = False
hideturtle()
else:
ontimer(tick, 1000)
draw_scene()
def go():
global playing
if playing == False and time_left > 0:
playing = True
hop()
ontimer(tick, 1000)
def press(name):
global score, time_left, place, playing
if name == "New game":
playing = False
score = 0
time_left = 30
place = 0
draw_scene()
ontimer(go, 1100)
if name == "Pause":
playing = False
hideturtle()
if name == "Go":
go()
def clicked(x, y):
global score, showing, delay
if playing and showing and inside(x, y, tx - 20, ty - 20, 40, 40):
score = score + 1
showing = False
hideturtle()
draw_scene()
for i in range(len(names)):
if inside(x, y, bxs[i], -190, 120, 40):
press(names[i])
for i in range(len(levels)):
if inside(x, y, lxs[i], -140, 120, 40):
delay = delays[i]
draw_scene()
onscreenclick(clicked)
listen()
mainloop()
from turtle import *
from ui import inside, draw_button
speed(0)
penup()
hideturtle()
xs = [-120, 0, 120, 120, 0, -120]
ys = [70, 110, 70, -20, -50, -20]
place = 0
tx = 0
ty = 0
score = 0
showing = False
time_left = 30
playing = False
delay = 1000
names = ["New game", "Pause", "Go"]
bxs = [-190, -60, 70]
levels = ["Easy", "Medium", "Hard"]
lxs = [-190, -60, 70]
delays = [1300, 1000, 700]
themes = ["Day", "Night", "Sea"]
txs = [-190, -60, 70]
backgrounds = ["white", "lightgrey", "lightblue"]
def draw_scene():
clear()
goto(-190, 160)
write(score, False, "left", ("Arial", 20, "normal"))
goto(150, 160)
write(time_left, False, "left", ("Arial", 20, "normal"))
for i in range(len(names)):
draw_button(bxs[i], -190, 120, 40, names[i])
for i in range(len(levels)):
draw_button(lxs[i], -140, 120, 40, levels[i])
for i in range(len(themes)):
draw_button(txs[i], 120, 120, 40, themes[i])
goto(tx, ty)
def hop():
global place, tx, ty, showing
if playing:
tx = xs[place]
ty = ys[place]
place = (place + 1) % len(xs)
goto(tx, ty)
showturtle()
showing = True
ontimer(hide, delay - 300)
ontimer(hop, delay)
def hide():
global showing
if showing:
showing = False
hideturtle()
def tick():
global time_left, playing
if playing:
time_left = time_left - 1
if time_left == 0:
playing = False
hideturtle()
else:
ontimer(tick, 1000)
draw_scene()
def go():
global playing
if playing == False and time_left > 0:
playing = True
hop()
ontimer(tick, 1000)
def press(name):
global score, time_left, place, playing
if name == "New game":
playing = False
score = 0
time_left = 30
place = 0
draw_scene()
ontimer(go, 1100)
if name == "Pause":
playing = False
hideturtle()
if name == "Go":
go()
def clicked(x, y):
global score, showing, delay
if playing and showing and inside(x, y, tx - 20, ty - 20, 40, 40):
score = score + 1
showing = False
hideturtle()
draw_scene()
for i in range(len(names)):
if inside(x, y, bxs[i], -190, 120, 40):
press(names[i])
for i in range(len(levels)):
if inside(x, y, lxs[i], -140, 120, 40):
delay = delays[i]
for i in range(len(themes)):
if inside(x, y, txs[i], 120, 120, 40):
bgcolor(backgrounds[i])
draw_scene()
onscreenclick(clicked)
listen()
mainloop()
Stretch: Add a row that chooses between two different paths (two pairs of lists) for the turtle.
Support: Give the difficulty row's three lists already written; students add only the two loops.
A panel has names = ["A", "B", "C"] and bxs = [-190, -60, 70], each button 120 wide. Which button does a click at x = -100 press?
A. Its button runs from -190 to -70, and -100 is inside that range.
Gradio lets you list controls (gr.Button, gr.Radio) and draws them and checks clicks for you. On a web page, HTML draws the buttons and JavaScript listens for clicks. Students have built what both do.