# Creating your own Tetris game using VB.NET

by Hannes du Preez

Tetris turned 25 and is still going strong. To honor that landmark we'll create our own version of this legendary video game using VB.NET.

## Introduction

Seeing the fact that the game Tetris has turned 25, recently, I thought it'd be nice to create my own version of the popular game.  OK, not entirely different, the game still needs to have all the necessary shapes, called tetrominoes, but we'll get to the complicated things a bit later.  For now, all you need to know is that we are going to create our own Tetris game, throughout this article series.

## Logic / Explanation

Tetris makes use of tetrominoes. A tetromino, also spelled tetramino or tetrimino, is a geometric shape composed of four squares, connected orthogonally. This is a particular type of polyomino, just like dominoes and pentominoes. Sometimes the term is generalized to apply to configurations of four orthogonally connected cubes.  There are seven possible shapes :

 Shape Picture I O T L J S Z

Planning this project actually took some time.  Obviously you cannot just jump in and create the project and it will be perfect.  you always have to think logically.  As it is, we can break down this game into 4 logical parts.

Part 1

The design; in other words the form's design, the user interface - what the user will see, and do.

Part 2

This part is probably the most important piece of the puzzle of all.  Without this piece, there will be no game at all.  I am talking about the Grid.  The grid is a set of invisible blocks.  Everything displayed, will be in the grid.  having a grid will help us in determining where precisely each shape ( and further each block of each shape ) is.  Having a grid further simplify our shapes' navigation.

Part 3

A Display object. No, it is not the graphics object with which we draw.  It is a separate object which basically assists in double buffering.  This means that there will be no glitches or breaks in animation during the game.  How this works is that it creates a separate graphics object, and copies the current objects to an from it, but more on that in the next instalment of this series.

Part 4

Shapes.  The shapes must be handled separately.  The physical drawing of the shapes, plus the movement of them.  This will be explained later in the article series.

We will cover only the first 2 logical parts of this game, in this article. The design is not much work.  The real work came in with creating the Grid.  With the Grid, we have to make sure we always know where is which object; we also need to make sure that all colours are placed correctly, and most importantly, we have to determine the empty spaces and the used spaces - we cannot place the shapes properly then. Let us start with the design of our form.

## Design

Start a new project and add the following to the form

 Object Property Setting Form Name frmTet Size 211, 363 Text Tetrimino Panel Name pnlTetGame Backcolor Black Size 161, 301

As you can see, we start with a very basic layout.  Honestly, do not expect much from this project we're creating, as this is only the beginning, and we are only actually creating the Grid.  Let's continue with the code.

## Coding

Open frmTet in Code View, and add the following sub procedure:

```    ''' <summary>
''' Displays The Letters HTG When Loaded
''' </summary>
''' <remarks></remarks>
Private Sub Splash()
Dim gStart As Graphics = pnlTetGame.CreateGraphics() 'Enable Drawing Onto Panel
gStart.Clear(Color.Black) 'Clear If There Was Something

'Create H Shape
gStart.FillRectangle(New SolidBrush(Color.Red), 25, 100, 5, 5)
gStart.DrawRectangle(New Pen(Color.White, 1), 25, 100, 5, 5)

gStart.FillRectangle(New SolidBrush(Color.Red), 25, 105, 5, 5)
gStart.DrawRectangle(New Pen(Color.White, 1), 25, 105, 5, 5)

gStart.FillRectangle(New SolidBrush(Color.Red), 25, 110, 5, 5)
gStart.DrawRectangle(New Pen(Color.White, 1), 25, 110, 5, 5)

gStart.FillRectangle(New SolidBrush(Color.Red), 25, 115, 5, 5)
gStart.DrawRectangle(New Pen(Color.White, 1), 25, 115, 5, 5)

gStart.FillRectangle(New SolidBrush(Color.Red), 25, 120, 5, 5)
gStart.DrawRectangle(New Pen(Color.White, 1), 25, 120, 5, 5)

gStart.FillRectangle(New SolidBrush(Color.Red), 30, 110, 5, 5)
gStart.DrawRectangle(New Pen(Color.White, 1), 30, 110, 5, 5)

gStart.FillRectangle(New SolidBrush(Color.Red), 35, 110, 5, 5)
gStart.DrawRectangle(New Pen(Color.White, 1), 35, 110, 5, 5)

gStart.FillRectangle(New SolidBrush(Color.Red), 40, 100, 5, 5)
gStart.DrawRectangle(New Pen(Color.White, 1), 40, 100, 5, 5)

gStart.FillRectangle(New SolidBrush(Color.Red), 40, 105, 5, 5)
gStart.DrawRectangle(New Pen(Color.White, 1), 40, 105, 5, 5)

gStart.FillRectangle(New SolidBrush(Color.Red), 40, 110, 5, 5)
gStart.DrawRectangle(New Pen(Color.White, 1), 40, 110, 5, 5)

gStart.FillRectangle(New SolidBrush(Color.Red), 40, 115, 5, 5)
gStart.DrawRectangle(New Pen(Color.White, 1), 40, 115, 5, 5)

gStart.FillRectangle(New SolidBrush(Color.Red), 40, 120, 5, 5)
gStart.DrawRectangle(New Pen(Color.White, 1), 40, 120, 5, 5)

'Create T Shape
gStart.FillRectangle(New SolidBrush(Color.Green), 55, 100, 5, 5)
gStart.DrawRectangle(New Pen(Color.White, 1), 55, 100, 5, 5)

gStart.FillRectangle(New SolidBrush(Color.Green), 60, 100, 5, 5)
gStart.DrawRectangle(New Pen(Color.White, 1), 60, 100, 5, 5)

gStart.FillRectangle(New SolidBrush(Color.Green), 65, 100, 5, 5)
gStart.DrawRectangle(New Pen(Color.White, 1), 65, 100, 5, 5)

gStart.FillRectangle(New SolidBrush(Color.Green), 60, 105, 5, 5)
gStart.DrawRectangle(New Pen(Color.White, 1), 60, 105, 5, 5)

gStart.FillRectangle(New SolidBrush(Color.Green), 60, 110, 5, 5)
gStart.DrawRectangle(New Pen(Color.White, 1), 60, 110, 5, 5)

gStart.FillRectangle(New SolidBrush(Color.Green), 60, 115, 5, 5)
gStart.DrawRectangle(New Pen(Color.White, 1), 60, 115, 5, 5)

gStart.FillRectangle(New SolidBrush(Color.Green), 60, 120, 5, 5)
gStart.DrawRectangle(New Pen(Color.White, 1), 60, 120, 5, 5)

'Create G Shape
gStart.FillRectangle(New SolidBrush(Color.Blue), 85, 100, 5, 5)
gStart.DrawRectangle(New Pen(Color.White, 1), 85, 100, 5, 5)

gStart.FillRectangle(New SolidBrush(Color.Blue), 90, 100, 5, 5)
gStart.DrawRectangle(New Pen(Color.White, 1), 90, 100, 5, 5)

gStart.FillRectangle(New SolidBrush(Color.Blue), 80, 105, 5, 5)
gStart.DrawRectangle(New Pen(Color.White, 1), 80, 105, 5, 5)

gStart.FillRectangle(New SolidBrush(Color.Blue), 80, 110, 5, 5)
gStart.DrawRectangle(New Pen(Color.White, 1), 80, 110, 5, 5)

gStart.FillRectangle(New SolidBrush(Color.Blue), 80, 115, 5, 5)
gStart.DrawRectangle(New Pen(Color.White, 1), 80, 115, 5, 5)

gStart.FillRectangle(New SolidBrush(Color.Blue), 85, 120, 5, 5)
gStart.DrawRectangle(New Pen(Color.White, 1), 85, 120, 5, 5)

gStart.FillRectangle(New SolidBrush(Color.Blue), 90, 120, 5, 5)
gStart.DrawRectangle(New Pen(Color.White, 1), 90, 120, 5, 5)

gStart.FillRectangle(New SolidBrush(Color.Blue), 95, 115, 5, 5)
gStart.DrawRectangle(New Pen(Color.White, 1), 95, 115, 5, 5)

gStart.FillRectangle(New SolidBrush(Color.Blue), 100, 115, 5, 5)
gStart.DrawRectangle(New Pen(Color.White, 1), 100, 115, 5, 5)

End Sub

Private Sub frmTet_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint
Splash() 'Draw
End Sub```

This creates the initial "splash screen" inside the main panel.  The letters HTG gets printed in blocks, as shown in the following figure:

[HTG_Splash.jpg]
Figure 1.1

## Let us now add the Grid class

```    Private arrGrids As Rectangle()() 'Each Block

Private arrGridBrushes As SolidBrush()() 'Each Shape's grid
Private arrColours() As SolidBrush 'Each Shape's Colour
```

Here we just create the modular variables to be used throughout clsTetGrid. We create the Grid, which occupies the whole game area, and where all the various shapes will be drawn onto.  Then, we create a brush along with a colour for each block. Now add the constructor :

```    ''' <summary>
''' Initialises The Grid
''' </summary>
''' <param name="intGridRows">Each Row</param>
''' <param name="intGridCols">Each Column</param>
''' <remarks></remarks>
Public Sub New(ByVal intGridRows As Integer, ByVal intGridCols As Integer)

arrGrids = New Rectangle(intGridRows)() {} 'Create Row(s)
arrGridBrushes = New SolidBrush(intGridRows)() {} 'Create Grid For Each Row
arrColours = New SolidBrush(7) {} 'Colours Of Shapes

Dim i As Integer 'Counter For Loop

For i = 0 To intGridRows - 1 'Loop Through Each Row

arrGrids(i) = New Rectangle(intGridCols) {} 'Create Each Column Block
arrGridBrushes(i) = New SolidBrush(intGridCols) {} 'Create Each Column Border

Next i

'Colours For Shapes
arrColours(0) = New SolidBrush(Color.Blue)
arrColours(1) = New SolidBrush(Color.Red)
arrColours(2) = New SolidBrush(Color.Green)
arrColours(3) = New SolidBrush(Color.Yellow)
arrColours(4) = New SolidBrush(Color.Brown)

arrColours(5) = New SolidBrush(Color.Orange)
arrColours(6) = New SolidBrush(Color.Purple)

End Sub```

The grid blocks physically get created here with the For Loop, how many there will be will depend on the number of Columns and rows inputted.  The colours of our 7 shapes also get initialised here.  More on Shapes later.

```    ''' <summary>
''' Get Whole Grid Object Array
''' </summary>
''' <returns></returns>
''' <remarks></remarks>
Public Function GetGrid() As Rectangle()()
Return arrGrids
End Function

Public Function GetGridBrushes() As SolidBrush()()
Return arrGridBrushes
End Function

''' <summary>
''' Get All Colours Associated With Grid Object Array
''' </summary>
''' <returns></returns>
''' <remarks></remarks>
Public Function GetColours() As SolidBrush()
Return arrColours
End Function```

```   ''' <summary>
''' Determine If block Is Unoccupied
''' </summary>
''' <param name="intRowNo">Specify Row</param>
''' <param name="intColNo">Specify Column</param>
''' <returns></returns>
''' <remarks></remarks>
Public Function IsLocEmpty(ByVal intRowNo As Integer, ByVal intColNo As Integer) As Boolean

If arrGrids(intRowNo)(intColNo).IsEmpty Then
Return True 'If Location is Empty
Else
Return False 'If Not
End If

End Function

''' <summary>
''' Get The Top Row Of The Main Game Panel Grid
''' </summary>
''' <remarks></remarks>
Public Sub FirstRow()

'Top Row Is Equal To The UpperBound Of arrGrids
arrGrids(0) = New Rectangle(arrGrids(1).Length) {}

End Sub
```

The function IsLocEmpty just determines if the current block is empty, or already occupied.  The FirstRow sub, just gets and sets the first row of the grid. Add the following:

```    ''' <summary>
''' When The Shape Moves Down, Row By Row
''' </summary>
''' <param name="intRowNo">Specify Row</param>
''' <param name="intColNo">Specify Column</param>
''' <remarks></remarks>
Public Sub DropDown(ByVal intRowNo As Integer, ByVal intColNo As Integer)

If Not IsLocEmpty(intRowNo - 1, intColNo) Then 'Check If Next Row Is Empty

'Set Current Location Equal To The Same Location Horizontally, But
'Next Row ( Vertically )
'Size Is 10
arrGrids(intRowNo)(intColNo) = New Rectangle(arrGrids((intRowNo - 1))(intColNo).X, arrGrids((intRowNo - 1))(intColNo).Y + 10, 10, 10)

'Make Sure Gridlines Move Too
arrGridBrushes(intRowNo)(intColNo) = arrGridBrushes((intRowNo - 1))(intColNo)

Else 'Next Row Is Not Empty
arrGrids(intRowNo)(intColNo) = arrGrids((intRowNo - 1))(intColNo)
End If

End Sub

''' <summary>
''' Make Sure Gridlines And Colours Follow Shapes
''' </summary>
''' <param name="intRowNo">Current Row</param>
''' <param name="intColNo">Current Column</param>
''' <param name="rctCell">Intersection Of Current Row And Current Column</param>
''' <param name="intShapeType">Shape Index</param>
''' <remarks></remarks>
Public Sub SetLoc(ByVal intRowNo As Integer, ByVal intColNo As Integer, ByVal rctCell As Rectangle, ByVal intShapeType As Integer)

arrGrids(intRowNo)(intColNo) = rctCell 'Identify Current Cell ( Block )

SetColourLoc(intRowNo, intColNo, intShapeType) 'Move Colour To Specified Cell

End Sub    ''' <summary>
''' Move Colour To Associated Row & Col
''' </summary>
''' <param name="intRowNo">Current Row</param>
''' <param name="intColNo">Current Col</param>
''' <param name="intShapeType">Shape Index</param>
''' <remarks></remarks>
Public Sub SetColourLoc(ByVal intRowNo As Integer, ByVal intColNo As Integer, ByVal intShapeType As Integer)

'Set Colour Index To Grid Index
arrGridBrushes(intRowNo)(intColNo) = arrColours((intShapeType - 1))

End Sub
These last 3 procedures of our Grid class work with the current position of the shape blocks.
As the shape blocks move down, the colour must follow it.  These 2
procedures don't move the shapes, you must understand that.  They just
keep track of the current shape and its position in the grid, so that the
grid can be updated and we can see where is which shape as we play.
This covers the Grid class.  The next part
we will cover is the Displaying of all objects, and smooth animation.```

## Conclusion

Well, short and sweet; for now at least.  Unfortunately this article has ended quite abruptly, or blunt, for me as well.  I figure that this would be a good place to stop, as the whole grid is covered.  From here on, we will create the shapes, and the physical display of the game, to ensure that we get smooth animation effects.  There is still a lot of work, so stick around for the next installment in this series.  Until then, happy coding!