Make sure all programs are saved to your cs21/labs/11
directory. Files outside this directory will not be graded.
$ update21
$ cd ~/cs21/labs/11
So far in this course we have used many different types of objects (Circle
, Point
, lists, strings, Zipcode
, Channel
, ...) but you haven't defined your own classes. The goal of this lab is to practice writing your own classes, with constructors, attributes, and methods.
In the file spaceblaster.py
, you will implement a game to navigate a starship across space, destroying asteroids along the way. Be careful---hitting an asteroid will damage your starship, and it's the only one you have!
SpaceBlaster! more SpaceBlaster!
To complete this assignment, you need to implement two classes: Asteroid
and Starship
. Asteroids come in different sizes, move in different directions, and at different speeds. The starship comes equipped with a high-powered laser that can blast asteroids to smithereens, but only when the asteroid is directly in front of the starship. The laser is so powerful it can blow through entire asteroids, destroying everything in its path. The player scores one point for each destroyed asteroid; play continues until the starship collides with an asteroid.
setCoords(0,0,100,100)
to make it easier to compute positions)Up
, Down
, Left
, or Right
keys. Note: the GraphWin
class has a method called checkKey()
that will tell you what the last key pressed was. checkKey()
returns a string containing what the last key pressed was since the last time it was called. If no key had been pressed since the last checkKey()
call, it returns the empty string.f
key. When the laser is fired, all asteroids immediately to the right of the starship's laser will be destroyed; the player earns one point for each destroyed asteroid.q
.q
, or "You've been hit!" if the starship collides with an asteroid. Also display the player's score.This game may look hard to program at first glance, but by breaking the program up into classes and testing each class incrementally, it will be relatively strightfoward to implement.
First, write and test the Asteroid
class that has the methods shown below. We encourage you to test each method as you write them. For example, write the constructor (__init__
), then write some test code to make a graphics window and create an Asteroid
object. Make sure its size, color, and position look correct. Then add the move()
method, and add more test code to move the asteroid in the right direction.
Your constructor should:
uniform(a,b)
method in the random
library returns a random number between a
and b
.GraphWin
object passed into the constructor, create and draw a Circle
object representing the Asteroid, of the radius, color, and starting position initialized above.has no parameters (except for self)
moves the Asteroid dx horizontally and dy vertically.
Note: this move()
method will call the Zelle graphics move()
method to move the Circle object. It's OK that it has the same name.
has no parameters (except for self)
returns True
if the Asteroid is completely off the screen. To see if the Asteroid is above the window, get the Y-coordinate of the Circle objects' center, subtract the radius, and see if it is above 100. Similarly, to see if the Asteroid is to the left of the window, get the X-coordinate of the Circle object's center, add the radius, and see if it is below 0. You'll also need to check to see if the Asteroid is below/to the right of the screen.
has no parameters (except for self)
returns nothing, but changes the color of the Circle to signify it has blown up. Also briefly pause to give the user a chance to see the asteroid as it is blown up. Finally, undraw
the Circle.
Here is a video showing a simple test of the Asteroid class. It creates an Asteroid
and moves it until it is off-screen.
Next, write and test the Starship
class that has the methods shown below. This class will craete the starship object itself (a rectangle for the main body with two smaller rectangles to represent tail fins), as well as the methods needed to move the starship, fire the laser, and decide if it collides with an Asteroid
.
First, implement the constructor method __init__(self, win)
. This method has an input parameter win
, a GraphWin
object. It should create a Rectangle object for the body of your starship, two smaller rectangle objects to represent tail fins, and draw the rectangles on the screen. In addition, the Starship has a speed and direction. These should be initially zero (the ship is at rest) and ("Right") respectively. Other specifications lie below:
The move()
method moves the Starship a small distance, depending on its current speed and direction. A Starship's direction should be one of "Up", "Down", "Left", "Right". When the move
method is called, two steps must be taken. First, update the direction and speed. Second, move the starship in the new direction according to the updated speed.
Updating direction/speed. The value of the input parameter new_direction
should be a string with value either"Up", "Down", "Left", "Right", or "None".
If the new direction is "None", then do not change the direction or speed.
If the new direction is the same as the current direction, don't change the direction, but increase the speed by 10%. (This signifies the user is happy w/current direction and chooses to increase speed)
If the new direction is different from the old direction, update direction to the new direction, and reset the speed to be 1. (changing direction reduces momentum almost to a complete stop!)
Moving the starship. Once you've updated direction and speed, move the starship in the appropriate direction by an increment of speed
. To move the starship, you'll need to call the Zelle Graphics move()
method on each Rectangle comprising your starship.
The collide()
method takes an asteroid object as input and returns True
if the starship has collided with this asteroid. There could be two reasons why the starship and asteroid collide---either the starship moves into the asteroid, or vice versa. To make things a little simpler, we've created a helper function intersect(rect, circ)
in a sb_utils
library. The intersect()
function has two input parameters: a Rectangle
object and a Circle
object. It returns True
if the two objects intersect. To use this method, first import the cs21s19
library:
from cs21s19 import intersect
The Starship
class contains three Rectangles
; the Asteroid
class is represented with a Circle
. To see if the Starship
collides with the Asteroid
, check to see if any part of the starship intersects with the asteroid.
The fire()
method has one input parameter in addition to self
: asteroids, a list of Asteroid
objects. The purpose of this method is to handle what happens when the Starship fires its laser gun. To represent the laser, draw a thin red Line from the right side of the Starship to the end of the graphics window.
To determine if an asteroid has been hit by the laser, check to see if two things occur: (i) the center of the asteroid is to the right of the Starship's center, and (ii) the starship's center's y-coordinate is within a radius
of the y-coordinate of the asteroid.
If an asteroid does get hit, it is destroyed.
Use incremental development and testing as you program your Starship class. First, implement the constructor and make sure your Starship appears on the window. Then, implement the move method, save your code, and test it by calling move
several times. Does the Starship move in the way you expect? Finally, create some Asteroid
objects, and move the Starship until it collides with an Asteroid. Does the starship correctly detect collisions?
Here are some sample test videos of the Starship
class:
Your main program should create a starship and an initial set of asteroids, and then loop until either the starship collides with an asteroid or the user enters q
.
Inside each iteration of the game loop, you should:
check to see if the user entered a key. If so, either move the Starship, fire the startship's laser, or quit the game, depending on what the user entered. Hint: remember to use the GraphWin
method checkKey()
, not the python input()
function.
pop
method to remove an item from the list. e.g. to remove the i-th item from a list ls
, call ls.pop(i)
.Hint: If you want to loop through a list and remove some elements, it's helpful to loop through starting from the end of the list instead of the front. Otherwise, the pop(i)
method will affect the indices of the list while you're looping through them, causing a syntax error.
Finally, each iteration of the while loop should with create a new asteroid with probability 1%. Hint: The random
library contains a function random()
which returns a value between 0 and 1. It has a one percent chance of being below 0.01.
Once the game ends, print a status message in the GraphWin
object describing the user's score and why the game ended; e.g., user quit or collided with an asteroid.
There are many possible extra challenges for this assignment if you're inclined to make this lab truly out of this world. Here are some suggestions:
Get creative with the design of the starship. Consider making the tail fins from objects other than rectangles, or change the shape of the main body.
Draw an Asteroid using the Polygon
object instead of a circle. How does this change the Asteroid class? How will you see if the asteroid collides with the Starship?
Enable the Starship to change directon of movement and/or fire the laser to the left instead of always to the right.
Make the game progressively harder as the game goes on; e.g., consider adding new Asteroids more quickly and/or making the asteroids bigger or faster as the game progresses.
Once you are satisfied with your programs, fill out the questionnaire in QUESTIONS-11.txt
. Then run handin21
a final time to make sure we have access to the most recent versions of your file.