REM
REM This is a port of my Go panel for ScriptBasic.
REM
REM The program does not actually play Go. But it allows to edit existing
REM Tsume Go problems and checks on atari and suicide.
REM
REM The reason of this port was to see how bigger programs were behaving in BaCon.
REM
REM April 23, 2009 - PvE - GPL.
REM November 3, 2009 - better library naming.
REM ----------------------------------------------------------------------------------------------------------------

CATCH GOTO Handle_Error

CONST Gtk = "libgtk-x11-2.0.so.0"
CONST Gdk = "libgdk-x11-2.0.so.0"
CONST Gobject = "libgobject-2.0.so.0"

IMPORT "g_signal_connect_data" FROM Gobject TYPE void
IMPORT "gdk_color_parse" FROM Gdk TYPE void
IMPORT "gdk_draw_arc" FROM Gdk TYPE void
IMPORT "gdk_draw_line(long, long, int, int, int, int)" FROM Gdk TYPE void
IMPORT "gdk_draw_rectangle" FROM Gdk TYPE void
IMPORT "gdk_gc_new" FROM Gdk TYPE long
IMPORT "gdk_gc_set_rgb_bg_color" FROM Gdk TYPE void
IMPORT "gdk_gc_set_rgb_fg_color" FROM Gdk TYPE void
IMPORT "gdk_pixmap_new" FROM Gdk TYPE long
IMPORT "gdk_window_get_pointer" FROM Gdk TYPE long
IMPORT "gtk_button_new_with_label" FROM Gtk TYPE long
IMPORT "gtk_container_add" FROM Gtk TYPE void
IMPORT "gtk_event_box_new" FROM Gtk TYPE long
IMPORT "gtk_exit" FROM Gtk TYPE void
IMPORT "gtk_frame_new" FROM Gtk TYPE long
IMPORT "gtk_frame_set_shadow_type" FROM Gtk TYPE void
IMPORT "gtk_image_new" FROM Gtk TYPE long
IMPORT "gtk_image_set_from_pixmap" FROM Gtk TYPE void
IMPORT "gtk_init" FROM Gtk TYPE void
IMPORT "gtk_main" FROM Gtk TYPE void
IMPORT "gtk_radio_button_new_with_label_from_widget" FROM Gtk TYPE long
IMPORT "gtk_statusbar_get_context_id" FROM Gtk TYPE long
IMPORT "gtk_statusbar_new" FROM Gtk TYPE long
IMPORT "gtk_statusbar_pop" FROM Gtk TYPE void
IMPORT "gtk_statusbar_push" FROM Gtk TYPE void
IMPORT "gtk_table_attach_defaults" FROM Gtk TYPE void
IMPORT "gtk_table_new" FROM Gtk TYPE long
IMPORT "gtk_toggle_button_get_active" FROM Gtk TYPE long
IMPORT "gtk_widget_get_parent_window" FROM Gtk TYPE long
IMPORT "gtk_widget_queue_draw" FROM Gtk TYPE void
IMPORT "gtk_widget_set_size_request" FROM Gtk TYPE void
IMPORT "gtk_widget_show_all" FROM Gtk TYPE void
IMPORT "gtk_window_new" FROM Gtk TYPE long
IMPORT "gtk_window_set_position" FROM Gtk TYPE void
IMPORT "gtk_window_set_title" FROM Gtk TYPE void

REM ----------------------------------------------------------------------------------------------------------------

SUB Draw_Board

    LOCAL xfactor, yfactor, i, j TYPE int

    REM Calculate square size
    xfactor = ROUND(CANVAS_WIDTH/9)
    yfactor = ROUND(CANVAS_HEIGHT/9)

    REM Draw vertical axes
    gdk_color_parse("#000000", col)
    gdk_gc_set_rgb_fg_color(gc, col)
    FOR i = xfactor/2 TO CANVAS_WIDTH - xfactor/2 STEP xfactor
        gdk_draw_line(pix, gc, ROUND(i), ROUND(yfactor/2), ROUND(i), ROUND(yfactor/2)+yfactor*8)
    NEXT i

    REM Draw horizontal axes
    FOR i = yfactor/2 TO CANVAS_HEIGHT - yfactor/2 STEP yfactor
        gdk_draw_line(pix, gc, ROUND(xfactor/2), ROUND(i), ROUND(xfactor/2)+xfactor*8, ROUND(i))
    NEXT i

    REM Draw the positions of the stones
    FOR i = 1 TO 9
        FOR j = 1 TO 9
            IF board[i][j] EQ BLACK_STONE THEN
                gdk_color_parse("#000000", col)
                gdk_gc_set_rgb_fg_color(gc, col)
                gdk_draw_arc(pix, gc, 1, (i-1)*xfactor, (j-1)*yfactor, xfactor-1, yfactor-1, 0, 360*64)
            ELIF board[i][j] EQ WHITE_STONE THEN
                gdk_color_parse("#ffffff", col)
                gdk_gc_set_rgb_fg_color(gc, col)
                gdk_draw_arc(pix, gc, 1, (i-1)*xfactor, (j-1)*yfactor, xfactor-1, yfactor-1, 0, 360*64)
                gdk_color_parse("#000000", col)
                gdk_gc_set_rgb_fg_color(gc, col)
                gdk_draw_arc(pix, gc, 0, (i-1)*xfactor, (j-1)*yfactor, xfactor-1, yfactor-1, 0, 360*64)
            END IF
        NEXT j
    NEXT i

    gtk_widget_queue_draw (canvas)

END SUB

REM ----------------------------------------------------------------------------------------------------------------

SUB New_Board

    LOCAL i, j TYPE int

    FOR j = 1 TO 9
        FOR i = 1 TO 9
            board[j][i] = EMPTY
        NEXT i
    NEXT j
    gdk_color_parse("#ffffff", col)
    gdk_gc_set_rgb_fg_color(gc, col)
    gdk_draw_rectangle(pix, gc, 1, 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT)

    CALL Draw_Board

    lastmovex = 0
    lastmovey = 0
    capturedbywhite = 0
    capturedbyblack = 0
    gtk_statusbar_pop(gostatus, cid)
    gtk_statusbar_push(gostatus, cid, "New game started!")

END SUB

REM ----------------------------------------------------------------------------------------------------------------

SUB Captured_Stones

    LOCAL i, j, group, freedoms, counter, curr, total TYPE int
    LOCAL groups[11][11] TYPE int
    LOCAL posx[82], posy[82] TYPE int

    REM Wipe groups board
    FOR j = 0 TO 10
        FOR i = 0 TO 10
            groups[j][i] = FAKE
        NEXT i
    NEXT j

    REM Copy current playing board to groups board
    FOR j = 1 TO 9
        FOR i = 1 TO 9
            groups[j][i] = board[j][i]
        NEXT i
    NEXT j

    REM Clear temp array's keeping stone positions
    FOR i = 1 TO 81
        posx[i] = 0
        posy[i] = 0
    NEXT i

    REM Initialize used variables
    total = 0
    FOR j = 1 TO 9
        FOR i = 1 TO 9
            REM Check: place is empty and is not the last color played
            IF (ABS(groups[j][i]) NE EMPTY) AND (groups[j][i] NE board[lastmovex][lastmovey]) THEN
                freedoms = 0
                counter = 1
                curr = 1
                posx[curr] = j
                posy[curr] = i
                group = groups[j][i]
                groups[j][i] = -5*group
                REPEAT
                    IF groups[posx[curr]-1][posy[curr]] EQ EMPTY THEN
                        freedoms = freedoms + 1
                    ELIF groups[posx[curr]-1][posy[curr]] EQ group THEN
                        groups[posx[curr]-1][posy[curr]] = -5*group
                        counter = counter + 1
                        posx[counter] = posx[curr]-1
                        posy[counter] = posy[curr]
                    END IF
                    IF groups[posx[curr]+1][posy[curr]] EQ EMPTY THEN
                        freedoms = freedoms + 1
                    ELIF groups[posx[curr]+1][posy[curr]] EQ group THEN
                        groups[posx[curr]+1][posy[curr]] = -5*group
                        counter = counter + 1
                        posx[counter] = posx[curr]+1
                        posy[counter] = posy[curr]
                    END IF
                    IF groups[posx[curr]][posy[curr]-1] EQ EMPTY THEN
                        freedoms = freedoms + 1
                    ELIF groups[posx[curr]][posy[curr]-1] EQ group THEN
                        groups[posx[curr]][posy[curr]-1] = -5*group
                        counter = counter + 1
                        posx[counter] = posx[curr]
                        posy[counter] = posy[curr]-1
                    END IF
                    IF groups[posx[curr]][posy[curr]+1] EQ EMPTY THEN
                        freedoms = freedoms + 1
                    ELIF groups[posx[curr]][posy[curr]+1] EQ group THEN
                        groups[posx[curr]][posy[curr]+1] = -5*group
                        counter = counter + 1
                        posx[counter] = posx[curr]
                        posy[counter] = posy[curr]+1
                    END IF
                    curr = curr + 1

                UNTIL curr > counter

                IF freedoms EQ 0 THEN
                    counter = 1
                    curr = 1
                    posx[curr] = j
                    posy[curr] = i
                    group = groups[j][i]
                    groups[j][i] = EMPTY
                    board[j][i] = EMPTY
                    REPEAT
                        IF groups[posx[curr]-1][posy[curr]] EQ group THEN
                            groups[posx[curr]-1][posy[curr]] = EMPTY
                            board[posx[curr]-1][posy[curr]] = EMPTY
                            counter = counter + 1
                            posx[counter] = posx[curr]-1
                            posy[counter] = posy[curr]
                        END IF
                        IF groups[posx[curr]+1][posy[curr]] EQ group THEN
                            groups[posx[curr]+1][posy[curr]] = EMPTY
                            board[posx[curr]+1][posy[curr]] = EMPTY
                            counter = counter + 1
                            posx[counter] = posx[curr]+1
                            posy[counter] = posy[curr]
                        END IF
                        IF groups[posx[curr]][posy[curr]-1] EQ group THEN
                            groups[posx[curr]][posy[curr]-1] = EMPTY
                            board[posx[curr]][posy[curr]-1] = EMPTY
                            counter = counter + 1
                            posx[counter] = posx[curr]
                            posy[counter] = posy[curr]-1
                        END IF
                        IF groups[posx[curr]][posy[curr]+1] EQ group THEN
                            groups[posx[curr]][posy[curr]+1] = EMPTY
                            board[posx[curr]][posy[curr]+1] = EMPTY
                            counter = counter + 1
                            posx[counter] = posx[curr]
                            posy[counter] = posy[curr]+1
                        END IF
                        curr = curr + 1
                    UNTIL curr > counter

                    gdk_color_parse("#ffffff", col)
                    gdk_gc_set_rgb_fg_color(gc, col)
                    gdk_draw_rectangle( pix, gc, 1, 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT)
                    CALL Draw_Board
                    total = total + counter
                END IF
            END IF
        NEXT i
    NEXT j

    IF total > 0 THEN
        gtk_statusbar_pop(gostatus, cid)
        gtk_statusbar_push(gostatus, cid, CONCAT$(STR$(total), " stones captured!"))
    END IF

    IF board[lastmovex][lastmovey] EQ WHITE_STONE THEN
        capturedbywhite = capturedbywhite + total
    END IF

    IF board[lastmovex][lastmovey] EQ BLACK_STONE THEN
        capturedbyblack = capturedbyblack + total
    END IF

END SUB

REM ----------------------------------------------------------------------------------------------------------------

SUB Determine_Suicide

    LOCAL i, j, group, freedoms, counter, curr TYPE int
    LOCAL groups[11][11] TYPE int
    LOCAL posx[82], posy[82] TYPE int

    REM Wipe groups board with fake value
    FOR j = 0 TO 10
        FOR i = 0 TO 10
            groups[j][i] = FAKE
        NEXT i
    NEXT j

    REM Copy current playing board to groups board
    FOR j = 1 TO 9
        FOR i = 1 TO 9
            groups[j][i] = board[j][i]
        NEXT i
    NEXT j

    REM Clear temp array's keeping stone positions
    FOR i = 1 TO 81
        posx[i] = 0
        posy[i] = 0
    NEXT i

    REM Initialize local variables
    freedoms = 0
    counter = 1
    curr = 1
    posx[curr] = lastmovex
    posy[curr] = lastmovey
    group = groups[lastmovex][lastmovey]
    groups[lastmovex][lastmovey] = -1*group
    REPEAT
        IF groups[posx[curr]-1][posy[curr]] EQ EMPTY THEN
            freedoms = freedoms + 1
        ELIF groups[posx[curr]-1][posy[curr]] EQ group THEN
            groups[posx[curr]-1][posy[curr]] = -1*group
            counter = counter + 1
            posx[counter] = posx[curr]-1
            posy[counter] = posy[curr]
        END IF
        IF groups[posx[curr]+1][posy[curr]] EQ EMPTY THEN
            freedoms = freedoms + 1
        ELIF groups[posx[curr]+1][posy[curr]] EQ group THEN
            groups[posx[curr]+1][posy[curr]] = -1*group
            counter = counter + 1
            posx[counter] = posx[curr]+1
            posy[counter] = posy[curr]
        END IF
        IF groups[posx[curr]][posy[curr]-1] EQ EMPTY THEN
            freedoms = freedoms + 1
        ELIF groups[posx[curr]][posy[curr]-1] EQ group THEN
            groups[posx[curr]][posy[curr]-1] = -1*group
            counter = counter + 1
            posx[counter] = posx[curr]
            posy[counter] = posy[curr]-1
        END IF
        IF groups[posx[curr]][posy[curr]+1] EQ EMPTY THEN
            freedoms = freedoms + 1
        ELIF groups[posx[curr]][posy[curr]+1] EQ group THEN
            groups[posx[curr]][posy[curr]+1] = -1*group
            counter = counter + 1
            posx[counter] = posx[curr]
            posy[counter] = posy[curr]+1
        END IF

        curr = curr + 1

    UNTIL curr > counter

    IF freedoms EQ 0 THEN
        board[lastmovex][lastmovey] = EMPTY
        gdk_color_parse("#ffffff", col)
        gdk_gc_set_rgb_fg_color(gc, col)
        gdk_draw_rectangle(pix, gc, 1, 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT)
        CALL Draw_Board
        gtk_statusbar_pop(gostatus, cid)
        gtk_statusbar_push(gostatus, cid, "This is an illegal move! Play again...")
    END IF

END SUB

REM ----------------------------------------------------------------------------------------------------------------

REM Callback to put stone on board
SUB Put_Stone(NUMBER widget, NUMBER gdkeventbutton, NUMBER userdata)

    LOCAL mousex, mousey, xfactor, yfactor TYPE int
    LOCAL i, j TYPE int

    win = gtk_widget_get_parent_window(widget)
    gdk_window_get_pointer(win, ADDRESS(mousex), ADDRESS(mousey), 0)

    xfactor = ROUND(CANVAS_WIDTH/9)
    yfactor = ROUND(CANVAS_HEIGHT/9)

    FOR j = 1 TO 9
        FOR i = 1 TO 9
            IF (mousex > (j-1)*xfactor+xfactor*0.5) AND (mousex < (j-1)*xfactor+xfactor*1.5) THEN
                IF (mousey > (i-1)*yfactor+yfactor*0.5) AND (mousey < (i-1)*yfactor+yfactor*1.5) THEN
                    IF gtk_toggle_button_get_active(radioblack) EQ 1 THEN
                        IF board[j][i] EQ EMPTY THEN
                            board[j][i] = BLACK_STONE
                            gtk_statusbar_pop(gostatus, cid)
                            gtk_statusbar_push(gostatus, cid, "Black played.")
                            lastmovex = j
                            lastmovey = i
                        ELSE
                            gtk_statusbar_pop(gostatus, cid)
                            gtk_statusbar_push(gostatus, cid, "Cannot play here!")
                        END IF
                    ELSE
                        IF board[j][i] EQ EMPTY THEN
                            board[j][i] = WHITE_STONE
                            gtk_statusbar_pop(gostatus, cid)
                            gtk_statusbar_push(gostatus, cid, "White played.")
                            lastmovex = j
                            lastmovey = i
                        ELSE
                            gtk_statusbar_pop(gostatus, cid)
                            gtk_statusbar_push(gostatus, cid, "Cannot play here!")
                        END IF
                    END IF
                END IF
            END IF
        NEXT i
    NEXT j
    CALL Draw_Board
    CALL Captured_Stones
    CALL Determine_Suicide

END SUB

REM ----------------------------------------------------------------------------------------------------------------

REM Callback for exit
SUB Exit_Prog

    gtk_exit(0)

ENDSUB

REM ----------------------------------------------------------------------------------------------------------------

SUB Initialize_Global

    LOCAL i, j TYPE int

    REM This array keeps the current go-board, wipe Go playing board
    FOR j = 1 TO 9
        FOR i = 1 TO 9
            board[i][j] = EMPTY
        NEXT i
    NEXT j
    REM These keep the coordinates of the last move played
    lastmovex = 0
    lastmovey = 0
    REM These keep the total amount of captured stones for each color
    capturedbywhite = 0
    capturedbyblack = 0

END SUB

REM ----------------------------------------------------------------------------------------------------------------

SUB Show_Prisoners

    gtk_statusbar_pop(gostatus, cid)
    gtk_statusbar_push(gostatus, cid, CONCAT$("Captured by white: ", STR$(capturedbywhite), " - Captured by black: ", STR$(capturedbyblack)))

END SUB

REM ----------------------------------------------------------------------------------------------------------------

SUB Create_Gopanel

    gtk_init(0, 0)
    gopanel = gtk_window_new(0)
    gtk_window_set_title(gopanel, "The Go panel Revived")
    gtk_window_set_position(gopanel, 1)
    gtk_widget_set_size_request(gopanel, 300, 350)
    g_signal_connect_data(gopanel, "delete-event", Exit_Prog, 0, 0, 0)
    gogrid = gtk_table_new(55, 50, 1)
    gtk_container_add(gopanel, gogrid)
    frame = gtk_frame_new(0)
    gtk_table_attach_defaults(gogrid, frame, 1, 49, 1, 43)
    gtk_frame_set_shadow_type(frame, 3)
    canvas = gtk_image_new()
    REM Create eventbox to catch mouseclick
    ebox = gtk_event_box_new()
    gtk_container_add(ebox, canvas)
    gtk_table_attach_defaults(gogrid, ebox, 2, 48, 2, 42)
    REM Connect mouse button signal to image
    g_signal_connect_data(ebox, "button-press-event", Put_Stone, 0, 0, 0)
    radioblack = gtk_radio_button_new_with_label_from_widget(0, "Black")
    gtk_table_attach_defaults(gogrid, radioblack, 24, 36, 44, 47)
    radiowhite = gtk_radio_button_new_with_label_from_widget(radioblack, "White")
    gtk_table_attach_defaults(gogrid, radiowhite, 24, 36, 48, 51)
    buttoncapt = gtk_button_new_with_label("Show\nprison")
    gtk_table_attach_defaults(gogrid, buttoncapt, 12, 22, 44, 50)
    g_signal_connect_data(buttoncapt, "clicked", Show_Prisoners, 0, 0, 0)
    newbutton = gtk_button_new_with_label("New")
    gtk_table_attach_defaults(gogrid, newbutton, 1, 11, 44, 50)
    g_signal_connect_data(newbutton, "clicked", New_Board, 0, 0, 0)
    quitbutton = gtk_button_new_with_label("Quit")
    gtk_table_attach_defaults(gogrid, quitbutton, 41, 49, 44, 50)
    g_signal_connect_data(quitbutton, "clicked", Exit_Prog, 0, 0, 0)
    gostatus = gtk_statusbar_new()
    gtk_table_attach_defaults(gogrid, gostatus, 0, 50, 52, 55)
    cid = gtk_statusbar_get_context_id(gostatus, gopanel)
    gtk_statusbar_push(gostatus, cid, "New game started!")
    gtk_widget_show_all(gopanel)
    REM Create the pixmap
    gdkwin = gtk_widget_get_parent_window(canvas)
    pix = gdk_pixmap_new(gdkwin, CANVAS_WIDTH, CANVAS_HEIGHT, -1)
    gc = gdk_gc_new(pix)
    gtk_image_set_from_pixmap(canvas, pix, 0)
    REM Allocate memory for GdkColor with some random widget
    col = gtk_frame_new(0)
    REM Now set foreground and backgroundcolors to WHITE
    gdk_color_parse("#ffffff", col)
    gdk_gc_set_rgb_bg_color(gc, col)
    gdk_gc_set_rgb_fg_color(gc, col)
    REM Clear the complete pixmap with WHITE
    gdk_draw_rectangle(pix, gc, 1, 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT)
    REM Show whole GUI
    gtk_widget_show_all(gopanel)

END SUB

REM ----------------------------------------------------------------------------------------------------------------

REM Some globals
DECLARE board[10][10] TYPE int

CONST FAKE = 12345
CONST EMPTY = 0
CONST WHITE_STONE = -1
CONST BLACK_STONE = 1
CONST GROUPIDSTART = 10
CONST CANVAS_WIDTH = 275
CONST CANVAS_HEIGHT = 246

Create_Gopanel
Draw_Board

gtk_main
END

REM ----------------------------------------------------------------------------------------------------------------

LABEL Handle_Error
    PRINT ERR$(ERROR)
    END