I'm working on modernizing Rosetta Code's infrastructure. Starting with communications. Please accept this time-limited open invite to RC's Slack.. --Michael Mol (talk) 20:59, 30 May 2020 (UTC)

Number triplets game

From Rosetta Code
Number triplets game is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.
Task


The rules are simple:
You can move squares to yellow and blue ones, you can not move to black and white ones.
The goal is:
Number Triplets Game - goal image
To see how it works watch the next video:
Number Triplets Game in Ring - video
Please if you can attach a video file how your sample works.

Julia[edit]

Gtk library version. Click to choose a tile, then click move the tile in a single direction.

using Random, Gtk
 
blue = GtkCssProvider(data="#blue {background:blue;}")
black = GtkCssProvider(data="#black {background:black;}")
yellow = GtkCssProvider(data="#yellow {background:yellow;}")
white = GtkCssProvider(data="#white {background:white;}")
red = GtkCssProvider(data="#red {background:red; font-size:40px}")
spblue = GtkStyleProvider(blue)
spblack = GtkStyleProvider(black)
spyellow = GtkStyleProvider(yellow)
spwhite = GtkStyleProvider(white)
spred = GtkStyleProvider(red)
sp = [spblue spblack spblack spblack spblack;
spblue spyellow spred spred spred;
spblue spwhite spwhite spwhite spwhite;
spblue spyellow spred spred spred;
spblue spwhite spwhite spwhite spwhite;
spblue spyellow spred spred spred;
spblue spwhite spwhite spwhite spwhite;
spblue spyellow spred spred spred;
spblue spblack spblack spblack spblack;]
colors = ["blue" "black" "black" "black" "black";
"blue" "yellow" "red" "red" "red";
"blue" "white" "white" "white" "white";
"blue" "yellow" "red" "red" "red";
"blue" "white" "white" "white" "white";
"blue" "yellow" "red" "red" "red";
"blue" "white" "white" "white" "white";
"blue" "yellow" "red" "red" "red";
"blue" "black" "black" "black" "black";]
startingpositions = [(2, 5), (2, 4), (2, 3), (2, 2), (4, 5), (4, 4), (4, 3), (4, 2),
(6, 5), (6, 4), (6, 3), (6, 2), (8, 5), (8, 4), (8, 3), (8, 2)]
labels = ["" "" "" "" "";
"" "" "1" "1" "1";
"" "" "" "" "";
"" "" "2" "2" "2";
"" "" "" "" "";
"" "" "3" "3" "3";
"" "" "" "" "";
"" "" "4" "4" "4";
"" "" "" "" ""]
 
mutable struct GameTile
style::GtkStyleProvider
color::String
label::String
end
 
function NumberTripletsApp(w=800, h=500)
moves, won, ygrid, xgrid, basetiles, tiles = 0, false, 5, 9, Matrix{GameTile}, Matrix{GameTile}
buttons = [GtkButton() for i in 1:xgrid, j in 1:ygrid]
for i in 1:xgrid, j in 1:ygrid
set_gtk_property!(buttons[i, j], :expand, true)
end
function newgame()
moves, won = 0, false
basetiles = [GameTile(sp[i, j], colors[i, j], labels[i, j])
for i in 1:xgrid, j in 1:ygrid]
tiles = deepcopy(basetiles)
for (i, p) in enumerate(shuffle(startingpositions))
x, y = startingpositions[i]
tiles[first(p), last(p)], tiles[x, y] = tiles[x, y], tiles[first(p), last(p)]
end
for i in 2:2:8, j in 2:5 # previous red tile start becomes yellow if empty
basetiles[i, j] = GameTile(spyellow, "yellow", "")
end
for i in 1:xgrid, j in 1:ygrid
tiles[i, j].color != "red" && (tiles[i, j] = basetiles[i, j])
end
setbuttonview()
end
function setbuttonview()
# set text, color of each GtkButton in buttons as per corresponding tiles
for i in 1:xgrid, j in 1:ygrid
GAccessor.label(buttons[i, j], " ")
GAccessor.label(buttons[i, j], tiles[i, j].label)
sc = GAccessor.style_context(buttons[i, j])
push!(sc, tiles[i, j].style, 550)
set_gtk_property!(buttons[i, j], :name, tiles[i, j].color)
end
end
newgame()
win = GtkWindow("Number Triplets Game", w, h)
vbox = GtkBox(:v)
newgamebutton = GtkButton("New Game")
signal_connect(w -> newgame(), newgamebutton, "clicked")
prompt1, prompt2 = "Choose a Red Button", "Choose a Yellow or Blue Button"
promptlabel = GtkLabel(prompt1)
grid = GtkGrid()
set_gtk_property!(grid, :column_homogeneous, true)
set_gtk_property!(grid, :row_homogeneous, true)
bstate, lasttile, lastx, lasty = 0, nothing, 0, 0
function process_click(i, j)
if bstate == 0
if tiles[i, j].label != ""
bstate, lasttile, lastx, lasty = 1, tiles[i, j], i, j
GAccessor.text(promptlabel, " ")
GAccessor.text(promptlabel, "(Moves: $moves) $prompt2")
end
elseif bstate == 1
if tiles[i, j].color in ["yellow", "blue"] && lasttile != nothing &&
hasclearpath(lastx, lasty, i, j)
moves += 1
lasttile = basetiles[i, j]
tiles[i, j] = lasttile
tiles[lastx, lasty], tiles[i, j] = basetiles[lastx, lasty], tiles[lastx, lasty]
setbuttonview()
if map(x -> x.label, tiles) == labels
 !won && info_dialog("You have won the game. (Moves: $moves)")
won = true
end
end
GAccessor.text(promptlabel, " ")
GAccessor.text(promptlabel, prompt1)
bstate, lasttile, lastx, lasty = 0, nothing, 0, 0
end
end
function hasclearpath(x1, y1, x2, y2)
dx, dy = x2 - x1, y2 -y1
((dx == 0 && dy == 0) || dx * dy != 0) && return false # rook-format moves
if dx != 0
for k in x1+sign(dx):x2
tiles[k, y2].color != "blue" && return false
end
elseif dy != 0
for k in y1+sign(dy):y2
 !(tiles[x2, k].color in ["yellow", "blue"]) && return false
end
end
return true
end
for i in 1:xgrid, j in 1:ygrid
signal_connect(w -> process_click(i, j), buttons[i, j], "clicked")
grid[i, j] = buttons[i, j]
end
push!(vbox, promptlabel)
push!(vbox, newgamebutton)
push!(vbox, grid)
push!(win, vbox)
done = Condition()
endit(w) = notify(done)
signal_connect(endit, win, :destroy)
showall(win)
wait(done)
end
 
NumberTripletsApp()
 

Perl[edit]

Change: Once a red square is selected, it stays selected (until you select a different red square). This means after the selection, it only takes one click to move squares instead of two. Update: I got tired of doing multiple clicks to move a square, so this version allows one click to move anywhere multiple clicks would allow. Update: disabled buttons that are invalid destinations.

#!/usr/bin/perl
 
use strict;
use warnings;
use Tk;
use List::Util qw( shuffle );
 
my (@buttons, $pressed);
my $mw = MainWindow->new;
$mw->geometry( '+800+300' );
$mw->title( 'Number Triplets Game - RosettaCode' );
my $grid = $mw->Frame->pack;
for my $row ( 0 .. 4 )
{
for my $col ( 0 .. 8 )
{
my $color = $row ? $col & 1 ?
'yellow' : 0 < $col < 8 ? 'white' : 'black' : 'blue';
push @buttons, [ $grid->Button(-text => ' ',
-bg => $color, -fg => 'white',
-width => 2, -height => 2, -font => 'timesbold 20',
-command => sub { press($row * 10 + $col) },
)->grid(-row => $row, -column => $col), $color, 0, $row * 10 + $col ];
}
push @buttons, [0, 'green', 0];
}
$mw->Button(-text => 'New Game', -command => \&newgame, -height => 2,
-bg => 'black', -fg => 'magenta', -font => 'timesbold 20',
)->pack(-fill => 'x');
 
newgame();
MainLoop;
 
sub reach
{
my $button = shift;
my $field = join '', map { $_->[0] ? $_->[2] ? $_->[2] :
$_->[1] =~ /blue|yellow/ ? ' ' : '#': "\n" } @buttons;
substr $field, $button->[3], 1, '-';
1 while $field =~ s/-(|.{9}) | (|.{9})-/-$+-/s;
$_->[0] and $_->[0]->configure(-state => $_->[2] ||
substr($field, $_->[3], 1) eq '-' ? 'normal' : 'disabled') for @buttons;
}
 
sub press
{
my ($n) = @_;
my $button = $buttons[$n];
if( $button->[2] ) # has number, change selected
{
reach( $pressed = $button );
}
else # swap
{
$button->[2] = $pressed->[2];
$pressed->[2] = 0;
$button->[0]->configure(-bg => 'red3', -text => $button->[2]);
$pressed->[0]->configure(-bg => $pressed->[1], -text => ' ');
reach( $pressed = $button );
}
}
 
sub newgame
{
$_->[0] && $_->[0]->configure(-bg => $_->[1], -text => ' ',
-state => 'disabled'), $_->[2] = 0, for @buttons; # clear
my @valid = shuffle grep $_->[1] =~ /blue|yellow/, @buttons;
$valid[0][0]->configure(-bg => 'red3', -text => $_, -state => 'normal'),
$valid[0][2] = $_, shift @valid for qw(1 2 3 4) x 3;
}

Phix[edit]

Library: Phix/online

You can run this online here. Resize to taste. Use the cursor keys to move (green focus box), space to pick up or drop a tile (carried numbers turn orange). You can also use the mouse: hover on a neighbour to "lure" the focus box to follow you, click on a focused tile to pick up or drop it. Moving the mouse about or clicking on distant squares (as in more than one square away horizontally or vertically from the green focus box) achieves nothing. Any tile being carried is automatically dropped whenever a bump occurs. When finished, keying space starts another game.

--
-- demo\rosetta\NumberTripletsGame.exw
-- ===================================
--
with javascript_semantics
include pGUI.e
Ihandle dlg, canvas
cdCanvas cdcanvas

constant title = "Number Triplets Game",
         background = {"BBBBBBBBB",
                       "KYPYPYPYK",
                       "KYPYPYPYK",
                       "KYPYPYPYK",
                       "KYPYPYPYK"},
         ccc = {{'B',CD_BLUE},
                {'K',CD_BLACK},
                {'Y',CD_YELLOW},
                {'P',CD_PARCHMENT},
                {'R',CD_RED}},
         {colour_codes,colours} = columnize(ccc),
         target = {"         ",
                   "         ",
                   " 1 2 3 4 ",
                   " 1 2 3 4 ",
                   " 1 2 3 4 "}
sequence board, -- (as per target)
         board_row_centres,
         board_col_centres
integer cursor_row,
        cursor_col
bool bTileSelected,
     bGameOver

procedure new_game()
    board = deep_copy(target)
    string s = shuffle("111222333444")
    integer sdx = 1
    for row=3 to 5 do
        for col=2 to 8 by 2 do
            board[row][col] = s[sdx]
            sdx += 1
        end for
    end for
    cursor_row = 1
    cursor_col = 1
    bTileSelected = false
    bGameOver = false
    board_row_centres = {}
    board_col_centres = {}
    IupSetStrAttribute(dlg,"TITLE",title)
end procedure

function redraw_cb(Ihandle /*ih*/, integer /*posx*/, /*posy*/)
    integer {width, height} = IupGetIntInt(canvas, "DRAWSIZE"),
            tilew = floor(width/9)-1,
            tileh = floor(height/5)-1,
            leftm = floor((width-(tilew*9+10))/2),
            topm = floor((height-(tileh*5+6))/2)
    cdCanvasActivate(cdcanvas)
    cdCanvasSetBackground(cdcanvas, CD_DARK_GREY)
    cdCanvasClear(cdcanvas)
    cdCanvasFont(cdcanvas, "Helvetica", CD_BOLD, -floor(tileh/2))
    {} = cdCanvasTextAlignment(cdcanvas,CD_CENTER)
    board_row_centres = repeat(0,5)
    board_col_centres = repeat(0,9)
    for row=1 to 5 do
        integer y = height-(topm+tileh*(row-1)+row),
                y2 = y-tileh,
                cy = y-floor(tileh/2)
        board_row_centres[row] = cy
        for col=1 to 9 do
            integer clr = background[row][col]
            if board[row][col]!=' ' then
                clr = 'R'
            end if
            clr = colours[find(clr,colour_codes)]
            cdCanvasSetForeground(cdcanvas, clr)
            integer x = leftm+tilew*(col-1)+col,
                    x2 = x+tilew,
                    cx = x+floor(tilew/2)
            board_col_centres[col] = cx
            cdCanvasBox(cdcanvas,x,x2,y,y2)
            bool bCurrent = (row==cursor_row and col==cursor_col)
            if bCurrent then
                cdCanvasSetForeground(cdcanvas, CD_GREEN)
                cdCanvasSetLineWidth(cdcanvas, 4)
                cdCanvasRect(cdcanvas,x+4,x2-5,y-5,y2+4)
            end if
            integer ch = board[row][col]
            if ch!=' ' then
                clr = iff(bCurrent and bTileSelected?CD_ORANGE:CD_PARCHMENT)
                cdCanvasSetForeground(cdcanvas, clr)
                cdCanvasText(cdcanvas,cx,cy,""&ch)
            end if
        end for
    end for
    cdCanvasFlush(cdcanvas)
    return IUP_DEFAULT
end function

function map_cb(Ihandle ih)
    atom res = IupGetDouble(NULL, "SCREENDPI")/25.4
    IupGLMakeCurrent(canvas)
    if platform()=JS then
        cdcanvas = cdCreateCanvas(CD_IUP, canvas)
    else
        cdcanvas = cdCreateCanvas(CD_GL, "10x10 %g", {res})
    end if
    cdCanvasSetBackground(cdcanvas, CD_PARCHMENT)
    return IUP_DEFAULT
end function

function canvas_resize_cb(Ihandle /*canvas*/)
    integer {canvas_width, canvas_height} = IupGetIntInt(canvas, "DRAWSIZE")
    atom res = IupGetDouble(NULL, "SCREENDPI")/25.4
    cdCanvasSetAttribute(cdcanvas, "SIZE", "%dx%d %g", {canvas_width, canvas_height, res})
    return IUP_DEFAULT
end function

procedure move(integer dy, dx, bool bValid)
    if not bValid then
        bTileSelected = false
    else
        integer ny = cursor_row+dy,
                nx = cursor_col+dx
        if bTileSelected then
            integer tile = board[cursor_row][cursor_col]
            if board[ny][nx]!=' ' then
                bTileSelected = false
            else
                board[cursor_row][cursor_col] = ' '
                board[ny][nx] = tile
                bGameOver = board=target
                if bGameOver then
                    IupSetStrAttribute(dlg,"TITLE",title & " - GAME OVER")
                    bTileSelected = false
                end if
            end if
        end if
        cursor_row = ny
        cursor_col = nx
    end if
end procedure

function key_cb(Ihandle /*ih*/, atom c)
    if c=K_ESC then return IUP_CLOSE end if
    if not bGameOver then
        if    c=K_LEFT  then move( 0,-1, cursor_row=1 and cursor_col>1 )
        elsif c=K_RIGHT then move( 0, 1, cursor_row=1 and cursor_col<9 )
        elsif c=K_DOWN  then move( 1, 0, cursor_row<5 and even(cursor_col) )
        elsif c=K_UP    then move(-1, 0, cursor_row>1 )
        elsif c=K_SP then
            if bTileSelected then
                bTileSelected = false
            elsif board[cursor_row][cursor_col]!=' ' then
                bTileSelected = true
            end if
        end if
    elsif c=K_SP then
        new_game()
    end if
    IupRedraw(canvas)
    return IUP_CONTINUE
end function

function check_position(integer px, py)
--
-- convert x,y mouse move/click to row/col
--
    if not bGameOver 
    and board_row_centres!={} then -- (when started with mouse cursor on-board)
        integer myrow = 1,
                mxcol = 9
        for row=1 to 4 do
            if py>(board_row_centres[row]+board_row_centres[row+1])/2 then
                myrow = 6-row
                exit
            end if
        end for
        for col=1 to 8 do
            if px<(board_col_centres[col]+board_col_centres[col+1])/2 then
                mxcol = col
                exit
            end if
        end for
        return {myrow, mxcol}
    end if
    return {0,0}
end function

function motion_cb(Ihandle canvas, integer x, y, atom /*pStatus*/)
    integer {myrow, mxcol} = check_position(x,y),
            dy = cursor_row-myrow,
            dx = cursor_col-mxcol
    if (dy=0 and abs(dx)=1)
    or (dx=0 and abs(dy)=1) then
        integer c = iff(dy=0?iff(dx=+1?K_LEFT:K_RIGHT)
                            :iff(dy=-1?K_DOWN:K_UP))
        return key_cb(canvas, c)
    end if  
    return IUP_CONTINUE
end function

function button_cb(Ihandle canvas, integer button, pressed, x, y, atom /*pStatus*/)
    if button=IUP_BUTTON1 and not pressed then      -- (left button released)
        integer {myrow, mxcol} = check_position(x,y)
        if myrow = cursor_row
        and mxcol = cursor_col then
            return key_cb(canvas, K_SP)     -- pickup/drop
        else
            -- fallback in case no hover, eg on tablet/phone(?)
            return motion_cb(canvas,x,y,NULL)
        end if
    end if
    return IUP_CONTINUE
end function

procedure main()
    IupOpen()
    canvas = IupGLCanvas("RASTERSIZE=640x340")
    sequence cb = {"MAP_CB", Icallback("map_cb"),
                   "ACTION", Icallback("redraw_cb"),
                   "RESIZE_CB", Icallback("canvas_resize_cb"),
                   "MOTION_CB", Icallback("motion_cb"),
                   "BUTTON_CB", Icallback("button_cb")}
    IupSetCallbacks(canvas, cb)
    dlg = IupDialog(canvas,`TITLE="%s"`,{title})
    IupSetCallback(dlg, "KEY_CB", Icallback("key_cb"))
    new_game()
    IupShow(dlg)
    IupSetAttribute(canvas, "RASTERSIZE", NULL) -- (allow full resize)
    if platform()!=JS then
        IupMainLoop()
        IupClose()
    end if
end procedure
main()

Ring[edit]


Number Triplets Game in Ring - video

 
# Project : Number Triplets Game
# Date  : 19/08/2021-06:30:09
# Author  : Gal Zsolt (~ CalmoSoft ~)
# Email  : <[email protected]>
 
load "stdlib.ring"
load "guilib.ring"
 
C_GAMETITLE = 'Number Triplets Game'
C_WINDOWBACKGROUND = "background-color: gray;"
 
if isMobile()
C_LABELFONTSIZE = "font-size:120px;"
C_BUTTONFONTSIZE = "font-size:160px;"
else
C_LABELFONTSIZE = "font-size:50px;"
C_BUTTONFONTSIZE = "font-size:80px;"
ok
 
C_NEWGAMESTYLE = 'color:magenta;background-color:rgb(50,50,50);border-radius:17px;' + C_LABELFONTSIZE
C_BUTTONTOP = 'border-radius:17px;color:white; background-color: blue ;' + C_BUTTONFONTSIZE
C_BUTTONREDSTYLE = 'border-radius:17px;color:white; background-color: red ;' + C_BUTTONFONTSIZE
C_BUTTONSTYLE = 'border-radius:17px;color:white; background-color: yellow ;' + C_BUTTONFONTSIZE
C_EMPTYBUTTONSTYLE = 'border-radius:17px;color:black; background-color: white ;' + C_BUTTONFONTSIZE
C_BUTTONBLACKSTYLE = 'border-radius:17px;color:black; background-color: black ;' + C_BUTTONFONTSIZE
 
C_LAYOUTSPACING = 10
 
sizex = 9
sizey = 5
flag = 0
x1 = 0
x2 = 0
y1 = 0
y2 = 0
 
button = newlist(sizex,sizey)
LayoutButtonRow = list(sizey)
 
app = new qApp {
StyleFusion()
processevents()
win = new qWidget() {
setWindowTitle(C_GAMETITLE)
setgeometry(100,100,800,600)
setminimumwidth(300)
setminimumheight(300)
if not isMobile()
grabkeyboard()
ok
setstylesheet(C_WINDOWBACKGROUND)
move(490,100)
newgame = new QLabel(win) {
setalignment(Qt_AlignHCenter | Qt_AlignVCenter)
setstylesheet(C_NEWGAMESTYLE)
settext('New Game')
myfilter = new qallevents(newgame)
myfilter.setMouseButtonPressEvent("pbegin()")
installeventfilter(myfilter)
}
for n = 1 to sizex
for m = 1 to sizey
button[n][m] = new QPushButton(win)
next
next
LayoutGrid = new QGridLayout() {
setSpacing(C_LAYOUTSPACING)
for n = 1 to sizey
for m = 1 to sizex
AddWidget(button[m][n],n-1,m-1,0)
next
next
}
LayoutButtonMain = new QVBoxLayout() {
AddLayout(LayoutGrid)
AddWidget(newGame)
}
setLayout(LayoutButtonMain)
show()
pbegin()
}
exec()
}
 
func pbegin
flag = 0
for n = 1 to sizex
for m = 1 to sizey
if n%2 = 1
button[n][m].setstylesheet(C_EMPTYBUTTONSTYLE)
button[n][m].setenabled(false)
else
button[n][m].setstylesheet(C_BUTTONSTYLE)
ok
if n = 1 or n = 9
button[n][m].setstylesheet(C_EMPTYBUTTONSTYLE)
button[n][m].setenabled(false)
button[n][m].settext('')
ok
if m = 1
button[n][m].setstylesheet(C_BUTTONTOP)
button[n][m].setenabled(true)
button[n][m].settext('')
ok
button[n][m] { setclickevent("keypress(" + string(n) + "," + string(m) + ")") }
next
next
for row = 2 to sizey
button[1][row].setstylesheet(C_BUTTONBLACKSTYLE)
button[9][row].setstylesheet(C_BUTTONBLACKSTYLE)
next
numRand = list(4)
pRandom()
 
func pRandom
for n = 1 to sizex
for m = 1 to sizey
button[n][m].settext("")
next
next
checkNum = []
randNum = list(4)
for p = 1 to 4
randNum[p] = 0
next
while true
xRand = random(sizex-1) + 1
yRand = random(sizey-1) + 1
numRand = random(3)+1
str = " "
if xRand%2 = 0 and yRand != 1
if randNum[numRand] < 3
numStr = string(xRand)+string(yRand)
ind = find(checkNum,numStr)
if ind < 1
add(checkNum,numStr)
temp = randNum[numRand]
randNum[numRand] = temp + 1
button[xRand][yRand].settext(str+string(numRand)+str)
button[xRand][yRand].setstylesheet(C_BUTTONREDSTYLE)
ok
ok
ok
if randNum[1] = 3 and randNum[2] = 3 and randNum[3] = 3 and randNum[4] = 3
exit
ok
end
 
func keyPress x,y
flag++
switch flag
on 1
x1 = x
y1 = y
on 2
x2 = x
y2 = y
flag = 0
pMove(x1,y1,x2,y2)
off
 
func pMove col1,row1,col2,row2
if col1!=0 and col2!=0 and row1!=0 and row2!=0
if ( col1=col2 and row2=row1+1 and col1%2=0 ) or
( col1=col2 and row2=row1-1 and col1%2=0 ) or
( row1=row2 and col2=col1+1 and row1=1 ) or
( row1=row2 and col2=col1-1 and row1=1 ) or
( row1=row2 and col2=col1-1 and row1=1 )
temp = button[col1][row1].text()
if temp!= ""
button[col2][row2].setstylesheet(C_BUTTONREDSTYLE)
button[col2][row2].settext(temp)
if row1 = 1
button[col1][row1].setstylesheet(C_BUTTONTOP)
else
button[col1][row1].setstylesheet(C_BUTTONSTYLE)
ok
button[col1][row1].settext("")
ok
ok
ok
 

Wren[edit]

Library: DOME
Library: Wren-ellipse
Library: Go-fonts

It's not currently possible to run a DOME application online in a browser but the following is designed to look and work more or less like the Ring entry. The only significant difference is that when a red button is selected then, to give some visual indication of this, it acquires a white border which disappears when the button is moved or a different red button is selected.

As it's unclear what the initial state of the game should be, this places the red buttons in their final positions but randomizes their labels each time the game is started.

import "dome" for Window
import "graphics" for Canvas, Color, Font
import "audio" for AudioEngine
import "input" for Mouse
import "random" for Random
import "./ellipse" for Button
 
var Rand = Random.new()
 
class NumberTriplets {
construct new() {
Window.resize(900, 600)
Canvas.resize(900, 600)
Window.title = "Number triplets game"
// see Go-fonts page
Font.load("Go-Regular30", "Go-Regular.ttf", 30)
Canvas.font = "Go-Regular30"
// download from https://soundbible.com/509-Mouse-Double-Click.html
AudioEngine.load("click", "mouse_click.wav")
}
 
init() {
newGame()
}
 
newGame() {
Canvas.cls(Color.darkgray)
_btnMap = List.filled(5, null)
for (r in 0..4) _btnMap[r] = List.filled(9, null)
for (c in 0..8) _btnMap[0][c] = [Color.blue, 0]
for (r in 1..4) {
for (c in [0, 8]) _btnMap[r][c] = [Color.black, 0]
for (c in [1, 3, 5, 7]) {
_btnMap[r][c] = (r == 1) ? [Color.yellow, 0] : [Color.red, 0]
}
for (c in [2, 4, 6]) _btnMap[r][c] = [Color.white, 0]
}
_sel = [-2, -2] // denotes no button selected
_btns = List.filled(5, null)
for (r in 0..4) {
_btns[r] = List.filled(9, null)
for (c in 0..8) {
_btns[r][c] = Button.square(c * 100 + 50, r * 100 + 50, 80)
_btns[r][c].drawfill(_btnMap[r][c][0])
}
}
_btnNew = Button.new(450, 550, 860, 80)
_btnNew.drawfill(Color.black)
Canvas.print("New Game", 370, 530, Color.purple)
// randomize labels on red buttons
for (r in 2..4) {
var labels = Rand.shuffle([1, 2, 3, 4])
var i = 0
for (c in [1, 3, 5, 7]) {
var b = _btns[r][c]
var lbl = _btnMap[r][c][1] = labels[i]
Canvas.print(lbl.toString, b.cx - 10, b.cy - 20, Color.white)
i = i + 1
}
}
}
 
update() {
if (Mouse["left"].justPressed) {
AudioEngine.play("click")
var x = Mouse.x
var y = Mouse.y
var r = (y / 100).floor
if (r == 5) {
if (_btnNew.contains(x, y)) newGame()
return
}
var m = y % 100
if (m < 10 || m > 90) return
var c = (x / 100).floor
var n = x % 100
if (n < 10 || n > 90) return
var b = _btnMap[r][c]
if (b[0] == Color.black || b[0] == Color.white) return
if (b[0] == Color.red) {
if (_sel[0] != -2) _btns[_sel[0]][_sel[1]].draw(Color.red)
_sel = [r, c]
_btns[r][c].draw(Color.white)
} else if (b[0] == Color.yellow) {
if (_sel[0] == r - 1 || _sel[0] == r + 1) {
var btn = _btns[r][c]
btn.drawfill(Color.red)
var lbl = _btnMap[_sel[0]][c][1]
_btnMap[r][c] = [Color.red, lbl]
Canvas.print(lbl.toString, btn.cx - 10, btn.cy - 20, Color.white)
if (_sel[0] == 0) {
_btns[0][c].drawfill(Color.blue)
_btnMap[0][c] = [Color.blue, 0]
} else {
_btns[_sel[0]][c].drawfill(Color.yellow)
_btnMap[_sel[0]][c] = [Color.yellow, 0]
}
_sel = [-2, -2]
}
} else if (b[0] == Color.blue) {
if (_sel[0] == r + 1 || _sel[1] == c - 1 || _sel[1] == c + 1) {
var btn = _btns[r][c]
btn.drawfill(Color.red)
var lbl = _btnMap[_sel[0]][_sel[1]][1]
_btnMap[r][c] = [Color.red, lbl]
Canvas.print(lbl.toString, btn.cx - 10, btn.cy - 20, Color.white)
if (_sel[0] == 0) {
_btns[0][_sel[1]].drawfill(Color.blue)
_btnMap[0][_sel[1]] = [Color.blue, 0]
} else if (_sel[0] > 0) {
_btns[_sel[0]][_sel[1]].drawfill(Color.yellow)
_btnMap[_sel[0]][_sel[1]] = [Color.yellow, 0]
}
_sel = [-2, -2]
}
}
}
}
 
draw(alpha) {}
}
 
var Game = NumberTriplets.new()