Keep the stars in a list and their speeds and points in lists beside it, so one loop moves them all, and find out why lists side by side are fragile.
A game with a hundred enemies does not have a hundred variables. It keeps them in a list and loops over it.
Last lesson every star cost 11 lines. This version keeps the stars in a list, and what each star needs to remember (its speed, its points) in more lists beside it. Place i in every list is about the same star.
Make your prediction first. Help opens once you've locked it in.
speeds = [3, 8, 5, 6]
points = [1, 1, 5, 1]
print(speeds[2], points[2])
speeds and points have one item per star. speeds[2] and points[2] are both about the third star. What will it print?
The same game, now with four stars made by a loop. Play it (click the picture, then use the arrow keys). stars.append(s) adds each new star to the end of the list.
from turtle import *
import random
speed(0)
penup()
goto(0, -180)
left(90)
score = 0
pen = Turtle()
pen.hideturtle()
pen.penup()
pen.goto(-220, 200)
stars = []
speeds = []
points = []
for i in range(4):
s = Turtle()
s.shape("circle")
s.color("gold")
s.penup()
s.goto(-150 + i * 100, 200)
s.setheading(270)
stars.append(s)
speeds.append(random.randint(3, 7))
points.append(1)
def show_score():
pen.clear()
pen.write("Score: " + str(score), False, "left", ("Arial", 16, "normal"))
def go_left():
goto(xcor() - 25, ycor())
def go_right():
goto(xcor() + 25, ycor())
def tick():
global score
for i in range(len(stars)):
stars[i].forward(speeds[i])
if stars[i].distance(xcor(), ycor()) < 25:
score += points[i]
stars[i].goto(random.randint(-200, 200), 200)
if stars[i].ycor() < -220:
stars[i].goto(random.randint(-200, 200), 200)
show_score()
ontimer(tick, 50)
onkey(go_left, "Left")
onkey(go_right, "Right")
listen()
show_score()
tick()
mainloop()
from turtle import *
import random
speed(0)
penup()
goto(0, -180)
left(90)
score = 0
pen = Turtle()
pen.hideturtle()
pen.penup()
pen.goto(-220, 200)
stars = []
speeds = []
points = []
for i in range(4):
s = Turtle()
s.shape("circle")
s.color("gold")
s.penup()
s.goto(-150 + i * 100, 200)
s.setheading(270)
stars.append(s)
speeds.append(random.randint(3, 7))
points.append(1)
def show_score():
pen.clear()
pen.write("Score: " + str(score), False, "left", ("Arial", 16, "normal"))
def go_left():
goto(xcor() - 25, ycor())
def go_right():
goto(xcor() + 25, ycor())
def tick():
global score
for i in range(len(stars)):
stars[i].forward(speeds[i])
if stars[i].distance(xcor(), ycor()) < 25:
score += points[i]
stars[i].goto(random.randint(-200, 200), 200)
if stars[i].ycor() < -220:
stars[i].goto(random.randint(-200, 200), 200)
show_score()
ontimer(tick, 50)
onkey(go_left, "Left")
onkey(go_right, "Right")
listen()
show_score()
tick()
mainloop()
What changes for more stars?
Follow i through the lists.
Lists that fell out of step.
speeds is [4, 7, 3, 5] and points is [1, 1, 1, 1]. Fill in what the loop in tick uses for each star.
| i | which star | speeds[i] | points[i] |
|---|---|---|---|
| 0 | 1st | 4 | 1 |
| 1 | 2nd | ||
| 3 | 4th |
Someone made a fifth star but it stops with an IndexError. Click the line where Python stops.
Play with six stars.
The number of stars is set in one place: the range in the loop that makes them.
Make each star worth a random 1 to 3 points.
points.append(1) gives every star 1 point. Use random.randint(1, 3) instead.
Play with six stars.
Run your program. This ticks when your code does all of the above.
Give each star a random 1 to 3 points.
Run your program. This ticks when your code does all of the above.
Make the first star a bad star: add a fourth list, bad, with True for the first star and False for the others. Colour the bad star red. Catching it costs 3 points; catching any other star adds its points.
Add a list called bad: the first star is red and costs 3 points when caught.
Run your program. This ticks when your code does all of the above.
Lists made the number of stars one number. But everything about one star is now spread over four lists. Next lesson keeps it together.
Students rebuild the stars game with a list of stars and lists of speeds and points beside it, so one loop moves every star, then meet the weakness of lists side by side (they must stay in step). About 50 minutes.
Open the teacher guide: answers, solutions, slides and a worksheet.
Read the unit primer first. stars.append(s) adds a turtle to the end of a list. speeds[i] and points[i] are the values for stars[i]: lists side by side ("parallel lists") linked only by the place number. They work while every list is changed together, and break when one is not. The bug task shows this: five stars but four speeds gives IndexError: list index out of range. The lesson ends with four lists for one star, which is the problem next lesson fixes.
Keeping lists side by side in step. Let the IndexError happen, then ask them to count the items in each list.
Stretch: Add a fifth list, sizes, and use it to make some stars bigger with turtlesize-style shapes (or different shapes) and worth more.
Support: Give students the bad list already filled in, and ask them only to change what happens when a star is caught.
Next lesson keeps everything about one star in one dictionary, so nothing can fall out of step. Databases have the same choice: separate columns kept in step, or one record per thing.