' Shisen-Sho game for Rapid-Q by William Yu 
' Set your display to at least 800x600 to play this game 
' This game was inspired by the original game, with all of the tiles 
' copied from the Linux/KDE version. 
' 
' Rules of the game: 
' ------------------ 
' Basically just join similar tiles together, with at most 3 lines in 
' its path.  You do not need to draw these paths, if the 2 tiles can be 
' connected with 3 lines or less then they will be marked and removed 
' from the playing field.  The object therefore is to eliminate all tiles 
' from the playing field as quick as possible. 
' Not all games are solvable, but most of them should be. 
' Click on a tile to select it, click on it again (or click a blank spot) 
' to deselect the tile. 

$TYPECHECK ON
$OPTIMIZE ON
$INCLUDE "RAPIDQ.INC"

$OPTION ICON "shisen.ico"
$RESOURCE block1_BMP AS "block1.bmp"
$RESOURCE block2_BMP AS "block2.bmp"
$RESOURCE block3_BMP AS "block3.bmp"
$RESOURCE block4_BMP AS "block4.bmp"
$RESOURCE block5_BMP AS "block5.bmp"
$RESOURCE block6_BMP AS "block6.bmp"
$RESOURCE block7_BMP AS "block7.bmp"
$RESOURCE block8_BMP AS "block8.bmp"
$RESOURCE block9_BMP AS "block9.bmp"
$RESOURCE block10_BMP AS "block10.bmp"
$RESOURCE block11_BMP AS "block11.bmp"
$RESOURCE block12_BMP AS "block12.bmp"
$RESOURCE block13_BMP AS "block13.bmp"
$RESOURCE block14_BMP AS "block14.bmp"
$RESOURCE block15_BMP AS "block15.bmp"
$RESOURCE block16_BMP AS "block16.bmp"
$RESOURCE block17_BMP AS "block17.bmp"
$RESOURCE block18_BMP AS "block18.bmp"
$RESOURCE block19_BMP AS "block19.bmp"
$RESOURCE block20_BMP AS "block20.bmp"
$RESOURCE block21_BMP AS "block21.bmp"
$RESOURCE block22_BMP AS "block22.bmp"
$RESOURCE block23_BMP AS "block23.bmp"
$RESOURCE block24_BMP AS "block24.bmp"
$RESOURCE block25_BMP AS "block25.bmp"
$RESOURCE block26_BMP AS "block26.bmp"
$RESOURCE block27_BMP AS "block27.bmp"
$RESOURCE block28_BMP AS "block28.bmp"
$RESOURCE block29_BMP AS "block29.bmp"
$RESOURCE block30_BMP AS "block30.bmp"
$RESOURCE block31_BMP AS "block31.bmp"
$RESOURCE block32_BMP AS "block32.bmp"
$RESOURCE block33_BMP AS "block33.bmp"
$RESOURCE block34_BMP AS "block34.bmp"
$RESOURCE block35_BMP AS "block35.bmp"
$RESOURCE block36_BMP AS "block36.bmp"

'-- To highlight just a portion of the block 
CONST blockSideColor = 6848648
CONST blockColor = RGB(208, 192, 160)
CONST highlightBlockColor = RGB(255, 220, 190)

CONST gapWidth = 40
CONST gapHeight = 40

CONST maxKinks = 2

TYPE TSelection
    x AS INTEGER
    y AS INTEGER
END TYPE

TYPE TSelectionPath
    x AS INTEGER
    y AS INTEGER
    kinks AS INTEGER
END TYPE

DIM blocks(1 TO 36) AS QBITMAP
    blocks(1).bmpHandle = block1_BMP
    blocks(2).bmpHandle = block2_BMP
    blocks(3).bmpHandle = block3_BMP
    blocks(4).bmpHandle = block4_BMP
    blocks(5).bmpHandle = block5_BMP
    blocks(6).bmpHandle = block6_BMP
    blocks(7).bmpHandle = block7_BMP
    blocks(8).bmpHandle = block8_BMP
    blocks(9).bmpHandle = block9_BMP
    blocks(10).bmpHandle = block10_BMP
    blocks(11).bmpHandle = block11_BMP
    blocks(12).bmpHandle = block12_BMP
    blocks(13).bmpHandle = block13_BMP
    blocks(14).bmpHandle = block14_BMP
    blocks(15).bmpHandle = block15_BMP
    blocks(16).bmpHandle = block16_BMP
    blocks(17).bmpHandle = block17_BMP
    blocks(18).bmpHandle = block18_BMP
    blocks(19).bmpHandle = block19_BMP
    blocks(20).bmpHandle = block20_BMP
    blocks(21).bmpHandle = block21_BMP
    blocks(22).bmpHandle = block22_BMP
    blocks(23).bmpHandle = block23_BMP
    blocks(24).bmpHandle = block24_BMP
    blocks(25).bmpHandle = block25_BMP
    blocks(26).bmpHandle = block26_BMP
    blocks(27).bmpHandle = block27_BMP
    blocks(28).bmpHandle = block28_BMP
    blocks(29).bmpHandle = block29_BMP
    blocks(30).bmpHandle = block30_BMP
    blocks(31).bmpHandle = block31_BMP
    blocks(32).bmpHandle = block32_BMP
    blocks(33).bmpHandle = block33_BMP
    blocks(34).bmpHandle = block34_BMP
    blocks(35).bmpHandle = block35_BMP
    blocks(36).bmpHandle = block36_BMP

DIM playField AS QBITMAP
    playField.width = 720 + (2 * gapWidth)
    playField.height = 448 + (2 * gapHeight)
    playField.paint(0, 0, 0, 0)

'-- PlayGrid stores the block values 
DIM playGrid(0 TO 9, 0 TO 19) AS BYTE

'-- HighLightGrid indicates the blocks that are highlighted 
DIM highlightGrid(1 TO 8, 1 TO 18) AS BYTE

DIM selectedBlock AS TSelection
    selectedBlock.x = 0
    selectedBlock.y = 0

DIM selectedPath(0 TO 3) AS TSelectionPath

DIM clockTicks AS INTEGER


DECLARE SUB formPaint (sender AS QFORM)
DECLARE SUB initPlayGrid (playGrid() AS BYTE)
DECLARE SUB highlightBlock (x AS INTEGER, y AS INTEGER)
DECLARE SUB deHighlightBlock (x AS INTEGER, y AS INTEGER)
DECLARE FUNCTION findPath (x1 AS INTEGER, y1 AS INTEGER, x2 AS INTEGER, y2 AS INTEGER) AS INTEGER
DECLARE SUB removeBlock (x AS INTEGER, y AS INTEGER)
DECLARE SUB formClick (sender AS QFORM)
DECLARE SUB traverseUpperPath (kinkyPath() AS BYTE, x AS INTEGER, y AS INTEGER, stopCount AS INTEGER)
DECLARE SUB traverseLowerPath (kinkyPath() AS BYTE, x AS INTEGER, y AS INTEGER, stopCount AS INTEGER)
DECLARE SUB traverseLeftPath (kinkyPath() AS BYTE, x AS INTEGER, y AS INTEGER, stopCount AS INTEGER)
DECLARE SUB traverseRightPath (kinkyPath() AS BYTE, x AS INTEGER, y AS INTEGER, stopCount AS INTEGER)
DECLARE FUNCTION checkForLeftPath (kinkyPath() AS BYTE, x1 AS INTEGER, y1 AS INTEGER, x2 AS INTEGER, y2 AS INTEGER, kinks AS INTEGER) AS INTEGER
DECLARE FUNCTION checkForLowerPath (kinkyPath() AS BYTE, x1 AS INTEGER, y1 AS INTEGER, x2 AS INTEGER, y2 AS INTEGER, kinks AS INTEGER) AS INTEGER
DECLARE FUNCTION checkForRightPath (kinkyPath() AS BYTE, x1 AS INTEGER, y1 AS INTEGER, x2 AS INTEGER, y2 AS INTEGER, kinks AS INTEGER) AS INTEGER
DECLARE FUNCTION checkForUpperPath (kinkyPath() AS BYTE, x1 AS INTEGER, y1 AS INTEGER, x2 AS INTEGER, y2 AS INTEGER, kinks AS INTEGER) AS INTEGER
DECLARE SUB newItemClick (sender AS QMENUITEM)
DECLARE SUB exitItemClick (sender AS QMENUITEM)
DECLARE SUB timer1Expired (sender AS QTIMER)


CREATE timer1 AS QTIMER
    enabled = 0
    interval = 1000
    onTimer = timer1Expired
END CREATE

CREATE form AS QFORM
    caption = "Shisen-Sho"
    clientWidth = playField.width
    height = playField.height + 80
    onPaint = formPaint
    onClick = formClick
    CREATE mainMenu AS QMAINMENU
        CREATE fileMenu AS QMENUITEM
            caption = "&File"
            CREATE newItem AS QMENUITEM
                caption = "&New game"
                onClick = newItemClick
            END CREATE
            CREATE breakItem AS QMENUITEM
                caption = "-"
            END CREATE
            CREATE exitItem AS QMENUITEM
                caption = "E&xit"
                onClick = exitItemClick
            END CREATE
        END CREATE
    END CREATE
    CREATE statusBar AS QSTATUSBAR
        addPanels "", ""
        panel(0).alignment = taCenter
        panel(0).caption = "00:00"
        panel(1).caption = "Shisen-Sho for Rapid-Q created by William Yu"
    END CREATE
END CREATE

initPlayGrid(playGrid)
clockTicks = 0
timer1.enabled = 1

form.showModal


'------------------------- Subroutines ---------------------------- 

SUB initPlayGrid (playGrid() AS BYTE)
    DEFBYTE numBlocks(1 TO 36)
    DEFINT  i, x, y

    FOR i = 1 TO 36
        numBlocks(i) = 0
    NEXT

    RANDOMIZE TIMER
    FOR y = 0 TO 9
        FOR x = 0 TO 19
            IF y = 0 OR y = 9 OR x = 0 OR x = 19 THEN
                playGrid(y, x) = 0
            ELSE
                highlightGrid(y, x) = 0
                i = INT(RND(36)) + 1
                while numBlocks(i) = 4
                    i = INT(RND(36)) + 1
                wend
                numBlocks(i) = numBlocks(i) + 1
                playGrid(y, x) = i
                playField.draw((x - 1) * 40 + gapWidth, (y - 1) * 56 + gapHeight, blocks(i).bmp)
            END IF
        NEXT
    NEXT
END SUB

SUB formPaint (sender AS QFORM)
    sender.draw(0, 0, playField.bmp)
END SUB

SUB highlightBlock (x AS INTEGER, y AS INTEGER)
    '-- Highlights a block 
    DEFINT i, j

    highlightGrid(y, x) = 1
    selectedBlock.x = x
    selectedBlock.y = y
    x = (x - 1) * 40 + gapWidth + 5
    y = (y - 1) * 56 + gapHeight + 1

    FOR i = y TO y + 51
        FOR j = x TO x + 38
            IF playField.pixel(j, i) = blockColor THEN
                playField.pixel(j, i) = highlightBlockColor
                ' playField.pixel(j, i) - &H333333 
            END IF
        NEXT
    NEXT
END SUB

SUB deHighlightBlock (x AS INTEGER, y AS INTEGER)
    '-- Dehighlights a block 

    highlightGrid(y, x) = 0
    selectedBlock.x = 0
    selectedBlock.y = 0

    playField.draw((x - 1) * 40 + gapWidth, (y - 1) * 56 + gapHeight, blocks(playGrid(y, x)).bmp)
END SUB


SUB traverseUpperPath (kinkyPath() AS BYTE, x AS INTEGER, y AS INTEGER, stopCount AS INTEGER)
    '-- Check Upper side 
    IF y = 0 THEN EXIT SUB

    WHILE y > 0
        DEC(y)
        IF y = 0 THEN
            '-- On the upper edge 
            traverseLeftPath(kinkyPath, x, y, stopCount)
            traverseRightPath(kinkyPath, x, y, stopCount)
        ELSE
            IF playGrid(y, x) = 0 AND kinkyPath(y, x) < stopCount THEN
                kinkyPath(y, x) = kinkyPath(y, x) + 1
                traverseLowerPath(kinkyPath, x, y, stopCount)
                traverseLeftPath(kinkyPath, x, y, stopCount)
                traverseRightPath(kinkyPath, x, y, stopCount)
            ELSE
                EXIT WHILE
            END IF
        END IF
    WEND
END SUB


SUB traverseLowerPath (kinkyPath() AS BYTE, x AS INTEGER, y AS INTEGER, stopCount AS INTEGER)
    '-- Check Upper side 
    IF y = 9 THEN EXIT SUB

    WHILE y < 9
        INC(y)
        IF y = 9 THEN
            '-- On the lower edge 
            traverseLeftPath(kinkyPath, x, y, stopCount)
            traverseRightPath(kinkyPath, x, y, stopCount)
        ELSE
            IF playGrid(y, x) = 0 AND kinkyPath(y, x) < stopCount THEN
                kinkyPath(y, x) = kinkyPath(y, x) + 1
                traverseUpperPath(kinkyPath, x, y, stopCount)
                traverseLeftPath(kinkyPath, x, y, stopCount)
                traverseRightPath(kinkyPath, x, y, stopCount)
            ELSE
                EXIT WHILE
            END IF
        END IF
    WEND
END SUB


SUB traverseRightPath (kinkyPath() AS BYTE, x AS INTEGER, y AS INTEGER, stopCount AS INTEGER)
    '-- Check left side 
    IF x = 19 THEN EXIT SUB

    WHILE x < 19
        INC(x)
        IF x = 19 THEN
            '-- On the left edge 
            traverseUpperPath(kinkyPath, x, y, stopCount)
            traverseLowerPath(kinkyPath, x, y, stopCount)
        ELSE
            IF playGrid(y, x) = 0 AND kinkyPath(y, x) < stopCount THEN
                kinkyPath(y, x) = kinkyPath(y, x) + 1
                traverseUpperPath(kinkyPath, x, y, stopCount)
                traverseLowerPath(kinkyPath, x, y, stopCount)
                traverseLeftPath(kinkyPath, x, y, stopCount)
            ELSE
                EXIT WHILE
            END IF
        END IF
    WEND
END SUB


SUB traverseLeftPath (kinkyPath() AS BYTE, x AS INTEGER, y AS INTEGER, stopCount AS INTEGER)
    '-- Check left side 
    IF x = 0 THEN EXIT SUB

    WHILE x > 0
        DEC(x)
        IF x = 0 THEN
            '-- On the left edge 
            traverseUpperPath(kinkyPath, x, y, stopCount)
            traverseLowerPath(kinkyPath, x, y, stopCount)
        ELSE
            IF playGrid(y, x) = 0 AND kinkyPath(y, x) < stopCount THEN
                kinkyPath(y, x) = kinkyPath(y, x) + 1
                traverseUpperPath(kinkyPath, x, y, stopCount)
                traverseLowerPath(kinkyPath, x, y, stopCount)
                traverseRightPath(kinkyPath, x, y, stopCount)
            ELSE
                EXIT WHILE
            END IF
        END IF
    WEND
END SUB


FUNCTION checkForUpperPath (kinkyPath() AS BYTE, x1 AS INTEGER, y1 AS INTEGER, x2 AS INTEGER, y2 AS INTEGER, kinks AS INTEGER) AS INTEGER
    '-- Check path on Upper side 
    result = 0

    IF kinks > maxKinks THEN
        EXIT FUNCTION        '-- Too many kinks 
    ELSEIF y1 = 0 OR kinkyPath(y1 - 1, x1) < 2 THEN
        '-- Dead end 
        EXIT FUNCTION
    ELSEIF x1 = x2 AND y1 - 1 = y2 THEN
        '-- Match found, we're done here 
        selectedPath(kinks + 1).x = x2
        selectedPath(kinks + 1).y = y2
        selectedPath(0).kinks = kinks + 1
        result = 1
        EXIT FUNCTION
    END IF

    WHILE y1 > 0
        DEC(y1)
        IF y1 = 0 THEN
            '-- On the upper edge 
            selectedPath(kinks + 1).x = x1
            selectedPath(kinks + 1).y = y1
            IF checkForLeftPath(kinkyPath, x1, y1, x2, y2, kinks + 1) THEN
                '-- Match found 
                result = 1
                EXIT FUNCTION
            END IF
            IF checkForRightPath(kinkyPath, x1, y1, x2, y2, kinks + 1) THEN
                '-- Match found 
                result = 1
                EXIT FUNCTION
            END IF
        ELSEIF x1 = x2 AND y1 = y2 THEN
            '-- Match found, we're done here 
            selectedPath(kinks + 1).x = x2
            selectedPath(kinks + 1).y = y2
            selectedPath(0).kinks = kinks + 1
            result = 1
            EXIT FUNCTION
        ELSE
            IF kinkyPath(y1, x1) <> 2 THEN
                '-- Dead end 
                EXIT WHILE
            ELSE
                '-- Look around 
                selectedPath(kinks + 1).x = x1
                selectedPath(kinks + 1).y = y1
                IF checkForLeftPath(kinkyPath, x1, y1, x2, y2, kinks + 1) THEN
                    '-- Match found 
                    result = 1
                    EXIT FUNCTION
                END IF
                IF checkForRightPath(kinkyPath, x1, y1, x2, y2, kinks + 1) THEN
                    '-- Match found 
                    result = 1
                    EXIT FUNCTION
                END IF
            END IF
        END IF
    WEND
END FUNCTION


FUNCTION checkForLowerPath (kinkyPath() AS BYTE, x1 AS INTEGER, y1 AS INTEGER, x2 AS INTEGER, y2 AS INTEGER, kinks AS INTEGER) AS INTEGER
    '-- Check path on Lower side 
    result = 0

    IF kinks > maxKinks THEN
        EXIT FUNCTION        '-- Too many kinks 
    ELSEIF y1 = 9 OR kinkyPath(y1 + 1, x1) < 2 THEN
        '-- Dead end 
        EXIT FUNCTION
    ELSEIF x1 = x2 AND y1 + 1 = y2 THEN
        '-- Match found, we're done here 
        selectedPath(kinks + 1).x = x2
        selectedPath(kinks + 1).y = y2
        selectedPath(0).kinks = kinks + 1
        result = 1
        EXIT FUNCTION
    END IF

    WHILE y1 < 9
        INC(y1)
        IF y1 = 9 THEN
            '-- On the lower edge 
            selectedPath(kinks + 1).x = x1
            selectedPath(kinks + 1).y = y1
            IF checkForLeftPath(kinkyPath, x1, y1, x2, y2, kinks + 1) THEN
                '-- Match found 
                result = 1
                EXIT FUNCTION
            END IF
            IF checkForRightPath(kinkyPath, x1, y1, x2, y2, kinks + 1) THEN
                '-- Match found 
                result = 1
                EXIT FUNCTION
            END IF
        ELSEIF x1 = x2 AND y1 = y2 THEN
            '-- Match found, we're done here 
            selectedPath(kinks + 1).x = x2
            selectedPath(kinks + 1).y = y2
            selectedPath(0).kinks = kinks + 1
            result = 1
            EXIT FUNCTION
        ELSE
            IF kinkyPath(y1, x1) <> 2 THEN
                '-- Dead end 
                EXIT WHILE
            ELSE
                '-- Look around 
                selectedPath(kinks + 1).x = x1
                selectedPath(kinks + 1).y = y1
                IF checkForLeftPath(kinkyPath, x1, y1, x2, y2, kinks + 1) THEN
                    '-- Match found 
                    result = 1
                    EXIT FUNCTION
                END IF
                IF checkForRightPath(kinkyPath, x1, y1, x2, y2, kinks + 1) THEN
                    '-- Match found 
                    result = 1
                    EXIT FUNCTION
                END IF
            END IF
        END IF
    WEND
END FUNCTION


FUNCTION checkForLeftPath (kinkyPath() AS BYTE, x1 AS INTEGER, y1 AS INTEGER, x2 AS INTEGER, y2 AS INTEGER, kinks AS INTEGER) AS INTEGER
    '-- Check path on left side 
    result = 0

    IF kinks > maxKinks THEN
        EXIT FUNCTION        '-- Too many kinks 
    ELSEIF x1 = 0 OR kinkyPath(y1, x1 - 1) < 2 THEN
        '-- Dead end 
        EXIT FUNCTION
    ELSEIF x1 - 1 = x2 AND y1 = y2 THEN
        '-- Match found, we're done here 
        selectedPath(kinks + 1).x = x2
        selectedPath(kinks + 1).y = y2
        selectedPath(0).kinks = kinks + 1
        result = 1
        EXIT FUNCTION
    END IF

    WHILE x1 > 0
        DEC(x1)
        IF x1 = 0 THEN
            '-- On the left edge 
            selectedPath(kinks + 1).x = x1
            selectedPath(kinks + 1).y = y1
            IF checkForUpperPath(kinkyPath, x1, y1, x2, y2, kinks + 1) THEN
                '-- Match found 
                result = 1
                EXIT FUNCTION
            END IF
            IF checkForLowerPath(kinkyPath, x1, y1, x2, y2, kinks + 1) THEN
                '-- Match found 
                result = 1
                EXIT FUNCTION
            END IF
        ELSEIF x1 = x2 AND y1 = y2 THEN
            '-- Match found, we're done here 
            selectedPath(kinks + 1).x = x2
            selectedPath(kinks + 1).y = y2
            selectedPath(0).kinks = kinks + 1
            result = 1
            EXIT FUNCTION
        ELSE
            IF kinkyPath(y1, x1) <> 2 THEN
                '-- Dead end 
                EXIT WHILE
            ELSE
                '-- Look around 
                selectedPath(kinks + 1).x = x1
                selectedPath(kinks + 1).y = y1
                IF checkForUpperPath(kinkyPath, x1, y1, x2, y2, kinks + 1) THEN
                    '-- Match found 
                    result = 1
                    EXIT FUNCTION
                END IF
                IF checkForLowerPath(kinkyPath, x1, y1, x2, y2, kinks + 1) THEN
                    '-- Match found 
                    result = 1
                    EXIT FUNCTION
                END IF
            END IF
        END IF
    WEND
END FUNCTION


FUNCTION checkForRightPath (kinkyPath() AS BYTE, x1 AS INTEGER, y1 AS INTEGER, x2 AS INTEGER, y2 AS INTEGER, kinks AS INTEGER) AS INTEGER
    '-- Check path on right side 
    result = 0

    IF kinks > maxKinks THEN
        EXIT FUNCTION        '-- Too many kinks 
    ELSEIF x1 = 19 OR kinkyPath(y1, x1 + 1) < 2 THEN
        '-- Dead end 
        EXIT FUNCTION
    ELSEIF x1 + 1 = x2 AND y1 = y2 THEN
        '-- Match found, we're done here 
        selectedPath(kinks + 1).x = x2
        selectedPath(kinks + 1).y = y2
        selectedPath(0).kinks = kinks + 1
        result = 1
        EXIT FUNCTION
    END IF

    WHILE x1 < 19
        INC(x1)
        IF x1 = 19 THEN
            '-- On the left edge 
            selectedPath(kinks + 1).x = x1
            selectedPath(kinks + 1).y = y1
            IF checkForUpperPath(kinkyPath, x1, y1, x2, y2, kinks + 1) THEN
                '-- Match found 
                result = 1
                EXIT FUNCTION
            END IF
            IF checkForLowerPath(kinkyPath, x1, y1, x2, y2, kinks + 1) THEN
                '-- Match found 
                result = 1
                EXIT FUNCTION
            END IF
        ELSEIF x1 = x2 AND y1 = y2 THEN
            '-- Match found, we're done here 
            selectedPath(kinks + 1).x = x2
            selectedPath(kinks + 1).y = y2
            selectedPath(0).kinks = kinks + 1
            result = 1
            EXIT FUNCTION
        ELSE
            IF kinkyPath(y1, x1) <> 2 THEN
                '-- Dead end 
                EXIT WHILE
            ELSE
                '-- Look around 
                selectedPath(kinks + 1).x = x1
                selectedPath(kinks + 1).y = y1
                IF checkForUpperPath(kinkyPath, x1, y1, x2, y2, kinks + 1) THEN
                    '-- Match found 
                    result = 1
                    EXIT FUNCTION
                END IF
                IF checkForLowerPath(kinkyPath, x1, y1, x2, y2, kinks + 1) THEN
                    '-- Match found 
                    result = 1
                    EXIT FUNCTION
                END IF
            END IF
        END IF
    WEND
END FUNCTION


FUNCTION findPath (x1 AS INTEGER, y1 AS INTEGER, x2 AS INTEGER, y2 AS INTEGER) AS INTEGER
    '-- Not quite shortest path, just find one with at most 3 kinks in it 
    DEFBYTE kinkyPath(0 TO 9, 0 TO 19)
    DEFINT  kinks = 0, x, y, count, pathExists

    '-- Try to eliminate options 
    findPath = 0

    '-- Find direct path 
    IF x1 = x2 THEN
        '-- Direct vertical path 
        count = 0
        IF y1 > y2 THEN
            FOR y = y2+1 to y1-1
                count += playGrid(y, x1)
            NEXT
        ELSE
            FOR y = y1+1 to y2-1
                count += playGrid(y, x1)
            NEXT
        END IF
        IF count = 0 THEN
            '-- We have a direct path 
            playField.fillRect((x1 - 1) * 40 + gapWidth + 20, (y1 - 1) * 56 + gapHeight + 28, _
                               (x1 - 1) * 40 + gapWidth + 25, (y2 - 1) * 56 + gapHeight + 28, &HFF)
            highlightBlock(x1, y1)
            formPaint(form)
            SLEEP 0.5
            playField.fillRect((x1 - 1) * 40 + gapWidth + 20, (y1 - 1) * 56 + gapHeight + 28, _
                               (x1 - 1) * 40 + gapWidth + 25, (y2 - 1) * 56 + gapHeight + 28, 0)
            findPath = 1
            removeBlock(x1, y1)
            removeBlock(x2, y2)
            selectedBlock.x = 0
            selectedBlock.y = 0
            EXIT FUNCTION
        END IF
    ELSEIF y1 = y2 THEN
        '-- Direct horizontal path 
        count = 0
        IF x1 > x2 THEN
            FOR x = x2+1 to x1-1
                count += playGrid(y1, x)
            NEXT
        ELSE
            FOR x = x1+1 to x2-1
                count += playGrid(y1, x)
            NEXT
        END IF
        IF count = 0 THEN
            '-- We have a direct path 
            playField.fillRect((x1 - 1) * 40 + gapWidth + 20, (y1 - 1) * 56 + gapHeight + 25, _
                               (x2 - 1) * 40 + gapWidth + 20, (y1 - 1) * 56 + gapHeight + 30, &HFF)
            highlightBlock(x1, y1)
            formPaint(form)
            SLEEP 0.5
            playField.fillRect((x1 - 1) * 40 + gapWidth + 20, (y1 - 1) * 56 + gapHeight + 25, _
                               (x2 - 1) * 40 + gapWidth + 20, (y1 - 1) * 56 + gapHeight + 30, 0)
            findPath = 1
            removeBlock(x1, y1)
            removeBlock(x2, y2)
            selectedBlock.x = 0
            selectedBlock.y = 0
            EXIT FUNCTION
        END IF
    END IF


    '-- No direct path, try the indirect approach. 
    FOR y = 0 TO 9
        FOR x = 0 TO 19
            kinkyPath(y, x) = 0
        NEXT
    NEXT

    traverseLeftPath(kinkyPath, x1, y1, 1)
    traverseRightPath(kinkyPath, x1, y1, 1)
    traverseUpperPath(kinkyPath, x1, y1, 1)
    traverseLowerPath(kinkyPath, x1, y1, 1)

    traverseLeftPath(kinkyPath, x2, y2, 2)
    traverseRightPath(kinkyPath, x2, y2, 2)
    traverseUpperPath(kinkyPath, x2, y2, 2)
    traverseLowerPath(kinkyPath, x2, y2, 2)

    kinkyPath(y1, x1) = 9
    kinkyPath(y2, x2) = 9
    kinkyPath(0, 0) = 2
    kinkyPath(9, 0) = 2
    kinkyPath(0, 19) = 2
    kinkyPath(9, 19) = 2
    'FOR y = 0 TO 9 
    '    FOR x = 0 TO 19 
    '        PRINT kinkyPath(y, x); " "; 
    '    NEXT 
    '    PRINT 
    'NEXT 

    IF (kinkyPath(y1-1, x1) = 2) OR (kinkyPath(y1+1, x1) = 2) OR (kinkyPath(y1, x1-1) = 2) OR (kinkyPath(y1, x1+1) = 2) THEN
        IF (kinkyPath(y2-1, x2) = 2) OR (kinkyPath(y2+1, x2) = 2) OR (kinkyPath(y2, x2-1) = 2) OR (kinkyPath(y2, x2+1) = 2) THEN
            x = x1: y = y1
            IF y1 > y2 THEN
                SWAP y1, y2
                SWAP x1, x2
            END IF
            IF (kinkyPath(y1+1, x1) <> 2) AND (kinkyPath(y1, x1-1) <> 2) AND (kinkyPath(y1, x1+1) <> 2) THEN
                IF (kinkyPath(y2-1, x2) <> 2) AND (kinkyPath(y2, x2-1) <> 2) AND (kinkyPath(y2, x2+1) <> 2) THEN
                    '-- Impossible move 
                    EXIT FUNCTION
                END IF
            END IF
            IF x1 > x2 THEN
                SWAP y1, y2
                SWAP x1, x2
            END IF
            IF (kinkyPath(y1+1, x1) <> 2) AND (kinkyPath(y1-1, x1) <> 2) AND (kinkyPath(y1, x1+1) <> 2) THEN
                IF (kinkyPath(y2+1, x2) <> 2) AND (kinkyPath(y2-1, x2) <> 2) AND (kinkyPath(y2, x2-1) <> 2) THEN
                    '-- Impossible move 
                    EXIT FUNCTION
                END IF
            END IF
            pathExists = 0
            IF y1 > y2 THEN
                IF checkForLowerPath(kinkyPath, x1, y1, x2, y2, 0) THEN
                    pathExists = 1
                ELSEIF checkForLeftPath(kinkyPath, x1, y1, x2, y2, 0) THEN
                    pathExists = 1
                ELSEIF checkForRightPath(kinkyPath, x1, y1, x2, y2, 0) THEN
                    pathExists = 1
                ELSEIF checkForUpperPath(kinkyPath, x1, y1, x2, y2, 0) THEN
                    pathExists = 1
                END IF
            ELSE
                IF checkForUpperPath(kinkyPath, x1, y1, x2, y2, 0) THEN
                    pathExists = 1
                ELSEIF checkForLeftPath(kinkyPath, x1, y1, x2, y2, 0) THEN
                    pathExists = 1
                ELSEIF checkForRightPath(kinkyPath, x1, y1, x2, y2, 0) THEN
                    pathExists = 1
                ELSEIF checkForLowerPath(kinkyPath, x1, y1, x2, y2, 0) THEN
                    pathExists = 1
                END IF
            END IF

            IF pathExists THEN
                findPath = 1
                highlightBlock(x, y)
                selectedPath(0).x = x1
                selectedPath(0).y = y1
                FOR count = 1 TO selectedPath(0).kinks
                    IF selectedPath(count - 1).y = selectedPath(count).y THEN
                        '-- Horizontal 
                        playField.fillRect((selectedPath(count - 1).x - 1) * 40 + gapWidth + 18, (selectedPath(count - 1).y - 1) * 56 + gapHeight + 26, _
                               (selectedPath(count).x - 1) * 40 + gapWidth + 22, (selectedPath(count - 1).y - 1) * 56 + gapHeight + 30, &HFF)
                    ELSE
                        '-- Vertical 
                        playField.fillRect((selectedPath(count - 1).x - 1) * 40 + gapWidth + 18, (selectedPath(count - 1).y - 1) * 56 + gapHeight + 26, _
                               (selectedPath(count - 1).x - 1) * 40 + gapWidth + 22, (selectedPath(count).y - 1) * 56 + gapHeight + 30, &HFF)
                    END IF
                NEXT
                formPaint(form)
                SLEEP 0.5
                findPath = 1
                removeBlock(x1, y1)
                removeBlock(x2, y2)
                selectedBlock.x = 0
                selectedBlock.y = 0
                FOR count = 1 TO selectedPath(0).kinks
                    IF selectedPath(count - 1).y = selectedPath(count).y THEN
                        '-- Horizontal 
                        playField.fillRect((selectedPath(count - 1).x - 1) * 40 + gapWidth + 18, (selectedPath(count - 1).y - 1) * 56 + gapHeight + 26, _
                               (selectedPath(count).x - 1) * 40 + gapWidth + 22, (selectedPath(count - 1).y - 1) * 56 + gapHeight + 30, 0)
                    ELSE
                        '-- Vertical 
                        playField.fillRect((selectedPath(count - 1).x - 1) * 40 + gapWidth + 18, (selectedPath(count - 1).y - 1) * 56 + gapHeight + 26, _
                               (selectedPath(count - 1).x - 1) * 40 + gapWidth + 22, (selectedPath(count).y - 1) * 56 + gapHeight + 30, 0)
                    END IF
                NEXT
            END IF
        END IF
    END IF
END FUNCTION


SUB removeBlock (x AS INTEGER, y AS INTEGER)
    '-- Removes a block from the grid 

    playGrid(y, x) = 0
    x = (x - 1) * 40 + gapWidth
    y = (y - 1) * 56 + gapHeight

    playField.fillRect(x, y, x + 40, y + 56, 0)
END SUB


SUB formClick (sender AS QFORM)
    DEFINT x, y

    x = FLOOR((MouseX - gapWidth) / 40) + 1
    y = FLOOR((MouseY - gapHeight) / 56) + 1

    IF (x < 1 OR x > 18) OR (y < 1 OR y > 8) THEN
        IF selectedBlock.x > 0 THEN deHighlightBlock(selectedBlock.x, selectedBlock.y)
        formPaint(sender)
        EXIT SUB
    ELSEIF playGrid(y, x) = 0 THEN
        IF selectedBlock.x > 0 THEN deHighlightBlock(selectedBlock.x, selectedBlock.y)
        formPaint(sender)
        EXIT SUB
    END IF

    IF selectedBlock.x = x AND selectedBlock.y = y THEN
        deHighlightBlock(x, y)
    ELSE
        IF selectedBlock.x > 0 THEN
            '-- Check for matches 
            IF playGrid(y, x) = playGrid(selectedBlock.y, selectedBlock.x) THEN
                IF findPath(x, y, selectedBlock.x, selectedBlock.y) THEN
                    '-- Found match, now check if we're finished 
                    DEFINT count = 0

                    FOR y = 1 TO 8
                        FOR x = 1 TO 18
                            count += playGrid(y, x)
                        NEXT
                    NEXT
                    IF count = 0 THEN
                        timer1.enabled = 0
                        SHOWMESSAGE "Congratulations, you've won!" + CHR$(13) + _
                                    "With a time of " + STR$(clockTicks) + " seconds!"
                    END IF
                END IF
            END IF
        ELSE
            highlightBlock(x, y)
        END IF
    END IF
    formPaint(sender)
END SUB


SUB newItemClick (sender AS QMENUITEM)
    initPlayGrid(playGrid)
    clockTicks = 0
    timer1.enabled = 1
    timer1.interval = 1000
    formPaint(form)
END SUB


SUB exitItemClick (sender AS QMENUITEM)
    form.close
END SUB


SUB timer1Expired (sender AS QTIMER)
    INC(clockTicks)
    statusBar.panel(0).caption = RIGHT$("00"+STR$(FLOOR(clockTicks / 60)), 2) + ":" + RIGHT$("00"+STR$(clockTicks MOD 60), 2)
END SUB