This project continues our work with classes within the domain of physical simulations.

This is the second part of a multi-part project where we will look at increasingly complex physical simulations. This week we add more sophisticated collisions with more types of objects.


Setting Up

If you have not already done so, mount your personal space, create a new project9 folder and bring up TextWrangler and a Terminal. As with last week, you will need the Zelle graphics package file graphics.py.

The Zelle documentation is also available.


A dictionary is a kind of data structure that stores information. Unlike a list (which is a different data structure that stores information), the information in a dictionary is not stored in sequential order. Instead, each value stored in a dictionary has a unique key, which plays the role of the index in a list: with a list, we access a value via its index; with a dictionary, we access a value via its key. The difference is that a key can be any Python type that is immutable (i.e., it can't be modified). These include the types int, str, float, and tuple. Examples of valid keys are: 1, 42, '42', 'ice cream', 'chocolate', 'cookie', 1.7, and (5, 'blueberry', 'muffins').

Create a new Python file called wordmap.py. As usual, put a comment block at the top (with your name, the date, etc.), then start a main function (no parameters). The goal of this program is to give the user a set of word prompts and record their responses to the prompts. To record the responses, we'll use a dictionary with each word-prompt as a key and the user's response to that key as the corresponding value.

  1. Print out instructions for the user that tell them what to do.
  2. Create a list of about ten words, call it words.
  3. Create an empty dictionary, calling it mapping.
  4. Loop over the words. Each time through the loop, do two things. First, assign to variable response the return value of input(), using the word as the prompt (i.e., the argument to the input() function). Second, using the word as the key, assign to your dictionary the response (the output from input()).
  5. Finally, loop over the keys in mapping (mapping.keys()) and print out the key-response pairs to show the user their results.


Inheritance: A mechanism for sharing the capabilities of one class with one or more other classes.

This week, we are going to use inheritance to simplify the code in the physics_objects.py file and make it easier to create new kinds of shapes. The concept is to create a parent class, Thing, that has all of the common fields and methods for physical objects. Then we create child classes, like Ball, Wall, and Floor, that make use of the parent Thing class.

Create a new file, physics_objects.py, in your project 9 directory. You may want to also have a copy of your physics_objects.py file from last week open, since you will be able to reuse a good bit of the code.

Start a new class, Thing. The __init__() method for Thing should have two required parameters, win and the_type. You can also add parameters with default argument values for the mass and radius, if you'd like, but that's not required.

Next, in your __init__() method, create the following fields:

typeString, indicating the type of the object.
massScalar value indicating the mass of the object.
radiusScalar value indicating the radius of the enclosing circle.
position2-element list indicating the current position of the object.
velocity2-element list indicating the current velocity of the object.
acceleration2-element list indicating acceleration acting on the object.
force2-element list indicating a force acting on the object.
elasticityScalar value indicating the proportion of energy retained after a collision.
scaleScalar value indicating the scale factor between the simulation space and pixels.
windowReference to the GraphWin object representing the window.
visList of Zelle graphics objects for visualization.

Be sure all of these fields get initialized to sensible default values for our simulations. For example, position, velocity, acceleration, and force might be initialized to [0,0] (e.g., self.position = [0,0]), and values for elasticity and scale might be initialized to values consistent with our work on last week's lab and project.

Getters & Setters

Create "getter" and "setter" methods for all of the above fields except vis and win. For the most part, you can copy and paste these from the Ball class from last week. The exception is setPosition(), which will be unique for each child class. For the Thing class, instead of copy and pasting from the Ball class, make a setPosition() method that does nothing but set the position field values--it should not move the graphics objects.

draw( ) & update( )

Create two methods draw() and update(), again copying from last week's Ball class methods. The draw() method should loop through the vis objects and draw them into the window. The update() method should move the simulation of the object forward by the given time step by modifying the position and velocity based on the current acceleration and force fields.

The Inheritance of Things

Create a new Ball class that inherits the Thing class.

class Ball(Thing):

Your Ball class will have two methods, __init__() and setPosition(). The __init__() method should have win as the only required argument, but you may also want to add x0, y0, mass, and radius as default arguments so you can create a Ball in a location (x0, y0), of a particular size and mass.

The first step in the __init__() method is to call the Thing (parent) __init__() method. Call it by using the name of the class, Thing, followed by the name of the method, __init__(), connected by a dot. The first three arguments should be self, win, and the string "ball". Then you may also want to pass in the mass and radius parameter values if you have them as optional arguments to the Thing __init__() method. For example:

Thing.__init__(self, win, "ball", mass = mass, radius = radius)

To initialize the position value, explicitly use a statement of the form self.position = [x0,y0] rather than entering arguments for the position when calling the __init__() function.

The second step is to assign to self.vis a new list with a Circle graphics object in it. You can copy and paste your code from last week here. If you want, you can then also set the color of the ball (look in the PDF documentation of the graphics library to find out how).

The setPosition() method in the ball class should be unchanged from last week

Once you have completed this, download the files test_ballclass.py and collision.py. Running test_ballclass.py should create two balls and send them into a collision.

When you are done with the lab exercises, you may start on the rest of the project.

© 2019 Eric Aaron (with contributions from Colby CS colleagues).