← Teacher guide

A Star class

Students rebuild the stars game with a Star class, set up each star in __init__, store its values with self, and add a second class for rocks.

Objects with turtles · Classes · about 55 minutes · Open the student lesson

Classroom resources

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.

Students will be able to

Timing

Predict and Run 10 min · Investigate 15 min · Modify 10 min · Make 20 min

Before class

Read the unit primer first. class Star: describes a new kind of object. __init__ (two underscores each side; say "init") runs every time Star(...) makes one, and self is the object being made. self.speed = 5 stores a value in that object; a plain speed = 5 only makes a variable that disappears when __init__ ends, which is the bug task (AttributeError later, when the game asks for s.speed). Values read as star.speed, like a turtle's own commands. No inheritance, getters or class diagrams in this course.

Running the lesson

  1. Predict: make two cards labelled a and b on the board, and write name and legs on each as init runs.
  2. Run: compare star.speed with last lesson's star["speed"]. Same idea, tidier.
  3. Investigate: self is the hardest word. 'The star being made right now' is enough for now.
  4. Bug task: speed = speed looks like it works. Ask where the value lives after init ends.
  5. Make: the Rock class is the Star class's shape again. Ask what is the same and what differs.

The hard part (on purpose)

self: why values need it to stay with each star. Ask them to point to where each value is stored, and whether it has self. in front.

When students get stuck

What you will seeA question to askThe fix
AttributeError: 'Star' object has no attribute ...Was the value stored with self. in front, inside __init__?self.speed = ..., not speed = ....
TypeError: __init__() missing 1 required positional argument.How many values does __init__ need, and how many does Star(...) pass?Pass one value for every parameter after self.
NameError: name 'self' is not defined.Is the line inside a method of the class (indented under def)?Use self only inside the class's methods; outside, use the object's name (star.speed).

Answers to the checks

  1. Predict: What does it print?
    Rex 2. Pet("Rex", 4) runs __init__ with name "Rex" and legs 4, and stores them in that pet. a.name is "Rex"; b is the other pet, so b.legs is 2.
  2. Match the lines: Match each line with what it does.
    class Star: → C. describes a new kind of object; def __init__(self, x): → B. runs each time a star is made; self.speed = random.randint(3, 7) → A. stores a speed in this star; Star(-150 + i * 100) → D. makes a star, with x = -150 + i * 100
  3. What is self?: Inside __init__, what is self?
    The star being made right now
    Each time Star(...) runs, self is the new star, so self.speed = ... stores a speed in that star only. Four stars, four speeds.
  4. Spot the bug: This stops with AttributeError: 'Star' object has no attribute 'speed'. Click the line that causes it.
    line 3
    speed = speed only changes a variable inside __init__, which is thrown away when it ends. self.speed = speed stores it in the star.
  5. Where this leads: In MakeCode Arcade or microStudio, each sprite has an x, a y and a speed. In Python words, what is each sprite?
    An object, made from a class, with its own values
    Every game engine works like this: a pattern (a class) and many objects made from it, each keeping its own values.

Solutions

One possible solution for each task. Other correct programs are fine.

Modify › A colour for each star

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)

class Star:
    def __init__(self, x, colour):
        self.turtle = Turtle()
        self.turtle.shape("circle")
        self.turtle.color(colour)
        self.turtle.penup()
        self.turtle.goto(x, 200)
        self.turtle.setheading(270)
        self.speed = random.randint(3, 7)
        self.points = 1

stars = []
for i in range(4):
    stars.append(Star(-150 + i * 100, "orange"))

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()

Modify › Different points

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)

class Star:
    def __init__(self, x):
        self.turtle = Turtle()
        self.turtle.shape("circle")
        self.turtle.color("gold")
        self.turtle.penup()
        self.turtle.goto(x, 200)
        self.turtle.setheading(270)
        self.speed = random.randint(3, 7)
        self.points = random.randint(1, 3)

stars = []
for i in range(4):
    stars.append(Star(-150 + i * 100))

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()

Make a Rock class

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)

class Star:
    def __init__(self, x):
        self.turtle = Turtle()
        self.turtle.shape("circle")
        self.turtle.color("gold")
        self.turtle.penup()
        self.turtle.goto(x, 200)
        self.turtle.setheading(270)
        self.speed = random.randint(3, 7)
        self.points = 1

stars = []
for i in range(4):
    stars.append(Star(-150 + i * 100))

class Rock:
    def __init__(self, x):
        self.turtle = Turtle()
        self.turtle.shape("square")
        self.turtle.color("gray")
        self.turtle.penup()
        self.turtle.goto(x, 200)
        self.turtle.setheading(270)
        self.speed = random.randint(4, 8)

rocks = []
for i in range(2):
    rocks.append(Rock(-100 + i * 200))

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)
    for rock in rocks:
        rock.turtle.forward(rock.speed)
        if rock.turtle.distance(xcor(), ycor()) < 25:
            score -= 3
            rock.turtle.goto(random.randint(-200, 200), 200)
        if rock.turtle.ycor() < -220:
            rock.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()

Stretch and support

Stretch: Give Star a size parameter and use shape() and points so bigger stars are worth more.

Support: Give students the Rock class already written, and ask them to make the list of rocks and move them in tick.

Exit ticket

class Dog: def __init__(self, name): self.name = name. d = Dog("Rex"). What is d.name?

"Rex": __init__ stored it in d with self.name = name.

What each step is for

Where it leads

Next lesson adds methods, so a star moves itself (star.move()) and adding a new kind of falling thing is one class. Every game engine is built from classes like these.