Keep everything about one star together in a dictionary, so adding a property is one line and nothing can fall out of step.
A contact on your phone keeps a name, a number and a photo together. A dictionary does the same for anything in a program.
Four lists for one star was fragile. A dictionary keeps everything about one star together: each value is stored under a name, called a key. The game keeps a list of dictionaries, one per star.
Make your prediction first. Help opens once you've locked it in.
star = {"speed": 5, "points": 2}
star["points"] = star["points"] + 1
print(star["speed"], star["points"])
{"speed": 5, "points": 2} is a dictionary: the key "speed" is paired with 5 and "points" with 2. star["points"] is the value stored under "points". What will it print?
The same game again. Each star is now one dictionary holding its turtle, speed and points, and for star in stars: visits each dictionary in turn. Play it, then find star["speed"] in tick.
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 = []
for i in range(4):
s = Turtle()
s.shape("circle")
s.penup()
s.goto(-150 + i * 100, 200)
s.setheading(270)
stars.append({
"turtle": s,
"speed": random.randint(3, 7),
"points": 1,
})
for star in stars:
star["turtle"].color("gold")
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 star in stars:
star["turtle"].forward(star["speed"])
if star["turtle"].distance(xcor(), ycor()) < 25:
score += star["points"]
star["turtle"].goto(random.randint(-200, 200), 200)
if star["turtle"].ycor() < -220:
star["turtle"].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 = []
for i in range(4):
s = Turtle()
s.shape("circle")
s.penup()
s.goto(-150 + i * 100, 200)
s.setheading(270)
stars.append({
"turtle": s,
"speed": random.randint(3, 7),
"points": 1,
})
for star in stars:
star["turtle"].color("gold")
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 star in stars:
star["turtle"].forward(star["speed"])
if star["turtle"].distance(xcor(), ycor()) < 25:
score += star["points"]
star["turtle"].goto(random.randint(-200, 200), 200)
if star["turtle"].ycor() < -220:
star["turtle"].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 each part of a dictionary does.
How much changes when every star needs one more value?
A key without its quotes.
Match each piece with what it does.
This should print each star's speed doubled, but it stops with a NameError. Click the line that causes it.
Give each star a random 1 to 3 points.
"points": 1 gives every star 1 point. Use random.randint(1, 3) instead.
Store a colour in each star's dictionary, and use it to colour the turtle.
Add a list of colours before the loop, and a "colour" key in each dictionary: colours[i % 3] picks gold, orange, yellow in turn.
Then colour each turtle with star["turtle"].color(star["colour"]).
Give each star a random 1 to 3 points.
Run your program. This ticks when your code does all of the above.
Store a colour in each star and use it to colour the turtle.
Run your program. This ticks when your code does all of the above.
Make the first star a bad star again, this time with a "bad" key in each dictionary (True for the first star only). The bad star is red and costs 3 points when caught. Compare it with last lesson's version: what did you not have to do this time?
Add a "bad" key: the first star is red and costs 3 points when caught.
Run your program. This ticks when your code does all of the above.
A list of dictionaries is how most programs store data: one record per thing. There is still one awkward part: star["turtle"].forward(star["speed"]). Next lesson makes a Star of your own, so it reads star.turtle.forward(star.speed).
Students keep each star's turtle, speed and points together in a dictionary, loop over a list of dictionaries, and see that a new property is now one key instead of one more list kept in step. About 50 minutes.
Open the teacher guide: answers, solutions, slides and a worksheet.
Read the unit primer first. A dictionary pairs keys with values: {"speed": 5, "points": 2}. star["speed"] reads the value; star["speed"] = 8 changes it. Keys are text, so they need quotes; star[speed] (the bug task) looks for a variable called speed and stops with a NameError. A list of dictionaries is a table: one row per thing. The Make task repeats last lesson's bad star so students can compare a new list with a new key.
Keys are text in quotes: star["speed"], not star[speed]. Ask them to read the NameError aloud and say what Python thinks speed is.
Stretch: Add a "hits" key that counts how often each star has been caught, and make a star faster every third catch.
Support: Give students the "bad" key already in the dictionary and ask them only to change what happens when a star is caught.
Next lesson makes a Star class, so a star's values read star.speed instead of star["speed"], and later the star can do things itself. Data detective uses lists of dictionaries for real data sets.