'===========================================================================
' Subject: SNAKE-GAME (LIKE NIBBLES)          Date: 07-11-98 (11:40)       
'  Author: Anders Olofsson                    Code: QB, QBasic, PDS        
'  Origin: anders.olofsson@mail.bip.net     Packet: GAMES.ABC
'===========================================================================
'
'   Simple snake-game with 10 different skill levels.
'
'     By Anders Olofsson 1998.
'
'        E-mail: anders.olofsson@mail.bip.net
'
'

DEFINT A-Z
DECLARE SUB Makelevel (Num%)
DECLARE SUB QuitProgram ()
DECLARE SUB RAlign (Row%, text$)
DECLARE SUB ClrScr (Dir%)
DECLARE SUB MoveSnake ()
DECLARE FUNCTION Keyboard% ()
DECLARE SUB Playgame ()
DECLARE SUB Intro ()
DECLARE SUB Center (Row%, text$)
DECLARE SUB Delay (HowLong!)
DECLARE FUNCTION Ascii% (Y%, X%)
DECLARE FUNCTION Colr% (Y%, X%)
DECLARE SUB YouWin ()
DECLARE SUB Updatesnake ()
DECLARE SUB Updatescreen ()
DECLARE SUB UpdateSnakes (NumSnakes%)
DECLARE SUB Demo ()


CONST Right = 1
CONST Up = 2
CONST Left = 3
CONST Down = 4

TYPE SnakeType      'Keeps information about the snake
 Row AS INTEGER
 Col AS INTEGER
 Score AS INTEGER
 Lives AS INTEGER
 Direction AS INTEGER
 SnakeColor AS INTEGER
 Currentlen AS INTEGER
 Maxlen AS INTEGER
END TYPE

TYPE BG              'To save background where the snake has been
 Row AS INTEGER
 Col AS INTEGER
 Old AS STRING * 2
END TYPE

DIM SHARED Background(1 TO 399) AS BG
DIM SHARED Snake AS SnakeType

CONST BorderColor = 1
CONST WallColor = 4

 CONST GameDelay = .1

CONST RArrow = -77, LArrow = -75, UpArrow = -72, DnArrow = -80
CONST MaxRows = 50, MaxCols = 80

'-------------------------------------------------------------------


RANDOMIZE TIMER

CLS


Intro        'Demo of snake & show my name...

Playgame

END

FUNCTION Ascii (Y, X)
 DEF SEG = &HB800
   Memloc = 80 * (Y - 1) + (X - 1)
   Memloc = Memloc * 2
   Ascii = PEEK(Memloc)

END FUNCTION

SUB Center (Row, text$)
LOCATE Row, 41 - LEN(text$) \ 2: PRINT text$;
END SUB

SUB ClrScr (WhichWay)

T! = TIMER
DO
 WAIT 64, 128: WAIT 64, 128, 128: P& = P& + 1
LOOP UNTIL TIMER - T! > .5

Del% = P& / 250


DEF SEG = &HB800

IF WhichWay = 1 THEN
StartC = 0: EndC = 4000: St = 1
ELSE
StartC = 4000: EndC = 0: St = -1
END IF

FOR S = StartC TO EndC STEP St
 POKE 4000 - S, 0
 POKE 4000 + S, 0
 FOR Z% = 0 TO Del%: WAIT 64, 128: WAIT 64, 128, 128: NEXT
NEXT

END SUB

FUNCTION Colr (Y, X)
 DEF SEG = &HB800
   Memloc = 80 * (Y - 1) + (X - 1)
   Memloc = Memloc * 2
   Colr = PEEK(Memloc + 1)

END FUNCTION

SUB Delay (HowLong!)
T! = TIMER: DO: LOOP UNTIL TIMER - T! > HowLong!
END SUB

SUB Intro

Updatescreen
 
  Snake.Row = 1
  Snake.Col = 2
  Snake.Direction = Down
  Snake.SnakeColor = 9 + RND * 5
  Snake.Maxlen = 40
  Snake.Currentlen = 0

COLOR 0, 3

Center MaxRows \ 2 - 1, "ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿"
    Center MaxRows \ 2, "³  Snake game by Anders Olofsson...  ³"
Center MaxRows \ 2 + 1, "ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ"

LOCATE , , 0

DO
 
  MoveSnake
 
  IF Snake.Row = 2 AND Snake.Direction = Up THEN
   IF Snake.Col <> 79 THEN Snake.Direction = Right ELSE Snake.Direction = Left
  END IF
 
  IF Snake.Row = MaxRows - 1 AND Snake.Direction = Down THEN
   IF Snake.Col <> MaxCols - 1 THEN Snake.Direction = Right ELSE Snake.Direction = Left
  END IF

  IF Snake.Col = MaxCols - 1 AND Snake.Direction = Right THEN
   IF Snake.Row <> 2 THEN Snake.Direction = Up ELSE Snake.Direction = Down
  END IF

  IF Snake.Col = 2 AND Snake.Direction = Left THEN
   IF Snake.Row <> MaxRows - 1 THEN Snake.Direction = Down ELSE Snake.Direction = Up
  END IF


 Updatesnake

 'Delay .02                       'use the one you prefer
 WAIT &H3DA, 8: WAIT &H3DA, 8, 1

 IF LEN(INKEY$) THEN EXIT DO
 IF (Snake.Row = 2 AND Snake.Col = 3) THEN NumTimes = NumTimes + 1

LOOP UNTIL NumTimes = 2

 ClrScr 2

END SUB

FUNCTION Keyboard
S$ = INKEY$
 IF LEN(S$) = 1 THEN
  Keyboard = ASC(S$)
 ELSEIF LEN(S$) = 2 THEN
  Keyboard = -ASC(RIGHT$(S$, 1))
 END IF
END FUNCTION

SUB Makelevel (Num)

COLOR WallColor, 0

SELECT CASE Num

CASE 2
 FOR R = MaxRows \ 2 - 10 TO MaxRows \ 2 + 10
  LOCATE R, MaxCols \ 2 - 20: PRINT CHR$(219);
  LOCATE R, MaxCols \ 2 + 20: PRINT CHR$(219);
 NEXT

CASE 3
 FOR R = MaxRows \ 2 - 10 TO MaxRows \ 2 + 10
  LOCATE R, MaxCols \ 2 - 10: PRINT CHR$(219);
  LOCATE R, MaxCols \ 2 + 10: PRINT CHR$(219);
  LOCATE R, MaxCols \ 2 - 20: PRINT CHR$(219);
  LOCATE R, MaxCols \ 2 + 20: PRINT CHR$(219);
 NEXT

CASE 4
  FOR R = MaxRows \ 2 - 11 TO MaxRows \ 2 + 11
   LOCATE R, MaxCols \ 2 - 20: PRINT CHR$(219);
   LOCATE R, MaxCols \ 2 + 20: PRINT CHR$(219);
  NEXT
 
  FOR C = MaxCols \ 2 - 18 TO MaxCols \ 2 + 18
   LOCATE MaxRows \ 2 - 10, C: PRINT CHR$(219);
   LOCATE MaxRows \ 2 + 10, C: PRINT CHR$(219);
  NEXT

CASE 5
 FOR C = 2 TO MaxCols - 2 STEP 3
  LOCATE MaxRows \ 2 - 15, C: PRINT CHR$(219);
  LOCATE MaxRows \ 2 + 15, C: PRINT CHR$(219);
 NEXT


CASE 6
 FOR R = MaxRows \ 2 - 10 TO MaxRows \ 2 + 10 STEP 2
  LOCATE R, MaxCols \ 2 - 20: PRINT CHR$(219);
  LOCATE R, MaxCols \ 2 + 20: PRINT CHR$(219);
 NEXT
 FOR C = MaxCols \ 2 - 20 TO MaxCols \ 2 + 20 STEP 2
  LOCATE MaxRows \ 2 - 10, C: PRINT CHR$(219);
  LOCATE MaxRows \ 2 + 10, C: PRINT CHR$(219);
 NEXT

CASE 7
 DIM Co AS SINGLE, Co2 AS SINGLE
 Co2 = MaxCols - 2

 FOR R = 2 TO MaxRows - 2
  Co = Co + (MaxCols / MaxRows)
  LOCATE R, Co: PRINT CHR$(219);
  Co2 = Co2 - (MaxCols / MaxRows)
  LOCATE R, Co2: PRINT CHR$(219);
 NEXT

CASE 8
 FOR R = 2 TO MaxRows - 2 STEP 2
  LOCATE R, MaxCols \ 2: PRINT CHR$(219);
 NEXT
 FOR C = 2 TO MaxCols - 2 STEP 2
  LOCATE MaxRows \ 2 - 2, C: PRINT CHR$(219);
 NEXT

CASE 9
 FOR R = 3 TO MaxRows - 3 STEP 2
  LOCATE R, MaxCols \ 2 - 20: PRINT CHR$(219);
  LOCATE R, MaxCols \ 2 + 20: PRINT CHR$(219);
  LOCATE R + 1, MaxCols \ 2 - 22: PRINT CHR$(219);
  LOCATE R - 1, MaxCols \ 2 + 22: PRINT CHR$(219);
 NEXT


CASE 10
 FOR R = 2 TO MaxRows - 2 STEP 4
 FOR C = 2 TO MaxCols - 2 STEP 4
  LOCATE R, C: PRINT CHR$(219);
 NEXT
 NEXT


END SELECT

END SUB

SUB MoveSnake
  IF Snake.Direction = Right THEN
   Snake.Col = Snake.Col + 1
  ELSEIF Snake.Direction = Left THEN
   Snake.Col = Snake.Col - 1
  ELSEIF Snake.Direction = Down THEN
   Snake.Row = Snake.Row + 1
  ELSEIF Snake.Direction = Up THEN
   Snake.Row = Snake.Row - 1
  END IF
  Snake.Currentlen = Snake.Currentlen + 1

END SUB

SUB Playgame

DO: LOOP WHILE LEN(INKEY$) 'Empty keyboardbuffer

NewGame:

 Updatescreen

 Snake.Score = 0
 Snake.SnakeColor = 11 + RND * 3
 Snake.Lives = 5
 CurrentSkill = 1                        'You can cheat!

TryAgain:
 Snake.Row = MaxRows \ 2
 Snake.Col = MaxCols \ 2
 Snake.Direction = 1
 Snake.Currentlen = 0
 Snake.Maxlen = 2
 Thistime = 0

  GOSUB PrintStatus
 
  Makelevel CurrentSkill

 DEF SEG = &HB800
  FOR X = 1 TO 10
ReTry:
   Row = 2 + RND * (MaxRows - 4)
   Col = 2 + RND * (MaxCols - 4)
   IF PEEK((Row * 80 + Col) * 2 + 1) <> 8 THEN GOTO ReTry:
   POKE (Row * 80 + Col) * 2, 1 + RND * 1
   POKE (Row * 80 + Col) * 2 + 1, 10
  NEXT

  LOCATE , , 0

DO

 '----- Key events
         
          Keys = Keyboard

  IF Keys = LArrow THEN
   IF Snake.Direction <> Right THEN Snake.Direction = Left

  ELSEIF Keys = RArrow THEN
   IF Snake.Direction <> Left THEN Snake.Direction = Right

  ELSEIF Keys = DnArrow THEN
   IF Snake.Direction <> Up THEN Snake.Direction = Down

  ELSEIF Keys = UpArrow THEN
   IF Snake.Direction <> Down THEN Snake.Direction = Up
 
  ELSEIF Keys = 27 THEN
   QuitProgram
 
'  ELSEIF Keys = 43 THEN                'No cheating here... anylonger :)
'   GOSUB ChangeSkill
 
'  ELSEIF Keys = 45 THEN
'   CurrentSkill = CurrentSkill - 2
'   GOSUB ChangeSkill

  END IF

 '----------

     MoveSnake
 
 '------
  
   IF Colr(Snake.Row, Snake.Col) = 10 THEN
    Snake.Maxlen = Snake.Maxlen + CurrentSkill * 3
    DEF SEG = &HB800
    POKE (80 * (Snake.Row - 1) + (Snake.Col - 1)) * 2, RND * 255 'Make a fake background
    POKE (80 * (Snake.Row - 1) + (Snake.Col - 1)) * 2 + 1, 8
    Snake.Score = Snake.Score + 10
    GOSUB PrintStatus
    Thistime = Thistime + 1: IF Thistime = 10 THEN GOSUB ChangeSkill
   END IF

   IF Colr(Snake.Row, Snake.Col) = Snake.SnakeColor OR Colr(Snake.Row, Snake.Col) <> 8 THEN
    IF Snake.Lives = 0 THEN GOTO TryAgainOrExit
    Snake.Lives = Snake.Lives - 1
    COLOR 0, 3
      Center MaxRows \ 2 - 1, "ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿"
          Center MaxRows \ 2, "³               Oh no!               ³"
      Center MaxRows \ 2 + 1, "³         Life is too short...       ³"
      Center MaxRows \ 2 + 2, "ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ"
    
     WHILE INKEY$ <> "": WEND
     DO: LOOP UNTIL Keyboard


    GOSUB PrintStatus
    Updatescreen
    GOTO TryAgain
   END IF

 '--------
   
    Updatesnake  ' Draw snake
 
 '--------
 
    Delay GameDelay    ' It goes *quite* fast without this... :)

 '--------


LOOP



EXIT SUB




'--------------------------

ChangeSkill:

IF CurrentSkill = 10 THEN YouWin

COLOR 0, 3
 
      Center MaxRows \ 2 - 1, "ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿"
          Center MaxRows \ 2, "³         You've made it!            ³"
Center MaxRows \ 2 + 1, LEFT$("³      Next level is: " + STR$(CurrentSkill + 1) + SPACE$(40), 37) + "³"
      Center MaxRows \ 2 + 2, "ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ"
  
     WHILE INKEY$ <> "": WEND
     DO: LOOP UNTIL Keyboard
 

 Updatescreen
 GOSUB PrintStatus

 CurrentSkill = CurrentSkill + 1

 GOTO TryAgain

RETURN


'----------------

PrintStatus:
  COLOR 3, 0
    RAlign 1, " Lives: " + LTRIM$(RTRIM$(STR$(Snake.Lives))) + " "
    LOCATE 1, 1: PRINT " Score: " + LTRIM$(RTRIM$(STR$(Snake.Score))) + " ";

RETURN


'---------------

TryAgainOrExit:

COLOR 0, 3
Center MaxRows \ 2 - 2, "ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿"
Center MaxRows \ 2 - 1, "³      *** G A M E   O V E R ***     ³"
    Center MaxRows \ 2, "³     Do you want to play again?     ³"
Center MaxRows \ 2 + 1, "³               (Y/N)                ³"
Center MaxRows \ 2 + 2, "ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ"

DO: S$ = UCASE$(INKEY$): LOOP UNTIL S$ = "N" OR S$ = "Y"

IF S$ = "Y" THEN GOTO NewGame

QuitProgram


END SUB

SUB QuitProgram
  CALL ClrScr(1): WIDTH 80, 25
  COLOR 7, 0
  PRINT "Snake-game by Anders Olofsson 1998."
  PRINT "E-mail: anders.olofsson@mail.bip.net"
  END

END SUB

SUB RAlign (Row, text$)
LOCATE Row, 81 - LEN(text$): PRINT text$;
END SUB

SUB Updatescreen

SCREEN 0: WIDTH 80, 50

DEF SEG = &HB800

FOR X = 0 TO MaxRows * 80 * 2 STEP 2
 POKE X, 255 * RND
 POKE X + 1, 8
NEXT

COLOR BorderColor, 0
FOR R = 1 TO MaxRows
 LOCATE R, 1: PRINT CHR$(219);
 LOCATE R, MaxCols: PRINT CHR$(219);
NEXT
FOR C = 1 TO MaxCols
 LOCATE 1, C: PRINT CHR$(219);
 LOCATE MaxRows, C: PRINT CHR$(219);
NEXT

END SUB

SUB Updatesnake
DEF SEG = &HB800


 Background(Snake.Currentlen).Old = CHR$(Colr(Snake.Row, Snake.Col)) + CHR$(Ascii(Snake.Row, Snake.Col))
 Background(Snake.Currentlen).Row = Snake.Row - 1
 Background(Snake.Currentlen).Col = Snake.Col - 1

 IF Snake.Currentlen >= Snake.Maxlen THEN
  DEF SEG = &HB800
   POKE (80 * Background(1).Row + Background(1).Col) * 2 + 1, ASC(Background(1).Old)
   POKE (80 * Background(1).Row + Background(1).Col) * 2, ASC(RIGHT$(Background(1).Old, 1))
  FOR T = 1 TO Snake.Maxlen - 1
   Background(T) = Background(T + 1)
  NEXT
  Snake.Currentlen = Snake.Currentlen - 1
 END IF

 LOCATE Snake.Row, Snake.Col
 COLOR Snake.SnakeColor
  PRINT CHR$(219);



END SUB

SUB YouWin


Updatescreen

  Snake.Row = 1
  Snake.Col = 2
  Snake.Direction = Down
  Snake.SnakeColor = 9 + RND * 5
  Snake.Maxlen = 50
  Snake.Currentlen = 0

COLOR 0, 3

Center MaxRows \ 2 - 1, "ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿"
    Center MaxRows \ 2, "³              YOU WIN!              ³"
Center MaxRows \ 2 + 1, "ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ"

LOCATE , , 0

DO

  MoveSnake

  IF Snake.Row = 2 AND Snake.Direction = Up THEN
   IF Snake.Col <> 79 THEN Snake.Direction = Right ELSE Snake.Direction = Left
  END IF

  IF Snake.Row = MaxRows - 1 AND Snake.Direction = Down THEN
   IF Snake.Col <> MaxCols - 1 THEN Snake.Direction = Right ELSE Snake.Direction = Left
  END IF

  IF Snake.Col = MaxCols - 1 AND Snake.Direction = Right THEN
   IF Snake.Row <> 2 THEN Snake.Direction = Up ELSE Snake.Direction = Down
  END IF

  IF Snake.Col = 2 AND Snake.Direction = Left THEN
   IF Snake.Row <> MaxRows - 1 THEN Snake.Direction = Down ELSE Snake.Direction = Up
  END IF


 Updatesnake

  WAIT &H3DA, 8: WAIT &H3DA, 8, 1 'Add some delays
  WAIT &H3DA, 8: WAIT &H3DA, 8, 1

 IF LEN(INKEY$) THEN EXIT DO

LOOP

QuitProgram

END SUB
