Microsoft Small Basic

Program Listing:
Embed this in your website
'=====================================================================
'CREATES A MAZE - HOPEFULLY GUARANTEED THAT ALL REGIONS ARE CONNECTED, WHILE STILL RANDOM AND HARD TO TRAVERSE
'box[i][j][k] is 1 for a border and 0 for a hole: i is row, j is column, k=1(left) k=2(right) k= 3(top) k=4(bottom)
'=====================================================================

'=====================================================================
'KEY PARAMETERS AND SETUP - CHANGE THESE HERE OR DURING THE GAME TO CREATE LEVELS
'=====================================================================

nbox = 30 'Number of cells in the X and Y directions
wbox = 800/nbox 'Width of each cell in pixels
nFood = 10 'Number of food
nPill = nbox/4 'Number of pills
timeStep = 100 'ms time step interval (speed)
pillUpdate = 4 'Update pill position frequency (larger number is slower pills - minimum 1 (every time step))

initialise()

'=====================================================================
'MAIN GAME LOOP
'=====================================================================

While (playing = 1)
  startTime = Clock.ElapsedMilliseconds
  'Chaeck if pill updates are needed for this time step
  ipill = ipill+1
  updatePill = 0
  If (Math.Remainder(ipill,pillUpdate) = 0) Then
    updatePill = 1
  EndIf
  'Move balls
  For i = 1 To 2
    If (keyStatus[keyLeft[i]] And box[x[i]][y[i]][1] = 0) Then
      x[i] = x[i]-1
    EndIf
    If (keyStatus[keyRight[i]] And box[x[i]][y[i]][2] = 0) Then
      x[i] = x[i]+1
    EndIf
    If (keyStatus[keyUp[i]] And box[x[i]][y[i]][3] = 0) Then
      y[i] = y[i]-1
    EndIf
    If (keyStatus[keyDown[i]] And box[x[i]][y[i]][4] = 0) Then
      y[i] = y[i]+1
    EndIf
    If (keyStatus[keyJump[i]]) Then
      x[i] = Math.GetRandomNumber(nbox)
      y[i] = Math.GetRandomNumber(nbox)
      numJump[i] = numJump[i]+1
      Shapes.SetOpacity(ball[i],Math.Max(10,100-10*numJump[i])) 'To make it harder to jump a lot
      keyStatus[keyJump[i]] = "False" 'Another keyDown needed to jump again
    EndIf
    'Draw movement
    xCen = (x[i]-0.5)*wbox
    yCen = (y[i]-0.5)*wbox
    Shapes.Move(ball[i],xCen-radius,yCen-radius)
    'Check for food Hit
    For j = 1 To nFood
      If (hitFood[j] = 0 And x[i] = xFood[j] And y[i] = yFood[j]) Then
        Shapes.Remove(food[j])
        score[i] = score[i]+1
        hitFood[j] = 1
        numFoodHit = numFoodHit+1
        Sound.PlayChime()
        GraphicsWindow.Title = "Red "+score[1]+" : Blue "+score[2]
      EndIf
    EndFor
    'Check for pill Hit
    If (updatePill = 1) Then
      For j = 1 To nPill
        If (x[i] = xPill[j] And y[i] = yPill[j]) Then
          score[i] = score[i]-1
          Sound.PlayBellRing()
          GraphicsWindow.Title = "Red "+score[1]+" : Blue "+score[2]
        EndIf
      EndFor
    EndIf
  EndFor
  'Move pills
  'An interesting problem to get the pills moving like they have a purpose - half successsful here
  If (updatePill = 1) Then
    For j = 1 To nPill
      moved = 0
      While (moved = 0)
        'Try to go the same direction as last time, and not back where we have just been
        'Pure random motion doesn't get the pills moving very far
        rand = Math.GetRandomNumber(100)
        If (rand > 50) Then 'Same direction 50%
          k = lastdir[j]
        ElseIf (rand > 5) Then 'Change direction by 90 degrees 45%
          If (lastdir[j] <=2) Then 'left/right last then up/down now
            k = 2+Math.GetRandomNumber(2)
          Else 'up/down last then left/right now
            k = Math.GetRandomNumber(2)
          EndIf
        Else 'Reverse direction 5%
          If (lastdir[j] <=2) Then
            k = 3-lastdir[j]
          Else
            k = 7-lastdir[j]
          EndIf
        EndIf
        If (box[xPill[j]][yPill[j]][k] = 0) Then
          If (k = 1) Then
            xPill[j] = xPill[j]-1
          ElseIf (k = 2) Then
            xPill[j] = xPill[j]+1
          ElseIf (k = 3) Then
            yPill[j] = yPill[j]-1
          ElseIf (k = 4) Then
            yPill[j] = yPill[j]+1
          EndIf
          xCen = (xPill[j]-0.5)*wbox
          yCen = (yPill[j]-0.5)*wbox
          Shapes.Animate(pill[j],xCen-radius,yCen-radius,timeStep*pillUpdate)
          moved = 1
          lastdir[j] = k
        EndIf
      EndWhile
    EndFor
  EndIf
  'Check for end of game
  If (numFoodHit = nFood Or keyStatus["Escape"]) Then
    endOfGame()
  EndIf
  delayTime = timeStep - (Clock.ElapsedMilliseconds-startTime) 'Remove time used for the calculations
  If (delayTime > 0) Then
    Program.Delay(delayTime)
  EndIf
EndWhile

'=====================================================================
'INITIALISATION AND FINISH SUBROUTINES
'=====================================================================

Sub splashScreen
  GraphicsWindow.Clear()
  GraphicsWindow.BrushColor = GraphicsWindow.BackgroundColor
  bg = Shapes.AddRectangle(gw,gh)
  op = 100
  Shapes.SetOpacity(bg,op)

  GraphicsWindow.FontSize = gw/40
  GraphicsWindow.BrushColor = "Red"
  GraphicsWindow.DrawText(gw/10,0.1*gh,"Player 1 is Red and uses controls")
  GraphicsWindow.DrawText(gw/5,0.14*gh,"Left - Left Arrow")
  GraphicsWindow.DrawText(gw/5,0.18*gh,"Right - Right Arrow")
  GraphicsWindow.DrawText(gw/5,0.22*gh,"Up - Up Arrow")
  GraphicsWindow.DrawText(gw/5,0.26*gh,"Down - Down Arrow")
  GraphicsWindow.DrawText(gw/5,0.30*gh,"Random Jump - Right Control")
  GraphicsWindow.BrushColor = "Blue"
  GraphicsWindow.DrawText(gw/10,0.4*gh,"Player 2 is Blue and uses controls")
  GraphicsWindow.DrawText(gw/5,0.44*gh,"Left - Z")
  GraphicsWindow.DrawText(gw/5,0.48*gh,"Right - X")
  GraphicsWindow.DrawText(gw/5,0.52*gh,"Up - Q")
  GraphicsWindow.DrawText(gw/5,0.56*gh,"Down - A")
  GraphicsWindow.DrawText(gw/5,0.60*gh,"Random Jump - Left Control")
  GraphicsWindow.BrushColor = "Black"
  GraphicsWindow.DrawText(gw/10,0.7*gh,"Collect the green food and avoid the yellow pills to win")
  GraphicsWindow.DrawText(gw/10,0.74*gh,"Press ESCAPE to restart a game")
  GraphicsWindow.DrawText(gw/10,0.78*gh,"Press SPACE to start")

  GraphicsWindow.BrushColor = GraphicsWindow.BackgroundColor
  While (GraphicsWindow.LastKey <> "Space")
    op = op-1
    Shapes.SetOpacity(bg,op)
    Program.Delay(100)
  EndWhile
EndSub

Sub initialise
  'Maze creation
  gw = nbox*wbox
  gh = gw
  GraphicsWindow.Width = gw
  GraphicsWindow.Height = gh
  GraphicsWindow.Top = 0
  GraphicsWindow.Left = (Desktop.Width-gw)/2
  GraphicsWindow.BackgroundColor = "LightBlue"
  GraphicsWindow.Title = "Red "+0+" : Blue "+0
  'GraphicsWindow.CanResize = "False"
  splashScreen()
  GraphicsWindow.Clear()
  radius = wbox/2-1
  createMaze()

  'Food - create before players so they are the bottom shapes
  GraphicsWindow.BrushColor = "Green"
  GraphicsWindow.PenColor = "Green"
  For i = 1 To nFood
    Shapes.Remove(food[i]) 'Remove shape (if it exists from previous game) before creating a new one
    food[i] = Shapes.AddEllipse(2*radius,2*radius)
    xFood[i] = Math.GetRandomNumber(nbox)
    yFood[i] = Math.GetRandomNumber(nbox)
    xCen = (xFood[i]-0.5)*wbox
    yCen = (yFood[i]-0.5)*wbox
    Shapes.Move(food[i],xCen-radius,yCen-radius)
    hitFood[i] = 0
  EndFor
  numFoodHit = 0

  'Pills - create before players so they are behind player balls
  GraphicsWindow.BrushColor = "Yellow"
  GraphicsWindow.PenColor = "Yellow"
  For i = 1 To nPill
    Shapes.Remove(pill[i]) 'Remove shape (if it exists from previous game) before creating a new one
    pill[i] = Shapes.AddEllipse(2*radius,2*radius)
    xPill[i] = Math.GetRandomNumber(nbox)
    yPill[i] = Math.GetRandomNumber(nbox)
    xCen = (xPill[i]-0.5)*wbox
    yCen = (yPill[i]-0.5)*wbox
    Shapes.Move(pill[i],xCen-radius,yCen-radius)
    lastdir[i] = Math.GetRandomNumber(4)
  EndFor

  'Player 1 Ball
  GraphicsWindow.BrushColor = "Red"
  GraphicsWindow.PenColor = "Red"
  Shapes.Remove(ball[1])
  ball[1] = Shapes.AddEllipse(2*radius,2*radius)
  x[1] = Math.GetRandomNumber(nbox)
  y[1] = Math.GetRandomNumber(nbox)
  keyLeft[1] = "Left"
  keyRight[1] = "Right"
  keyUp[1] = "Up"
  keyDown[1] = "Down"
  keyJump[1] = "RightCtrl"
  score[1] = 0
  numJump[1] = 0

  'Player 2 Ball
  GraphicsWindow.BrushColor = "Blue"
  GraphicsWindow.PenColor = "Blue"
  Shapes.Remove(ball[2])
  ball[2] = Shapes.AddEllipse(2*radius,2*radius)
  x[2] = Math.GetRandomNumber(nbox)
  y[2] = Math.GetRandomNumber(nbox)
  keyLeft[2] = "Z"
  keyRight[2] = "X"
  keyUp[2] = "Q"
  keyDown[2] = "A"
  keyJump[2] = "LeftCtrl"
  score[2] = 0
  numJump[2] = 0

  GraphicsWindow.KeyDown = OnKeyDown
  GraphicsWindow.KeyUp = OnKeyUp
  playing = 1
EndSub

Sub EndOfGame
  GraphicsWindow.Clear()
  GraphicsWindow.FontSize = gw/15
  GraphicsWindow.BrushColor = "Red"
  GraphicsWindow.DrawText(gw/10,0.2*gh,"Player 1 scores "+score[1])
  GraphicsWindow.BrushColor = "Blue"
  GraphicsWindow.DrawText(gw/10,0.4*gh,"Player 2 scores "+score[2])
  GraphicsWindow.FontSize = gw/40
  GraphicsWindow.BrushColor = "Black"
  GraphicsWindow.DrawText(gw/10,0.6*gh,"Press SPACE to start")
  While (GraphicsWindow.LastKey <> "Space")
    Program.Delay(100)
  EndWhile
  initialise()
EndSub

'=====================================================================
'MAZE CREATION SUBROUTINES
'=====================================================================

Sub createMaze
  'Start by all faces set to a border (barrier)
  For i = 1 To nbox
    For j = 1 To nbox
      For k = 1 To 4
        box[i][j][k] = 1
      EndFor
    EndFor
  EndFor

  'Make some holes in the box sides - not essential but gives a better grid faster
  If ("True") Then
    For i = 1 To nbox
      For j = 1 To nbox
        For l = 1 To 1 'Just one hole here
          holeMade = 0
          While (holeMade = 0) 'Keep trying until an internal hole is made
            k = Math.GetRandomNumber(4)
            makeHole()
          EndWhile
        EndFor
      EndFor
    EndFor
  EndIf

  'Make some random holes in blocks that have 3 or more borders - not essential but gives a better grid faster
  If ("True") Then
    For n = 1 To nbox*nbox
      i = Math.GetRandomNumber(nbox)
      j = Math.GetRandomNumber(nbox)
      nborder = 0
      For k = 1 To 4
        nborder = nborder + box[i][j][k]
      EndFor
      If (nborder >= 3) Then
        holeMade = 0
        ntry = 0
        While (holeMade = 0 And ntry < 10) 'Keep trying until an internal hole is made OR we cant
          ntry = ntry+1
          k = Math.GetRandomNumber(4)
          While (box[i][j][k] = 0) 'Must be a face that is currently a border so keep having a go while we find faces with a hole
            k = Math.GetRandomNumber(4)
          EndWhile
          makeHole()
        EndWhile
      EndIf
    EndFor
  EndIf

  'Ensure all boxes are connected - add some more holes as required - some debugging available here
  checkConnectivity()

  'Draw the box borders
  GraphicsWindow.PenColor = "Black"
  For i = 1 To nbox
    For j = 1 To nbox
      xCen = (i-0.5)*wbox
      yCen = (j-0.5)*wbox
      For k = 1 To 4
        If (box[i][j][1] = 1) Then
          GraphicsWindow.DrawLine(xCen-0.5*wbox,yCen-0.5*wbox,xCen-0.5*wbox,yCen+0.5*wbox)
        EndIf
        If (box[i][j][2] = 1) Then
          GraphicsWindow.DrawLine(xCen+0.5*wbox,yCen-0.5*wbox,xCen+0.5*wbox,yCen+0.5*wbox)
        EndIf
        If (box[i][j][3] = 1) Then
          GraphicsWindow.DrawLine(xCen-0.5*wbox,yCen-0.5*wbox,xCen+0.5*wbox,yCen-0.5*wbox)
        EndIf
        If (box[i][j][4] = 1) Then
          GraphicsWindow.DrawLine(xCen-0.5*wbox,yCen+0.5*wbox,xCen+0.5*wbox,yCen+0.5*wbox)
        EndIf
      EndFor
    EndFor
  EndFor
EndSub

Sub makeHole
  holeMade = 0
  'If a hole is made on one box, then it must also be a hole on its neighbour
  'Don't make holes on the outside maze boundary
  If (k = 1 And i > 1) Then
    box[i][j][1] = 0
    box[i-1][j][2] = 0
    holeMade = 1
  ElseIf (k = 2 And i < nbox) Then
    box[i][j][2] = 0
    box[i+1][j][1] = 0
    holeMade = 1
  ElseIf (k = 3 And j > 1) Then
    box[i][j][3] = 0
    box[i][j-1][4] = 0
    holeMade = 1
  ElseIf (k = 4 And j < nbox) Then
    box[i][j][4] = 0
    box[i][j+1][3] = 0
    holeMade = 1
  EndIf
EndSub

'Utility to check maze connectivity - the idea is to find all boxes in unconnected regions,
'then open up a connecting hole between unconnected regions
Sub checkConnectivity
  'Initially no regions defined
  For i = 1 To nbox
    For j = 1 To nbox
      conn[i][j] = 0
    EndFor
  EndFor
  'Recursively find which unconnected region each box is in
  connection = 0
  'GraphicsWindow.FontSize = 10 'Debugging
  For i = 1 To nbox
    For j = 1 To nbox
      If (conn[i][j] = 0) Then
        connection = connection+1
        'The stacks hold all the unprocessed blocks in the current connection region
        'Unprocessed is those where all connected neighbours have not been found (and added to the stack for later processing)
        'When the stack is empy there are no more connected blocks in this region so check for next block with no region set (it must be a new region)
        Stack.PushValue("connectI",i)
        Stack.PushValue("connectJ",j)
        While (Stack.GetCount("connectI") > 0)
          ii = Stack.PopValue("connectI")
          jj = Stack.PopValue("connectJ")
          conn[ii][jj] = connection
          'xCen = (ii-0.5)*wbox 'Debugging
          'yCen = (jj-0.5)*wbox 'Debugging
          'GraphicsWindow.DrawText(xCen-(wbox/2-2),yCen-(wbox/2-2),connection) 'Debugging
          If (box[ii][jj][1] = 0 And conn[ii-1][jj] = 0) Then
            Stack.PushValue("connectI",ii-1)
            Stack.PushValue("connectJ",jj)
          EndIf
          If (box[ii][jj][2] = 0 And conn[ii+1][jj] = 0) Then
            Stack.PushValue("connectI",ii+1)
            Stack.PushValue("connectJ",jj)
          EndIf
          If (box[ii][jj][3] = 0 And conn[ii][jj-1] = 0) Then
            Stack.PushValue("connectI",ii)
            Stack.PushValue("connectJ",jj-1)
          EndIf
          If (box[ii][jj][4] = 0 And conn[ii][jj+1] = 0) Then
            Stack.PushValue("connectI",ii)
            Stack.PushValue("connectJ",jj+1)
          EndIf
        EndWhile
      EndIf
    EndFor
  EndFor
  'Open holes between adjacent unconnected regions - only want to open the first connected to lower region number
  'i.e connect region 2 to 1, 3 to (2 or 1), 4 to (3, 2, or 1) etc with only one connection (hole) opened between them
  'GraphicsWindow.PenColor = "Red" 'Debugging
  For iCon = 2 To connection
    For i = 1 To nbox
      For j = 1 To nbox
        If (conn[i][j] = iCon) Then
          'xCen = (i-0.5)*wbox 'Debugging
          'yCen = (j-0.5)*wbox 'Debugging
          'Any neighbour block with a different region number must have a barrier, so open it and move to the next region at 'nextCon'
          If (i > 1 And conn[i-1][j] < iCon) Then
            box[i][j][1] = 0
            box[i-1][j][2] = 0
            'GraphicsWindow.DrawLine(xCen-0.5*wbox,yCen-0.5*wbox,xCen-0.5*wbox,yCen+0.5*wbox) 'Debugging
            Goto nextCon
          EndIf
          If (i < nbox And conn[i+1][j] < iCon) Then
            box[i][j][2] = 0
            box[i+1][j][1] = 0
            'GraphicsWindow.DrawLine(xCen+0.5*wbox,yCen-0.5*wbox,xCen+0.5*wbox,yCen+0.5*wbox) 'Debugging
            Goto nextCon
          EndIf
          If (j > 1 And conn[i][j-1] < iCon) Then
            box[i][j][3] = 0
            box[i][j-1][4] = 0
            'GraphicsWindow.DrawLine(xCen-0.5*wbox,yCen-0.5*wbox,xCen+0.5*wbox,yCen-0.5*wbox) 'Debugging
            Goto nextCon
          EndIf
          If (j < nbox And conn[i][j+1] < iCon) Then
            box[i][j][4] = 0
            box[i][j+1][3] = 0
            'GraphicsWindow.DrawLine(xCen-0.5*wbox,yCen+0.5*wbox,xCen+0.5*wbox,yCen+0.5*wbox) 'Debugging
            Goto nextCon
          EndIf
        EndIf
      EndFor
    EndFor
    nextCon:
  EndFor
EndSub

'=====================================================================
'KEYPRESS SUBROUTINES
'=====================================================================

Sub OnKeyDown
  key = GraphicsWindow.LastKey
  keyStatus[key] = "True"
EndSub

Sub OnKeyUp
  key = GraphicsWindow.LastKey
  keyStatus[key] = "False"
EndSub
Copyright (c) Microsoft Corporation. All rights reserved.