Hands-on Day

Ball Simulation

For this quiz, we are providing you with a basic Ball class and a simulation that animations many balls moving in a world.  However, unlike a recent assignment, there is no gravity in this world. In fact, best to consider the animation as a birds-eye view from above of balls on something like a billiards/snooker table. To begin download the following three files (or all three as this zip file).

The simulation is the file you should execute, and as given, you should see an animation of a random set of balls moving in random directions until they leave the screen.

Instances of our Ball class maintain a position (x,y), a velocity (vx,vy), a radius and a color for the sake of animation. They also support an advance(world) method that by default simply advances the position based upon the velocity. The World instance sent as a parameter supports methods getWidth() and getHeight() that describe the extent of the world. For one of the more advanced challenge, it also supports a method getBalls() that returns a list of all balls currently in the word, and a method removeBall(b) to remove a ball from the world.

As a first demonstration of inheritance, we have provided a class named BouncingBall that dectects the edges of the world when advancing, and changes its direction of motion if reaching an edge. Our implementation is already given in the ball.py file, though it is commented out by default. If you uncomment that file and rerun the simulation, each ball is randomly either the original Ball (drawn in black) or the new BouncingBall (drawn in light blue). A sample simulation is illustrated as follows.

The key to the inheritance is simply to specialize the advance method. In short, we call the superclass version of advance and after that completes, we wish to see if the ball has made contact with any of the edges. While we could have written separate code to check each of the four edges, we reduced code duplicaiton by defining a utility function named clip() that returns a new position and velocity along an axis, given the current position, velocity, and bounds for that axis; usually those values are unchanged but if a boundary has been encroached the velocity is reversed and the position is adjusted appropriately into range.

Your Tasks

You are to use inheritance to define a variety of other new ball types. We don't expect students to necessarily get through all of the following, but do what you can during the time allotted. When done, one person from the team should submit the modified Ball.py file to the quiz24a folder in the git repository, making sure all names are given in the comment at the top of the source code.

  1. Develop a new class DampeningBall (colored orange) that is a subclass of BouncingBall. Every time the ball reflects off a wall, its velocity should be set to 50% of its previous velocity. The key to implement this is to record the current velocity values immediately before calling the inherited version of advance, and then after that call, if you detect that either component of the velocity has changed then a wall must have been reached and you should update the new velocities to 50% of their value.

  2. Define a new class WrappingBall (colored light green) that is a subclass of the original Ball class. Unlike the BouncingBall class that reflects when reaching an edge, a WrappingBall should wrap around from one edge of the world to the opposite edge. That is, if reaching the top edge it should reenter the world at the corresponding part of thebottom edge. If the ball goes through the right edge, it should return from the left edge. Note well that the velocity never changes for such a ball, it is only the position that must be updated. As we did with the BouncingBall, you might definine a utility function that returns the updated position along a general axis, based on the current position, velocity, and bounds on the range.

  3. Define a new class ExplosiveBall (colored red) that is a subclass of the original Ball class. Each time an ExplosiveBall advances, it considers all other balls in the world and tests whether it has collided with such ball. (Two balls collide if the distance between the centers is less than the sum of their radii.) If a collision is detected this ball and all with which it collides should be removed from the world. To aide in this challenge, the world supports a getBalls() method that provides a list of all balls in the world, and a method removeBall(b) that can be used to remove a ball b.  Be careful not to consider a ball colliding with itself!
  4. Here is where things get fun! We can use multiple inheritance to combine some of our functionality. Define a new class ExplosiveWrappingBall that simultaneously inherits from parents ExplosiveBall and WrappingBall. If things go well, you should not need to make any adjustments to the code, since the functionality of wrapping and the functionality of exploding are independent. The only change we suggest is making the color dark green so we can easily identify balls from this class.

  5. In similar fashoin, define a new class ExplosiveBouncingBall that simultaneously inherits from parents ExplosiveBall and BouncingBall, yet colored bright blue.

A final simulation with all ball types is given below.


Michael Goldwasser
Last modified: Wednesday, 25 December 2019