Statement

gg2.gif (611 bytes) Brief Description

This is a Tetris game. 
    To start a new game, click "START" button. 
    To stop a new game, click "STOP" button. 
    To pause/resume a new game, click "PAUSE" button. 
    Using arrow keys to control the movement of the dropping figure:

    LEFT moving left             UP clockwise-rotating figure 
    RIGHT moving right         DOWN speeding up the dropping 

There are 12 levels altogether, with gradually faster dropping speeds respectively.
    Gaining every 5000 points advances the player to the next level. And,
    Points for landing a figure: 10 
    Points for filling up one row: 500
    Points for filling up four rows in a time: 3000


gg2.gif (611 bytes) Complete Instructions for compiling and running the program

Unzip all the files in Tetris.jar into one directory. If you want to recompile it, you can delete all the class files, and run "javac Tetris.java" to regenerate all those class files. 
Make sure that Tetris.jar and other image files are present in the same directory with the HTML file. 
Embed the following code in your HTML file:
<applet Archive="Tetris.jar" Code="Tetris.class" Width="190" Height="424">
Cannot play game because your browser either doesn't support JAVA or JAVA isn't enabled. 
</applet>



gg2.gif (611 bytes) Java Features Summarization

Use java.awt.Color.darker( ) to get a better 3D visual effect of dropping figures.

Use java.awt.Graphics.dispose( ) to dispose the graphics object and release any system resources that is using. When a Java program runs, a large number of Graphics objects can be created within a short time frame. Although the finalization process of the garbage collector also disposes of the same system resources, it is preferable to manually free the associated resources by calling this method rather than to rely on a finalization process which may not run to completion for a long period of time. 

Use GUI controllers, like canvas, panel, label, button.
(I tried to use imagebutton/icon in SWING, but it didn't work. :( )

Use even handler, e.g. KeyListener in class PlayingField, which is an extension of Canvas component. 
Its responsibilities are:
(1) Listen to keyPressed event and carry out one of the following actions depending on a key: 
    a. Move figure to left or right
    b. Rotate the figure
    c. Drop the figure
(2) Track filled cells and rows. If a whole row has been filled up, eliminate it.
(3) Check that the height of the pile does not exceed the limit.
(4) Check when a figure has hit the bottom.

PlayingFieldKeyListener is also added to the START and PAUSE buttons, so that when the GUI focus is on these buttons, the KeyListener will still work.


gg2.gif (611 bytes) Design Patterns Used 

SINGLETON
Using SINGLETON for class Field, which is the baseclass of PlayingField, in order to ensure there is only one instance of Field, also provide a global point of access to it.

FACTORY METHOD
1)class ShapeFactory is used as a parameterized FACTORY METHOD. By overriding this parameterized factory method, you can easily and selectively extend or change the shape object that Build() produces. You can introduce new identifiers for new kinds of Shapes, or you can associate existing identifiers with different shape objects.

2)class ShapeColorFactory is used as a parameterized FACTORY METHOD. Create() produces different colors according to the shapeNumber, which is assigned when the object of ShapeColorFactory is instantiated. You can introduce new identifiers for new color of Shapes, or you can associate existing color with different shape objects.

TEMPLATE METHOD
Using TEMPLATE METHOD for class MoveController, which is the baseclass of LeftKeyController, RightKeyController, UpKeyController and DropController. For handling any movement, it has to perform the following actions:
    erase();
    action();
    draw();

although different event has different action(), the three steps have to be done in the same order, that's why TEMPLATE METHOD is used here.

STRATEGY
Using STRATEGY for class LowestCell, whose subclasses implement the concrete algorithms of calculating the position of the LowestCell of different shapes.

VISITOR
Using VISITOR for class FillingCells. There are four subclasses (FillingCellsat12, FillingCellsat9, FillingCellsat6, FillingCellsat3) calculating the four positions for each shape object whenever the figure is rotated.


gg2.gif (611 bytes) Benefits and Drawbacks 

BENIFITS
Using design patterns to implement the traditional Tetris makes it easier to reuse the application architecture. The system becomes more flexible and extensible, say, if we want 
to change the shape or color of an existing figure; or add as many new colors/shapes as we want; 
or randomly assign a color to a shape object when it is initiated (so that we may have colorful cubes, instead of always having red cube and greed stick); 
or change the algorithms for calculating positions of objects; 
or create different events handler (other than the KeyListeners);
... ...

DRAWBACKS
Without design patterns, Tetris game can be implemented with 15 classes, while it creates 37 classes after I used design patters. It complicated the relationship between classes, and made the code lass understandable for other readers (even though I've commented the functionalities in detail). Another side effect is, more classes mean more objects will be generated at run time, which consume more memory resources to some degree and will slightly slow down the running speed.

Also, I currently put all the classes in one directory, which is not desirable for a professional implementation of design patterns. I will package them up separately in the future version.


gg2.gif (611 bytes) For people who want to reuse my code

There are three hookmethods (placeholders) in class Shape, which provides basic functionality for all shapes derived from it (such as, TShape, LShape, StickShape etc... ). 

    public abstract Color getShapeColor( );
    // You need to modify the ShapeColorFactory to change or add new colors

    public abstract Cells getLowestCell( );
    // You need to create new subclasses of LowestCell for new shapes or modify its current subclass

    public abstract FilledCells getFilledCells( );
    // Since VISITOR pattern is used for FillingCells, you need to add concrete algorithms in the four subclasses (FillingCellsat12, FillingCellsat9, FillingCellsat6, FillingCellsat3) whenever a new shape is added.

To define more different shapes, just remember to: 
    (1) Write your own class that extends Shape.
    (2) Make sure that your custom class defines the above three hookmethods in Shape class. 
    (3) In the pickShape( ) method of the PlayingField class, add the name of your custom class to the shapes[ ] array.