Statement
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
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>
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.
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.
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.
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.