JGameGrid
 
 

The source code of all applications is included in the JGameGrid distribution.

 

Tetris
is one of the most famous computer games on the world. It has been invented by the Russian computer engineer Alexey Pajitnov (Алексей Леонидович Пажитнов) back in 1984 and implemented on nearly every computer platform. The game design is very simple: Seven different shapes, called tetrominoes, composed of four square blocks fall down in a rectangle grid one-by-one in a random sequence. During their vertical motion they may be pushed to the left or right, and rotated by 90 degrees by hitting the cursor-left, cursor-right and cursor-up key. The cursor-down key drops the tetromino rapidly to speed-up the game. Whenever a line is filled with blocks, the line is erased, all blocks above are moved one line down and the score is increased by one. When new tetrominoes cannot be added because the grid is fill to the top, the game is over.
[Ref. http://en.wikipedia.org/wiki/Tetris]

The game provides a wonderful ambiance for teaching OOP: it is by far not trivial to by implemented in a classical programming environment. Using a OOP-based game library like JGameGrid simplifies the game construction heavily and let point out important features of programming instead of struggling with implementation details.

The class design is ruther obvious: A tetromino is a instance of the class Tetromino that encapsulates the principal behaviour of the game figures. The seven special shapes called I, J, ...., are modeled by subclassing the Tetromino class. Because there is no instance of the superclass, Tetromino is declared abstract. A tetromino occupies for grid cells; these parts are modeled by the class TetroBlock and a tetromino contains an ArrayList of four TetroBlock instances. Each TetroBlock contains an array of 4 locations relative to its tetromino for the rotated positions 0, 90, 180 and 270 degrees. It defines the sprite image used for its tetromino, labeled tetroblock0.gif to tetroblock6.gif.

// TetroBlock.java

import
 ch.aplu.jgamegrid.*;

public
 class TetroBlock extends Actor
{
  
private Location[] relLoc = new Location[4];

  
public TetroBlock(int blockId, Location[] relLoc)
  
{
    
super("sprites/tetroblock" + blockId + ".gif");
    
this.relLoc = relLoc.clone();
  
}

  
public Location getRelLoc(int rotId)
  
{
    
return relLoc[rotId];
  
}
}

Each concrete type of tetrominos I, J, L, O, S, T and Z contains a Location matrix that defines the relative location of its 4 blocks in the 4 rotated positions, e.g. for the class I:

// I.java

import
 ch.aplu.jgamegrid.*;

class
 I extends Tetromino
{
  
private final int blockId = 0;
  
private Location[][] r = new Location[4][4];

  
I(Tetris tetris)
  
{
    
super(tetris);
    
// rotId 0
    r
[0][0] = new Location(new Location(-1, 0));
    r
[1][0] = new Location(new Location(0, 0));
    r
[2][0] = new Location(new Location(1, 0));
    r
[3][0] = new Location(new Location(2, 0));
    
// rotId 1
    r
[0][1] = new Location(new Location(0, -1));
    r
[1][1] = new Location(new Location(0, 0));
    r
[2][1] = new Location(new Location(0, 1));
    r
[3][1] = new Location(new Location(0, 2));
    
// rotId 2
    r
[0][2] = new Location(new Location(-1, 0));
    r
[1][2] = new Location(new Location(0, 0));
    r
[2][2] = new Location(new Location(1, 0));
    r
[3][2] = new Location(new Location(2, 0));
    
// rotId 3
    r
[0][3] = new Location(new Location(0, -1));
    r
[1][3] = new Location(new Location(0, 0));
    r
[2][3] = new Location(new Location(0, 1));
    r
[3][3] = new Location(new Location(0, 2));

    
for (int i = 0; i < r.length; i++)
      blocks.
add(new TetroBlock(blockId, r[i]));
  
}
}

 

This data structure design is somewhat tedious, but avoids the the need of calculating the rotated positions of the tetrominos. Both classes, Tetromino and TetroBlock are derived from Actor, but a tetromino has no associated sprite image. The falling movement of tetrominos is implemented in Tetromino.act(). Care must be taken to stop falling when another tetromino is hit or the bottom is reached. We also must prevent a move outside the game grid. In advance() we test if the next move is valid:

private boolean advance()
{
  
boolean canMove = true;
  
for (TetroBlock a : blocks)
  
{
    
if (!gameGrid.isInGrid(a.getNextMoveLocation()))
    
{
      canMove 
= false;
      
break;
    
}
  
}

  
for (TetroBlock a : blocks)
  
{
    TetroBlock block 
=
      
(TetroBlock)(gameGrid.getOneActorAt(a.getNextMoveLocation(),
      TetroBlock.
class));
    
if (block != null && !blocks.contains(block))
    
{
      canMove 
= false;
      
break;
    
}
  
}

  
if (canMove)
  
{
    
move();
    
return true;
  
}
  
return false;
}

Because we want a graphical user interface, the application class Tetris is not derived from GameGrid and therefore GameGrid's act() cannot be overridden. In order to get act events in the application class we must implement an ActListener where we implement the keyboard actions.

private class MyActListener implements GGActListener
{
  
public void act()
  
{
    
removeFilledLine();

    
switch (gameGrid1.getKeyCode())
    
{
      
case KeyEvent.VK_UP:
        tetromino.
rotate();
        
break;
      
case KeyEvent.VK_LEFT:
        tetromino.
left();
        
break;
      
case KeyEvent.VK_RIGHT:
        tetromino.
right();
        
break;
      
case KeyEvent.VK_DOWN:
        tetromino.
drop();
        
break;
      
default:
        
return;
    
}
  
}
}
 

The application class Tetris is written using the NetBeans GUI builder. The GameGrid bean is added using the palette manager. Actually two GameGrid instances are used, one for the game window and one for the preview of the next tetromino.

tetrispalette


Using a GUI builder, the layout is very flexible and the application has a professional look-and-feel. You may study the full source code to inform you about implementation details.

Tetris

Execute the program locally using WebStart.