Microsoft Small Basic

Program Listing:
Embed this in your website
' SNAKEBITE-SB
' by Davey~Wavey, v1 Nov. 2009
' Written for SmallBasic v0.7
'
' Inspired by the ZX81 game SNAKEBITE.
'
' Eat the snake by chewing off it's tail. Eat all tail segments to progress to next level.
' Watch out though, you may just make it angry!
'
' Use the cursor keys to move around. (You can press any other key to stop moving!)

' NOTE: If it crashes a lot, try commenting out the 'Sound.PlayChimes()' line. SB doesn't like sound!
'
' I've laid the code out so that all the normal routines used in a game are present. If you're
' learning to write your own game, a similar setup should work well for you. Have fun!

' set up the permanent loop (Q key terminates when on intro screen)
While 1=1

  ' initialise game environment
  environment()

  ' show intro
  introScreen()

  While lives > 0
    ' INITIALISE LEVEL

    ' Next line won't play anything if we have already played the intro screen music!?!?!?!!!
    ' It also seems to cause the prog to crash more often when enabled.
    'Sound.Play (progDir + "\muzak.mid")

    levelConfig()           ' level maps can be found at end of program listing below - have fun designing some
    paintLevel()
    snakeSetup()
    playerSetup()


    ' MAIN LOOP
    While (snakeSize > 2) And lives > 0

      ' if snake 'change direction' delay has expired, try to change snake direction
      If snakeMoveDelay = 0 Then
        snakeDirection()          ' will only change direction at random times

        moveSnake()
      EndIf

      ' alternative method to check for key press - also delays movement of player
      If playerMoveDelay = 0 Then
        checkKeys()

        movePlayer()

        ' Check for PLAYER COLLISIONS
        playerCollisions()
      EndIf



      ' ADJUST (or reset) DELAYS
      ' I admit this isn't the best way to do this.
      ' To get accurate speed, you should be calculating the
      ' frames-per-second and using that to slow various elements down.
      If snakeMoveDelay > 0 Then
        snakeMoveDelay = snakeMoveDelay-1
      Else
        snakeMoveDelay = level[thisLevel]["snakeDelay"]
      EndIf

      If playerMoveDelay > 0 Then
        playerMoveDelay = playerMoveDelay-1
      Else
        playerMoveDelay = playerDelay
      EndIf


      ' test snake length increase - NOT CURRENTLY USED
      'If Mouse.IsLeftButtonDown="True" AND processing=0 Then
      ' addSegment()
      'ElseIf Mouse.IsLeftButtonDown="False" And processing=1 Then
      ' mUp()
      'EndIf

    EndWhile  ' main level (or lives) loop



    ' next line is irrellevant as the Sound.Play command above doesn't work!
    'Sound.Stop (progDir + "\muzak.mid")



    ' if we still have some lives, then we must have eaten all the snake
    If lives > 0 Then

      moveSnake()   ' update tail position since snake length just decreased

      GraphicsWindow.ShowMessage("YEAH, YOU BEAT THIS SNAKE!!!", "WHOOPIE!")

      ' move to next level
      thisLevel=thisLevel+1

      ' go back to level 1 if we've completed them all
      If thisLevel>levels Then
        thisLevel=1
      EndIf

      ' increase player speed (by decreasing the delay) - player gets a slight advantage for each level cleared :-)
      If playerDelay > 20 Then
        playerDelay = playerDelay - 10
      EndIf

      GraphicsWindow.Clear()
    EndIf


  EndWhile  ' lives > 0

  ' DEAD MEAT!

  ' next line doesn't work!
  'Sound.Play (progDir + "\gameover.mid")

  GraphicsWindow.ShowMessage("THAT PUNKY SNAKE BEAT YOU THIS TIME!!! You scored: "+score, "EATEN!!!")

  ' next line is irrellevant as the Play command above doesn't work!
  'Sound.Stop (progDir + "\gameover.mid")

EndWhile





' =====================================================================
' =====================================================================
' ====================== S U B R O U T I N E S ==============================
' =====================================================================
' =====================================================================





Sub environment
  ' initialise the game environment

  ' progDir = Program.Directory + "\assets" ' this is used to locate the graphics and music used in the game
  progDir = "http://smallbasic.com/drop/snakebite"

  GraphicsWindow.Title  = "~S~N~A~K~E~B~I~T~E~ v1 for SmallBasic by Davey~Wavey, Nov 2009"
  GraphicsWindow.BackgroundColor = "Black"
  GraphicsWindow.Width  = 800       ' grass sprite is 128 x 128
  GraphicsWindow.Height = 600       ' 4 x 128 plus status area
  GraphicsWindow.Left   = desktop.Width/2-(GraphicsWindow.Width/2)  ' 800
  GraphicsWindow.Top    = desktop.Height/2-(GraphicsWindow.Height/2)-100   '50

  gwTop     = 18                                                 ' set game window top offset
  gwBottom  = gwTop+(4*128)
  gwLeft    = (GraphicsWindow.Width/2)-(3*128)                  ' set game window left offset
  gwRight   = gwLeft+(6*128)

  ' initialise game environment
  thisLevel       = 1
  score           = 0
  lives           = 5
  playerDelay     = 500   ' speed of player movement - lower value = faster player = easier (unless you go too fast!)
  snakeMoveDelay  = 0     ' this gets set by each level and is the speed of the snake - lower = faster

  angrySnake      = -1    ' a counter. if >0 it indicates that snake is angry (makes snake faster)
  digestionDelay  = 0     ' delay to prevent multiple tail pieces being eaten at once.
  processing      = 0     ' a flag used in sub processes to prevent multiple runs

  player          = ""    ' blank player array
  snake           = ""    ' blank snake array

  'Timer.Interval = 2 ' set interval to check for key presses
  'Timer.Tick = checkKeys

EndSub


' =====================================================================


Sub introScreen
  ' display the game intro screen and wait for Space to be pressed

  GraphicsWindow.Clear()

  ' this Play command works, but then prevents any other music playing!?!?!
  Sound.Play (progDir + "\intro.mid")

  ' show intro graphic
  img = ImageList.LoadImage(progDir + "\titleScreen_800x600.jpg")
  GraphicsWindow.DrawImage(img, 0,0 )

  ' wait for keypress
  lastkey = ""
  While lastkey <> "Space"
    lastkey = GraphicsWindow.LastKey

    If lastkey = "Q" Then
      Program.End()
    EndIf
  EndWhile

  Sound.Stop (progDir + "\intro.mid")
  Program.Delay(500)
EndSub


' =====================================================================


Sub paintLevel
  ' draw the level objects at the start of each level


  ' create a black rectangle over the whole graphics window so we can create a fade effect after the level has been drawn
  GraphicsWindow.BrushColor   = "Black"
  bbox                        = Shapes.AddRectangle( 800, 600 )
  Shapes.SetOpacity           (bbox, 100)


  ' draw dirt
  img = ImageList.LoadImage(progDir + "\status_800x70.png")
  For y = 0 to 600 Step 70
    GraphicsWindow.DrawImage (img, 0, y)
  EndFor


  ' draw the grass
  img = ImageList.LoadImage(progDir + "\grass_128d.png")
  For x = 0 To 5
    For y = 0 To 3
      GraphicsWindow.DrawImage (img, (x*128)+gwLeft, (y*128)+gwTop)
    EndFor
  EndFor


  ' draw the flowers
  img = ImageList.LoadImage(progDir + "\flower1_32.png")
  Shapes.SetOpacity(img, 20)
  For x = 1 To 20
    GraphicsWindow.DrawImage (img, ((Math.GetRandomNumber(24)-1)*32)+gwLeft, ((Math.GetRandomNumber(16)-1)*32)+gwTop )
  EndFor
  img = ImageList.LoadImage(progDir + "\flower2_32.png")
  Shapes.SetOpacity(img, 20)
  For x = 1 To 10
    GraphicsWindow.DrawImage (img, ((Math.GetRandomNumber(24)-1)*32)+gwLeft, ((Math.GetRandomNumber(16)-1)*32)+gwTop )
  EndFor


  ' draw the level blocks
  img = ImageList.LoadImage(progDir + "\wall_32.png")
  For my = 1 To 16
    For mx = 1 To 24
      block = text.GetSubText( level[thisLevel][my], mx, 1 )

      ' position WALLS
      If block = "W" Then
        GraphicsWindow.DrawImage (img, ((mx-1)*32)+gwLeft, ((my-1)*32)+gwTop) ' DrawRectangle( (mx-1)*32, (my-1)*32, 32, 32 )
      EndIf

      ' position PLAYER
      If block = "P" Then
        player["x"] = ((mx-1)*32+12)+gwLeft    ' calculate x,y from 32 x 32 level grid blocks
        player["startX"] = player["x"]         ' remember start position for when player dies
        player["y"] = ((my-1)*32+12)+gwTop
        player["startY"] = player["y"]
      EndIf

      ' position SNAKE
      If block = "S" Then
        level[thisLevel]["snakeX"] = ((mx-1)*32+16)+gwLeft
        level[thisLevel]["snakeY"] = ((my-1)*32+16)+gwTop
      EndIf

    EndFor
  EndFor


  ' draw the border (using the snake body part)
  img = ImageList.LoadImage(progDir + "\body_18.png")
  For y = 0 To 600 Step 530
    For x=5 to 790 Step 18
      ' draw top and bottom border
      GraphicsWindow.DrawImage ( img, x, y)
    EndFor
  EndFor
  For x = 0 To 800 Step 784
    For y = 15 to 530 Step 18
      ' draw left and right border
      GraphicsWindow.DrawImage ( img, x, y)
    EndFor
  EndFor


  ' show status bar
  status()


  ' fade the screen in
  Shapes.Move (bbox, 0, 0)
  For lwp = 100 To 0 Step -1
    Shapes.SetOpacity(bbox, lwp)
    Program.Delay(20)
  EndFor

EndSub


' =====================================================================


Sub snakeSetup
  ' create the snake array and display snake in starting position

  headPart    = ImageList.LoadImage(progDir + "\head_24.png")
  headAngryPart = ImageList.LoadImage(progDir + "\headAngry_24.png")
  headSize    = 22 '24 ' radius of head
  headOffset  = 11

  bodyPart    = ImageList.LoadImage(progDir + "\body_18.png")
  bodySize    = 18 '18
  bodyOffset  = 9

  tailPart    = ImageList.LoadImage(progDir + "\tail_16.png")
  tailSize    = 16 '32 '16
  tailOffset  = 8

  snake = ""
  snake[0]["happySnake"] = Shapes.AddImage(headPart)
  snake[0]["angrySnake"] = Shapes.AddImage(headAngryPart)
  snakeSize = level[thisLevel]["snakeSize"]   ' this value will change during game as snake eats/is eaten

  ' remember current pen width
  'pw = GraphicsWindow.PenWidth
  'GraphicsWindow.PenWidth = 0

  For snakePart = 1 to snakeSize

    ' create the snake segments
    If snakePart = 1 Then
      'GraphicsWindow.BrushColor="Brown"
      snake[snakePart]["sprite"] = snake[0]["happySnake"]   'Shapes.AddEllipse(headSize,headSize) ' head
      ' Zoom for this shape has been done above

    ElseIf snakePart <> snakeSize Then
      'GraphicsWindow.BrushColor="Chocolate"
      snake[snakePart]["sprite"] = Shapes.AddImage(bodyPart)  'Shapes.AddEllipse(bodySize,bodySize) ' body
    Else
      'GraphicsWindow.BrushColor="SandyBrown"
      snake[snakePart]["sprite"] = Shapes.AddImage(tailPart)    'Shapes.AddEllipse(tailSize,tailSize) ' tail
    EndIf

    ' locate snake on screen - probably needs to be part of each level config
    snake[snakePart]["x"] = level[thisLevel]["snakeX"]
    snake[snakePart]["y"] = level[thisLevel]["snakeY"]

    ' as all segments start at the same x,y we must increment the initial delay for each successive segment
    If snakePart > 2 Then
      snake[snakePart]["delay"] = ((snakePart-1)*(bodySize-4))+4
    Else
      snake[2]["delay"] = bodySize '18
    EndIf

  EndFor

  ' set initial direction
  snake[1]["moves"] = level[thisLevel]["snakeDir"]
  snake[1]["delay"] = "0"   ' need to initialise delay for snake head - used to determine delay before direction change

  ' reinstate previous pen width
  'GraphicsWindow.PenWidth=pw
EndSub


' =====================================================================


Sub playerSetup
  ' create the player and display at start position

  'GraphicsWindow.BrushColor="Lime"
  img = ImageList.LoadImage(progDir + "\player2_24.png")    ' from www.freeicons.dk
  player["sprite"] = Shapes.AddImage(img)    'Shapes.AddRectangle( 20,20 )
  Shapes.Move( player["sprite"], player["x"]-11, player["y"]-11 )     ' -11 offset instead of -12 works better!?!?!

  playerMoveDelay  = playerDelay                     ' initialise player movement speed (delay)

EndSub


' =====================================================================


Sub snakeDirection
  ' check if snake is ready for a CHANGE of DIRECTION

  ' using the 'delay' element of the snake head (snake[1]) as a counter for the change direction delay
  ' this delay gets decreased in the moveSnake sub
  If snake[1]["delay"] = 0  Then

    ' reset change direction delay
    snake[1]["delay"] = level[thisLevel]["dirDelay"]

    ' yes, time for a direction change, but only 25% chance of changing
    If Math.GetRandomNumber(4) <> 3 Then

      ' not yet - leave the snake going the same way and delay another direction change for a while
      snake[1]["delay"] = level[thisLevel]["dirDelay"]

    Else
      ' yep, let's change direction
      dir = Math.GetRandomNumber(4)

      ' EDGE BOUNCE - if snake is heading into window edge, force a direction change
      'If (dir = 1 And snake[1]["y"] < 20) Then
      ' dir = 3 ' go S
      'EndIf
      'If (dir = 3 And snake[1]["y"] > GraphicsWindow.Height-(20)) Then
      ' dir = 1 ' go N
      'EndIf
      'If (dir = 4 And snake[1]["x"] < 20) Then
      ' dir = 2 ' go E
      'EndIf
      'If (dir = 2 And snake[1]["x"] > GraphicsWindow.Width-(8+20)) Then
      ' dir = 4 ' go W
      'EndIf

      ' point snake head in appropriate direction
      snake[1]["moves"] = dir

      ' reset delay until next direction change
      snake[1]["delay"] = level[thisLevel]["dirDelay"]

    EndIf



    ' ANGRY SNAKE calculation
    ' to slow this effect down, we're only trying to instigate 'angry mode' at each direction change
    If (level[thisLevel]["getAngry"] = 1) Then

      ' only try to instigate angry mode in snake is currently happy
      If angrySnake < 0 Then
        'GraphicsWindow.Title="HAPPY SNAKE {:-)"

        rn = Math.GetRandomNumber(100)
        If (rn = 7) Then
          angrySnake = Math.GetRandomNumber(50)+20
          level[thisLevel]["snakeDelay"] = level[thisLevel]["snakeDelay"]/2
        EndIf

        ' if angrySnake counter has reached zero, reset snake speed back to normal
      ElseIf angrySnake = 0 Then
        level[thisLevel]["snakeDelay"] = level[thisLevel]["snakeDelay"]*2      ' set snake delay back to level setting

      EndIf

      If angrySnake > -1 Then
        angrySnake = angrySnake-1     ' decrease angry snake counter
        'GraphicsWindow.Title="ANGRY SNAKE }:-/"
      EndIf

    EndIf

  EndIf   ' change direction delay=0

EndSub


' =====================================================================


Sub moveSnake
  ' move snake segments

  For snakePart = 1 To snakeSize

    ' find out which direction we're moving in
    dir = text.GetSubText(snake[snakePart]["moves"], 1, 1)


    ' check if head is about to collide with a wall
    If snakePart = 1 Then
      shapes.Rotate(snake[1]["sprite"], ((dir-1)*90) )    ' point snake head in correct direction

      snakeCollisions()
    EndIf



    ' only move snakepart if it's delay is zero - or this is the head, which always gets moved
    If snakePart = 1 Or snake[snakePart]["delay"] = 0 Then


      ' take direction off moves list for this snake part
      If snakePart > 1 Then
        snake[snakePart]["moves"] = Text.GetSubTextToEnd(snake[snakePart]["moves"], 2)
      EndIf

      ' add this movement onto next snake part's movement list
      If snakePart < snakeSize Then
        snake[snakepart+1]["moves"] = text.Append(snake[snakepart+1]["moves"],dir)
      EndIf

      ' update x,y co-ordinates of snake part
      If dir = 1 Then
        snake[snakePart]["y"] = snake[snakePart]["y"]-1      ' moving N

      ElseIf dir = 2 Then
        snake[snakePart]["x"] = snake[snakePart]["x"]+1      ' moving E

      ElseIf dir = 3 Then
        snake[snakePart]["y"] = snake[snakePart]["y"]+1      ' moving S

      ElseIf dir = 4 Then
        snake[snakePart]["x"] = snake[snakePart]["x"]-1      ' moving W
      EndIf


      ' wrap snake at window edge
      If snake[snakePart]["x"] < gwLeft Then
        snake[snakePart]["x"] = gwRight
      ElseIf snake[snakePart]["x"] > gwRight Then
        snake[snakePart]["x"] = gwLeft
      ElseIf snake[snakePart]["y"] < gwTop Then
        snake[snakePart]["y"] = gwBottom-8
      ElseIf snake[snakePart]["y"] > gwBottom-8 Then
        snake[snakePart]["y"] = gwTop
      EndIf

    EndIf


    ' The offsets allow for the various snake segment sizes
    If snakePart = 1 Then
      offset=headOffset
    ElseIf snakePart<>snakeSize Then
      offset = bodyOffset
    Else
      offset = tailOffset
    EndIf

    ' DRAW SNAKE PART in new position
    shapes.Move(snake[snakePart]["sprite"], snake[snakePart]["x"]-offset, snake[snakePart]["y"]-offset )

    ' DECREASE SNAKE PART DELAY
    If snake[snakePart]["delay"] > 0 Then
      snake[snakePart]["delay"] = snake[snakePart]["delay"] - 1
    EndIf

  EndFor


  ' as snake gets shorter, processing speed increases, so we need to add a delay
  If snakeSize < 7 Then   'level[thisLevel]["snakeSize"] Then
    'For snakePart = snakeSize To level[thisLevel]["snakeSize"]
      Program.Delay (0.2*(level[thisLevel]["snakeSize"]-snakeSize))
    'EndFor
  EndIf

EndSub


' =====================================================================


Sub snakeCollisions
  ' this is a recursive function
  ' it checks for a snake collision with a level object (i.e. a wall) in the current snake direction
  ' if it finds one, it chooses a new direction, then calls itself to check it for a collision
  ' after completing, 'dir' will have a direction that does not cause a collision

  ' Up
  If dir = 1 Then
    sx = math.Floor((snake[1]["x"]-gwLeft)/32)+1
    sy = math.Floor(((snake[1]["y"]-gwTop)-(headSize/2))/32)+1

    If Text.GetSubText( level[thisLevel][sy], sx, 1) = "W" Then
      ' choose a new direction then check it for collisions by recursing this sub
      dir = Math.GetRandomNumber(4)
      snakeCollisions()
    EndIf

    ' Down
  ElseIf dir = 3 Then
    sx = math.Floor((snake[1]["x"]-gwLeft)/32)+1
    sy = math.Floor(((snake[1]["y"]-gwTop)+(headSize/2))/32)+1

    If Text.GetSubText( level[thisLevel][sy], sx, 1) = "W" Then
      ' choose a new direction then check it for collisions by recursing this sub
      dir = Math.GetRandomNumber(4)
      snakeCollisions()
    EndIf

    ' Right
  ElseIf dir = 2 Then
    sx = math.Floor(((snake[1]["x"]-gwLeft)+(headSize/2))/32)+1
    sy = math.Floor((snake[1]["y"]-gwTop)/32)+1

    If Text.GetSubText( level[thisLevel][sy], sx, 1) = "W" Then
      ' choose a new direction then check it for collisions by recursing this sub
      dir = Math.GetRandomNumber(4)
      snakeCollisions()
    EndIf

    ' Left
  ElseIf dir = 4 Then
    sx = math.Floor(((snake[1]["x"]-gwLeft)-(headSize/2))/32)+1
    sy = math.Floor((snake[1]["y"]-gwTop)/32)+1

    If Text.GetSubText( level[thisLevel][sy], sx, 1) = "W" Then
      ' choose a new direction then check it for collisions by recursing this sub
      dir = Math.GetRandomNumber(4)
      snakeCollisions()
    EndIf
  EndIf

  ' set snake in new safe direction
  snake[1]["moves"] = dir

EndSub


' =====================================================================


Sub checkKeys

  ' indicate if player is to move
  keyPress = GraphicsWindow.LastKey

EndSub


' =====================================================================


Sub movePlayer
  ' depending on the key pressed, move the player in the required direction
  ' unless we hit the edge of the screen, or a wall

  If keyPress <> "" Then    ' only process if a key was pressed

    move = 0  ' initially prevent any movement

    ' CHECK LEFT
    If keyPress = "Left" Then
      px = math.Floor(((player["x"]-13)-gwLeft)/32)+1     ' subtract 13 from x to allow for half sprite width offset
      py = math.Floor(((player["y"]-gwTop))/32)+1

      ' hit edge of screen?
      If player["x"]-13 > gwLeft Then
        move = 1
      EndIf

      ' hit a wall?
      If Text.GetSubText( level[thisLevel][py], px, 1) = "W" Then
        move = 0
      EndIf
    EndIf

    ' CHECK RIGHT
    If keyPress = "Right" Then
      px = math.Floor(((player["x"]+13)-gwLeft)/32)+1
      py = math.Floor((player["y"]-gwTop)/32)+1

      ' hit edge of screen?
      If player["x"]+13 < gwRight Then
        move = 1
      EndIf

      ' hit a wall?
      If Text.GetSubText( level[thisLevel][py], px, 1) = "W" Then
        move = 0
      EndIf
    EndIf

    ' CHECK UP
    If keyPress = "Up"  Then
      px = math.Floor(((player["x"])-gwLeft)/32)+1
      py = math.Floor(((player["y"]-13)-gwTop)/32)+1

      ' hit edge of screen?
      If player["y"]-13 > gwTop Then
        move = 1
      EndIf

      ' hit a wall?
      If Text.GetSubText( level[thisLevel][py], px, 1) = "W" Then
        move = 0
      EndIf
    EndIf

    ' CHECK DOWN
    If keyPress = "Down" Then
      px = math.Floor(((player["x"])-gwLeft)/32)+1
      py = math.Floor(((player["y"]+13)-gwTop)/32)+1

      ' hit edge of screen?
      If player["y"]+13 < gwBottom Then
        move = 1
      EndIf

      ' hit a wall?
      If Text.GetSubText( level[thisLevel][py], px, 1) = "W" Then
        move = 0
      EndIf
    EndIf


    ' ADJUST PLAYER LOCATION and rotate player to point the correct direction
    If keyPress = "Left" Then
      player["x"] = player["x"] - move
      Shapes.Rotate(player["sprite"], 270)
    EndIf
    If keyPress = "Right" Then
      player["x"] = player["x"] + move
      Shapes.Rotate(player["sprite"], 90)
    EndIf
    If keyPress = "Up"  Then
      player["y"] = player["y"] - move
      Shapes.Rotate(player["sprite"], 0)
    EndIf
    If keyPress = "Down"  Then
      player["y"] = player["y"] + move
      Shapes.Rotate(player["sprite"], 180)
    EndIf


    ' MOVE PLAYER SPRITE
    Shapes.Move( player["sprite"], player["x"]-11, player["y"]-11 )
    keyPress = ""
  EndIf
EndSub


' =====================================================================


Sub playerCollisions
  ' detect player colliding with various things

  ' detect player/snake tail collision
  If (digestionDelay <= 0) Then

    ' have no idea why I have to subtract 4 from the tailOffset calculations here!!
    If (math.Abs(player["x"] - (snake[snakeSize]["x"]-tailOffset)-4) < 10) And (Math.Abs(player["y"] - (snake[snakeSize]["y"]-tailOffset)-4) < 10) AND snake[snakeSize]["delay"] = 0 Then
      digestionDelay = 60         ' don't allow another piece to be eaten for a while

      ' decrease snake length
      snakeSize = snakeSize - 1
      shapes.Remove(snake[snakeSize]["sprite"])
      snake[snakeSize]["sprite"] = snake[snakeSize+1]["sprite"]

      'level[thisLevel]["snakeDelay"] = level[thisLevel]["snakeDelay"] - (2*thisLevel) ' make snake faster each level - now set in level settings, therefore redundant here

      Sound.PlayChimes()
      score = score + 10        ' increase player score
      status()                ' update status area
    EndIf
  Else
    digestionDelay = digestionDelay-1   ' just decrease the delay
  EndIf

  ' detect player/snake head collision
  If (math.Abs(player["x"] - (snake[1]["x"]-headOffset)) < 16) And (Math.Abs(player["y"] - (snake[1]["y"]-headOffset)) < 16) Then
    playerEaten()
  EndIf

EndSub


' =====================================================================


Sub playerEaten
  ' when eaten, animate the player sprite for a while

  Sound.PlayBellRing()
  lives = lives - 1
  status()              ' update status area

  ' set player back to their start location
  player["x"] = player["startX"]
  player["y"] = player["startY"]

  ' if player was eaten at player start location, move player to snake start location to prevent continuous death
  If (math.Abs(player["x"] - (snake[1]["x"]-headOffset)) < 36) And (Math.Abs(player["y"] - (snake[1]["y"]-headOffset)) < 36) Then
    player["x"] = level[thisLevel]["snakeX"]
    player["y"] = level[thisLevel]["snakeY"]
  EndIf

  ' you spin me right round baby, right round, like a record....
  For x = 1 to 3
    For y = 0 To 360
      Shapes.Rotate( player["sprite"], y )
      Shapes.SetOpacity( player["sprite"], y/2 )
      Program.Delay(2)
    EndFor
  EndFor

  If lives > 0 Then
    Shapes.Animate( player["sprite"], player["x"]-11, player["y"]-11, 500 )
    For lwp = 1 to 1000000
    EndFor
  EndIf

EndSub


' =====================================================================


Sub addSegment
  ' NOT USING THIS AT PRESENT - USEFUL IF FOOD IS ADDED FOR SNAKE TO EAT
  ' this code hasn't been updated since snake was changed from Ellipse shapes to graphic shapes, so needs some work if used!

  processing = 1  ' flag that we're working on it!

  ' increase snake size and delay movement of new tail
  snakeSize = snakeSize + 1
  snake[snakeSize]["delay"] = snake[snakeSize-1]["delay"]+12

  ' need to remove old tail due to Small Basic's layering of graphic shapes
  Shapes.Remove(snake[snakeSize-1]["sprite"])

  ' add a new body part
  GraphicsWindow.BrushColor = "Chocolate"
  snake[snakeSize-1]["sprite"] = Shapes.AddEllipse(bodySize, bodySize)
  Shapes.Move(snake[snakeSize-1]["sprite"], snake[snakeSize-1]["x"], snake[snakeSize-1]["y"])

  ' add new tail
  snake[snakeSize]["x"] = snake[snakeSize-1]["x"]
  snake[snakeSize]["y"] = snake[snakeSize-1]["y"]
  GraphicsWindow.BrushColor = "SandyBrown"
  snake[snakeSize]["sprite"] = Shapes.AddEllipse(tailSize, tailSize)
  Shapes.Move(snake[snakeSize]["sprite"], snake[snakeSize]["x"], snake[snakeSize]["y"])
EndSub

Sub mUp
  ' NOT USED AT PRESENT - put here to handle mouse button presses
  ' used in conjunction with AddSegment subroutine above
  processing = 0  ' cancel the mousedown action - have to do this 'cos the 'puter is much faster than our likkle fingers
EndSub


' =====================================================================


Sub status
  ' show the various game values in status area

  GraphicsWindow.BrushColor    = "White"
  GraphicsWindow.PenColor      = "White"
  GraphicsWindow.FontSize      = 32

  GraphicsWindow.DrawText      ( 20, 550, "Round: Lives: Score:")
  GraphicsWindow.FillRectangle ( 140,555, 80, 32 )
  GraphicsWindow.FillRectangle ( 370,555, 80, 32 )
  GraphicsWindow.FillRectangle ( 610,555, 160, 32 )

  GraphicsWindow.BrushColor    = "Black"
  GraphicsWindow.DrawText      ( 170, 550, thisLevel )
  GraphicsWindow.DrawText      ( 400, 550, lives )
  GraphicsWindow.DrawText      ( 640, 550, score )
EndSub


' =====================================================================


Sub levelConfig
  ' configuration settings for each of the levels
  '
  ' to add new levels, simply duplicate the last one below, change it's level number, make a new layout,
  ' then increase the 'levels' value below. To test your new level without playing all the earlier ones, change
  ' the 'thisLevel' variable (at the beginning of the program code above).


  levels = 5


  level[1]["snakeDir"]  = Math.GetRandomNumber(4)  ' start snake off in a random direction: 1=N, 2=E, 3=S, 4=W
  level[1]["snakeSize"] = 12      ' length of snake
  level[1]["snakeDelay"]= 700    ' speed of snake (higher = slower)
  level[1]["dirDelay"]  = 50      ' delay (moves) before snake can change direction (lower = more jittery snake)
  level[1]["getAngry"]  = 1      ' flag indicating if the snake can switch to angry mode (=1) or not (=0)


  ' W = wall segment
  ' S = Snake start location
  ' P = Player start location

  level[0][0]=  ".........1.........2...."    ' just a ruler
  level[1][1]=  "W W"
  level[1][2]=  " "
  level[1][3]=  " WWWWWWWWWWWWWW "
  level[1][4]=  " "
  level[1][5]=  " "
  level[1][6]=  " S "
  level[1][7]=  " "
  level[1][8]=  " "
  level[1][9]=  " "
  level[1][10]= " "
  level[1][11]= " "
  level[1][12]= " P "
  level[1][13]= " "
  level[1][14]= " WWWWWWWWWWWWWW "
  level[1][15]= " "
  level[1][16]= "W W"


  level[2]["snakeDir"]  = Math.GetRandomNumber(4)  ' start snake off in a random direction: 1=N, 2=E, 3=S, 4=W
  level[2]["snakeSize"] = 14      ' length of snake
  level[2]["snakeDelay"]= 1100    ' speed of snake (higher = slower)
  level[2]["dirDelay"]  = 50      ' delay (moves) before snake can change direction (lower = more jittery snake
  level[2]["getAngry"]  = 1      ' flag indicating if the snake can switch to angry mode (=1) or not (=0)

  level[0][0]=  ".........1.........2...."    ' just a ruler
  level[2][1]=  " "
  level[2][2]=  " WWWWWWWWWWWWWWWW "
  level[2][3]=  " W W "
  level[2][4]=  " W WWWWWWWWWWWWW W "
  level[2][5]=  " W WW WWW WW W "
  level[2][6]=  " W WWWWWWWWWWWWW W "
  level[2][7]=  " W P WW S W "
  level[2][8]=  " WWWWW W WWWWWW "
  level[2][9]=  " WW W W "
  level[2][10]= " W WWWWWWW W "
  level[2][11]= " WWW W WW WW W "
  level[2][12]= " W WWWW WWWWWWW W "
  level[2][13]= " W W "
  level[2][14]= " WWWWWWWWWWWWWWWW "
  level[2][15]= " "
  level[2][16]= " "


  level[3]["snakeDir"]  = 1  'Math.GetRandomNumber(4) ' start snake off in a random direction: 1=N, 2=E, 3=S, 4=W
  level[3]["snakeSize"] = 16      ' length of snake
  level[3]["snakeDelay"]= 600    ' speed of snake (higher = slower)
  level[3]["dirDelay"]  = 30      ' delay (moves) before snake can change direction (lower = more jittery snake
  level[3]["getAngry"]  = 1      ' flag indicating if the snake can switch to angry mode (=1) or not (=0)

  level[0][0]=  ".........1.........2...."    ' just a ruler
  level[3][1]=  " "
  level[3][2]=  " WWWW WWWW "
  level[3][3]=  " WW W W WWW "
  level[3][4]=  " WW W WW "
  level[3][5]=  " W WWW WWW W "
  level[3][6]=  " W W W W "
  level[3][7]=  " WW WWWWW WWWWWW WW "
  level[3][8]=  " W P W "
  level[3][9]=  " WW WWWWW WWWWWW WW "
  level[3][10]= " W WW W W WW W "
  level[3][11]= " W WWWW WWWWW W "
  level[3][12]= " WW W W WW "
  level[3][13]= " WWWWWWW WWWWWWWW "
  level[3][14]= " WSW "
  level[3][15]= " W "
  level[3][16]= " "


  level[4]["snakeDir"]  = 3  'Math.GetRandomNumber(4) ' start snake off in a random direction: 1=N, 2=E, 3=S, 4=W
  level[4]["snakeSize"] = 18      ' length of snake
  level[4]["snakeDelay"]= 500    ' speed of snake (higher = slower)
  level[4]["dirDelay"]  = 20      ' delay (moves) before snake can change direction (lower = more jittery snake
  level[4]["getAngry"]  = 0      ' flag indicating if the snake can switch to angry mode (=1) or not (=0)

  level[0][0]=  ".........1.........2...."    ' just a ruler
  level[4][1]=  " W "
  level[4][2]=  " WSW "
  level[4][3]=  " W W "
  level[4][4]=  " W W "
  level[4][5]=  " W W "
  level[4][6]=  " W WW "
  level[4][7]=  " W WW "
  level[4][8]=  " W WW "
  level[4][9]=  " WW W "
  level[4][10]= " WW W "
  level[4][11]= " WW W "
  level[4][12]= " W W "
  level[4][13]= " W W "
  level[4][14]= " W W "
  level[4][15]= " WPW "
  level[4][16]= " W "



  level[5]["snakeDir"]  = 3  'Math.GetRandomNumber(4) ' start snake off in a random direction: 1=N, 2=E, 3=S, 4=W
  level[5]["snakeSize"] = 20      ' length of snake
  level[5]["snakeDelay"]= 300    ' speed of snake (higher = slower)
  level[5]["dirDelay"]  = 20      ' delay (moves) before snake can change direction (lower = more jittery snake
  level[5]["getAngry"]  = 0      ' flag indicating if the snake can switch to angry mode (=1) or not (=0)

  level[0][0]=  ".........1.........2...."    ' just a ruler
  level[5][1]=  "WW WW"
  level[5][2]=  "W P W"
  level[5][3]=  " WWWWWWWWWWWWWW "
  level[5][4]=  "WWWWWW WWW WW"
  level[5][5]=  " "
  level[5][6]=  " WWWWWWWWWWWWWWW "
  level[5][7]=  "WW WWW WWWWW"
  level[5][8]=  " "
  level[5][9]=  "WW WWW WWW WW"
  level[5][10]= " WWWWWWWWWWWWWW "
  level[5][11]= " "
  level[5][12]= "WWWWWW WWW WW"
  level[5][13]= " WWWWWWWWWWWWWW "
  level[5][14]= " "
  level[5][15]= "W WWWWW WWWWW W"
  level[5][16]= "WW S WW"

EndSub

Copyright (c) Microsoft Corporation. All rights reserved.