Turn the game into an app with a panel of buttons made from lists.
Every app is a panel of controls over some state. Build the panel from data, and adding a control is one more item in a list.
Your game has one button. Real apps have panels: rows of controls that change what the program does. Here the buttons come from lists, so a loop draws them and a loop finds which one was clicked.
Make your prediction first. Help opens once you've locked it in.
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]
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])
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
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])
draw_scene()
onscreenclick(clicked)
listen()
mainloop()
names and bxs hold one button each at the same index. draw_scene loops over them to draw, and clicked loops over them to check.
Press New game, play, pause, then go again.
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]
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])
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
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])
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]
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])
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
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])
draw_scene()
onscreenclick(clicked)
listen()
mainloop()
Which button does the loop find?
What if the lists disagree?
A click at x = 100, y = -170. Follow the loop in clicked.
| i | bxs[i] | inside? |
|---|---|---|
| 0 | -190 | no |
| 1 | ||
| 2 |
Add a second row: Easy, Medium and Hard. Each one sets how fast the turtle hops. Three more lists, two more loops.
Three new lists: levels = ["Easy", "Medium", "Hard"], lxs = [-190, -60, 70], delays = [1300, 1000, 700].
Copy the drawing loop in draw_scene, but use levels, lxs and y = -140.
Copy the checking loop in clicked: when a level is clicked, delay = delays[i] (and add delay to the global line).
A second row of buttons from lists, which sets the difficulty.
Run your program. This ticks when your code does all of the above.
Add a row of controls you design. Where this leads: in Gradio, you list the controls (gr.Button, gr.Radio) and Gradio draws them and checks the clicks for you. On a web page, HTML draws the buttons and JavaScript listens for clicks. You have just built what both of them do.
A theme row: names like "Day" and "Night" with a list of colours, and bgcolor(colours[i]) when one is clicked.
A path chooser: two pairs of lists, and a button that swaps which pair xs and ys use.
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. About 45 minutes.
Open the teacher guide: answers, solutions, slides and a worksheet.
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.
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.
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.