bare-bones tetris clone example code

Show Index

This page demonstrates a bare-bones tetris clone.  In other words, the minimum code necessary in order to make a tetris game.

Jump ahead to see the final result...

To see a version that includes more features...

tetris clone screenshot

Start with setting up the graphics:

Graphics 800, 600					;2D graphics at a resolution of 800x600
SetBuffer BackBuffer()					;do all drawing to the back drawing buffer

 

Seed the random number generator:

SeedRnd MilliSecs()					;needed to get random numbers

 

Dimension the arrays that you will need:

Dim Board(9, 22)					;10x20 game board (+3 rows above the board)
Dim BlockColor(7, 2)					;stores r,g,b colors (0-2) for each of the 7 pieces
Dim Piece(28, 3, 3)					;28 (4x4) piece descriptions (7 pieces in each of 4 rotations)

 

Read in the color data for the pieces:

For iter = 1 To 7					;loop through seven pieces
  Read BlockColor(iter, 0)				;read the red amount
  Read BlockColor(iter, 1)				;read the green amount
  Read BlockColor(iter, 2)				;read the blue amount
Next
Data 255, 255, 0;yellow					;color of square
Data 0, 128, 255;turquoise				;color of line
Data 0, 0, 255  ;blue					;color of left L
Data 255, 128, 0;orange					;color of right L
Data 255, 0, 0  ;red					;color of left s
Data 0, 255, 0  ;green					;color of right s
Data 128, 0, 255;purple					;color of tee

 

Read the piece descriptions into the description array:

For iter = 1 To 28					;loop through 28 descriptions (7x4)
  For yiter = 0 To 3					;loop through the rows of the description
    For xiter = 0 To 3					;loop through the columns of the description
      Read Piece(iter, xiter, yiter)			;read in the data (0=blank, 1=filled)
    Next
  Next							;example of right L    0100
Next							;as a 4x4 matrix       0100
							;in its 90deg rotation 0110
;piece descriptions					;  (right L rot 1)     0000
Data 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 ;square
Data 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 ;line
Data 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 ;left L
Data 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 ;right L
Data 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 ;left s
Data 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ;right s
Data 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 ;tee up
;rotated 90 degrees cw
Data 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 ;square
Data 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0 ;line rot 1
Data 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 ;left L rot 1
Data 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0 ;right L rot 1
Data 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 ;left s sideways
Data 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 ;right s sideways
Data 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0 ;tee right
;rotated 180 degrees cw
Data 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 ;square
Data 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0 ;line rot 2
Data 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 ;left L rot 2
Data 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0 ;right L rot 2
Data 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 ;left s
Data 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ;right s
Data 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0 ;tee down
;rotated 270 degrees cw
Data 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 ;square
Data 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0 ;line rot 3
Data 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0 ;left L rot 3
Data 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 ;right L rot 3
Data 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 ;left s sideways
Data 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 ;right s sideways
Data 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 ;tee left

 

We'll set up a screen origin just so that we don't have to offset all of our drawing commands.

Origin 200, 10						;set screen origin (for convenience)

 

Set up some global variables (so that they can be modified from within the Functions) and initialize the ones that need to be:

Global Level = 1					;Start the game at level one
Global Lines = 5					;Set the number of lines to clear to go to level two
Global EndOfGame					;This is a flag that is True if the game is over
Global CurrentPiece = Rand(1, 7)			;select a random starting piece
Global CurPieceX = 3					;set the pieces position to the center of the board
Global CurPieceY					;set the pieces position (zero) to the top of the board
Global CurPieceRot					;set the pieces rotation to the first rotation (zero)
Global RepeatSpeed					;stores the amount of time before the piece can move left/right
Global RepeatDelay = True				;makes the piece pause momentarily before repeating left/right
Global NowTime						;current time returned from the Millisecs() command
Global LastDropTime = MilliSecs()			;stores the time of the last drop
Global DropSpeed = 735					;time before piece can drop down normally (decreases each level)
Global DropDelay					;amount of time before the piece can drop (normally or fast)

 

And here is the main game loop:

Repeat							;start of the main loop
  Cls							;clears the screen (backbuffer)
  DrawBoard()						;calls the DrawBoard function (even when the game is over)
  
  If Not EndOfGame					;while the game is running...
    DrawCurrentPiece()					;draws the current piece at its location
    DropDelay = DropSpeed				;sets DropDelay to equal delay time for current level
    ProcessInputs()					;move left, right, down or rotate if necessary
    NowTime = MilliSecs()				;get the current time
    If NowTime > LastDropTime + DropDelay		;if we've delayed long enough to drop another line...
      LastDropTime = NowTime				;store the current time as the last drop time
      If Not DropPiece()				;attempt to drop the piece
        If Not PlacePiece()				;if can't drop piece, then attempt to add the piece to board
          EndOfGame = True				;if can't add the piece to the board (because the piece
        EndIf						; is too high) then end the game
      EndIf
    EndIf
  EndIf
  
  Flip							;flip backbuffer to the front
Until KeyHit(1)  ;Esc key				;repeats main loop until the user presses the Esc key
End							;ends the program

 

Add a function to gather the player keypresses and deal with them:

Function ProcessInputs()				;collect user input
  keyRotate = KeyHit(57)  ;spacebar			;It's a good practice to collect these inputs only once
  keyLeft = KeyDown(203)  ;left arrow			;per loop.  This will prevent odd behaviors from happening,
  keyRight = KeyDown(205) ;right arrow			;for instance if the state of a key changes between multiple
  keyDwn = KeyDown(208)   ;down arrow			;checks of that key while still in the same loop.

  If keyRotate Then RotatePiece()			;attempt to rotate the piece
  If (Not keyLeft) And (Not keyRight)			;if neither the left key nor the right key is pressed then...
    RepeatDelay = True					;reset RepeatDelay (so delay will occur when next key is pressed)
  Else							;if one of the left/right keys is pressed
    If RepeatSpeed < NowTime				;if enough time has passed since the last repeat
      If RepeatDelay					;if RepeatDelay is true...
        RepeatSpeed = NowTime + 150			;make next repeat time longer than normal
        RepeatDelay = False				;set to false now that we're done with it
      Else						;if RepeatDelay is false...
        RepeatSpeed = NowTime + 35			;make next repeat time short (so repeats fast)
      EndIf
      If keyLeft Then MoveLeft()			;attempt to move left
      If keyRight Then MoveRight()			;attempt to move right
    EndIf
  EndIf
  If keyDwn Then DropDelay = 35				;if down key is pressed then set DropDelay to short time
End Function

 

This function draws the game board and the level and lines remaining text:

Function DrawBoard()
  Local block						;temp variable (used only for better readability of code)
  ;draw border
  Color 255, 0, 0					;set color to red
  Rect -3, 72, 255, 505, 0				;draw board outside border
  ;draw blocks
  For yiter = 0 To 22					;loop from (very) top to bottom of board
    For xiter = 0 To 9					;loop from left to right
      block = Board(xiter, yiter)			;set block to color of board at xiter, yiter location
      If block <> 0					;if location isn't empty then set color to color of
        Color BlockColor(block, 0), BlockColor(block, 1), BlockColor(block, 2)	;that type of block
        Rect xiter*25, yiter*25, 24, 24			;draw the block
      EndIf
    Next
  Next
  ;draw level and lines remaining
  Color 255, 255, 255					;set color to white
  Text 300, 200, "Level:" + RSet$(Str$(Level), 3)	;prints current level (right justified)
  Text 300, 240, "Lines:" + RSet$(Str$(Lines), 3)	;prints lines remaining (right justified)
End Function

 

Add a function to draw the current piece:

Function DrawCurrentPiece()				;set color to color of the current piece
  Color BlockColor(CurrentPiece, 0), BlockColor(CurrentPiece, 1), BlockColor(CurrentPiece, 2)
  For yiter = 0 To 3					;loop from top to bottom of piece
    For xiter = 0 To 3					;loop from left to right of piece
      If Piece(CurrentPiece + (CurPieceRot * 7), xiter, yiter)		;if location on piece isn't empty
        Rect CurPieceX*25+xiter*25, CurPieceY*25+yiter*25, 24, 24	;draw this block of the piece
      EndIf
    Next
  Next
End Function

 

Add a function to determine if the current piece can be dropped down a line:

Function DropPiece()
  For yiter = 3 To 0 Step -1				;loop from bottom to top of piece
    For xiter = 0 To 3					;loop from left to right of piece
      If Piece(CurrentPiece + (CurPieceRot * 7), xiter, yiter) <> 0	;if location on piece isn't empty
        If CurPieceY+yiter+1 < 23			;make sure block is still within bottom board limit
          If Board(CurPieceX+xiter, CurPieceY+yiter+1) <> 0 Then Return False	;if board location not empty then fail
        Else						;...block would be below board limit if dropped
          Return False					;so return a False to show a failure
        EndIf
      EndIf
    Next
  Next
  CurPieceY = CurPieceY + 1				;successful - drop down one line
  Return True						;show that drop was a success by returning True
End Function

 

Here's a function to add the piece to the board (once it can drop no furthur):

Function PlacePiece()
  Local outofbounds					;keeps track of whether this function fails or not
  For yiter = 0 To 3					;loop from top to bottom
    For xiter = 0 To 3					;loop from left to right
      If Piece(CurrentPiece + (CurPieceRot * 7), xiter, yiter) <> 0	;if location on piece isn't empty
        Board(CurPieceX+xiter, CurPieceY+yiter) = CurrentPiece	;set board location = piece block type
        If CurPieceY+yiter < 3 Then outofbounds = True		;above the 10x20 playing area - game over
      EndIf
    Next
  Next
  If outofbounds Then Return False			;piece was too high so return a False to show failure
  ;check if any lines were created
  ClearLines()						;clear any lines that may have been created
  ;reset piece to top center
  CurPieceX = 3						;set back to center
  CurPieceY = 0						;set back to top
  CurPieceRot = 0					;set back to first rotation
  CurrentPiece = Rand(1, 7)				;get a new random piece
  Return True						;return a True to show a success at placing the piece
End Function

 

Now you need a function to handle clearing any lines that may have been created.

Function ClearLines()
  Local blockcount					;counts the number of blocks on each line
  Local numLines					;stores the number of lines cleared (0-4)
  For yiter = 22 To 3 Step -1				;loop from bottom to top of board
    For xiter = 0 To 9					;loop from left to right
      If Board(xiter, yiter) <> 0 Then blockcount = blockcount + 1	;if location isn't empty then increment blockcount
    Next
    If blockcount = 10					;if 10 blocks found then...
      numLines = numLines + 1				;increment number of lines
      ;shift blocks down				;shift all of the upper blocks downward
      For lineIter = yiter To 3 Step -1			;loop from current line iteration up to top
        For blockIter = 0 To 9				;loop from left to right
          Board(blockIter, lineIter) = Board(blockIter, lineIter - 1)	;block below = block above
        Next
      Next
      yiter = yiter + 1					;adjust for line that was just removed
    EndIf
    blockcount = 0					;reset the block count
  Next
  ;1 line  = 1 point
  ;2 lines = 3 points
  ;3 lines = 5 points					;subtract number of lines cleared from the lines remaining
  ;4 lines = 8 points
  If numLines > 0 Then Lines = Lines - (numLines + (numLines - 1)) - (1 * (numLines = 4))
  ;if cleared 5 lines (per level) then level up
  If Lines < 1						;if lines remaining are cleared...
    DropSpeed = DropSpeed - 50 + (50 * (DropSpeed < 50));decrease drop time (but not less than 35 millisecs)
    Level = Level + 1					;increment level
    Lines = Level * 5					;set new lines goal
  EndIf
End Function

 

This function will move the piece left if it's clear:

Function MoveLeft()
  For xiter = 0 To 3					;loop from left to right
    For yiter = 0 To 3					;loop from top to bottom
      If Piece(CurrentPiece + (CurPieceRot * 7), xiter, yiter) <> 0	;if location on piece isn't empty
        If CurPieceX+xiter-1 > -1			;make sure block is still within left board limit
          If Board(CurPieceX+xiter-1, CurPieceY+yiter) <> 0 Then Return	;if board location not empty then fail
        Else						;...block would be left of board limit if moved
          Return					;so return because attempted move failed
        EndIf
      EndIf
    Next
  Next
  CurPieceX = CurPieceX - 1				;successful - move piece left one block
End Function

 

And this function will move the piece right if it's clear:

Function MoveRight()
  For xiter = 3 To 0 Step -1				;loop from right to left
    For yiter = 0 To 3					;loop from top to bottom
      If Piece(CurrentPiece + (CurPieceRot * 7), xiter, yiter) <> 0	;if location on piece isn't empty
        If CurPieceX+xiter+1 < 10			;make sure block is still within right board limit
          If Board(CurPieceX+xiter+1, CurPieceY+yiter) <> 0 Then Return	;if board location not empty then fail
        Else						;...block would be right of board limit if moved
          Return					;so return because attempted move failed
        EndIf
      EndIf
    Next
  Next
  CurPieceX = CurPieceX + 1				;successful - move piece right one block
End Function

 

And finally, a function to handle rotating the piece:

Function RotatePiece()
  For yiter = 0 To 3					;loop from top to bottom
    For xiter = 0 To 3					;loop from left to right
      If Piece(CurrentPiece + (((CurPieceRot + 1) * (CurPieceRot < 3)) * 7), xiter, yiter) <> 0	;if location isn't empty
        If CurPieceX+xiter > -1 And CurPieceX+xiter < 10 And CurPieceY+yiter < 23;make sure block is within board limits
          If Board(CurPieceX+xiter, CurPieceY+yiter) <> 0 Then Return	;if board location not empty then fail
        Else						;...block would be outside of board if rotated
          Return					;so return because attempted rotation failed
        EndIf
      EndIf
    Next
  Next
  CurPieceRot = (CurPieceRot + 1) * (CurPieceRot < 3)	;successful - rotate the piece
End Function

 

And here is the entire code pieced together so that you can copy&paste it and immediately run it to see how it works.

Keys used in this example:

keyboard.jpg (7994 bytes)

	
Left key	Move piece left
Right key	Move piece right
Down key	Move piece down fast
Spacebar	Rotate piece
Esc key		Exits program

 

If you've reached this page and there's no index on the left, Click here to show the Index Page