Microsoft Small Basic

Program Listing:
Embed this in your website
' Maze Game 1.56
' Copyright (c) 2012 Nonki Takahashi. All rights reserved.

' History :
' 1.56 2012/12/03 Bug fixed. (XHL585-5)
' 1.3b 2012/12/03 Changed to a game. (PNC833-14)
' 1.2 2012/10/29 Modified for Sliverlight (about pen width). (PNC833-12)
' 0.5 2012/10/27 Modified for Sliverlight and removed debug routines. (PNC833-1)
' 0.4 2012/09/04 Changed time unit from [ms] to [s]. (PNC833-0)
' 0.3 2012/06/22 Maze genaration algorithm changed. (PNC833)
' 0.2 2012/06/08 Title added.
' 0.1 2012/06/08 Created.

' Reference :
' [1] Wikipedia, Randomized Prim's algorithm, http://en.wikipedia.org/wiki/Maze_generation_algorithm#Randomized_Prim.27s_algorithm

' Data structure :
' start : cell[2][2]
' goal : cell[2 * cols][2 * rows]

' Example : (cols = 4, rows = 3)
' ■■■■■■■■■
' ■○      ■
' ■ ■■■ ■ ■
' ■ ■   ■ ■
' ■■■ ■■■ ■
' ■     ■○■
' ■■■■■■■■■
title = "Maze Game 1.56"
GraphicsWindow.Title = title
int = 500 ' interval time to synchronize with Silverlight
cols = 19 '30
rows = 12 '19
cols2 = 2 * cols + 1
rows2 = 2 * rows + 1
widthp = 30  '20 ' path width
widthw = 8   '6 ' wall width (even number)
colorWall = "#444444" ' don't use color name in order to compare with GetPixel
colorPassage = "White"
x0 = (GraphicsWindow.Width - cols * widthp) / 2
y0 = 1.8 * widthp
Program.Delay(int)  ' to synchronize with Silverlight
GraphicsWindow.FontSize = widthp * 0.8
GraphicsWindow.BrushColor = "Black"
GraphicsWindow.FillRectangle(x0 - widthw / 2, y0 - 40, widthw + widthp * cols, 30)
GraphicsWindow.BrushColor = "White"
GraphicsWindow.DrawText(x0, y0 - 40, title)
high_score = 999
' The following line could be harmful and has been automatically commented.
' path = File.GetSettingsFilePath()
' The following line could be harmful and has been automatically commented.
' record = File.ReadLine(path, 1)
If record["high_score"] <> "" Then
  high_score = record["high_score"]
  player = record["player"]
  ShowScore()
EndIf
colAdj = "0=1;1=0;2=-1;3=0;"
rowAdj = "0=0;1=1;2=0;3=-1;"
AT_Init()
InitTurtle()
bStart = "False"
While bStart = "False"
  GenerateMaze()
  GraphicsWindow.BackgroundColor = "Gray"
  ReadyGo()
  bStart = "True"
  If bStart Then
    t0 = Clock.ElapsedMilliseconds
    SolveMaze()
    t1 = Clock.ElapsedMilliseconds
    If pos = goal Then
      winner = "CPU"
    ElseIf pos2 = goal Then
      winner = "You"
    EndIf
    score = (t1 - t0) / 1000
    GraphicsWindow.Title = title + " - " + winner + " win. time: " + score + " [s]"
    GraphicsWindow.BrushColor = colorPassage
    GraphicsWindow.FillRectangle(x0 - widthw / 2, y0 - widthw / 2, widthp * cols + widthw, widthp * rows + widthw)
    If score < high_score Then
      high_score = score
      player = winner
      ShowScore()
      record["high_score"] = high_score
      record["player"] = player
      GraphicsWindow.BrushColor = "LightSeaGreen"
      Program.Delay(int)  ' to synchronize with Silverlight
      GraphicsWindow.FontSize = 40
      GraphicsWindow.DrawText(210, 200, "High Score")
' The following line could be harmful and has been automatically commented.
' File.WriteLine(path, 1, record)
    EndIf
    Program.Delay(5000)
    InitTurtle()
    bStart = "False"
  EndIf
EndWhile
' program end

Sub AddWallToList
  ' param col, row - cell
  ' return nWalls - number of wall list
  ' return iWalls - index of wall list
  ' return colWalls[], rowWalls[] - wall list
  ' return colOpp[], rowOpp[] - cell list (on the opposite side)
  For d = 0 To 3
    colw = col + colAdj[d]
    roww = row + rowAdj[d]
    If 1 < colw And colw < cols2 And 1 < roww And roww < rows2 And cell[colw][roww] = 1 Then
      i = colw + (roww - 1) * cols2
      ' find i in wall list
      FindWallInList()
      If i = iFound Then ' found
        ' remove the wall
        nWalls = nWalls - 1
        iWalls[iPrev] = iWalls[iFound]
      Else
        ' add the wall to wall list
        nWalls = nWalls + 1
        iNext = iWalls[0]
        iWalls[0] = i
        iWalls[i] = iNext
        colWall[i] = colw            ' wall
        rowWall[i] = roww
        colOpp[i] = colw + colAdj[d] ' cell on the opposite side
        rowOpp[i] = roww + rowAdj[d]
      EndIf
    EndIf
  EndFor
EndSub

Sub ClearMaze
  ' param x0, y0
  ' param cols, rows
  ' param width
  Program.Delay(int)  ' to synchronize with Silverlight
  GraphicsWindow.PenWidth = widthw
  GraphicsWindow.PenColor = colorWall                     ' dummy for Silverlight
  _x = x0                                                 ' dummy for Silverlight
  _y = y0                                                 ' dummy for Silverlight
  _width = widthp * cols                                  ' dummy for Silverlight
  _height = widthp * rows                                 ' dummy for Silverlight
  GraphicsWindow.DrawRectangle(_x, _y, _width, _height)   ' dummy for Silverlight
  GraphicsWindow.BrushColor = "Gray"
  GraphicsWindow.FillRectangle(x0, y0, widthp * cols, widthp * rows)
  check = GraphicsWindow.GetPixel(x0 - 1, y0 - 1)         ' check for Silverlight
  If Text.GetLength(check) = 9 Then                       ' check for Silverlight
    check = "#" + Text.GetSubTextToEnd(check, 4)          ' check for Silverlight
  EndIf                                                   ' check for Sliverlight
  If check = colorWall Then                               ' check for Silverlight
    small = "False"                                       ' check for Sliverlight
  Else                                                    ' check for Sliverlight
    small = "True"    ' DrawRectangle is small ' check for Sliverlight
  EndIf
  col0 = 1 'Math.GetRandomNumber(cols) ' start cell of maze generate
  row0 = 1 'Math.GetRandomNumber(rows)
  x = x0 + (col0 - 1) * widthp
  y = y0 + (row0 - 1) * widthp
  GraphicsWindow.BrushColor = colorPassage
  GraphicsWindow.FillRectangle(x + (widthw / 2), y + (widthw / 2), widthp - widthw, widthp - widthw)  ' start cell
  GraphicsWindow.PenColor = colorWall
  If small Then                                                                                                         ' for Silverlight
    GraphicsWindow.DrawRectangle(x0 - (widthw / 2), y0 - (widthw / 2), widthp * cols + widthw, widthp * rows + widthw)  ' for Silverlight
  Else                                                                                                                  ' for Silverlight
    GraphicsWindow.DrawRectangle(x0, y0, widthp * cols, widthp * rows)
  EndIf                                                                                                                 ' for Silverlight
  y1 = y0 + widthp * rows
  For col = 1 To cols - 1
    x = x0 + widthp * col
    GraphicsWindow.DrawLine(x, y0, x, y1)
  EndFor
  x1 = x0 + widthp * cols
  For row = 1 To rows - 1
    y = y0 + widthp * row
    GraphicsWindow.DrawLine(x0, y, x1, y)
  EndFor
  For row = 1 To rows2
    For col = 1 To cols2
      cell[col][row] = 1
    EndFor
  EndFor
  cell[2 * col0][2 * row0] = 0  ' generate start
  nWalls = 0
EndSub

Sub DoNothing
EndSub

Sub DrawGoal
  ' param x0, y0
  ' param cols, rows
  ' param width
  xg = x0 + (cols - 1) * widthp
  yg = y0 + (rows - 1) * widthp
  GraphicsWindow.BrushColor = "Red"
  GraphicsWindow.FillTriangle(xg + 0.3 * widthp, yg + 0.2 * widthp, xg + 0.7 * widthp, yg + 0.4 * widthp, xg + 0.3 * widthp, yg + 0.6 * widthp)
  GraphicsWindow.PenColor = "Black"
  Program.Delay(int)  ' to synchronize with Silverlight
  GraphicsWindow.PenWidth = 2
  GraphicsWindow.DrawTriangle(xg + 0.3 * widthp, yg + 0.2 * widthp, xg + 0.7 * widthp, yg + 0.4 * widthp, xg + 0.3 * widthp, yg + 0.6 * widthp)
  GraphicsWindow.DrawLine(xg + 0.3  * widthp, yg + 0.6 * widthp, xg + 0.3 * widthp, yg + 0.8 * widthp)
  Program.Delay(int)  ' to synchronize with Silverlight
  GraphicsWindow.PenWidth = widthw
EndSub

Sub FindPassage
  xt = Turtle.X
  yt = Turtle.Y
  back = Math.Remainder(Turtle.Angle + 180, 360)
  Turtle.TurnRight()
  nturn = 1
  IsWall()
  If bWall Then
    Turtle.TurnLeft()
    nturn = nturn - 1
    IsWall()
  EndIf
  While bWall
    Turtle.TurnRight()
    nturn = nturn + 1
    IsWall()
  EndWhile
EndSub

Sub FindWallInList
  ' param i - index to remove of wall list
  ' return iPrev - previous index of i
  ' return iFound - i if found
  iFound = 0
  c = 1
  While i <> iFound And c <= nWalls
    c = c + 1
    iPrev = iFound
    iFound = iWalls[iFound]
  EndWhile
EndSub

Sub GenerateMaze
  ' Generate maze with randomized Prim's algorithm
  ' param x0, y0
  ' param cols, rows
  ' param width
  ' 1. Start with a grid full of walls.
  ClearMaze()
  ' 2. Pick a cell, mark it as part of the maze. Add the walls of the cell to the wall list.
  col = 2 * col0
  row = 2 * row0
  AddWallToList()
  ' 3. While there are walls in the list:
  While nWalls > 0
    ' 1. Pick a random wall from the list. If the cell on the opposite side isn't in the maze yet:
    GetRandomIndex()
    col = colOpp[i]
    row = rowOpp[i]
    If cell[col][row] = 1 Then
      ' 1. Make the wall a passage and mark the cell on the opposite side as part of the maze.
      colw = colWall[i]
      roww = rowWall[i]
      RemoveWallFromList()
      cell[colw][roww] = 0  ' wall
      cell[col][row] = 0    ' cell on the opposite side
      KnockDownWall()
      ' 2. Add the neighboring walls of the cell to the wall list.
      AddWallToList()
    Else
    ' 2. If the cell on the opposite side already was in the maze, remove the wall from the list.
      RemoveWallFromList()
    EndIf
  EndWhile
  DrawGoal()
EndSub

Sub GetRandomIndex
  ' get random index of wall list
  ' return i - index
  n = Math.GetRandomNumber(nWalls)
  i = 0
  For c = 1 To n
    i = iWalls[i]
  EndFor
EndSub

Sub InitTurtle
  AT_Show()
  Turtle.Show()
  Turtle.PenUp()
  Turtle.Speed = 9
  x = x0 + widthp / 2
  y = y0 + widthp / 2
  Turtle.MoveTo(x, y)
  AT_MoveTo()
  angle = 90 - Turtle.Angle
  Math_AdjustAngle()
  Turtle.Turn(angle)
  AT_Turn()
  Turtle.Speed = 10
  dir["Up"] = 0
  dir["Down"] = 180
  dir["Right"] = 90
  dir["Left"] = 270
EndSub

Sub IsWall
  ' param xt - x coodinate of Turtle
  ' param yt - y coodinate of Turtle
  ' return a - angle of Turtle
  ' return bWall - "True" if wall is in front of Turtle
  a = Math.Remainder(Turtle.Angle, 360)
  If a = back And nturn <= 2 Then
    bWall = "True"
  Else
    x = Math.Floor(xt + (widthp / 2) * Math.Sin(a / 180 * Math.Pi))
    y = Math.Floor(yt - (widthp / 2) * Math.Cos(a / 180 * Math.Pi))
    color = GraphicsWindow.GetPixel(x, y)
    If Text.GetLength(color) = 9 Then  ' for Silverlight
      color = "#" + Text.GetSubText(color, 4, 6)
    EndIf
    If color = colorWall Then
      bWall = "True"
    Else
      bWall = "False"
    EndIf
  EndIf
EndSub

Sub IsWall2
  ' param AT_x - x coodinate of player Turtle
  ' param AT_y - y coodinate of player Turtle
  ' return a2 - angle of player Turtle
  ' return bWall2 - "True" if wall is in front of Turtle
  a2 = Math.Remainder(AT_angle, 360)
  x2 = Math.Floor(AT_x + (widthp / 2) * Math.Sin(a2 / 180 * Math.Pi))
  y2 = Math.Floor(AT_y - (widthp / 2) * Math.Cos(a2 / 180 * Math.Pi))
  color2 = GraphicsWindow.GetPixel(x2, y2)
  If Text.GetLength(color2) = 9 Then  ' for Silverlight
    color2 = "#" + Text.GetSubText(color2, 4, 6)
  EndIf
  If color2 = colorWall Then
    bWall2 = "True"
  Else
    bWall2 = "False"
  EndIf
EndSub

Sub KnockDownWall
  ' param colw, roww - wall
  ' param col, row - cell on the opposite side
  GraphicsWindow.BrushColor = colorPassage
  x = x0 + (col / 2 - 1) * widthp
  y = y0 + (row / 2 - 1) * widthp
  GraphicsWindow.FillRectangle(x + (widthw / 2), y + (widthw / 2), widthp - widthw, widthp - widthw)
  GraphicsWindow.PenColor = colorPassage
  If Math.Remainder(colw, 2) = 1 Then ' vertical wall
    x = x0 + (colw - 1) / 2 * widthp
    y1 = y0 + (roww / 2 - 1) * widthp
    y2 = y0 + (roww / 2) * widthp
    GraphicsWindow.DrawLine(x, y1 + (widthw / 2), x, y2 - (widthw / 2))
  ElseIf Math.Remainder(roww, 2) = 1 Then ' horizontal wall
    x1 = x0 + (colw / 2 - 1) * widthp
    x2 = x0 + (colw / 2) * widthp
    y = y0 + (roww - 1) / 2 * widthp
    GraphicsWindow.DrawLine(x1 + (widthw / 2), y, x2 - (widthw / 2), y)
  EndIf
EndSub

Sub Move
  ' param a - angle of CPU Turtle
  ' return pos - Turtle position
  Turtle.Move(widthp)
  If a = 0 Then
    pos = pos - (cols + 1)
  ElseIf a = 90 Then
    pos = pos + 1
  ElseIf a = 180 Then
    pos = pos + (cols + 1)
  ElseIf a = 270 Then
    pos = pos - 1
  EndIf
EndSub

Sub Move2
  ' param a2 - angle of player Turtle
  ' return pos2 - Turtle position
  ' return moves2 - number of moves
  distance = widthp
  AT_Move()
  If a2 = 0 Then
    pos2 = pos2 - (cols + 1)
  ElseIf a2 = 90 Then
    pos2 = pos2 + 1
  ElseIf a2 = 180 Then
    pos2 = pos2 + (cols + 1)
  ElseIf a2 = 270 Then
    pos2 = pos2 - 1
  EndIf
EndSub

Sub OnKeyDown
  GraphicsWindow.KeyDown = DoNothing
  key = GraphicsWindow.LastKey
  angle = dir[key] - AT_angle
  Math_AdjustAngle()
  If angle <> 0 Then
    AT_Turn()
  EndIf
  IsWall2()
  If bWall2 = "False" Then
    Move2()
  EndIf
  GraphicsWindow.KeyDown = OnKeyDown
EndSub

Sub ReadyGo
  GraphicsWindow.BrushColor = "Black"
  GraphicsWindow.FillRectangle(x0 + 220, y0 - 40, 90, 30)
  Sound.PlayMusic("O5G4")
  Program.Delay(1000)
  GraphicsWindow.BrushColor = GraphicsWindow.GetColorFromRGB(225, 0, 0)
  GraphicsWindow.FillEllipse(x0 + 230, y0 - 35, 20, 20)
  Sound.PlayMusic("G4")
  Program.Delay(1000)
  GraphicsWindow.BrushColor = GraphicsWindow.GetColorFromRGB(255, 213, 11)
  GraphicsWindow.FillEllipse(x0 + 255, y0 - 35, 20, 20)
  Sound.PlayMusic("G4")
  Program.Delay(1000)
  GraphicsWindow.BrushColor = GraphicsWindow.GetColorFromRGB(2, 168, 72)
  GraphicsWindow.FillEllipse(x0 + 280, y0 - 35, 20, 20)
  Sound.PlayMusic("O6G2")
EndSub

Sub RemoveWallFromList
  ' param i - index to remove of wall list
  ' param wall[] - wall list
  ' param nWalls - number of wall list
  FindWallInList()
  If i = iFound Then ' found
    iWalls[iPrev] = iWalls[iFound]
    iWalls[iFound] = ""
    nWalls = nWalls - 1
  EndIf
EndSub

Sub ShowScore
  ' param high_score
  ' param player
  GraphicsWindow.BrushColor = "Black"
  GraphicsWindow.FillRectangle(x0 + 340, y0 - 40, 200, 30)
  Program.Delay(int)  ' to synchronize with Silverlight
  GraphicsWindow.FontSize = widthp * 0.4
  GraphicsWindow.BrushColor = "White"
  GraphicsWindow.DrawText(x0 + 350, y0 - 32, "High Score: " + high_score + "[s] by " + player)
EndSub

Sub SolveMaze
  pos = 1
  pos2 = 1
  goal = cols + (rows - 1) * (cols + 1)
  GraphicsWindow.KeyDown = OnKeyDown
  While pos <> goal And pos2 <> goal
    FindPassage()
    Move()
    Program.Delay(40)
  EndWhile
  GraphicsWindow.KeyDown = DoNothing
  Sound.PlayBellRing()
EndSub

Sub TrickForHandlerCompileError
  OnKeyDown = 0
  DoNothing = 0
EndSub

Sub AT_Init
  ' Another Turtle | Initialize
  ' param x, y - initial position
  AT_SIZE = 16
  AT_OFFSET = AT_SIZE / 2 - 2
  AT_obj = Shapes.AddImage("http://www.nonkit.com/smallbasic.files/AnotherTurtle.png")
  Program.Delay(int)  ' to synchronize with Silverlight
  x = Turtle.X
  y = Turtle.Y
  AT_MoveTo()
  angle = Turtle.Angle - AT_angle
  Math_AdjustAngle()
  AT_Turn()
  'Shapes.HideShape(AT_obj)
EndSub

Sub AT_Show
  ' Another Turtle | Show
  Shapes.ShowShape(AT_obj)
EndSub

Sub AT_Move
  ' Another Turtle | Move
  ' param distance
  AT_x = AT_x + distance * Math.Sin(Math.GetRadians(AT_angle))
  AT_y = AT_y - distance * Math.Cos(Math.GetRadians(AT_angle))
  Shapes.Animate(AT_obj, AT_x - AT_OFFSET, AT_y - AT_OFFSET, distance * 5)
EndSub

Sub AT_MoveTo
  ' Another Turtle | Move to given position
  ' param x
  ' param y
  Stack.PushValue("local", x)
  Stack.PushValue("local", y)
  x = x - AT_x
  y = y - AT_y
  Math_CartesianToPolar()
  angle = a + 90 - AT_angle
  distance = r
  AT_Turn()
  AT_Move()
  y = Stack.PopValue("local")
  x = Stack.PopValue("local")
EndSub

Sub AT_Turn
  ' Another Turtle | Turn given angle
  ' param angle
  Stack.PushValue("local", angle)
  angle = AT_angle + angle
  Math_AdjustAngle()
  If AT_angle < angle Then
    sa = 1
  Else
    sa = -1
  EndIf
  For _a = AT_angle To angle Step sa
    Shapes.Rotate(AT_obj, _a)
    Program.Delay(2)
  EndFor
  AT_angle = angle
  angle = Stack.PopValue("local")
EndSub

Sub AT_TurnRight
  ' Another Turtle | Turn Right
  For angle = AT_angle To AT_angle + 90
    Shapes.Rotate(AT_obj, a)
    Program.Delay(2)
  EndFor
  AT_angle = angle - 1
EndSub

Sub Math_AdjustAngle
  ' Math | adjust angle [0, 360) degree
  ' param angle
  ' return angle
  angle = Math.Remainder(angle, 360)
  If angle < 0 Then
    angle = angle + 360
  EndIf
EndSub

Sub Math_CartesianToPolar
  ' Math | convert cartesian coodinate to polar coordinate
  ' param x, y - cartesian coordinate
  ' return r, a - polar coordinate
  r = Math.SquareRoot(x * x + y * y)
  If x = 0 And y > 0 Then
    a = 90 ' [degree]
  ElseIf x = 0 And y < 0 Then
    a = -90
  Else
    a = Math.ArcTan(y / x) * 180 / Math.Pi
  EndIf
  If x < 0 Then
    a = a + 180
  ElseIf x > 0 And y < 0 Then
    a = a + 360
  EndIf
EndSub
Copyright (c) Microsoft Corporation. All rights reserved.