6.031 — Software Construction
Fall 2017

Flingball Specification (Phase 2)

All the requirements of the phase 1 specification still apply to phase 2. If there is any conflict between the phase 1 spec and the phase 2 spec, then the phase 2 spec takes precedence.

Flingball Physics

Ball-ball collisions. Balls that collide with each other must interact in a perfectly elastic collision (i.e., coefficient of reflection 1.0).

Keyboard Control

In addition to gadgets triggering other gadgets (as in phase 1), the user should now be able to trigger gadgets using keyboard keys. In a board file, the new keydown and keyup commands specify that the action of a named gadget is associated with a particular key being pressed or released. For example:

keyup key=space action=Abs

specifies that the gadget named Abs should be activated whenever the space bar key is released.

Formally, the file format commands are:

keydown key=KEY action=NAME
keyup key=KEY action=NAME

where

KEY ::=   [a-z] 
        | [0-9]
        | shift | ctrl | alt | meta
        | space
        | left | right | up | down
        | minus | equals | backspace
        | openbracket | closebracket | backslash
        | semicolon | quote | enter
        | comma | period | slash

These key names correspond to physical keys on the standard keyboard. We are using a subset that is shared by all three major platforms, Windows, Mac, and Unix. Because we only need to name physical keys, we don’t have separate names for other characters that share the same physical key, like colon or question mark. The chosen names closely correspond to the names of the VK_ constants in KeyEvent, which are integer values returned by KeyEvent.getKeyCode() method. For your convenience, we provide a small test program that maps the integer values to their names in the board file format: KeyNames.java.

Note also that these key names do not distinguish between the left shift key and the right shift key. Both are treated as the same key.

On Linux platforms, Java has a known bug where holding down a key produces repeated keyPressed/keyReleased events. You will be able to see this behavior when you run the KeyNames program. If your platform experiences this problem, you have two workarounds. One workaround is to turn off automatic key repeat globally for all programs, using the command xset -r. To reenable autorepeat, use xset r. The second workaround is to wrap your KeyListener with a class that we provide, MagicKeyListener.java. The KeyNames test program also takes an optional command-line argument, --magic, that uses this wrapper class so you can see how the workaround works, and you can look at the source code of KeyNames to see how to use it.

Client-Server Play

Flingball also has a client-server mode. In this mode, each client simulates its own board. The server joins the outer walls of clients together, so that a ball exiting one client’s playing area can enter another client’s playing area.

The server has a set of connected clients. Each client manages a single 20x20 Flingball board. The server keeps track of how the outer walls of these boards are joined together, so that balls can pass between the boards.

A board can have a name, specified in the board line at the top of its board file. Only named boards can be joined with other boards. Clients who connect to the server while running an unnamed board can’t participate in joins with other clients. If two or more clients are running a board with the same name, the behavior is unspecified.

The server is configured by a command-line interface read from System.in (typed by a user). Each command is on one line, and whitespace at the start or end of lines is irrelevant. Extra whitespace between tokens of a line are not important. Lines that do not match commands should report an error message.

Two boards can be joined side-by-side using the following command:

h NAME_left NAME_right

The effect of this command is to join NAME_left’s right wall with NAME_right’s left wall. NAME_left and NAME_right are both board names, which follow the same syntax allowed for NAME in the board file.

Two boards can be joined top-and-bottom using the following command:

v NAME_top NAME_bottom

The effect of this command is to join NAME_top’s bottom wall with NAME_bottom’s top wall.

When two walls are joined, both walls become invisible, permeable to balls. A ball arriving on one side of the joined wall, on one client, is immediately transported to the corresponding position on the other side, on the other client. It maintains the same velocity vector, but now obeys the gravity and friction of the new board it has entered.

Boards can be joined in arbitrary topologies, even if they can’t exist in two-dimensional Euclidean space. For example, a board’s own walls can be joined together, forming a torus.

If one of the walls involved in a join command was previously joined to a different board, then the previous join is broken, reverting the previous board’s wall to solid.

When a client disconnects from the server, any boards joined to it revert to solid walls that reflect balls instead of transporting them. The server forgets the lost client’s joins, so if a client reconnects with the same board name, it does not automatically regain its joined walls, but must be rejoined with fresh h and v commands to the server.

When a client disconnects, any balls on the client’s board are lost.

Note that this section does not specify the network protocol between client and server. The commands in this section are not sent over the network, but are instead typed by a user on the console, to instruct the server how to join together clients that are currently connected to it. The network protocol is different. You will have to define your own network protocol that specifies how a client connects to the server, tells the server what it needs to know about its board, and sends and receives balls through the server.

Your graphical display should show to the user which walls are joined with other boards, by displaying the other board’s name (or as much as fits) in the joined wall. For example, a board that is joined at the top to a board named Mars and on the left to a board named Mercury might look like this (sketched here as ASCII art, but yours will be drawn using Java Graphics):

..........Mars........
.    #####           .
.                    .
.                    .
.                    .
.                    .
.                    .
M                    .
e                   \.
r                    .
c                    .
u           *        .
r   O          O     .
y                    .
.                    .
.                    . 
.    |       --      . 
.    |               .
.                    .
.                    .
.====================.
......................

The exact position and content of the board labels is left unspecified, but they should be readable to a human being.

New Gadgets

In addition to the gadgets of phase 1, you should support the following new gadgets.

Flipper

Size and shape
generally-rectangular rotating shape with bounding box of size 2L × 2L
Orientation
for a left flipper, the default orientation (0 degrees) places the flipper’s pivot point in the northwest corner. For a right flipper, the default orientation puts the pivot point in the northeast corner.
Coefficient of reflection
0.95 (but see below)
Trigger
generated whenever the ball hits it
Action
rotates 90 degrees, as described below

File format commands

    rightFlipper name=NAME x=INTEGER y=INTEGER (orientation=0|90|180|270)?
    leftFlipper name=NAME x=INTEGER y=INTEGER (orientation=0|90|180|270)?

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

A flipper should never extend outside its bounding box at any time, either while rotating or while at rest. The pictures below show flipper placements for various initial rotations. When a flipper is first triggered, it sweeps 90 degrees in the direction indicated by the arrows. If triggered again, the flipper sweeps back 90 degrees 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 positions

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.

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.

Portal

Size and shape
a circular hole with diameter 1L which teleports a ball to another portal gadget, possibly on a different board.
Orientation
not applicable (symmetric to 90 degree rotations)
Coefficient of reflection
not applicable (the ball either passes the portal unaffected, or the ball is teleported)
Trigger
generated whenever the ball hits it
Action
none

File format commands

    portal name=NAME x=INTEGER y=INTEGER (otherBoard=NAME)? otherPortal=NAME

This command creates a portal with the given name and coordinates, which we’ll call the source portal for the purpose of this paragraph. The command also names another portal using otherPortal and (optionally) otherBoard, which we’ll call the target portal. When a ball collides with the source portal, then the ball is immediately teleported to the target portal, exiting the target portal with the same velocity vector with which it entered the source portal.
If the target portal does not exist (e.g., otherPortal is not found on the board, or otherBoard is not currently the name of a board connected to the server), then the ball passes unaffected over the source portal, without teleporting or reflecting.

Portals do not have to be symmetrically connected. For example:

    portal name=Alpha x=5 y=7 otherPortal=Beta
    portal name=Beta x=15 y=7 otherBoard=Mercury otherPortal=Gamma

A ball falling into Alpha will exit from Beta on the same board, but a ball falling into Beta will exit on Gamma on the board named Mercury.

If another gadget is using a portal as as a trigger to fire its action, then the trigger fires whenever a ball strikes the portal, whether or not the ball actually teleports.

Portals should look visibly different from circle bumpers.

Main Programs

Client. The Flingball client should have its main method in a class called Flingball.java. Its package name is up to you, but use a sensible package structure so that your TA can find your client easily. The Flingball client is started with command-line arguments as follows:

Usage:

Flingball [--host HOST] [--port PORT] [FILE]

HOST is an optional hostname or IP address of the server to connect to. If no HOST is provided, then the client runs in single-machine play mode, running a board and allowing the user to play it without any network connection to any other board.

PORT is an optional integer in the range 0 to 65535 inclusive, specifying the port where the server is listening for incoming connections. The default port is 10987.

FILE is an optional argument specifying a file pathname of the Flingball board that this client should run. If FILE is not provided, then your Flingball client should run the default benchmark board as described in the phase 1 specification.

Server. Your Flingball server should have its main method in a class called FlingballServer.java. Again, the package structure is up to you, but make it sensible so that your TA can find the server easily. The Flingball server should be started with command-line arguments as follows:

Usage:

FlingballServer [--port PORT]

Square brackets mean that the arguments are optional. PORT is an integer in the range 0 to 65535 inclusive, specifying the port where the server should listen for incoming connections. If this argument is not given, then the default port is 10987.

Recall that there are several ways to run a Java program with command-line arguments.

Example Boards

Several sample board files have been provided. Refer to the spec above to understand what they should do.