'
' Inversi - all direct attached squares change color, so how to get all squares to the new color?
'
' Originally created by Frantisek Sodomka in newLisp with TCL/TK - Oct 2005
'
' Re-implemented from scratch by Peter van Eerten in BaCon using HUG - December 2010, GPL
'
' Version 1.0: first release
' Version 1.1: -right mouse button to reset current level
'              -keep track of hiscore
'              -improved scaling
' Version 1.2: -better drawing performance
'              -added explanation of rules
'              -more informative messages (needs HUG 0.30)
' Version 1.3: -some adjustments for BaCon 3.0
'
'--------------------------------------------------------------------------------------------------------

INCLUDE "hug.bac"

CONST BoardSize = 512
CONST Hiscore$ = CONCAT$(GETENVIRON$("HOME"), "/.inversi.hiscore")

CONST Rules$ = "\t\t*** The Game of Inversi ***\n\nAt each click the square underneath the mouse and the squares directly attached changes color.\n\nTry to get all squares to the other color in as few steps as possible!"

' Current level
DECLARE CurrentLevel = 2

' Define global boardcolors
DECLARE ColorNew, ColorOld = 0

' Associative arrays to keep track of status
DECLARE BoardStatus ASSOC int
DECLARE CacheStatus ASSOC int

' Read global colornames
DECLARE Colors$[8], Coldef$[8]

' Amount of steps
DECLARE StepsTaken

FOR i = 0 TO 7
    READ Colors$[i]
    READ Coldef$[i]
NEXT

DATA "BLACK", "#000000", "RED", "#FF0000", "GREEN", "#00FF00", "YELLOW", "#FFFF00"
DATA "BLUE", "#0000FF", "MAGENTA", "#FF00FF", "CYAN", "#00FFFF", "WHITE", "#FFFFFF"

'--------------------------------------------------------------------------------------------------------

SUB DetermineColors

    ' Determine colors, no black
    ColorOld = RANDOM(7)+1
    ColorNew = ColorOld
    WHILE ColorNew EQ ColorOld
        ColorNew = RANDOM(7)+1
    WEND

    ' Show color to user
    TEXT(window, "I N V E R S I   -   from " & Colors$[ColorOld] & " to " & Colors$[ColorNew])

END SUB

'--------------------------------------------------------------------------------------------------------

SUB ResetBoard

    LOCAL x, y

    FOR y = 0 TO CurrentLevel-1
        FOR x = 0 TO CurrentLevel-1
            BoardStatus(STR$(x) & "," & STR$(y)) = 0
            CacheStatus(STR$(x) & "," & STR$(y)) = -1
        NEXT
    NEXT

END SUB

'--------------------------------------------------------------------------------------------------------

SUB DrawBoard

    LOCAL x, y

    ' Fill in squares and stire status
    FOR y = 0 TO CurrentLevel - 1
        FOR x = 0 TO CurrentLevel - 1
            ' Only draw changed squares
            IF CacheStatus(CONCAT$(STR$(x), ",", STR$(y))) ISNOT BoardStatus(CONCAT$(STR$(x), ",", STR$(y))) THEN
                IF BoardStatus(CONCAT$(STR$(x), ",", STR$(y))) THEN
                    SQUARE(Coldef$[ColorNew], x*((BoardSize-12)/CurrentLevel), y*((BoardSize-12)/CurrentLevel), (BoardSize-12)/CurrentLevel, (BoardSize-12)/CurrentLevel, TRUE)
                ELSE
                    SQUARE(Coldef$[ColorOld], x*((BoardSize-12)/CurrentLevel), y*((BoardSize-12)/CurrentLevel), (BoardSize-12)/CurrentLevel, (BoardSize-12)/CurrentLevel, TRUE)
                END IF
                SQUARE("#000000", x*((BoardSize-12)/CurrentLevel), y*((BoardSize-12)/CurrentLevel), (BoardSize-12)/CurrentLevel, (BoardSize-12)/CurrentLevel, FALSE)
                CacheStatus(CONCAT$(STR$(x), ",", STR$(y))) = BoardStatus(CONCAT$(STR$(x), ",", STR$(y)))
            END IF
        NEXT
    NEXT

    ' Finalize drawing
    SQUARE("#000000", 0, 0, BoardSize-13, BoardSize-13, 0)

END SUB

'--------------------------------------------------------------------------------------------------------

SUB CheckWin

    LOCAL x, y, amount

    FOR y = 0 TO CurrentLevel - 1
        FOR x = 0 TO CurrentLevel - 1
            IF BoardStatus( STR$(x) & "," & STR$(y) )  THEN INCR amount
        NEXT
    NEXT

    IF amount IS CurrentLevel*CurrentLevel THEN
        TEXT(WinnerMsg, "Well done! Continue to level " & STR$(CurrentLevel) & "?")
        SHOW(WinnerMsg)
    END IF

END SUB

'--------------------------------------------------------------------------------------------------------

SUB HandleMouse

    LOCAL xcoord, ycoord, x, y, button

    xcoord = MOUSE(0)
    ycoord = MOUSE(1)
    button = MOUSE(2)

    IF button IS 1 THEN
        FOR y = 0 TO CurrentLevel - 1
            IF ycoord > y*((BoardSize-12)/CurrentLevel) AND ycoord < (y+1)*((BoardSize-12)/CurrentLevel) THEN
                FOR x = 0 TO CurrentLevel - 1
                    IF xcoord > x*((BoardSize-12)/CurrentLevel) AND xcoord < (x+1)*((BoardSize-12)/CurrentLevel) THEN
                        BoardStatus( CONCAT$(STR$(x), ",", STR$(y) ) ) = 1 - BoardStatus(CONCAT$(STR$(x), ",", STR$(y) ) )
                        BoardStatus( CONCAT$(STR$(x-1), ",", STR$(y) ) ) = 1 - BoardStatus(CONCAT$(STR$(x-1), ",", STR$(y) ) )
                        BoardStatus( CONCAT$(STR$(x+1), ",", STR$(y) ) ) = 1 - BoardStatus(CONCAT$(STR$(x+1), ",", STR$(y) ) )
                        BoardStatus( CONCAT$(STR$(x), ",", STR$(y-1) ) ) = 1 - BoardStatus(CONCAT$(STR$(x), ",", STR$(y-1) ) )
                        BoardStatus( CONCAT$(STR$(x), ",", STR$(y+1) ) ) = 1 - BoardStatus(CONCAT$(STR$(x), ",", STR$(y+1) ) )
                    END IF
                NEXT
            END IF
        NEXT

        DrawBoard
        CheckWin
        INCR StepsTaken

    ELIF button IS 3 THEN
        SHOW(ResetMsg)
    END IF

END SUB

'--------------------------------------------------------------------------------------------------------

SUB HandleWinner(NUMBER dialog, int button)

    HIDE(dialog)

    IF button IS GTK_RESPONSE_YES THEN
        INCR CurrentLevel
        ResetBoard
        DetermineColors
        DrawBoard
    ELSE
        IF FILEEXISTS(Hiscore$) THEN
            OPEN Hiscore$ FOR READING AS hiscore
                READLN name$ FROM hiscore
                READLN level$ FROM hiscore
                READLN steps$ FROM hiscore
            CLOSE FILE hiscore
            IF VAL(level$) < CurrentLevel OR (VAL(level$) IS CurrentLevel AND StepsTaken < VAL(steps$) ) THEN
                TEXT(TxtMsg, CONCAT$("You have reached level ", STR$(CurrentLevel), " in only ", STR$(StepsTaken), " steps.") )
                SHOW(NewhighMsg)
            ELSE
                TEXT(ExitMsg, CONCAT$("You have reached level ", STR$(CurrentLevel), " in ", STR$(StepsTaken), " steps."))
                SHOW(ExitMsg)
            END IF
        ELSE
            TEXT(TxtMsg, CONCAT$("You have reached level ", STR$(CurrentLevel), " in only ", STR$(StepsTaken), " steps.") )
            SHOW(NewhighMsg)
        END IF
    END IF

END SUB

'--------------------------------------------------------------------------------------------------------

SUB HandleReset(NUMBER dialog, int button)

    HIDE(dialog)

    IF button IS GTK_RESPONSE_YES THEN
        ResetBoard
        DrawBoard
    END IF

END SUB

'--------------------------------------------------------------------------------------------------------

SUB StoreData

    OPEN Hiscore$ FOR WRITING AS hiscore
        WRITELN GRAB$(entry) TO hiscore
        WRITELN CurrentLevel TO hiscore
        WRITELN StepsTaken TO hiscore
    CLOSE FILE hiscore

    HIDE(NewhighMsg)

    SHOW(ExitMsg)

END SUB

'--------------------------------------------------------------------------------------------------------

' No scaling because of the canvas
HUGOPTIONS("NOSCALING")

' Create main window in pixelsize
window = WINDOW("", BoardSize, BoardSize)
frame = FRAME(BoardSize-10, BoardSize-10)
ATTACH(window, frame, 5, 5)
canvas = CANVAS(BoardSize-12, BoardSize-12)
ATTACH(window, canvas, 6, 6)
CALLBACK(canvas, HandleMouse)

' Restore the scaling
HUGOPTIONS("SCALING")

' Create message dialog
InfoMsg = MSGDIALOG(Rules$, 450, 230, 0, 1)
CALLBACK(InfoMsg, HIDE)
SHOW(InfoMsg)

' Create Win dialog
WinnerMsg = MSGDIALOG("", 300, 120, 2, 4)
CALLBACK(WinnerMsg, HandleWinner)

' Create Reset dialog
ResetMsg = MSGDIALOG("Reset current level?", 300, 120, 2, 4)
CALLBACK(ResetMsg, HandleReset)

' Create Exit dialog
ExitMsg = MSGDIALOG("Thank you for playing!", 350, 120, 0, 2)
CALLBACK(ExitMsg, QUIT)

' Create Highscore dialog
NewhighMsg = WINDOW("New highscore!", 350, 200)
txt1 = MARK("Congratulations! A new hiscore was achieved!", 330, 30)
ATTACH(NewhighMsg, txt1, 10, 10)
TxtMsg = MARK("You have reached level " & STR$(CurrentLevel) & " in only " & STR$(StepsTaken) & " steps.", 330, 30)
ATTACH(NewhighMsg, TxtMsg, 10, 50)
txt3 = MARK("Please enter your name:", 200, 30)
entry = ENTRY(GETENVIRON$("USER"), 130, 30)
ATTACH(NewhighMsg, txt3, 10, 100)
ATTACH(NewhighMsg, entry, 210, 100)
button = STOCK("gtk-save", 100, 30)
ATTACH(NewhighMsg, button, 130, 160)
CALLBACK(button, StoreData)
FOCUS(entry)
HIDE(NewhighMsg)

' Initialize
ResetBoard
DetermineColors
DrawBoard

' Endless GTK event handler
DISPLAY