MIT OpenCourseWare
  • OCW home
  • Course List
  • about OCW
  • Help
  • Feedback
  • Support MIT OCW

Projects

Read the amendments to this document.

Handout GB

Contents: 

Introduction

Your final project is to design, document, build, and test a program that plays Gizmoball. Gizmoball is a version of pinball, an arcade game in which the object is to keep a ball moving around in the game, without falling off the bottom of the playing area. The player controls a set of flippers that can bat at the ball as it falls.

The advantage of Gizmoball over a traditional pinball machine is that Gizmoball allows users to construct their own machine layout by placing gizmos (such as bumpers, flippers, and absorbers) on the playing field. These machine layouts may also form complicated "Rube Goldberg" contraptions that are intended to be watched rather than played. (If you don't know what a Rube Goldberg machine is, see Argonne National Lab's Rube Goldberg Machine Contests or The Official Rube Goldberg Web Site). As an optional extension (after you have designed, documented, implemented, and tested all required functionality), you may create new varieties of gizmos that can be placed on a playing field.
Top

Gizmoball Overview

Because this project is in part a design exercise, the assignment specifies what the user should be able to do and leaves it up to you to figure out what modules and interfaces are appropriate. This section gives an overview of Gizmoball. A more detailed specification is given in Appendix 1. To enable automated testing, your implementation must support a file format (defined in Appendix 2), in addition to the loosely-specified graphical user interface.

Gizmoball has a graphical user interface with two modes, building and running. In building mode, a user can:

  • create and edit square, circular, and triangular bumpers on the playing surface,

  • create and edit flippers,

  • connect the action of flippers and bumpers to triggers, such as a keyboard key being hit or one of the bumpers being bumped, and

  • save and load the user's game configuration to and from a file.

In running mode, the user can play the game.


A screenshot of one implementation of Gizmoball.
Your implementation may look different, and your ball motion may not match the animation given exactly.


The picture above illustrates the most important features of Gizmoball.

  • A gizmo palette on the side provides the user with a variety of operations (square, circle, triangle, flipper) for placing gizmos in the playing area.
  • A "modifications" toolbar on the bottom provides the user with a variety of operations (move, delete, rotate) for editing the gizmos in the playing area.
  • The modifications toolbar also provides a connect button that connects the trigger of one gizmo to the action of another. After this button is pressed the user can connect gizmos together. For example, the user might press the connect button, then click on one of the circular bumpers, and then click on one of the flippers. As a result, every time the bumper's trigger is activated (which occurs when a ball hits the bumper), the flipper's action (to rotate around its pivot) will occur. Alternatively, the user might press the connect button, then press a key, then answer a question about whether the up or down keypress is of interest, then click on one of the flippers. As a result, every time the user depresses (or, respectively, releases) that key, the flipper will move. Several triggers may activate the same gizmo.
  • The purple bar across the bottom of the playing area is the absorber gizmo. When a ball enters the absorber, the ball stops moving and is held in the absorber's lower right-hand corner. The absorber gizmo's action is to shoot a ball it is holding (if any) straight up in the direction of top of the playing area. Connecting the absorber to itself allows the game to loop continually: every time a ball enters the absorber, it is immediately shot out again.
  • The menu at the top of the window allows the user to save or load game configurations and to run or stop the game. In the animation, a game is in progress: the ball is the small blue circle which started in the lower right-hand corner. The ball bounces off the red, green, and blue bumpers, and is hit by the yellow flippers.

Top

Grading and ScheduleGrading

You will work in teams of three or four. All members of a team will receive the same grade, except in unusual circumstances.

Stage % of Project Grade Graded On
Preliminary design 10% Have you identified the issues?
Weekly meetings with TA 5% Did all of the team members participate constructively?
Preliminary Release 25% Is it a good design? Is the required functionality present?
Design Critique 25% Are the tradeoffs & alternatives thoroughly analyzed?
Implementation & test 35% Does it work? Have you demonstrated that it works?

Each stage of this assignment should be handed in electronically and/or as hardcopy to your TA on its due date. All of the source and compiled code should be put online on Dec. 10, 2001 in your team project directory.
Because of end of term constraints, late assignments will not be accepted.

Top

Preliminary Design

The preliminary design must be submitted in hardcopy on the due date.

You should express your preliminary design in under 15 pages. Your document will be a subset of that described in the Documenting a Software System handout. It will include a revised specification, a design (including a problem object model), and an implementation overview (including a code object model or models and a module dependency diagram). You do not need to include information about your validation strategy.

The revised specification, design, and implementation overview must discuss, among other issues:

  • the editor with which users construct gameboards. For instance, the specification should include drawings or screen shots of the editor and use cases for basic build mode tasks.
  • your physics loop, which describes how and in what order motion, collision detection and resolution, triggering, friction/gravity, drawing, and other factors are handled.
  • the triggering system which connects triggers to actions.

The preliminary design should also include a project plan, which lists milestones for the team and allocation of tasks to each team member. A milestone is what you expect to be done by some date, such as a tested module or a working feature -- an achievement that is objective, easy to evaluate, and significant. There must be at least two milestones in the project between the preliminary design and the final deadline. The division of labor should be equitable. (It is most useful to combine the milestones and the task allocation into a single table with task, who, and when columns, rather than making the milestones and task allocation into to separate documents which cannot be easily related.)

The preliminary design will be graded for clarity of expression and for whether you have thought rigorously about the problem, identified the major issues and challenges, and proposed a design that makes sense.

Top

Weekly Meetings with TA

Each team will meet with its TA once a week for half an hour. The official time for these meetings is during the regular class time. To receive full participation credit:

  • All team members must be present at all meetings.
  • All team members must answer questions and participate in the discussion at each meeting.
  • A clear progress document that is useful for productive discussions must be handed in each week at the meeting. At the first meeting a draft of the Preliminary Design will serve as the progress document.

Although the progress document must be clear, it is short and informal. This document will form the basis of discussion during the meeting, and the TA will keep it on file as a record of progress made. The team should bring multiple copies to the meeting, one for each team member and one for the TA. This progress document should include the following information:

  • A description of all the new issues that have been discovered during the previous week. This includes both a list of newly discovered bugs, and a list of unresolved design issues.
  • A description of all the issues that have been solved over the past week. This includes a list of bugs that were fixed, and how they were fixed, and a list of design issues that were resolved, and how they were resolved.
  • A list of all the issues from previous weeks that are still unresolved.
  • A plan for the next week, with specific actions and goals for each team member.
  • An assessment of success at meeting the previous week's plan.
  • The document may also contain any other material that you feel describes your progress, such as object model or MDD fragments showing changes to the design.

Top

Preliminary Release

The preliminary release has two components: a final design writeup and a demonstration of basic functionality.

Top

Final Design

The final design document is an improved and completed version of the preliminary design document. (For instance, it should include a section on your validation/testing strategy.) You should express your final design in under 20 pages. In the project plan section, explain any milestones that you missed and give reasons why you were unable to reach them.

Since the final design is due as part of the preliminary release milestone, you should have found all major mistakes in your design. Therefore the final design will be graded on whether it is a good design, in addition to clarity of expression.

Top

Required Functionality

At the preliminary release deadline, we will ask you to demonstrate some specific functionality.

In order to demo what you had done at the preliminary release deadline, even if the demo is slightly later and you have made modifications in the meanwhile, you must to retain a copy of your code as it existed before the deadline. You can create a snapshot of your source and/or compiled code by using a jar file. Then, you can run the demo by placing the jarfile in your classpath, or by making the jar itself executable.

It is acceptable for you to run up to four separate Java™ programs to demonstrate the following four features:

  1. Demonstrate key-press triggering of a flipper on the screen. When a key is pressed, the flipper should rotate 90 degrees; after the key is released, the flipper should rotate back to its original position. You should be able to trigger it a second or third time by pressing the key again after it has returned to the original position. (You need not demonstrate connecting the key to the flipper in build mode.)
  2. Demonstrate a working absorber, ball motion, gravity, and friction. In running mode, with no bumpers or flippers on the screen and the ball sitting still in the absorber, you should be able to press a key, observe the ball shoot up out of the absorber, slow down as it rises, fall back to the absorber, and return to its original position. Also demonstrate that you can shoot it out a second time. (Note that you do not yet need to support configurable gravity or friction constants.)
  3. Handle ball collisions with bumpers and the walls. Proper handling of ball-flipper collision is not required at this stage. During running mode, a ball shot out of the absorber must behave properly when it collides with bumpers or with the outer walls.
  4. Demonstrate loading files in the standard format. Given a test file, your implementation should display the gizmos specified in that file at the specified locations on the screen. You should be able to load and display all the standard gizmos.

Your animation in run mode should be smooth and adequate to demonstrate the features required above.

Top

Amendment

Shortly after submitting the preliminary release, you will receive an amendment to the program specification. In large programming projects, the requirements often change during development. Your design should be flexible enough to adapt easily to possible changes in the specification.

Top

Implementation and Critique

It should be possible for you to describe your implementation and critique your design in under 20 pages (except for the Specifications in Item 2, which may take another 5-10 pages and should be placed in an appendix). Conciseness is important. The team that won the 1999 Gizmoball design competition submitted a 7 page final report.

Put your code online in your team project directory. You do not need to turn in a printout of your code.

The final report includes a section on Design Changes. Detail what changes you have made to the design. Be precise. It is vitally important that your TA have versions of the descriptive parts of the Revised Specification, Implementation Overview, and Validation Strategy that accurately describe your submitted code and what you actually did for testing or validation. If the description is not accurate, the submission will not be graded accurately.

For demonstrations (as described immediately below), you must also make a jar file of your implementation. This file must be runnable and must include all of your code in both source and compiled form.

Demonstrate your working program for your TA. All team members must be present to give a brief presentation and answer questions pertaining to their responsibilities in the development process. The presentation and demonstration should not last for more than 15 minutes. Expect another half hour of questions and discussion.

Top

Design Contest

When you submit your implementation and critique, indicate on the first page if you want it to be considered for one or more of the following prizes.

  1. Best design: Awarded for the project with the best abstraction, modularity, extensibility, simplicity, etc. The quality of the final report is also considered.
  2. Best Gizmoball game: Awarded for the project with the best game-play. Part of your submission for this prize should be an input file that sets up the playing area; the prize is for the playable game itself, not for the construction kit.
  3. Most artistic: Awarded for the project that is the most beautiful or fascinating to watch. Part of your submission for this prize should be an input file that sets up the playing area. When run with this input file, the ball or balls should bounce around forever without the user needing to press any keys.

Whether your program implements only the basic required functionality or extra gizmos etc. will not be considered when making the design award. However extra functionality that improves game-play or "Rube Goldberg" artistry will be an asset in competing for the other two awards.

Judging will be done initially by the TAs and final judgments will be made by the lecturers. One project can win multiple prizes. Winners will be announced at the last class.

Top

What We Give You

Animations in Java™ are quite challenging. You will use the Java.awt and Javax.swing packages to construct your graphical user interface (GUI). We have provided you with a demonstration program in Example.Java™ (JAVA) that shows how to animate the movement of a ball bouncing around the window. It also demonstrates how to get your program to listen to user events, such as clicking on a toolbar button, pressing a key or dragging the mouse. All members of your group should be able to compile and execute this demo GUI.

We have also provided you with a library of physics routines (see Appendix 3) for calculating the dynamics of elastic collisions. You are welcome to use this code as is, or modify it in any way that you like.

Top

Hints General

Design

A careful design will save you a lot of time in the long run. It's well known that a small mistake made early in a project can become a big problem if it's not caught until much later. The preliminary design is a major part of the project (more so than its proportion of the grade might indicate). Do it very carefully, trying to anticipate problems that may arise. Then the rest of your project will be more straightforward and more fun.

Prototype

One of the largest challenges for this kind of design problem is figuring out where the "gotchas" are. If you are having difficulty imagining how to structure one part of the design it sometimes helps to build a small prototype. Plan to throw away your prototypes. Once you've figured out how to do design something correctly, it rarely makes sense to try to retrofit a hacked up, broken version.

Validate early and often

Validation shouldn't be an afterthought! You may choose a design because its implementation will be easier to test. Make sure you validate your code as you implement.

Document early and often

Incomplete documentation is better than no documentation at all. If a potential problem or subtlety occurs to you, but you don't have time (or are unable) to formulate it properly, then just add a few sentences in your document describing the issue. Later, if you have time, you can go back and fix it.

Don't overdocument

Don't include any redundant material. For example, there's no need to explain the difference between black-box and glass-box testing. Just indicate which of your test cases fall in each category. Similarly, being rigorous is not the same as belaboring the obvious. You can assume that your TA knows what a set or a stack is. There's no need to explain something from scratch when you can use standard terms and notions.

Have fun being on a team

Enjoy being part of a team. Run new ideas past your partners, and discuss problems with them. Read and discuss each others code. A good way to find a bug is to ask someone else to look at your code. Start early!

Communicate effectively

Each meeting you hold with your team members (or your TA) should have

  • an agenda. Don't get together unless you know the reason. This will help you avoid wasting time.
  • a designated leader to facilitate the meeting. The leader ensures that the meeting stays on track, encourages all group members to participate, and helps to resolve problems.
  • a secretary who takes notes and distributes them to the remainder of the group afterward. These notes highlight the important decisions made, issues resolved (and not resolved), etc. They ensure that all decisions are agreed upon by everyone and that everyone is aware of the issues raised at the meeting.

The roles should rotate among the group members; in 6.170, no one individual should perform any of the roles disproportionately often.

Prioritize

We had fun putting together this project. Our goal was to provide you with a project that is both very challenging and offers many opportunities for you to be creative. We encourage you to experiment. Make your implementation of Gizmoball as beautiful to watch, and as fun to play as possible. That said, make sure you get the basic functionality working before you add new gizmos, or other bells and whistles. The best way to approach extensions to the project is to make your initial design flexible and extensible.

Top

Coding

The syntax of Gizmoball files is designed to be easy for a program to read. The BufferedReader.readLine() method can read a command. The StringTokenizer class can decompose a command into its constituent parts.

You should acquire background knowledge about Swing before attempting to code your GUI. Refer to Sun's Swing tutorial (particularly the quick tour and overview sections).

Do not try to use the realtime clock in order to determine timing information. Instead, arrange to receive a timer event every 1/framesPerSecond and proceed to do the simulation and screen updates in response to this event. If you get behind and time slows down, so be it. A simple way to set this up is do use the Javax.swing.Timer class, as in the example GUI. Using this approach will simplify the implementation of your code and will also avoid the need to deal with synchronization issues in a multi-threaded program.

If you are using Swing and wish to paint your own component, as you will need to do in order to actually draw the board, gizmos, and ball, you should extend Javx.swing.JComponent and implement your own paint routines. In order to do this you will need to override the paint method of your JComponent to paint the board. The painting is done by calling methods on the supplied Java.awt.Graphics object. Unless you explicitly turn it off, Swing components are automatically double-buffered to reduce flicker. If you do not understand this, do not worry about it. In addition to Graphics Java™ now has an alternative graphics context Java.awt.Graphics2D which provides more sophisticated capabilities than the traditional Graphics object. Note that the calls your components receive to paint(Graphics) will always have a Graphics2D passed as the argument, so if you want to work with Graphics2D, you may simply cast the Graphics object. You may implement Gizmoball using either style of graphics, but here are some differences which you might want to consider:

  • The Graphics object works in terms of integer values for pixels allowing you to more directly control which pixels are updated.
  • Graphics2D, on the other hand, accepts floating point values to define geometric shapes to be rendered and performs the rasterization itself. This is somewhat more automatic, but also makes it more difficult to directly set individual pixels.
  • The Graphics2D class also allows AffineTransforms to be applied to it. (An affine transform is a geometric transform which preserves parallel lines.)

In order to respond to mouse and keyboard actions from the user you will want to create and install MouseListener, MouseMotionListener, and KeyListener all of which can be found in the Java.awt.event package. Information about Java™ keycodes can be found in the documentation for Java.awt.event.KeyEvent.

Keypress

The specifications for handling keyboard input in Gizmoball require that an object connected to a key is triggered when that key is pressed or released. This provides behavior similar to that of a real pinball game: hitting the button causes the flipper to swing upward and releasing the button causes the flipper to return to its rest position.

Keyboard events

The Java™ specifications for Java.awt.event.KeyEvent describe three types of key events, KEY_PRESSED, KEY_TYPED, and KEY_RELEASED. The documentation suggests that KEY_PRESSED events occur when a key is actually depressed by the user and KEY_RELEASED events occur when the key is released. It would therefore seem reasonable to trigger when receiving a KEY_PRESSED or KEY_RELEASED event for a given key bound to a gizmo.

Unfortunately, most Java™ runtime environments fire multiple KEY_PRESSED and in some cases multiple KEY_RELEASED events when the user has only pressed the key once. Additionally, in some environments you may never receive the KEY_RELEASED events for an upstroke. This is because the behavior of KEY_PRESSED and KEY_RELEASED is system dependent. The behavior occurs through an interaction with the operating system's handling of key repeats that occur when you hold down a key for a period of time.

On Windows

On Windows, Java™ will produce multiple KEY_PRESSED events as the key is held down and only one KEY_RELEASED when the key is actually released. For example, holding down the 'A' key will generate these events:

PRESSED 'A'
PRESSED 'A'
...
RELEASED 'A'

On Unix

On Unix, multiple pairs of KEY_PRESSED and KEY_RELEASED are received as the key is held down:

PRESSED 'A'
RELEASED 'A'
PRESSED 'A'
RELEASED 'A'
...
PRESSED 'A'
RELEASED 'A'

Top

Test Program

If you want to explore the behavior of your in your environment, you can use the KeypressTest class provided by the staff. The application will dump all keyboard events to the console for inspection.

The source code is available at KeypressTest.java (JAVA)

Top

Solutions

You should feel free to handle this nuance of the Java™ API as you see fit. One easy solution is to shut off the operating system's automatic key press repeat mechanism and thereby cause the KEY_PRESSED and KEY_RELEASED events to more closely correspond to the actual actions of the user.

  • Unix/Linux: Type "xset -r" to shut off autorepeat. To re-enable autorepeat use "xset r"

  • Windows: Go to the Control Panel's Accessibility Options applet. On the Keyboard tab select the Settings... button for FilterKeys. Select Ignore quick keystrokes and slow down the repear rate. Select the Settings... button next to that option. Make sure No keyboard repeat is selected. Slide the SlowKeys slider to Short (0.00). Press OK twice. Check Use FilterKeys and press OK. To enable and disable these changes, simply check or uncheck the UseFilterKeys checkbox.

Asking the end user to perform settings such as these is acceptable, but should be included in your Gizmoball documentation.

An alternative solution is to take advantage of a special key listener decorator provided by the staff. The class is available in compiled form in the gb-lib.jar file as staffui.MagicKeyListener. Refer to the documentation for MagicKeyListener or use the provided source code (JAVA) as your own starting point.

Top

System Administration and Computer Tools: Revision Control

We strongly recommend that you use a revision control package such as CVS to help you coordinate your work, prevent loss of code, and permit backing up to previous versions.

Top

Make: System Model

Another tool you may find useful is make. This tool allows you to write a makefile that is a model of your system: which files contain code, what work needs to be done to compile the system, what work needs to be done to run your tests, what work needs to be done to clean up the directory, etc. Once you have written the makefile, you can do any of these tasks by typing a single command.

On Athena, type info make.

Top

Java™ Archive (JAR) files

You will need to create jar files as a way of collecting all parts of your application for delivery to your TA. jar files are useful because they can store all of the source code, compiled code, and associated data files (such as images or sounds) into one centralized archive, which can then be easily distributed.

To learn more about jar files, review the jar tool manual from Sun.

As a quick reference, here are a few sample uses of the jar command:

To create gizmo.jar from classes in packages gizmo and ball:

jar cvf gizmo.jar gizmo ball

To list the contents of the jar file, use:

jar tf gizmo.jar

To create a runnable gizmo.jar

jar cvfm gizmo.jar JarMainManifest gizmo ball

In the example above, the file JarMainManifest should contain a single line which names the entry point:

Main-Class: gizmo.StartGizmoball

To run the application using the jar file, use:

Java -jar gizmo.jar

Important: Note that, when using the -jar option to run an application, the CLASSPATH variable and the -cp command line switch are ignored. Therefore, to include the physics library in an application stored in a runnable jar, you will have to extract the .class files from our jar, and include them in your own. You can extract a jar file into the current directory this way:

jar xvf /mit/6.170/lib/gb-lib.jar

Using a Makefile, as described the section above, can simplify and automate the creation of jar files. Given the complexity and maintenance issues involved with merging the physics class files in with your application, we recommend the use of a Makefile to automate the process.

Top

Appendix 1: Detailed Requirements General

Your implementation must support two modes of execution, building and running. In building mode, the user can add gizmos to the playing area and can modify the existing ones. In running mode, a ball moves around the playing area and interacts with the gizmos.

Playing Area

To describe dimensions in the playing area, we define L be the basic distance unit, equal to the edge length of a square bumper. Corresponding to standard usage in the graphics community, the origin is in the UPPER left-hand corner with coordinates increasing to the right and DOWN.

The playing area must be at least 20 L wide by 20 L high. That is, 400 square bumpers could be placed on the playing area without overlapping. The upper left corner is (0,0) and the lower right corner is (20,20). When we say a gizmo is at a particular location, that means that the gizmo's origin is at that location. The origin of each of the standard gizmos is the upper left-hand corner of its bounding box, so the location furthest from the origin at which a gizmo may be placed is (19,19) on a 20L x 20L board. The origin of a ball is at its center.

During building mode, Gizmos should "snap" to a 1 L by 1 L grid. That is, a user may only place gizmos at locations (0,0), (0,1), (0,2), and so on.

During running mode the animation grid may be no coarser than 0.05 L by 0.05 L. Suppose that the ball is at (1,1) and is moving in the (1,0) direction -- that is, left to right -- at a rate of .05L per frame redraw. Then the ball should be displayed at least in positions (1,1), (1.05,1), (1.10,1), and can be displayed at more positions if you wish the animation to be smoother. Rotating flippers can be animated somewhat more coarsely; see the precise description of flippers below. If the ball is moving faster than the animation grid size per frame redraw, it need not be redrawn in each animation grid position.

Top

Building Mode

In building mode the user can:

  • Add any of the available types of gizmos to the playing area.
    • An attempt to place a gizmo in such a way that it overlaps a previously placed gizmo or the boundary of the playing area should be rejected (i.e., it should have no effect).
  • Move a gizmo from one place to another on the playing area.
    • An attempt to place a gizmo in such a way that it overlaps a previously placed gizmo or the boundary of the playing area should be rejected (i.e., it should have no effect).
  • Apply a 90 degree clockwise rotation to any gizmo.
    • Rotation has no effect on gizmos with rotational symmetry. For example, circular bumpers look and act the same, no matter how many times they have been rotated by 90 degrees.
  • Connect a particular gizmo's trigger to a particular gizmo's action.
    • The standard gizmos produce a trigger when hit by the ball, and exhibit at most one action (for example, moving a flipper, shooting the ball out of an absorber, or changing the color of a bumper). The trigger that a gizmo produces can be connected to the actions of many gizmos. Likewise, a gizmo's action can be activated by many triggers. The required triggers and actions for the basic gizmos are described below.
    • Note that triggers do not "chain". That is, when A is connected to B and B is connected to C, a ball hitting A should only cause the action of B to be triggered.
  • Connect a key-press trigger to the action of a gizmo.
    • Each keyboard key generates a unique trigger when pressed. As with gizmo-generated triggers, key-press triggers can also be connected to the actions of many gizmos.
  • Delete a gizmo from the playing area.
  • Add a ball to the playing area.
    • The user should be able to specify a position and velocity.
    • An attempt to place the ball in such a way that it overlaps a previously placed gizmo or the boundary of the playing area should be rejected (i.e., it should have no effect). There is one exception in the standard gizmo set: a stationary ball may be placed inside an absorber.
  • Save to a file named by the user.
    • You must be able to save to a file in the standard format given in Appendix 2. You may, if you wish, define an extension to the standard format that handles special features of your implementation. If you do so, the user must have the choice of saving in the standard format or in your special format.
    • The saved file must include information about all the gizmos currently in the playing area, all of the connections between triggers and actions, and the current position and velocity of the ball.
  • Load from a file named by the user. You must be able to load a game saved in the standard format.
  • Switch to running mode
  • Quit the application.

Top

Running Mode

In running mode, the user can:

  • Switch to building mode at any time.
    • If the user requests to switch to building mode while a flipper is in motion, it is acceptable to delay switching until the flipper has reached the end of its trajectory.
    • Similar short delays in order to finish transitional states of gizmos you create are also acceptable.
  • Press keys, thereby generating triggers that may be connected to the actions of gizmos.
  • Quit the application.

In running mode, Gizmoball should:

  • Provide visually smooth animation of the motion of the ball.
    • The ball by default must have a diameter of approximately 0.5L.
    • Ball velocities must range at least from 0.01 L/sec to 200 L/sec and can cover a larger range if you wish. 0 L/sec (stationary) must also be supported.
    • An acceptable frame rate should be used to generate a smooth animation. We have found that 20 frames per second tends to work well across a reasonably wide range of platforms.
  • Provide intuitively reasonable interactions between the ball and the gizmos in the playing area. That is, the ball should bounce in the direction and with the resulting velocity that you would expect it to bounce in a physical pinball game.
  • Continually modify the velocity of the ball to account for the effects of gravity.
    • You should support the standard gravity value of 25 L/sec2, which resembles a pinball game with a slightly tilted playing surface.
  • Continually modify the velocity of the ball to account for the effects of friction.
    • You should model friction by scaling the velocity of the ball using the frictional constants mu and mu2. For sufficiently small delta_t's you can model friction as Vnew = Vold * (1 - mu * delta_t - mu2 * |Vold| * delta_t).
    • The default value of mu should be 0.025 per second.
    • The default value of mu2 should be 0.025 per L.

Top

Standard Gizmos

There are seven standard gizmos that must be supported: bumpers (square, circular, and triangular), flippers (left and right), absorbers, and outer walls.

A coefficient of reflection of 1.0 means that the energy of the ball leaving the bumper is equal to the energy with which it hit the bumper, but the ball is traveling in a different direction. As an extension, you may support bumpers with coefficients above or below 1.0 as well.

Square Bumper

A square shape with edge length 1L
Trigger: generated whenever the ball hits it
Action: none required
Coefficient of reflection: 1.0

Top

Circular Bumper

A circular shape with diameter 1L
Trigger: generated whenever the ball hits it
Action: none required
Coefficient of reflection: 1.0

Top

Triangular Bumper

A right-triangular shape with sides of length 1L and hypotenuse of length Sqrt(2)L
Trigger: generated whenever the ball hits it
Action: none required
Coefficient of reflection: 1.0

Top

Flipper

A generally rectangular rotating shape with bounding box of size 2Lx2L
Trigger: generated whenever the ball hits it
Action: rotates 90 degrees (see below)
Coefficient of reflection: 0.95 (but see below)

Flippers are required to come in two different varieties, left flippers and right flippers. A left flipper begins its rotation in a counter-clockwise and a right flipper begins its rotation in a clockwise direction.

During run mode, a flipper should never extend outside its bounding box. In edit mode the flipper should not be permitted to be placed in any way which would cause the flipper to extend outside of its bounding box during run mode, or would cause the flipper's bounding box to overlap with (the bounding box of) another gizmo.

The below pictures show flipper placements for various initial rotations. In run-mode, when a flipper is first triggered, it sweeps 90° in the direction indicated by the arrows. If triggered again, the flipper sweeps back 90° to the initial position.

In the pictures, the shape and design of the flippers are for illustrative purpose only -- your final design may differ.


Flipper initial placements and initial directions of rotation.

As with the three standard bumpers, a flipper generates a trigger whenever the ball hits it.

When a flipper's action is triggered, the flipper rotates at a constant angular velocity of 1080 degrees per second to a position 90 degrees away from its starting position. When its action is triggered a second time, the flipper rotates back to its original position at an angular velocity of 1080 degrees per second.

If its action is triggered while the flipper is rotating, the exact behavior is at your discretion. Here are some suggestions, but you are not limited to these options:

  1. Ignore triggers while the flipper is in motion. This behavior may be undesirable for the user because a single press and release of a key might not cause the flipper to return to its original position.
  2. Wait until the flipper finishes rotating (and responding to any previously-received triggers) before responding to the action. This behavior may be undesirable for the user because several quick keypresses in a row could cause the flipper to flip repeatedly for a long period of time.
  3. Queue at most one trigger during the initial forward motion and have no queue during the return motion. With this model, a keypress which generated two triggers would cause the flipper to flip and return, but quick repeated keypresses would not tie up the flipper for a long time.
  4. Respond to all triggers immediately. If a flipper is in a forward motion and is triggered, it will immediately switch to a backward motion. In this way, flippers with a key up and down as triggers will behave most like flippers in a real-world pinball game.

The standard coefficient of reflection for a flipper is 0.95. However, when computing the behavior of a ball bouncing off the flipper, you must account for the linear velocity of the part of the flipper that contacts the ball; therefore the ball may leave the flipper with a higher energy than it had when it reached it.

Top

Absorber

A rectangle with integral-length sides
Trigger: generated whenever the ball hits it
Action: shoots out a stored ball (see below)
Coefficient of reflection: not applicable; the ball is captured

When a ball hits an absorber, the absorber stops the ball and holds it (unmoving) in the bottom right-hand corner of the absorber. The ball's center is .25L from the bottom of the absorber and .25L from the right-hand side of the absorber.

If the absorber is holding a ball, then the action of an absorber, when it is triggered, is to shoot the ball straight upwards in the direction of the top of the playing area. By default, the initial velocity of the ball should be 50L/sec. (With the default gravity and the default values for friction, the value of 50L/sec gives the ball enough energy to lightly collide with the top wall, if the bottom of the absorber is at y=20L.) If the absorber is not holding the ball, or if the previously ejected ball has not yet left the absorber, then the absorber takes no action when it receives a trigger signal.

Absorbers cannot be rotated.

Top

Outer Walls

Impermeable barriers surrounding the playfield.
Trigger: generated whenever the ball hits it
Action: none required
Coefficient of reflection: 1.0

A Gizmoball game supports exactly one set of outer walls. The user cannot move, delete, or rotate the outer walls. The outer walls lie just outside the playing area:

  • There is one horizontal wall just above the y=0L coordinate.
  • There is one horizontal wall just below the y=20L coordinate.
  • There is one vertical wall just to the left of the x=0L coordinate.
  • There is one vertical wall just to the right of the x=20L coordinate.

It is not required that the user be able to use the GUI to connect the trigger produced by the outer walls with any of the other gizmos. However, the standard file format does support this kind of connection.

Top

Appendix 2: The Gizmoball File Format Informal Description

Gizmoball files are command scripts. The syntax is, perhaps, somewhat richer than one might design for a simple game, but is also usable for debugging and testing the program independently of the GUI. That is, the file format specifies a standard interface, so that the TAs can test the functionality of your program. The files contain not just a list of gizmos and their positions, but rather commands for exercising all the Gizmoball functionality, including placing, deleting, and modifying gizmos.

Each line of a Gizmoball file contains a command. Each command consists of an opcode and zero or more arguments; these components are separated by spaces and/or tabs. For example:

Triangle T1 0 7

This is a single command. Its opcode is "Triangle" and its arguments are "T1", "0", and "7". This command specifies that the program should create a new triangular bumper at the location (0,7), and that the triangle can later be referred to in the file by the name "T1". There are similar commands for creating square and circular bumpers and flippers.

The Triangle command places the triangle at a default rotation. If you want the triangle at a different rotation, use the "Rotate" command:

Triangle T2 3 5
Rotate T2
Rotate T2

This creates a triangular bumper at position (3,5) and then rotates it twice by 90 degrees in the clockwise direction, for a total rotation of 180 degrees. Rotating a flipper does not change its bounding box but does change its pivot. Along with the Rotate command, there are also commands for moving gizmos and deleting gizmos. For example:

Square S1 10 2
Square S2 15 15
Move S1 19 17
Delete S2

This creates a square bumper at (10,2) and another at (15,15), moves first bumper to (19,17), and deletes the second, leaving one square bumper at (19,17).

There are also commands for connecting triggers to actions. For example:

Square S3 13 13
LeftFlipper LF1 5 18
Connect S3 LF1

This creates a square bumper at (13,13) and a left-handed flipper at (5,18). The "Connect" command specifies that the flipper's action should be triggered whenever the ball hits the square bumper.

The KeyConnect command specifies that the action of a gizmo is associated with a particular key being pressed or released:

RightFlipper RF1 9 18
KeyConnect key 32 down RF1
KeyConnect key 32 up RF1

This specifies that the right-handed flipper at (9,18) should be activated whenever the space bar key is pressed or released ("32" is the number that the Java™ AWT gives to the space bar).

Because you might also want to allow the outer walls to trigger various actions, there is a special identifier reserved for it:

Connect OuterWalls GIZ

This command would cause the ball hitting any of the outer walls trigger the action of the gizmo named by "GIZ".

The Ball command allows you to specify the position and velocity of the ball. Because the ball can be at intermediate points within a particular square, the coordinates are specified as floating point numbers:

Ball B1 14.2 4.5 -3.4 -2.3

This places a ball with its center at (14.2,4.5) and an initial velocity of 3.4L per second to the left and 2.3L per second upward.

The Gravity and Friction commands can be used to set global properties of the game. Each takes floating point values as arguments such as:

Gravity 16.0 Friction 0.0 0.0

This would reduce the gravity in the game to only 16L/sec2 and remove any effects of friction. Only the last occurrence of each Gravity and Friction in the file should be used.

Here is the Gizmoball file for the example shown above. It specifies a triangular bumper in the upper right-hand corner, a bunch of circular and square bumpers, and a few flippers. The action of the upper flippers is triggered by the "space" key, the action of the lower flippers are triggered by the "q" and "w" keys, and also by hitting some of the circular bumpers. The action of the absorber is triggered both by the "delete" key and also by the absorber itself! This allows the game to run continuously. Every time the ball hits the absorber, the absorber immediately shoots it back up towards the top again.

Triangle T 19 0 Rotate T

Triangle T2 1 1

Square S02 0 2
Square S12 1 2
Square S22 2 2
Square S32 3 2
Square S42 4 2
Square S52 5 2
Square S62 6 2
Square S72 7 2
Square S82 8 2
Square S132 13 2
Square S142 14 2
Square S152 15 2
Square S162 16 2
Square S172 17 2
Square S182 18 2

Circle C43 4 3
Circle C54 5 4
Circle C65 6 5
Circle C76 7 6
Circle C99 9 9
Circle C109 10 9
Circle C1110 11 10
Circle C129 12 9
Circle C139 13 9
Circle C156 15 6
Circle C165 16 5
Circle C174 17 4
Circle C183 18 3


LeftFlipper LF92 9 2
KeyConnect key 32 down LF92
KeyConnect key 32 up LF92 RightFlipper RF112 11 2
KeyConnect key 32 down RF112
KeyConnect key 32 up RF112
LeftFlipper LF87 8 7
KeyConnect key 81 down LF87
KeyConnect key 81 up LF87
Connect C43 LF87
Connect C54 LF87
Connect C65 LF87
Connect C76 LF87
Connect C109 LF87
Connect C1110 LF87
Connect C139 LF87

RightFlipper RF137 13 7
KeyConnect key 87 down RF137
KeyConnect key 87 up RF137
Connect C99 RF137
Connect C1110 RF137
Connect C129 RF137
Connect C156 RF137
Connect C165 RF137
Connect C174 RF137
Connect C183 RF137
Absorber A 0 19 20 20
KeyConnect key 127 down A
Connect A A
Ball B 1.0 11.0 0.0 0.0

Top

Formal Syntax

<file> ::= <commandline>*

<commandline> ::= <command>"\n" | "\n"

<command> ::= <gizmoOp> <name> <int-pair> |
Absorber <name> <int-pair> <int-pair> |
Ball <name> <float-pair> <float-pair> |
Rotate <name> |
Delete <name> |
Move <name> <number-pair> |
Connect <name> <name> |
KeyConnect <keyid> <name> |
Gravity FLOAT |
Friction FLOAT FLOAT

<name> ::= IDENTIFIER

<gizmoOp> ::= Square | Circle | Triangle | RightFlipper | LeftFlipper

<number-pair> ::= <int-pair> | <float-pair>

<int-pair> ::= INTEGER INTEGER

<float-pair> ::= FLOAT FLOAT

<keyid> ::= "key" KEYNUM "down" |
"key" KEYNUM "up"
IDENTIFIER   represents any string composed only from the characters {'0'..'9','A'..'Z','a..z','_'}. The identifier "OuterWalls" is a special reserved word which refers to the outer walls; no other item may use this identifier
INTEGER   represents any integer number
FLOAT   represents any floating point number
KEYNUM   represents any numeric key identifier (which are integers)

Gizmoball keywords are case insensitive.

Top

Semantics

"Square" (IDENTIFIER name) (INTEGER x) (INTEGER y)
"Circle" (IDENTIFIER name) (INTEGER x) (INTEGER y)
"Triangle" (IDENTIFIER name) (INTEGER x) (INTEGER y)
"RightFlipper" (IDENTIFIER name) (INTEGER x) (INTEGER y)
"LeftFlipper " (IDENTIFIER name) (INTEGER x) (INTEGER y)
Creates the given gizmo with its upper left-corner at (x,y), in the default orientation. Within the file, the name must be unique, and may be used later to refer to this specific gizmo. The default orientation for each gizmo is:
Square
none (all orientations are equivalent)
Circle
none (all orientations are equivalent)
Triangle
One corner in the north-east, one corner in the north-west, and the last corner in the south-west. The diagonal goes from the south-west corner to north-east corner.
LeftFlipper
pivot in north-west corner, other end in south-west corner
RightFlipper
pivot in north-east corner, other end in south-east corner
"Absorber" (IDENTIFIER name) (INTEGER x1) (INTEGER y1) (INTEGER x2) (INTEGER y2)
Creates an absorber with its upper left-hand corner at (x1,y1) and its lower right-hand corner at (x2,y2). The second position must be at least 1L to the right of and at least 1L below the first position. Within the file, the name must be unique, and may be used later to refer to this specific absorber.
"Ball" (IDENTIFIER name) (FLOAT x) (FLOAT y) (FLOAT vx) (FLOAT vy)
Creates a ball whose center is (x,y) and whose velocity is (vx,vy). Within the file, the name must be unique, and may be used later to refer to this specific ball.
"Rotate" (IDENTIFIER name)
Performs a 90 degree clockwise rotation on the item named name. Note that some items (like the absorber, outer walls, or balls) can not be rotated. Rotating a flipper does not change its bounding box but does change its pivot.
"Delete" (IDENTIFIER name)
Deletes the item named name. After this operation, the item will no longer exist.
"Move" (IDENTIFIER name) (INTEGER x) (INTEGER y)
"Move" (IDENTIFIER name) (FLOAT x) (FLOAT y)
In the first form, moves the gizmo with the given name so that its upper-left corner is at (x,y). In the second form, moves the ball with the given name so that its center is at (x,y).
"Connect" (IDENTIFIER producer) (IDENTIFIER consumer)
Makes the gizmo named by consumer a consumer of the triggers produced by the gizmo described by producer. That is, every time a ball hits the producer, the consumer's action will happen.
"KeyConnect" "key" (KEYNUM num) "down" (IDENTIFIER consumer)
"KeyConnect" "key" (KEYNUM num) "up" (IDENTIFIER consumer)
Makes the item named by consumer a consumer of the trigger produced when the key represented by num is pressed (or released, respectively).
"Gravity" (FLOAT g)
Changes the gravity of the board to be gL/sec2 in the downward direction. This command overrides any previous setting of gravity. If no Gravity command appears in the file the default value should be used.
"Friction" (FLOAT mu) (FLOAT mu2)
Changes the global friction constants to be mu and mu2 as described in the friction formula. This command overrides any previous setting of friction. If no Friction command appears in the file the default values should be used.

Top

Appendix 3: The Physics Package

The provided physics library consists of immutable abstract data types such as AngleVectLineSegment, and Circle, as well as a class Geometry that contains static methods to model the physics of elastic collisions between balls and other circles and line segments. You are welcome to use or not use this code as you please, and to modify it to meet your needs.

Documentation for the physics package can be found in documentation-code/generated-documentation/physics.

Byte-compiled .class files are stored in /mit/6.170/lib/gb-lib.jar, which should automatically be on your classpath.

Source for the physics library can be found in Java-source-code/physics (ZIP). While this source is provided in the event that you wish to examine or modify it, we strongly discourage you from the latter. In the past, students who have not used the physics library as-is have had poor results on their projects. Most groups will not need to copy the source code to their own directories, add it to their CVS repositories, or compile it, but will just use the gb-lib.jar file and examine the specifications.

Top

Change Log

This section will detail the changes made during revisions of this handout. You may use the version number at the bottom of your hardcopy to determine what has changed since you printed this handout.

  • v. 11/02/2001:
    • Fix to Angle.equals() in Physics package. Introduced a tolerance of EPSILON to the equals() comparison.