'===========================================================================
' Subject: JEWEL - LIKE COLUMNS               Date: 10-20-96 (18:11)       
'  Author: The ABC Programmer                 Code: QB, QBasic, PDS        
'  Origin: Converted from Pascal code       Packet: GAMES.ABC
'===========================================================================
'********************************************************
'* WARNING: CODE BELOW CONTAINS VERY ADDICTIVE GAME PLAY
'********************************************************
'*  JEWEL (ASCII Version) - Like Columns for Sega(tm)
'*   100% Public Domain - all rights relinquished
'*   Colour/Monochrome compatible!
'*
'*  This version has been 95% ported from Turbo Pascal
'*    code to BASIC by William Yu  (10-20-96)
'*  Minor alterations/optimization routines have been
'*    implemented.  Game has been ported AS IS.
'*    Not responsible for misinterpreted translation.
'*
'*  Original code for XWindows - Author Unknown
'*  Original code for TP - Jurgen Prins (???)
'*
'*  Turbo Pascal code can be found at (as of 10/96):
'*    http://www.cs.vu.nl/~jprins/ftp/src/jewel.zip
'*
'* Instructions for gameplay (as written):
'*
'*  Try to get three same colors in an horizontal,
'*  vertical or diagonal row. Switch colors with Up,
'*  move with Left & Right, force placement
'*  with Down. P is pause, Esc is quit.
'*
'* Notice: (as discovered)
'*
'*  The "clear all" jewel will clear all jews of
'*  ONE colour.  So if the "clear all" jewel lands
'*  on the colour red, it would clear all red jewels.
'*       
'********************************************************
DEFINT A-Z
DECLARE FUNCTION RandomColor% ()
DECLARE SUB InitField ()
DECLARE SUB InitVars ()
DECLARE SUB InitScreen ()
DECLARE SUB RefreshScreen ()
DECLARE FUNCTION GenerateNewBlock% ()
DECLARE FUNCTION MoveBlockDown% (col%, startRow%, endRow%, checkflag%)
DECLARE SUB SwapBlockColors ()
DECLARE SUB ForceBlockDown ()
DECLARE SUB MoveBlockLeft ()
DECLARE SUB MoveBlockRight ()
DECLARE SUB GetKeyboardEvent ()
DECLARE FUNCTION MatchesFound% ()
DECLARE SUB WriteLives ()
DECLARE SUB WriteScore ()

SCREEN 0, 0, 0, 0

CONST JewelChr = ""
CONST JewelChance = 20       ' Chance of receiving the "clear all" jewel
CONST MatchBackGround = 0
CONST MatchForeGround = 31
CONST False = 0
CONST True = NOT False
CONST Mono = False           ' <--- Playable even with monochrome display!
                             ' Change to TRUE if you don't have color.
CONST BigScreen = True       ' For WIDTH 40, 25 or for graphics mode

DIM SHARED Well(0 TO 31, 0 TO 22) AS INTEGER

DIM SHARED Colors, Rows, Cols, LeftX, RightX, TopY, BotY
DIM SHARED BlockCol, BlockEndRow, SpeedDelay!, Lives, Level
DIM SHARED IncreaseSpeed!, ScoreObtained
DIM SHARED AchievedScore AS LONG
DIM SHARED Score AS LONG
DIM SHARED Pause, Escape

'********************************
'   Initialize variables
'********************************

    IF BigScreen THEN WIDTH 40, 25

    SpeedDelay! = .5       ' Delay in seconds
    IncreaseSpeed! = .025  ' Increase speed for each ScoreObtained
    ScoreObtained = 2500   ' Speed increases with every 2500 points obtained
    AchievedScore = ScoreObtained

    Level = 100       ' Levels have not been implemented, this affects score
    Cols = 6          ' Max of 30 depending on WIDTH
    Rows = 20         ' Max of 22
    Colors = 5        ' Number of color gems used, Max of 10
    Score = 0         ' Starting score
    Lives = 3         ' How many lives remaining

'*********************************

    InitScreen
    WHILE (Lives > 0)
        InitField
        WriteLives
        RefreshScreen
        WHILE (GenerateNewBlock) AND (NOT Escape)
            RefreshScreen
            WHILE MoveBlockDown(BlockCol, BlockEndRow - 2, BlockEndRow, True) AND (NOT Escape)
                BlockEndRow = BlockEndRow + 1
                RefreshScreen
                T! = TIMER
                DO
                    GetKeyboardEvent
                LOOP UNTIL TIMER - T! >= SpeedDelay! AND (NOT Pause)
            WEND
            DO
            LOOP UNTIL NOT MatchesFound
        WEND
        Lives = Lives - 1
        SpeedDelay! = .5
    WEND
    WriteScore

END

SUB ForceBlockDown

    WHILE MoveBlockDown(BlockCol, BlockEndRow - 2, BlockEndRow, True)
        BlockEndRow = BlockEndRow + 1
    WEND

END SUB

FUNCTION GenerateNewBlock

    RANDOMIZE TIMER
    midCol = (Cols / 2) + 1
    IF Well(midCol, 3) <> 0 THEN
      GenerateNewBlock = False
    ELSE
        IF INT(RND * JewelChance) = 0 THEN
            FOR I = 1 TO 3
              Well(midCol, I) = 24
            NEXT I
        ELSE
            FOR I = 1 TO 3
              Well(midCol, I) = RandomColor
            NEXT I
        END IF
        GenerateNewBlock = True
    END IF
    BlockCol = midCol
    BlockEndRow = 3

END FUNCTION

SUB GetKeyboardEvent

  Ch$ = INKEY$
  IF LEN(Ch$) THEN
      IF LEN(Ch$) = 2 THEN Ch = -(ASC(RIGHT$(Ch$, 1))) ELSE Ch = ASC(UCASE$(Ch$))
      SELECT CASE Ch
        CASE -72
          SwapBlockColors
          RefreshScreen
        CASE -80
          ForceBlockDown
          RefreshScreen
        CASE -75
          MoveBlockLeft
          RefreshScreen
        CASE -77
          MoveBlockRight
          RefreshScreen
        CASE 27
          Escape = True
        CASE 80
          Pause = NOT Pause
      END SELECT
  END IF

END SUB

SUB InitField

    FOR row = 1 TO Rows
        FOR col = 1 TO Cols
            Well(col, row) = 0
        NEXT col
    NEXT row

END SUB

SUB InitScreen

    COLOR 6
    IF BigScreen THEN MidPoint = 20 ELSE MidPoint = 40

    TopY = ((25 - Rows) / 2)
    BotY = TopY + Rows - 1
    LeftX = MidPoint - Cols
    RightX = MidPoint - 1 + Cols

    LOCATE TopY - 1, LeftX
    PRINT STRING$(Cols * 2 + 1, CHR$(196));

    LOCATE BotY + 1, LeftX
    PRINT STRING$(Cols * 2 + 1, CHR$(196));
   
    FOR y = TopY - 1 TO BotY + 1
        LOCATE y, LeftX - 1
        PRINT CHR$(179);
        LOCATE y, RightX + 1
        PRINT CHR$(179);
    NEXT y
    LOCATE TopY - 1, MidPoint - 1 - Cols
    PRINT CHR$(218);
    LOCATE TopY - 1, MidPoint + Cols
    PRINT CHR$(191);
    LOCATE BotY + 1, MidPoint - 1 - Cols
    PRINT CHR$(192);
    LOCATE BotY + 1, MidPoint + Cols
    PRINT CHR$(217);

END SUB

SUB InitVars

' Moved to main program

END SUB

FUNCTION MatchesFound

DIM MatchField(0 TO 31, 0 TO 22)  AS INTEGER

    Found = False

    '(* initialize the Matchfield *)
    FOR J = 1 TO Rows
        FOR I = 1 TO Cols
            MatchField(I, J) = MatchBackGround
        NEXT I
    NEXT J

    IF Well(BlockCol, BlockEndRow) = 24 THEN  '(* we have a jewel here! *)
        IF BlockEndRow < Rows THEN
            Colour = Well(BlockCol, BlockEndRow + 1)
            FOR J = 1 TO Rows
                FOR I = 1 TO Cols
                    IF (Well(I, J) = Colour) OR (Well(I, J) = 24) THEN MatchField(I, J) = MatchForeGround
                NEXT I
            NEXT J
        ELSE
            FOR BlockRow = BlockEndRow TO BlockEndRow - 2 STEP -1
                MatchField(BlockCol, BlockRow) = MatchForeGround
            NEXT BlockRow
        END IF
        Found = True
    ELSE
        FOR BlockRow = BlockEndRow TO BlockEndRow - 2 STEP -1
            Colour = Well(BlockCol, BlockRow)

            '(* Look for vertical matches, first downwards, then upwards *)
            J = BlockRow + 1
            WHILE ((J <= Rows) AND (Well(BlockCol, J) = Colour))
                J = J + 1
            WEND
            J = J - 1
            Endmatch = J
            WHILE ((J >= 1) AND (Well(BlockCol, J) = Colour))
                J = J - 1
            WEND
            Startmatch = J + 1
            IF (Endmatch - Startmatch > 1) AND (Colour <> 0) THEN
                FOR J = Startmatch TO Endmatch
                    MatchField(BlockCol, J) = MatchForeGround
                NEXT J
                Found = True
            END IF

            '(* Look for horizontal matches, first rightwards, then to the left*)
            I = BlockCol + 1
            WHILE ((I <= Cols) AND (Well(I, BlockRow) = Colour))
                I = I + 1
            WEND
            I = I - 1
            Endmatch = I
            WHILE ((I >= 1) AND (Well(I, BlockRow) = Colour))
                I = I - 1
            WEND
            Startmatch = I + 1
            IF (Endmatch - Startmatch > 1) AND (Colour <> 0) THEN
                FOR I = Startmatch TO Endmatch
                    MatchField(I, BlockRow) = MatchForeGround
                NEXT I
                Found = True
            END IF

            '(* look for diagonal matches, first rightdownwards, then leftup *)
            J = BlockRow + 1
                I = BlockCol + 1
            WHILE ((J <= Rows) AND (I <= Cols) AND (Well(I, J) = Colour))
                I = I + 1
                J = J + 1
            WEND
            I = I - 1
            J = J - 1
            Endmatch = I
            WHILE ((J >= 1) AND (I >= 1) AND (Well(I, J) = Colour))
                I = I - 1
                J = J - 1
            WEND
                Startmatch = I + 1
            IF (Endmatch - Startmatch > 1) AND (Colour <> 0) THEN
                FOR I = Startmatch TO Endmatch
                    MatchField(I, I - (BlockCol - BlockRow)) = MatchForeGround
                NEXT I
                Found = True
            END IF

            '(* look for diagonal matches, first leftdownwards, then rightup *)
            J = BlockRow + 1
            I = BlockCol - 1
            WHILE ((J <= Rows) AND (I >= 1) AND (Well(I, J) = Colour))
                I = I - 1
                J = J + 1
            WEND
            I = I + 1
            J = J - 1
            Endmatch = I
            WHILE ((J >= 1) AND (I <= Cols) AND (Well(I, J) = Colour))
                I = I + 1
                J = J - 1
            WEND
            Startmatch = I - 1
            IF (Startmatch - Endmatch > 1) AND (Colour <> 0) THEN
                FOR I = Startmatch TO Endmatch STEP -1
                    MatchField(I, (BlockCol + BlockRow) - I) = MatchForeGround
                NEXT I
                Found = True
            END IF
        NEXT BlockRow

    END IF

    '(* handle the first row (there are no blocks above to move down) *)
    FOR I = 1 TO Cols
        IF MatchField(I, 1) = MatchForeGround THEN Well(I, 1) = 0
    NEXT I

    '(* scan Matchfield and move at each encounter upper blocks down *)
    FOR J = 2 TO Rows
        FOR I = 1 TO Cols
            IF MatchField(I, J) = MatchForeGround THEN
                MV = MoveBlockDown(I, 1, J - 1, False)
                'ForceBlockDown
                IF (I = BlockCol) AND (BlockEndRow < Rows) THEN BlockEndRow = BlockEndRow + 1
                RefreshScreen
                Score = Score + (10000 / Level)
                IF (Score > AchievedScore) THEN
                  AchievedScore = AchievedScore + ScoreObtained
                  IF (SpeedDelay! > .1) THEN SpeedDelay! = SpeedDelay! - IncreaseSpeed!
                END IF
            END IF
        NEXT I
    NEXT J

    MatchesFound = Found

END FUNCTION

FUNCTION MoveBlockDown (col, startRow, endRow, checkflag)
   
    IF endRow = Rows THEN
        MoveBlockDown = False
    ELSE
        IF checkflag AND (Well(col, endRow + 1) <> 0) THEN
            MoveBlockDown = False
        ELSE
            FOR I = endRow + 1 TO startRow + 1 STEP -1
                Well(col, I) = Well(col, I - 1)
            NEXT I
            Well(col, startRow) = 0
            MoveBlockDown = True
        END IF
    END IF

END FUNCTION

SUB MoveBlockLeft

    IF BlockCol > 1 THEN
        IF (Well(BlockCol - 1, BlockEndRow) = 0) AND (Well(BlockCol - 1, BlockEndRow - 1) = 0) AND (Well(BlockCol - 1, BlockEndRow - 2) = 0) THEN
            BlockCol = BlockCol - 1
            FOR I = BlockEndRow - 2 TO BlockEndRow
                Well(BlockCol, I) = Well(BlockCol + 1, I)
                Well(BlockCol + 1, I) = 0
            NEXT I
        END IF
    END IF

END SUB

SUB MoveBlockRight

    IF BlockCol < Cols THEN
        IF (Well(BlockCol + 1, BlockEndRow) = 0) AND (Well(BlockCol + 1, BlockEndRow - 1) = 0) AND (Well(BlockCol + 1, BlockEndRow - 2) = 0) THEN
            BlockCol = BlockCol + 1
            FOR I = BlockEndRow - 2 TO BlockEndRow
                Well(BlockCol, I) = Well(BlockCol - 1, I)
                Well(BlockCol - 1, I) = 0
            NEXT I
        END IF
    END IF

END SUB

FUNCTION RandomColor

    RANDOMIZE TIMER
    SELECT CASE INT(RND * Colors)
        CASE 0
          RandomColor = 1
        CASE 1
          RandomColor = 2
        CASE 2
          RandomColor = 4
        CASE 3
          RandomColor = 7
        CASE 4
          RandomColor = 14
        CASE 5
          RandomColor = 11
        CASE 6
          RandomColor = 13
        CASE 7
          RandomColor = 9
        CASE 8
          RandomColor = 12
        CASE 9
          RandomColor = 15
    END SELECT

END FUNCTION

SUB RefreshScreen

    row = 1
    col = 1
    x = LeftX
    y = TopY
    WHILE (y <= BotY)
        WHILE (x < RightX)
            LOCATE y, x
            IF NOT Mono THEN
              COLOR Well(col, row)
              PRINT JewelChr;
            ELSE
              IF Well(col, row) = 0 THEN
                PRINT "  "
              ELSE
                  PRINT CHR$(Well(col, row) + 64);
              END IF
            END IF
            x = x + 2
            col = col + 1
        WEND
        y = y + 1
        row = row + 1
        x = LeftX
        col = 1
    WEND
    COLOR 5
    LOCATE TopY - 1, LeftX + 1
    PRINT Score;

END SUB

SUB SwapBlockColors

    I = Well(BlockCol, BlockEndRow - 2)
    Well(BlockCol, BlockEndRow - 2) = Well(BlockCol, BlockEndRow - 1)
    Well(BlockCol, BlockEndRow - 1) = Well(BlockCol, BlockEndRow)
    Well(BlockCol, BlockEndRow) = I

END SUB

SUB WriteLives

    COLOR 5
    LOCATE TopY - 1, RightX - 1
    PRINT Lives;

END SUB

SUB WriteScore

    WIDTH 80
    COLOR 7
    PRINT "Jewel for DOS - Converted from Pascal code to BASIC by William Yu (10/96)"
    COLOR 2
    PRINT "Total score : "; Score
    COLOR 7
    PRINT

END SUB
