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 intersectThe 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.