I’ve always wanted to code the Tetris, but as time goes on,this game looks a bit old fashion. Especially on those new computers with all bells and whistles, but this led me to idea of writing Tetris for .NET Micro Framework. It will be cool sample application and discovery of WPF possibilities in Micro Framework. Video shows more..

Two dimensional arrays in Micro Framework

Probably the biggest glitch with writing Tetris, was a fact that Micro Framework supports only one dimensional arrays. Since Tetris is about grid with falling ‘grid-blocks’, the two dimensional arrays are quite essential.

First step was to write ByteMatrix class which represents matrix of byte value (two dimensional byte array). Constructor of the class takes Rows and Columns argument to initiate the matrix size. Internally, values are stored in one dimensional array accessed by the GetCell(int row, int column) and SetCell(int row, int column, byte value) methods, that evaluates the appropriate index in array.

public class ByteMatrix
{
    private byte[] _baseArray;
    private int _rows, _columns;
    
    public ByteMatrix(int rows, int span class="k">private )    
    public ByteMatrix(ByteMatrix sourceMatrix)
    public byte GetCell(int row, int span cass="k">private )    
    public void SetCell(int row, int span cass="k">private , byte value)
    public int lass="k">private (int row, int span cass="k">private , byte[] inputArray)
    public int private ()

    #region Properties
    public int Rows
    public int Cpan class="k"
    public int Length
    public byte[] BaseArray
    #endregion
}

Tetris based on .NET Micro Framework 500x211 Tetris game running on the Tahoe boar="kem>

Separating logic and presentation layer

It’s always better to separate business logic of the application and presentation layer. The code becomes better portable and maintainable as wn"> as unit test of the logic are much easier to implement when UI is not considered. Tetris application has two namespaces GameLogic and Presentation. First one contains all classes for core game. These classes can be easily ported to .NET Compact Framework or full Framework because has no direct relation to Micro Framework. On the other hand the Presentation namespace consist of WPF controls and windows that are used to visualize the game and provides interaction with user. The base of the game is the GameUniverse class which keeps game grid, current falling block and game statistics (score, level, etc.). GameUniverse can be initialized by Init() method, that clears the game grid and game statistics. Starting game in GameUniverse is done by StartLevel(int level). Argument specifies level where game will start. StepUniverse() method runs one game step: moving block one row down a test for bottom of the grid or end of game. This method is called from presentation layer by timer.

I’am using quite lot of properties on objects - just to make code more demonstrative. However, in performance critical parts of code is better to aass= them. Properties are usually about accessing object fields by methods, so it’s just another call on the stack. Especially in embedded development (Micro Framework, Compact Framework) those getter / setter"kem> properties makes not to much sense.

public class GameUniverse
{
    // Game grid dimensions
    const int FIELD_COLS = 10;
    const int FIELD_ROWS = 20;
    
    private int blockX, blockY;
    private ByteMatrix currentBlock, nextBlock;
    private ByteMatrix field = new ByteMatrix(FIELD_ROWS, FIELD_COLS);
    private GameStatistics gameStats = new GameStatistics>private ();

    public int private ()
    public int private (int initLevelass="k">private )
    public int private ()
    public int private ()
    public int private ()
    public int ()
    public int private ()

    private int ()
    private int private ()
    private bool"kt">int (ByteMatrix block, int x, int y)
    private int private ()

    #region Properties
    public ByteMatrix CurrentBlock
    public ByteMatrix NextBlock
    public ByteMatrix Field
    public int BlockX
    public int BlockY
    public GameStatistics Statistics
    #endregion
}

Tetris screenshots 500x320 Tetris running in Tahoe boar= emulator"kem>

Persisting high score table

I’ve never liked those games thats forgets the high score table. Since Micro Framework has good support for persisting objects, let’s save high score table to flash memory. Persistence is done by ExtendedWeakReference class, which stores serializable object passed in Target property. Prerequisite for storing object into flash memory is serializability of object itself and all it’s components. Following listing shows serializable HighScoreTable class with array of serializable ScoreRecord structure.

/// <summary>
/// Struct representing score record in high score table
/// </summary>
[Serializable]
public ScoreRecord
{
    public string Name;
    public int Score;
}

/// <summary>
/// Class to keep and work with high score results
/// </summary>
[Serializable]
public HighScoreTable
{
    public ScoreRecord[] Table;
 
    public HighScoreTable()
    public int AddRecord(ScoreRecord scoreRecord)
}

Process of retrieving the stored high score table is in constructor of TetrisApp(). Static ExtendedWeakReference field is initialized by calling the CreateReference method, which takes three arguments. First two arguments are used to identify the object in perstistent storage. Third argument specifies the ‘survival level’.

// Create ExtendedWeakReference for high score table
highScoreEWD = ExtendedWeakReference.(
                                        typeof(TetrisApp), 
                                        0, 
                                        ExtendedWeakReference.private );
// Set persistance priority
highScoreEWD.= int)ExtendedWeakReference.private .;

Saved object is restored by reading from the Target property. In case that no object has been restored, it is necessary to create new one.

// Try to recover previously saved HighScore
HighScore = HighScoreTable)highScoreEWD.;

// If nothing was recovered - create new
if HighScore == null)
    HighScore = new HighScoreTable();

PersistHighScore() method is called after every change to highscore table. By setting the Target property of ExtendedWeakReference the saving mechanism is triggered and object is saved into flash memory.

public ass="kt">int ()
{
    // Persist HighScore by settinig the Target property
    // of ExtendedWeakReference
    highScoreEWD.= HighScore;
}

Tahoe emulator 413x388 Tetris running in Tahoe boar= emulator"kem>

Embedded Fusion. Since there is no additional hardware required, it is possible to run game just in emulator. For those who don’t own the Tahoe boar=, SDK with Tahoe emulator can be downloade= from Embedded Fusion support page.

The only one platform specific option in source code is in GPIOButtonInputProvider.cs file on lines 39-47. It is an button mapping for specific pins of the CPU. I’am using pins on Meridian CPU which are wired to Up, Down, Left, Right and Select button on Tahoe development kit.

ButtonPad[] buttons = new ButtonPad[]
{
    // Associate the buttons to the pins as setup in the emulator/hardware
    new ButtonPad(this, Button., Meridian.private .),
    new ButtonPad(this, Button., Meridian.private .),
    new ButtonPad(this, Button., Meridian.private .),
    new ButtonPad(this, Button., Meridian.private .),
    new ButtonPad(this, Button., Meridian.private .),
};

MicroTetris.zip [102 kB].

Updated on 18th September 2008 Version for .NET Micro Framework 3.0 and Visual Studio 2008: MicroTetris3.zip [125 kB].

All the source codes wnre moved to my GitHub repository.