Introduction
I present two simple versions of the well-known brick game for Pocket PC. In this article, I'll present some techniques used to develop the game, also providing the source code. I hope the reader enjoys it, because for me it was a real treat.
The first version is the classic one, with the colored blocks falling from the top. With the second version, the pieces are composed of balloons instead of bricks, and the user has to correctly place them in lines on the top of the screen as the balloons go up.
I should say that I was motivated mainly by the fabulous work of Jonas Follesø in his article Pocket 1945 - A C# .NET CF Shooter. That article was uploaded to The Code Project in 2004, and is still impressive (and will continue to be impressive for many years). So, after reading the "Pocket 1945" article, I decided to write a game myself, and I ended up borrowing some of the concepts Jonas used in his game.
Background
The first brick game was programmed in 1985 by Alexey Pajitnov while he was working for the Dorodnicyn Computing Centre of the Academy of Science of the USSR in Moscow (thanks, Wikipedia). The game was virtually ported to every known platform, and was even played with human beings instead of the traditional colored bricks (see video).
The Game
Basically, Bricks! is a never-ending puzzle of 4-segment pieces. There are 7 types of pieces (that I call "I", "J", "L", "O", "S", "T", and "Z" in the game), and only one piece falls at a time from the top of the game board. The type of the tetromino is produced randomly. Every time the player fills a row in the board, that row is removed and the bricks above collapse, making room for more pieces to go.
Every time a row is completed, the player scores 10 points, multiplied by the number of the game level. A new level is reached every time the player completes 10 lines.
Application Design
The solution comprises of two projects: Bricks4Pocket and Bricks4Pocket.Core. The first basically contains the UI components, while the second contains the hard-work functionalities. I believe that just a few classes from the project are worth mentioning here.
I decided to use a simplified MVP (Model-View-Presenter) pattern in the game. In doing so, I could enforce the separation of concerns by assuring that the UI component would only respond passively to the events thrown by the Presenter component.
The Code
The game has two main entities: the shape (BaseShape
-inherited classes) and the board (the BricksBoard
class). Both inherit from the BaseBricksArray
abstract class that implements a bi-dimensional array of bricks (Brick
class).
protected IBrick[,] shapeArray = null;
The heart of the game is the BricksBoard
class. It contains a 10 x 16 array of bricks, and also the game logic functionalities such as moving and rotating a piece according to the arrow buttons, testing whether a piece can still be moved, deciding if there are completed rows that must be removed, and deciding when the game is over.
As soon as the game starts, the GetRandomShape
is invoked, and a shape of a random type and a random color is created:
private IShape GetRandomShape()
{
IShape newShape = null;
Random randomClass = new Random();
int randomCode = randomClass.Next((int)ShapeCodes.Stick,
(int)ShapeCodes.Z + 1);
.
.
.
Before every move attempt, the BricksBoard
class will invoke the TestPieceOnPosition
method, to assure that the shape will not go off board and will not collide with other bricks in the board.
public bool TestPieceOnPosition(IShape shape, int x, int y)
{
for (int row = 0; row < shape.Height; row++)
{
for (int column = 0; column < shape.Width; column++)
{
if (column + x < 0)
return false;
if (row + y < 0)
return false;
if (column + x >= width)
return false;
if (row + y >= height)
return false;
if (
shapeArray[column + x, row + y] != null &&
shape.ShapeArray[column, row] != null)
{
return false;
}
}
}
return true;
}
If the piece can be placed according to the move attempt, then the method PutPieceOnPosition
is invoked in order to transfer the bricks from the shape to the board.
The MoveLeft
and MoveRight
methods of the BaseShape
class just move the bricks of the shape to the left or to the right, if possible:
public bool MoveLeft()
{
bool test = false;
if (!anchored)
{
if (containerBoard == null)
throw new NullContainerBoardException();
containerBoard.RemovePieceFromCurrentPosition(this);
test = containerBoard.TestPieceOnPosition(this, this.X - 1, this.Y);
if (test)
{
containerBoard.RemovePieceFromCurrentPosition(this);
containerBoard.PutPieceOnPosition(this, this.X - 1, this.Y);
}
}
return test;
}
The Rotate90
and Rotate270
methods implement the clockwise and counterclockwise rotations for a shape, respectively. This is done by transposing the bricks from the columns to the rows of the shape, before trying to put the shape on the board:
public bool Rotate270()
{
bool test = false;
if (!anchored)
{
if (containerBoard == null)
throw new NullContainerBoardException();
containerBoard.RemovePieceFromCurrentPosition(this);
IBrick[,] newShapeArray = new IBrick[height, width];
IBrick[,] oldShapeArray = new IBrick[width, height];
for (int row = 0; row < height; row++)
{
for (int column = 0; column < width; column++)
{
newShapeArray[row, width - column - 1] = shapeArray[column, row];
oldShapeArray[column, row] = shapeArray[column, row];
}
}
.
.
.
}
For the key-handling capabilities, I merely copied the Input
class from Jonas Follesø's article. Besides, I created a InputBuffer
class that encapsulates the Input
class. The InputBuffer
class has the ability to process buffered keystrokes, and also allows that views can subscribe to the utility class. I do this by implementing the Observer Pattern in the InputBuffer
class and in the subscriber view:
public void AddObserver(IInputObserver observer)
{
observers.Add(observer);
}
History
- 2008-04-24: Initial posting.
- 2008-05-01: Code and article renaming; Input problem solved; New game Ballons! added.