Random Walk 3 - snakes!!

Remember the old Nokia game "SNAKE"?  Well, I decided to make a snake-like program in BB4W for today's random walk.  The program produces a number of snakes on the screen which then move randomly around.  Yes, you can have more than one snake.  Yes, they don't walk off the edge (that bit was quite tricky which is why this program took over an hour to code).  The snakes are different random colours, with the strongest colours indicated the heads.  Oh, and two other rules: snakes CAN move over each other, and themselves, but they cannot turn immediately back on themselves.

Enjoy!

Indiana: There's a big snake in the plane, Jock!
Jock: Oh, that's just my pet snake Reggie.
Indiana: I hate snakes, Jock! I hate 'em!
Jock: Come on! Show a little backbone, will ya!


     REM Snake Walk 1
     REM T Street
     REM 2014-11-12
     
MODE 10  : OFF
     
SCREEN_WIDTH% = 1440
     SCREEN_HEIGHT% = 1152

     REM snake parameters
     
SNAKE_RADIUS% = 16 : REM size of the segments
     
SEGMENTS% = 20 : REM don't change to larger than this

     REM create each snake
     
DIM snake1{ x%(SEGMENTS%-1), y%(SEGMENTS%-1),  head%, tail%, dir%, red%, green% }
     DIM snake2{} = snake1{}
     DIM snake3{} = snake1{}
     DIM snake4{} = snake1{}
     DIM snake5{} = snake1{}

     REM setup the initial conditions for the snake
     
PROC_initialSnake( snake1{} )
     PROC_initialSnake( snake2{} )
     PROC_initialSnake( snake3{} )
     PROC_initialSnake( snake4{} )
     PROC_initialSnake( snake5{} )

     REM main loop
     
REPEAT
       
*refresh off
       PROC_drawSnake( snake1{} )
       PROC_drawSnake( snake2{})
       PROC_drawSnake( snake3{})
       PROC_drawSnake( snake4{})
       PROC_drawSnake( snake5{})
       *refresh on

       PROC_moveSnake( snake1{} )
       PROC_moveSnake( snake2{} )
       PROC_moveSnake( snake3{} )
       PROC_moveSnake( snake4{} )
       PROC_moveSnake( snake5{} )

       WAIT 5 : REM wait a 'mo
       
CLS

     UNTIL FALSE


     
DEFPROC_initialSnake( this{} )
     LOCAL i%
     REM intial position of snake
     
FOR i% = 0 TO SEGMENTS%-1
       this.x%(i%) = i% + 8
       this.y%(i%) = 12
     NEXT
     
this.head% = SEGMENTS%-1
     this.tail% = 0
     this.dir% = RND(4) : REM initial direction
     
this.red% = RND(255-SEGMENTS%*6)   : REM colour of snake
     
this.green% = RND(255-SEGMENTS%*6) : REM colour of snake
     
ENDPROC


     
DEFPROC_drawSnake( this{} )
     REM plots one snake on the screen
     
LOCAL colCycle%
     LOCAL i%
     LOCAL ok%
     i% = this.head%
     WHILE NOTok%
       COLOUR 2, this.red%+colCycle%*6, this.green%+colCycle%*6, 0
       GCOL 0,2
       colCycle% +=1
       PROC_circle( this.x%(i%), this.y%(i%) )
       i% -= 1
       IF i% <0 THEN i% = SEGMENTS%-1
       IF i% = this.tail% THEN
         
ok% = TRUE
         PROC
_circle( this.x%(i%), this.y%(i%) )
       ENDIF
     ENDWHILE
     ENDPROC


     
DEFPROC_moveSnake( this{} )
     LOCAL dir%, ok%
     LOCAL newHead%, newTail%
     LOCAL checkx%, checky%

     newHead% = this.head% + 1
     IF newHead%=SEGMENTS% THEN
       
newHead% = 0
     ENDIF
     
newTail% = this.tail% + 1
     IF newTail% =SEGMENTS% THEN
       
newTail% = 0
     ENDIF


     REPEAT
       
ok% = TRUE
       
REM check snake is not going back on itself
       
REPEAT
         
dir% = RND(4)
       UNTIL FN_dirOk(dir%, this.dir% )

       REM check that snake will not move off the screen
       
CASE dir% OF
         WHEN 
1
           checkx% = this.x%(this.head%) +1
         WHEN 2
           checkx%  = this.x%(this.head%) -1
         WHEN 3
           checky% = this.y%(this.head%) + 1
         WHEN 4
           checky% = this.y%(this.head%) -1
       ENDCASE

       IF 
dir% = 1 THEN
         IF 
checkx%*SNAKE_RADIUS%*2 > SCREEN_WIDTH% THEN
           
ok% = FALSE
         ENDIF
       ENDIF
       IF 
dir% = 2 THEN
         IF 
checkx%*SNAKE_RADIUS%*2 <= SNAKE_RADIUS%*2 THEN
           
ok% = FALSE
         ENDIF
       ENDIF
       IF 
dir% = 3 THEN
         IF 
checky%*SNAKE_RADIUS%*2 > SCREEN_HEIGHT% THEN
           
ok% = FALSE
         ENDIF
       ENDIF
       IF 
dir% = 4 THEN
         IF 
checky%*SNAKE_RADIUS%*2 <= SNAKE_RADIUS%*2 THEN
           
ok% = FALSE
         ENDIF
       ENDIF

     UNTIL 
ok%

     REM snake has made a valid random move
     
CASE dir% OF
       WHEN 
1
         this.x%(newHead%) = this.x%(this.head%) +1
         this.y%(newHead%) = this.y%(this.head%)
       WHEN 2
         this.x%(newHead%) = this.x%(this.head%) -1
         this.y%(newHead%) = this.y%(this.head%)

       WHEN 3
         this.y%(newHead%) = this.y%(this.head%) +1
         this.x%(newHead%) = this.x%(this.head%)

       WHEN 4
         this.y%(newHead%) = this.y%(this.head%) -1
         this.x%(newHead%) = this.x%(this.head%)

     ENDCASE

     
this.head% = newHead%
     this.tail% = newTail%
     this.dir% = dir%
     ENDPROC


     
DEFFN_dirOk( direction%, current% )
     REM check snake not moving back on itself
     
IF ABS(direction%-current%) = 1 AND direction%+current% <> 5 THEN
       
FALSE
     ELSE
       
TRUE
     ENDIF


     
DEFPROC_circle( x%, y% )
     CIRCLE FILL x%*SNAKE_RADIUS%*2, y%*SNAKE_RADIUS%*2, SNAKE_RADIUS%
     ENDPROC


Five Nokia snakes moving randomly around the screen.  Have fun changing the size and lengths of the snakes.