GCPP Neverbite : Implementation Notes

The aim of this page is to explain the programming ideas applied to the GCPP Neverbite Games Gardens puzzle. This comes in complement to the comments in the source code, that can be downloaded calling

cvs -d :pserver:anonymous@skovran.com:/usr/local/cvsroot co neverbite

This puzzle was programmed according to a design for the Grand Crafting Puzzle Project.

Java Source Compatibility Level
A note on Java 5 : the Eclipe versions that support Java 5 code parsing, refactoring, etc. don't run on the (prehistorical) operating system used for developping the game. Thus, none of the nice features of Java 5 are used - for example, the 'invoke' method uses Object[] rather than varargs.

Board setup
The puzzle consists in a board with 6 vats, 6 patterns, two rails and next piece previews. In a AWT/Swing environment, it is elegant to represent each entity with a different Component object, and build a hierarchy where each Component is added and placed in the Container, much like buttons are added to a dialog window.

However, the MediaPanel implementation does not allow for child components, except sprites and animations. Two new classes, MediaComponent and MediaContainer implement this concept:
 * MediaContainer extends MediaPanel, redefining the paintBehind method for painting its children.
 * MediaContainer.PaintableComponent is a static interface defined for components that can be added to a MediaContainer.
 * MediaComponent is a Swing JComponent that also implements FrameParticipant and MediaContainer.PaintableComponent.

The NeverbiteBlockView and NeverbiteProgressView classes both extend MediaComponent, implementing paintDirtyRect. The NeverbiteBoardView extends MediaContainer, and defines several child components. These new MediaContainer and MediaComponent classes are generic and can be reused whenever needed.

Generic Block Drawing
Several elements on the board are rectangular blocks of pieces. A generic class, NeverbiteBlockView, takes over this job, working for blocks of 2 pieces, vats of up to 12 pieces or patterns of 12 smaller pieces. A block simply has to implement the NeverbiteBlock interface in order to be drawn by a NeverbiteBlockView. Thus, "Block"s of 2 pieces implement NeverbiteBlock, as well as NeverbiteVat's of up to 12 pieces.

Positioning
Due to the fact that the hook can hold either horizontal or vertial block, and that valid position are only above a vat (and, for example for a horizontal block, not overlapping with two vats), describing the position of a hook is rather complex. All this magic has been hidden in a handy class, NeverbiteHookPosition, that features:
 * converting a hook position from a pixel position on the board, for creating a hook position from a Mouse position,
 * converting a hook position to a pixel position on the board, for positioning the sprites,
 * creating the next position one tick left or right, for keyboard input,
 * getting the vat and the column within the vat, to know where the block will fall.

Smooth moves
The puzzle is supposed to represent ironmongery in action, that is, mechanical movements. The player shall understand and follow the moves of the hook, for example when it switches rails, or when it grabs a new block. Therefore, the hook has to move smoothly on the board, it shall not pop up suddenly at a new location.

Following the common libraries design pattern, the NeverbiteBoardView handles the graphical part of the job. The main interface method is "moveHook", that makes the NeverbiteBoardView move the hook from one position to the other. As an argument, in addition to the starting and end position of the block, it takes an observer that will be noticed at the end of the move.

This observer is the key to the synchronization between the animations. For example, whenever a block is dumped, the Controller requests the BoardView to move the hook out of the view with the block to be dumped. The observer it gives will be noticed once this is achieved, so that the next move is scheduled right afterwards, moving the new block back into the view.

In some cases, both the top rail and bottom rail hooks move at the same time, and the move can be considered as finished only when both are done. For this case, an additional level is introduced : a "LastMoveObserver" is defined, waiting for several moves to be finished before noticing the end of the animation.

A consequence of smoothing the moves
Due to the fact that the hooks don't move instantaneously from one place to the other, the user has time to continue to move the mouse or give keyboard inputs while the hook is moving. In order to give the best playability, these inputs shall not be ignored.

To cope with this situation, incoming actions are stored in an event queue, and handled in sequence. Mouse keyboard and GUI inputs, are all handled by the NeverbiteControllerDelegate class, that transforms them into events and posts them to an event handler in the NeverbiteController. There are 5 types of events:
 * moving the hook from one position to another,
 * dropping the block currently hold by the active hook,
 * dumping the block currently hold by the active hook,
 * grabbing a new block (the hook moves off-board and back with the new block),
 * switching blocks (which is just another type of move).

The event queue is managed by the EventManager that resides inside the NeverbiteController. A special class has been defined, NeverbiteEvent, that has basic support for coalescing events.

Coalescing is used for example in the case of two move events added to the queue one after the other. Rather than moving to one position, then to the other, the hook shall move directly to the target position: the two events shall be coalesced into one.

Reactivity
Unlike strategy games where every move is exchanged only once a turn, and response time is not a problem, GCPP Neverbite is a single player puzzle. Players expect a fast feedback to their actions. From a computer in France, the round-trip time to the Game Gardens server is 200ms. This is already a sensible delay, but (maybe due to the TCP implementation) the response time in some games goes sometimes up to several seconds. The goal was to give the best possible reactivity, while keeping the Controller - Manager design pattern common to Game Gardens games.

To achieve this, the user input events are immediately handled at the active player's client, without Manager feedback: this ensures that the players has an immediate reaction to his inputs. The events are also sent to the Manager (the same event class is used as for the controller event queues) However, the move events are filtered, in order not to send too many messages over the network.

The manager can check the moves legacy, does the scoring, and keeps the game status up to date for watchers. These events are forwarded by the manager to each watcher through the game object field 'lastEvent' (the active player will ignore them, since they are a mirror of what it has sent).

The manager is still responsible for choosing the next blocks, and next patterns to be matched, which are sent over through the game object.

Conclusion
The aspirations presented here are interesting and shall contribute to its playability. However, they are also challenging, and make the implementation more complex. Several problems are still remaining in the current version, that would not have appeared in the case of a straightforward implementation...