CS 152: Lab 9

Title image Project 9
Spring 2017

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.


Tasks

  1. The first task is to create a new Floor class using inheritance (the parent class is Thing). The __init__ method should have five parameters.

    winA reference to a GraphWin object
    x0The anchor point x value. The anchor is in the middle of the left side.
    y0The anchor point y value. The anchor is in the middle of the left side.
    widthThe horizontal distance of the floor.
    heightThe 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 width and height 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 and put it in self.vis.

    Finally, add two methods, getHeight and getWidth that return the width and height of the object.

  2. 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.
  3. 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, y0+height/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.

  4. 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, for example.

    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.

  5. Create a scene that is like a pinball table (make a new file and name it pinball.py). 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):
        ''' Given a GraphWin (win), build all of the objects necessary 
            for an obstacle course. Draw them in the window, and return
            them in a list.'''
    
        # 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.

  6. 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 (and on whether or not the shape should move in response to 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 that if you choose to have it collide like a ball, then you will need to make sure the anchor point (self.position) is in the center of the shape. Also, you will need to set the radius so that the 'ball' encloses the shape. If you choose to have it collide like a block, the anchor position will also need to be in the middle. In this case, you will need to give values to width and height parameters that make sense and write getWidth and getHeight. 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.


Extensions

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.


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