Here's a simple Tetris game written in Microsoft Small Basic.

If you want to just run the program, you can download the self-contained EXE here:
http://cid-30e1dc9cc197bc9d.skydrive.live.com/browse.aspx/.Public/Small%20Basic%20Tetris

Voted Sample of the Week:
http://blogs.msdn.com/smallbasic/archive/2008/12/29/tetris-sample-of-the-week.aspx

New Version : this code was originally written with an older version of Small Basic. It became incompatible with the current versions (1.0 and 1.1). So this article was updated to be executed with the current versions. You can always get the original code by visiting the links above.

Import the new version : WQF322.


See Screenshot below:



Source code:

GraphicsWindow.KeyDown = HandleKey
GraphicsWindow.BackgroundColor = GraphicsWindow.GetColorFromRGB( 253, 252, 251 )

While "True"
  BOXES = 4      ' number of boxes per piece
  BWIDTH = 25    ' box width in pixels
  XOFFSET = 40   ' Screen X offset in pixels of where the board starts
  YOFFSET = 40   ' Screen Y offset in pixels of where the board starts
  CWIDTH = 10    ' Canvas Width, in number of boxes
  CHEIGHT = 20   ' Canvas Height, in number of boxes.
  STARTDELAY = 800
  ENDDELAY = 175
  PREVIEW_xpos = 13
  PREVIEW_ypos = 2
  
  GraphicsWindow.Clear()
  GraphicsWindow.Title = "Small Basic Tetris"
  GraphicsWindow.Height = 580
  GraphicsWindow.Width = 700
  GraphicsWindow.Show()

  SetupTemplates()
  SetupCanvas()
  MainLoop()
  
  GraphicsWindow.ShowMessage( "Game Over", "Small Basic Tetris" )
EndWhile

Sub MainLoop
  template = Text.Append("template", Math.GetRandomNumber(7))

  CreatePiece() ' in: template  ret: h
  nextPiece = h

  end = 0
  sessionDelay = STARTDELAY
  While end = 0
    If sessionDelay > ENDDELAY Then
      sessionDelay = sessionDelay - 1
    EndIf
    
    delay = sessionDelay

    thisPiece = nextPiece

    template = Text.Append("template", Math.GetRandomNumber(7))


    CreatePiece() ' in: template  ret: h
    nextPiece = h
    DrawPreviewPiece()
    
    h = thisPiece    
    
    ypos = 0
    done = 0
    xpos = 3 ' always drop from column 3
    CheckStop() ' in: ypos, xpos, h  ret: done
    If done = 1 Then
      ypos = ypos - 1
      MovePiece()  'in: ypos, xpos, h
      end = 1
    EndIf
    
    yposdelta = 0
    While done = 0 Or yposdelta > 0
      MovePiece()  'in: ypos, xpos, h
      
      ' Delay, but break if the delay get set to 0 if the piece gets dropped
      delayIndex = delay      
      While delayIndex > 0 And delay > 0
        Program.Delay(10)
        delayIndex = delayIndex - 10
      EndWhile  

      If yposdelta > 0 Then
        yposdelta = yposdelta - 1  ' used to create freespin, when the piece is rotated
      Else
        ypos = ypos + 1            ' otherwise, move the piece down.
      EndIf
      
      ' Check if the piece should stop.
      CheckStop() ' in: ypos, xpos, h  ret: done            
    EndWhile
  EndWhile
EndSub


Sub HandleKey
  ' Stop game
  If GraphicsWindow.LastKey = "Escape" Then
    Program.End()
  EndIf

  ' Move piece left
  If GraphicsWindow.LastKey = "Left" Then
    moveDirection = -1
    ValidateMove()  ' in: ypos, xpos, h, moveDirection  ret: invalidMove = 1 or -1 or 2 if move is invalid, otherwise 0
    If invalidMove = 0 Then
      xpos = xpos + moveDirection
    EndIf
    MovePiece()  'in: ypos, xpos, h
  EndIf
  
  ' Move piece right
  If GraphicsWindow.LastKey = "Right" Then
    moveDirection = 1
    ValidateMove()  ' in: ypos, xpos, h, moveDirection  ret: invalidMove = 1 or -1 or 2 if move is invalid, otherwise 0
    If invalidMove = 0 Then
      xpos = xpos + moveDirection
    EndIf
    MovePiece()  'in: ypos, xpos, h
  EndIf
  
  ' Move piece down
  If GraphicsWindow.LastKey = "Down" or GraphicsWindow.LastKey = "Space" Then
    delay = 0
  EndIf
  
  ' Rotate piece
  If GraphicsWindow.LastKey = "Up" Then
    basetemplate = Array.GetValue(h, -1)  ' Array.GetValue(h, -1) = the template name
    template = "temptemplate"
    rotation = "CW"
    CopyPiece()  'in basetemplate, template, rotation

    Array.SetValue(h, -1, template) ' Array.GetValue(h, -1) = the template name
    moveDirection = 0
    ValidateMove()  ' in: ypos, xpos, h, moveDirection  ret: invalidMove = 1 or -1 or 2 if move is invalid, otherwise 0
    
    ' See if it can be moved so that it will rotate.
    xposbk = xpos
    yposdelta = 0
    While yposdelta = 0 And Math.Abs(xposbk - xpos) < 3 ' move up to 3 times only
      
      ' if the rotation move worked, copy the temp to "rotatedtemplate" and use that from now on
      If invalidMove = 0 Then
        basetemplate = template
        template = "rotatedtemplate"
        Array.SetValue(h, -1, template) ' Array.GetValue(h, -1) = the template name
        rotation = "COPY"
        CopyPiece()  'in basetemplate, template, rotation
        yposdelta = 1 ' Don't move down if we rotate
        MovePiece()  'in: ypos, xpos, h
      ElseIf invalidMove = 2 Then
        ' Don't support shifting piece when hitting another piece to the right or left.
        xpos = 99 ' exit the loop
      Else
        ' if the rotated piece can't be placed, move it left or right and try again.
        xpos = xpos - invalidMove
        ValidateMove()  ' in: ypos, xpos, h, moveDirection  ret: invalidMove = 1 or -1 or 2 if move is invalid, otherwise 0
      EndIf
    EndWhile
        
    If invalidMove <> 0 Then
      xpos = xposbk
      Array.SetValue(h, -1, basetemplate) ' Array.GetValue(h, -1) = the template name
      template = ""
    EndIf      
  EndIf
EndSub


Sub DrawPreviewPiece
  xpos = PREVIEW_xpos
  ypos = PREVIEW_ypos
  h = nextPiece

  XOFFSETBK = XOFFSET
  YOFFSETBK = YOFFSET
  XOFFSET = XOFFSET + Array.GetValue(Array.GetValue(h, -1), "pviewx") ' Array.GetValue(h, -1) = the template name
  YOFFSET = YOFFSET + Array.GetValue(Array.GetValue(h, -1), "pviewy") ' Array.GetValue(h, -1) = the template name
  MovePiece()  'in: ypos, xpos, h

  XOFFSET = XOFFSETBK
  YOFFSET = YOFFSETBK
  
EndSub


' creates template that's a rotated basetemplate
Sub CopyPiece  'in basetemplate, template, rotation  
  L = Array.GetValue(basetemplate, "dim")
  
  If rotation = "CW" Then
    For i = 0 to BOXES - 1 '      x' = y     y' = L - 1 - x
      v = Array.GetValue(basetemplate, i)

      'x = Math.Floor(v/10)
      'y = Math.Remainder(v, 10)
      
      ' new x and y
      x = (Math.Remainder(v, 10))
      y = (L - 1 - Math.Floor(v/10))
      Array.SetValue(template, i, x * 10 + y)
    EndFor
  ' Count-Cockwise is not currently used
  ElseIf rotation = "CCW" Then
    For i = 0 to BOXES - 1 '      x' = L - 1 - y     y' = x
      v = Array.GetValue(basetemplate, i)
      'x = Math.Floor(v/10)
      'y = Math.Remainder(v, 10)

      ' new x and y
      x = (L - 1 - Math.Remainder(v, 10))
      y = Math.Floor(v/10)
      Array.SetValue(template, i, x * 10 + y)
    EndFor
  ElseIf rotation = "COPY" Then
    For i = 0 to BOXES - 1
      Array.SetValue(template, i, Array.GetValue(basetemplate, i))
    EndFor
  Else
    GraphicsWindow.ShowMessage("invalid parameter", "Error")
    Program.End()
  EndIf
  
  ' Copy the remain properties from basetemplate to template.
  Array.SetValue(template, "color", Array.GetValue(basetemplate, "color"))
  Array.SetValue(template, "dim", Array.GetValue(basetemplate, "dim"))
  Array.SetValue(template, "pviewx", Array.GetValue(basetemplate, "pviewx"))
  Array.SetValue(template, "pviewy", Array.GetValue(basetemplate, "pviewy"))
EndSub
  
  
Sub CreatePiece ' in: template  ret: h
  ' Create a new handle, representing an arrayName, that will represent the piece
  hcount = hcount + 1
  h = Text.Append("piece", hcount)
  
  Array.SetValue(h, -1, template) ' Array.GetValue(h, -1) = the template name

  GraphicsWindow.PenWidth = 1
  GraphicsWindow.PenColor = "Black"
  GraphicsWindow.BrushColor = Array.GetValue(template, "color")
  
  For i = 0 to BOXES - 1
    s = Shapes.AddRectangle(BWIDTH, BWIDTH)
    Shapes.Move(s, -BWIDTH, -BWIDTH) ' move off screen
    Array.SetValue(h, i, s)
  EndFor
EndSub
  

Sub MovePiece 'in: ypos, xpos, h.  ypos/xpos is 0-19, representing the top/left box coordinate of the piece on the canvas.  h returned by CreatePiece
  For i = 0 to BOXES - 1
    v = Array.GetValue(Array.GetValue(h, -1), i)  ' Array.GetValue(h, -1) = the template name
    x = Math.Floor(v/10)
    y = Math.Remainder(v, 10)
    
    ' Array.GetValue(h, i) = box for piece h.
    ' xpos/ypos = are topleft of shape.  x/y is the box offset within the shape.
    Shapes.Move(Array.GetValue(h, i), XOFFSET + xpos * BWIDTH + x * BWIDTH, YOFFSET + ypos * BWIDTH + y * BWIDTH)
  EndFor
EndSub


Sub ValidateMove ' in: ypos, xpos, h, moveDirection  ret: invalidMove = 1 or -1 or 2 if move is invalid, otherwise 0
  i = 0
  invalidMove = 0
  While i < BOXES
    v = Array.GetValue(Array.GetValue(h, -1), i)  ' Array.GetValue(h, -1) = the template name

    'x/y is the box offset within the shape.
    x = Math.Floor(v/10)
    y = Math.Remainder(v, 10)
    
    If (x + xpos + moveDirection) < 0 Then
      invalidMove = -1
      i = BOXES ' force getting out of the loop
    EndIf

    If (x + xpos + moveDirection) >= CWIDTH Then
      invalidMove = 1
      i = BOXES ' force getting out of the loop
    EndIf

    If Array.GetValue("c", (x + xpos + moveDirection) + (y + ypos) * CWIDTH) <> "." Then
      invalidMove = 2
      i = BOXES ' force getting out of the loop
    EndIf

    i = i + 1
  EndWhile
EndSub


Sub CheckStop ' in: ypos, xpos, h  ret: done
  done = 0
  i = 0
  While i < BOXES
    v = Array.GetValue(Array.GetValue(h, -1), i)  ' Array.GetValue(h, -1) = the template name

    'x/y is the box offset within the shape.
    x = Math.Floor(v/10)
    y = Math.Remainder(v, 10)
    
    If y + ypos > CHEIGHT Or Array.GetValue("c", (x + xpos) + (y + ypos) * CWIDTH) <> "." Then
      done = 1
      i = BOXES ' force getting out of the loop
    EndIf

    i = i + 1
  EndWhile

  ' If we need to stop the piece, move the box handles to the canvas
  If done = 1 Then
    For i = 0 to BOXES - 1
      v = Array.GetValue(Array.GetValue(h, -1), i) ' Array.GetValue(h, -1) = the template name
      'x = Math.Floor(v/10)
      'y = Math.Remainder(v, 10)      
      Array.SetValue("c", (Math.Floor(v/10) + xpos) + (Math.Remainder(v, 10) + ypos - 1) * CWIDTH, Array.GetValue(h, i))
    EndFor
    
    ' 1 points for every piece successfully dropped
    score = score + 1
    PrintScore()
     
    ' Delete clared lines
    DeleteLines()
  EndIf
EndSub


Sub DeleteLines
  linesCleared = 0
  
  ' Iterate over each row, starting from the bottom
  For y = CHEIGHT - 1 to 0 Step -1  
    
    ' Check to see if the whole row is filled
    x = CWIDTH
    While x = CWIDTH
      x = 0
      While x < CWIDTH
        piece = Array.GetValue("c", x + y * CWIDTH)
        If piece = "." then
          x = CWIDTH
        EndIf
        x = x + 1
      EndWhile
      
      ' if non of them were empty (i.e "."), then remove the line.
      If x = CWIDTH Then
        
        ' Delete the line
        For x1 = 0 to CWIDTH - 1
          Shapes.Remove(Array.GetValue("c", x1 + y * CWIDTH))
        EndFor
        linesCleared = linesCleared + 1
        
        ' Move everything else down one.
        For y1 = y To 1 Step -1
          For x1 = 0 to CWIDTH - 1
            piece = Array.GetValue("c", x1 + (y1 - 1) * CWIDTH)
            Array.SetValue("c", x1 + y1 * CWIDTH, piece)
            Shapes.Move(piece, Shapes.GetLeft(piece), Shapes.GetTop(piece) + BWIDTH)
          EndFor
        EndFor
      EndIf
    EndWhile
  EndFor
  
  If linesCleared > 0 Then
    score = score + 100 * Math.Round(linesCleared * 2.15 - 1)
    PrintScore()
  EndIf
EndSub

Sub SetupCanvas
'    GraphicsWindow.DrawResizedImage( Flickr.GetRandomPicture( "bricks" ), 0, 0, GraphicsWindow.Width, GraphicsWindow.Height)

  
  GraphicsWindow.BrushColor = GraphicsWindow.BackgroundColor
  GraphicsWindow.FillRectangle(XOFFSET, YOFFSET, CWIDTH*BWIDTH, CHEIGHT*BWIDTH)

  Program.Delay(200)
  GraphicsWindow.PenWidth = 1
  GraphicsWindow.PenColor = "Pink"
  For x = 0 To CWIDTH-1
    For y = 0 To CHEIGHT-1
      Array.SetValue("c", x + y * CWIDTH, ".") ' "." indicates spot is free
      GraphicsWindow.DrawRectangle(XOFFSET + x * BWIDTH, YOFFSET + y * BWIDTH, BWIDTH, BWIDTH)
    EndFor
  EndFor

  GraphicsWindow.PenWidth = 4
  GraphicsWindow.PenColor = "Black"
  GraphicsWindow.DrawLine(XOFFSET, YOFFSET, XOFFSET, YOFFSET + CHEIGHT*BWIDTH)
  GraphicsWindow.DrawLine(XOFFSET + CWIDTH*BWIDTH, YOFFSET, XOFFSET + CWIDTH*BWIDTH, YOFFSET + CHEIGHT*BWIDTH)
  GraphicsWindow.DrawLine(XOFFSET, YOFFSET + CHEIGHT*BWIDTH, XOFFSET + CWIDTH*BWIDTH, YOFFSET + CHEIGHT*BWIDTH)
  
  GraphicsWindow.PenColor = "Lime"
  GraphicsWindow.DrawLine(XOFFSET - 4, YOFFSET, XOFFSET - 4, YOFFSET + CHEIGHT*BWIDTH + 6)
  GraphicsWindow.DrawLine(XOFFSET + CWIDTH*BWIDTH + 4, YOFFSET, XOFFSET + CWIDTH*BWIDTH + 4, YOFFSET + CHEIGHT*BWIDTH + 6)
  GraphicsWindow.DrawLine(XOFFSET - 4, YOFFSET + CHEIGHT*BWIDTH + 4, XOFFSET + CWIDTH*BWIDTH + 4, YOFFSET + CHEIGHT*BWIDTH + 4)
  
  GraphicsWindow.PenColor = "Black"
  GraphicsWindow.BrushColor = "Pink"
  x = XOFFSET + PREVIEW_xpos * BWIDTH - BWIDTH
  y = YOFFSET + PREVIEW_ypos * BWIDTH - BWIDTH
  GraphicsWindow.FillRectangle(x, y, BWIDTH * 5, BWIDTH * 6)
  GraphicsWindow.DrawRectangle(x, y, BWIDTH * 5, BWIDTH * 6)
  
  GraphicsWindow.FillRectangle(x - 20, y + 190, 310, 170)
  GraphicsWindow.DrawRectangle(x - 20, y + 190, 310, 170)
  
  GraphicsWindow.BrushColor = "Black"
  GraphicsWindow.FontItalic = "False"
  GraphicsWindow.FontName = "Comic Sans MS"
  GraphicsWindow.FontSize = 16
  GraphicsWindow.DrawText(x, y + 200, "Game control keys:")
  GraphicsWindow.DrawText(x + 25, y + 220, "Left Arrow = Move piece left")
  GraphicsWindow.DrawText(x + 25, y + 240, "Right Arrow = Move piece right")
  GraphicsWindow.DrawText(x + 25, y + 260, "Up Arrow = Rotate piece")
  GraphicsWindow.DrawText(x + 25, y + 280, "Down Arrow = Drop piece")
  GraphicsWindow.DrawText(x, y + 320, "Press  to stop game")

  Program.Delay(200) ' without this delay, the above text will use the fontsize of the score 

  GraphicsWindow.BrushColor = "Black"
  GraphicsWindow.FontName = "Georgia"
  GraphicsWindow.FontItalic = "True"
  GraphicsWindow.FontSize = 36
  GraphicsWindow.DrawText(x - 20, y + 400, "Small Basic Tetris")
  Program.Delay(200) ' without this delay, the above text will use the fontsize of the score 
  GraphicsWindow.FontSize = 16
  GraphicsWindow.DrawText(x - 20, y + 440, "ver.0.1")

  Program.Delay(200) ' without this delay, the above text will use the fontsize of the score 
  score = 0
  PrintScore() 
EndSub


Sub PrintScore
  GraphicsWindow.PenWidth = 4
  GraphicsWindow.BrushColor = "Pink"
  GraphicsWindow.FillRectangle(500, 65, 153, 50)
  GraphicsWindow.BrushColor = "Black"
  GraphicsWindow.DrawRectangle(500, 65, 153, 50)
  GraphicsWindow.FontItalic = "False"
  GraphicsWindow.FontSize = 32
  GraphicsWindow.FontName = "Impact"
  GraphicsWindow.BrushColor = "Black"
  GraphicsWindow.DrawText(505, 70, Text.Append(Text.GetSubText( "00000000", 0, 8 - Text.GetLength( score ) ), score))
EndSub


Sub SetupTemplates
  ' each piece has 4 boxes.
  ' the index of each entry within a piece represents the box number (1-4)
  ' the value of each entry represents to box zero-based box coordinate within the piece: tens place is x, ones place y

  '_X_
  '_X_
  '_XX

  Array.SetValue("template1", 0, 10)
  Array.SetValue("template1", 1, 11)
  Array.SetValue("template1", 2, 12)
  Array.SetValue("template1", 3, 22)
  Array.SetValue("template1", "color", "Yellow")
  Array.SetValue("template1", "dim", 3)
  Array.SetValue("template1", "pviewx", -12)
  Array.SetValue("template1", "pviewy", 12)


  '_X_
  '_X_
  'XX_
  Array.SetValue("template2", 0, 10)
  Array.SetValue("template2", 1, 11)
  Array.SetValue("template2", 2, 12)
  Array.SetValue("template2", 3, 02)
  Array.SetValue("template2", "color", "Magenta")
  Array.SetValue("template2", "dim", 3)
  Array.SetValue("template2", "pviewx", 12)
  Array.SetValue("template2", "pviewy", 12)
  
  
  '_X_
  'XXX
  '_
  Array.SetValue("template3", 0, 10)
  Array.SetValue("template3", 1, 01)
  Array.SetValue("template3", 2, 11)
  Array.SetValue("template3", 3, 21)
  Array.SetValue("template3", "color", "Gray")
  Array.SetValue("template3", "dim", 3)
  Array.SetValue("template3", "pviewx", 0)
  Array.SetValue("template3", "pviewy", 25)
  
  
  'XX_
  'XX_
  '_
  Array.SetValue("template4", 0, 00)
  Array.SetValue("template4", 1, 10)
  Array.SetValue("template4", 2, 01)
  Array.SetValue("template4", 3, 11)
  Array.SetValue("template4", "color", "Cyan")
  Array.SetValue("template4", "dim", 2)
  Array.SetValue("template4", "pviewx", 12)
  Array.SetValue("template4", "pviewy", 25)
  
  
  'XX_
  '_XX
  '_
  Array.SetValue("template5", 0, 00)
  Array.SetValue("template5", 1, 10)
  Array.SetValue("template5", 2, 11)
  Array.SetValue("template5", 3, 21)
  Array.SetValue("template5", "color", "Green")
  Array.SetValue("template5", "dim", 3)
  Array.SetValue("template5", "pviewx", 0)
  Array.SetValue("template5", "pviewy", 25)
  
  
  '_XX
  'XX_
  '_
  Array.SetValue("template6", 0, 10)
  Array.SetValue("template6", 1, 20)
  Array.SetValue("template6", 2, 01)
  Array.SetValue("template6", 3, 11)
  Array.SetValue("template6", "color", "Blue")
  Array.SetValue("template6", "dim", 3)
  Array.SetValue("template6", "pviewx", 0)
  Array.SetValue("template6", "pviewy", 25)
  
  
  '_X
  '_X
  '_X
  '_X
  Array.SetValue("template7", 0, 10)
  Array.SetValue("template7", 1, 11)
  Array.SetValue("template7", 2, 12)
  Array.SetValue("template7", 3, 13)
  Array.SetValue("template7", "color", "Red")
  Array.SetValue("template7", "dim", 4)
  Array.SetValue("template7", "pviewx", 0)
  Array.SetValue("template7", "pviewy", 0)
    
EndSub

By Kenny Kasajian.

Other Languages