Project 9: Pinball
The focus on this project is to provide you with experience writing classes with inheritance. We will also be using a dictionary to make working with collisions easier.
The first task is to create a new Floor class using
inheritance (the parent class is Thing). The __init__ method
should have five parameters.
win A reference to a GraphWin object x0 The anchor point x value. The anchor is in the middle of the left side. y0 The anchor point y value. The anchor is in the middle of the left side. length The horizontal distance of the floor. thickness The vertical distance of the floor.
As with the Ball class, the first action in the __init__ method is to call the parent Thing.__init__ method. Specify the type as the string "floor", and pass in [x0, y0] as the position. Next, assign the parameters length and thickness to two fields width and height. Since you have three classes (Floor, Wall, and then Block) that will be rectangular in shape, it makes sense to use width and height as the fields for that information in all three classes. However, since those fields are not created in the Thing class, you need to create and assign them here.
Next, create the Rectangle object to represent the floor, just as you did in the prior lab (copy and paste that line of code).
Finally, add two methods, getHeight and getWidth that return the width and height of the object.
- Repeat this process with the Wall class, which will look almost identical. Use "wall" as the type of the Wall class when calling the Thing.__init__ method.
Repeat this process with a new Block class. The parameters for the
block class should be the same as the Floor and Wall classes. When
creating the visualization for the Block, put the anchor point in the
center of the block. One corner should be at (x0-width/2,
y0-height/2) and the other corner should be at (x0+width/2,
Note, the primary difference between a Wall, a Floor, and a Block is in how they are treated with respect to collisions. A Floor is a horizontal plane that extends infinitely. A Wall is a vertical plane that extends infinitely. A Block is just a block and is finite in both directions. The Floor and Wall classes let us do collisions a little faster than a Block.
The next task is to modify the collision.py file to make it
simpler to call the right collision function no matter what types of
objects are involved. Note, the collision functions all work with a
ball and something else. We don't yet have block-block collisions,
The idea is to use a dictionary with the two types involved as the key, making use of the type field in the Thing class. For example, if we have two objects, item1 and item2, we can generate a key by writing:
key = (item1.getType(), item2.getType())
This creates a typle that has two strings in it. Then, we can generate a dictionary entry that contains all the possible keys and stores the proper function to call in each case. For example, the following creates an entry in the dictionary collision_router with the key ('ball', 'ball') and sets its value to the function reference collision_ball_ball.
collision_router[ ('ball', 'ball') ] = collision_ball_ball
At the bottom of the collision.py file, at the top level (meaning totally unindented), create an empty dictionary called collision_router. Then add an entry to the dictionary (as above) for each possible of a ball and another type. There are four other types: floor, wall, block, and ball. There are also four collision functions, one for each combination.
Finally, create a function called collision that takes in two Thing objects plus a time step and uses the collision_router dictionary to call the right function. For now, you can assume the first argument will be a ball. However, think about how you could code this up, possibly by creating a few additional one line functions, such that it would not matter which order the objects appeared in the collision function parameters.
Create a scene that is like a pinball table. It should have overall
boundaries, then some obstacles inside the bounding box that are all
stationary. Create a launch location and launch a single ball into the
scene. Respawn if the ball goes out of play.
The main file should have the following structure.
# build the obstacle course def buildGame(win, x0, y0, width, height): # Create all of the obstacles in the scene and put them in a list # Draw all of the obstacles # You might want to set the obstacle elasticity to something larger than 1 # return the list of obstacles # launch the ball into the scene def launch( ball, x0, y0, dx, dy, forceMag ): d = math.sqrt(dx*dx + dy*dy) dx /= d dy /= d fx = dx * forceMag fy = dy * forceMag ball.setElasticity( 0.9 ) ball.setPosition( (x0, y0) ) ball.setForce( (fx, fy) ) for i in range(5): ball.update(0.02) ball.setForce( (0., 0.) ) ball.setAcceleration( (0., -1.) ) # main code def main(): # Create a GraphWin # Assign to obstacles the result of calling buildGame # Make a Ball object, draw it, and launch it into the scene # assign to dt the value 0.01 # assign to frame the value 0 # while win.checkMouse is equal to None: # assign to collided the value False # for item in obstacles # if the result of calling the collision function with the ball and the item is True # set collided to True # if collided is False # call the update method of the ball with dt as the time step # if frame modulo 10 is equal to 0 # call win.update() # increment frame # if the ball goes out of the window, re-launch it # wait for a mouse click, then close the window if __name__ == "__main__": main()
Create a video of your obstacle course in action and include a link to it on your wiki.
Create a new class that makes a new shape. Specify its type as either
a block or a ball, depending on which shape's outline is better suited
for collisions. Add an example of this shape to the obstacle course.
For example, you could make a shape like an H and specify that it is of type 'ball' for the purposes of collisions. Then you can shoot it into your obstacle course and it should act like a ball with respect to collisions. Note, things of type 'block' can't move, but things of type 'ball' can (at least with respect to collisions).
Include a picture of your second shape as part of your wiki. If you are feeling ambitious, include the shape in your video or make a second video.
Each assignment will have a set of suggested extensions. The required tasks constitute about 85% of the assignment, and if you do only the required tasks and do them well you will earn a B+. To earn a higher grade, you need to undertake one or more extensions. The difficulty and quality of the extension or extensions will determine your final grade for the assignment. One complex extension, done well, or 2-3 simple extensions are typical.
- Create a RectangularThing class that will serve as the parent class to Floor, Wall, and Block. This class will manage the height and width fields so that you do not need to have that code appear in all three classes. The structure would be a grandparent class Thing, a parent class RectangularThing, and child classes Floor, Wall, and Block.
- Create more elaborate spaces and developing interesting additional simulation videos.
- Create more shape classes and show that they collide and simulate appropriately.
- This simulation does not take into account masses. A challenging extension would be to modify the collision code so that when two things collide the velocity changes take into account the masses. (Conservation of momentum.)
- Add the capability to whack the ball to your pinball simulation.
- Create a separate scene like putt-putt golf and let the user shoot the golf ball.
Write-up and Hand-in
Turn in your code by putting it into your private hand-in directory on the Courses server. All files should be organized in a folder titled "Project 9" and you should include only those files necessary to run the program. We will grade all files turned in, so please do not turn in old, non-working, versions of files.
Make a new wiki page for your assignment. Put the label cs152f16project9 in the label field on the bottom of the page. But give the page a meaningful title (e.g. Milo's Project 9).
In general, your intended audience for your write-up is your peers in the class. Your goal should be to be able to use it to explain to friends what you accomplished in this project and to give them a sense of how you did it. Follow the outline below.
- A brief summary of the task, in your own words. This should be no more than a few sentences. Give the reader context and identify the key purpose of the assignment. Each project has both a coding/learning purpose (e.g. learning about dictionaries) and a non-coding objective (e.g. creating complex scenes).
- A description of your solution to the tasks, including any text output or images you created. This should be a description of the form and functionality of your final code. Note any unique computational solutions you developed or any insights you gained from your code's output. You may want to incorporate code snippets in your description to point out relevant features. Code snippets should be small segments of code--usually less than a whole function--that demonstrate a particular concept. If you find yourself including more than 5-10 lines of code, it's probably not a snippet.
- A description of any extensions you undertook, including text output or images demonstrating those extensions. If you added any modules, functions, or other design components, note their structure and the algorithms you used.
- A brief description (1-3 sentences) of what you learned. Think about the answer to this question in terms of the stated purpose of the project. What are some specific things you had to learn or discover in order to complete the project?
- A list of people you worked with, including TAs and professors. Include in that list anyone whose code you may have seen, such as those of friends who have taken the course in a previous semester.
- Double-check the label. When you created the page, you should have added a the label cs152f16project9. Make sure it is there.