#include <u.h>#include <libc.h>#include <draw.h>#include <event.h> enum{ /* sid */ Ssmall, Snormal, Slarge, Scustom, /* state */ Tgame, Twin, Tfail, /* cell flags & masks */ Flood = 1<<7, Redraw = 1<<6, ColorMask = 0x0f, NumColors = 6, ButtonSize = 32}; static int sizes[] = { 14, 21, 28, 1}; static int turns[] = { 25, 35, 50, 1}; static const ulong srccolors[NumColors][2] = { {0x6060a8ff, 0x000000ff}, {0xf6f61dff, 0x303030ff}, {0x46b0e0ff, 0x606060ff}, {0x7ea020ff, 0x909090ff}, {0xf070a0ff, 0xc0c0c0ff}, {0xdc4a20ff, 0xf0f0f0ff},}; static char *mstr[] = { "14x14 / 25", "25x25 / 35", "28x28 / 50", "exit", nil, /* the extra for "custom" */ nil}; static int size;static int sid;static int state;static int clickwait;static int turnsleft;static uchar *cells;static Image *colors[NumColors];static Rectangle buttons[NumColors];static Image *colora, *colorb; static voidfloodneighbours(uchar color, int x, int y); static voidredraw(Image *screen, int full){ static Point p, sp, strsize; int i, x, y, w, h, csize, left; uchar *c; Rectangle r; char s[64]; Font *f; sp.x = sp.y = 0; /* clear the old caption */ draw(screen, Rect(p.x, p.y, p.x+strsize.x, p.y+strsize.y), colora, nil, ZP); if(state == Tgame) sprint(s, "%d", turnsleft); else if(state == Twin) sprint(s, "You won using %d turns (of %d)", turns[sid]-turnsleft, turns[sid]); else if(state == Tfail) sprint(s, "You failed"); f = display->defaultfont; strsize = stringsize(f, s); w = Dx(screen->r); h = Dy(screen->r) - ButtonSize - 4 - strsize.y; csize = (w < h ? w : h) / size; w = size*csize; left = screen->r.min.x + (Dx(screen->r) - w)/2; /* buttons top */ y = screen->r.min.y + h + 2 + strsize.y; if(full){ /* background */ draw(screen, screen->r, colora, nil, ZP); /* buttons */ x = left + (w - NumColors*ButtonSize)/2; for(i = 0; i < NumColors; i++, x += ButtonSize){ buttons[i] = Rect(x, y, x+ButtonSize, y+ButtonSize); draw(screen, buttons[i], colors[i], nil, ZP); } } /* caption */ p.x = left + w/2 - strsize.x/2; p.y = y - strsize.y; string(screen, p, colorb, sp, f, s); /* cells */ c = cells; for(x = 0; x < size; x++){ for(y = 0; y < size; y++, c++){ if((*c & Redraw) != 0 || full){ *c &= ~Redraw; r.min.x = left + x*csize; r.min.y = screen->r.min.y + 2 + y*csize; r.max.x = r.min.x + csize; r.max.y = r.min.y + csize; draw(screen, r, colors[*c & ColorMask], nil, ZP); } } } flushimage(display, 1);} static voidfloodrecurse(uchar color, int x, int y){ uchar *c; c = &cells[x + y*size]; if((*c & Flood) == 0 && (*c & ColorMask) == color){ floodneighbours(color, x, y); }} static voidfloodneighbours(uchar color, int x, int y){ cells[x + y*size] = color|Flood|Redraw; if(x > 0) floodrecurse(color, x-1, y); if(x < size-1) floodrecurse(color, x+1, y); if(y > 0) floodrecurse(color, x, y-1); if(y < size-1) floodrecurse(color, x, y+1);} static intreflood(uchar color){ int n, x, y; color &= ColorMask; n = 0; for(x = 0; x < size; x++){ for(y = 0; y < size; y++){ if(cells[x + y*size] & Flood){ floodneighbours(color, x, y); n++; } } } return n;} static voidflood(uchar color){ int n; if((cells[0] & Flood) != 0 && (cells[0] & ColorMask) == color) return; if(!turnsleft) return; turnsleft--; n = reflood(color); if(n == size*size){ state = Twin; clickwait = 1; }else if(!turnsleft){ state = Tfail; clickwait = 1; } redraw(screen, 0);} static voidnewgame(int sid){ uchar *c; int i, maxsize; state = Tgame; size = sizes[sid]; turnsleft = turns[sid]; clickwait = 0; if(cells == nil){ maxsize = (size > sizes[Slarge]) ? size : sizes[Slarge]; cells = malloc(maxsize*maxsize); } /* randomize */ c = cells; for(i = 0; i < size*size; i++){ *c++ = nrand(NumColors); } cells[0] |= Flood|Redraw; reflood(cells[0]); redraw(screen, 1);} voideresized(int new){ if(new && getwindow(display, Refnone) < 0) sysfatal("can't reattach to window: %r"); redraw(screen, 1);} static voidusage(void){ fprint(2, "usage: cflood [-b] [-s size] [-t turns]\n"); exits("usage");} voidmain(int argc, char** argv){ int key, p, i, oldbuttons, sidmax, bw; Event e; Mouse m; Menu menu; Rectangle r; sid = Ssmall; bw = 0; ARGBEGIN{ case 's': sid = Scustom; sizes[sid] = atoi(ARGF()); if(sizes[sid] < 1) usage(); break; case 't': sid = Scustom; turns[sid] = atoi(ARGF()); if(turns[sid] < 1) usage(); break; case 'b': bw = 1; break; default: usage(); }ARGEND if(initdraw(nil, nil, "cflood") < 0) sysfatal("initdraw failed"); r = Rect(0, 0, 1, 1); for(i = 0; i < NumColors; i++) colors[i] = allocimage(display, r, RGB24, 1, srccolors[i][bw]); colora = display->white; colorb = display->black; einit(Emouse); menu.item = mstr; menu.lasthit = 0; srand(time(0)); sidmax = (sid > Slarge) ? sid : Slarge; if(sid == Scustom){ /* move "exit" and add "custom" size/turns */ sidmax = sid; mstr[Scustom+1] = mstr[Scustom]; mstr[Scustom] = smprint("%dx%d / %d", sizes[sid], sizes[sid], turns[sid]); } newgame(sid); for(oldbuttons = 0;;){ key = event(&e); m = e.mouse; if(key != Emouse) continue; if(m.buttons & 4){ p = emenuhit(3, &m, &menu); if(p >= Ssmall && p <= sidmax) newgame(sid = p); else if(p > 0) break; }else if(clickwait && !oldbuttons && m.buttons){ newgame(sid); }else if(m.buttons & 1){ for(i = 0; i < NumColors; i++){ if(ptinrect(m.xy, buttons[i])){ flood(i); break; } } } oldbuttons = m.buttons; } exits(nil);}
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
enum{
/* sid */
Ssmall,
Snormal,
Slarge,
Scustom,
/* state */
Tgame,
Twin,
Tfail,
/* cell flags & masks */
Flood = 1<<7,
Redraw = 1<<6,
ColorMask = 0x0f,
NumColors = 6,
ButtonSize = 32
};
static int sizes[] = {
14, 21, 28, 1
};
static int turns[] = {
25, 35, 50, 1
};
static const ulong srccolors[NumColors][2] = {
{0x6060a8ff, 0x000000ff},
{0xf6f61dff, 0x303030ff},
{0x46b0e0ff, 0x606060ff},
{0x7ea020ff, 0x909090ff},
{0xf070a0ff, 0xc0c0c0ff},
{0xdc4a20ff, 0xf0f0f0ff},
};
static char *mstr[] = {
"14x14 / 25",
"25x25 / 35",
"28x28 / 50",
"exit",
nil, /* the extra for "custom" */
nil
};
static int size;
static int sid;
static int state;
static int clickwait;
static int turnsleft;
static uchar *cells;
static Image *colors[NumColors];
static Rectangle buttons[NumColors];
static Image *colora, *colorb;
static void
floodneighbours(uchar color, int x, int y);
static void
redraw(Image *screen, int full)
{
static Point p, sp, strsize;
int i, x, y, w, h, csize, left;
uchar *c;
Rectangle r;
char s[64];
Font *f;
sp.x = sp.y = 0;
/* clear the old caption */
draw(screen, Rect(p.x, p.y, p.x+strsize.x, p.y+strsize.y), colora, nil, ZP);
if(state == Tgame)
sprint(s, "%d", turnsleft);
else if(state == Twin)
sprint(s, "You won using %d turns (of %d)", turns[sid]-turnsleft, turns[sid]);
else if(state == Tfail)
sprint(s, "You failed");
f = display->defaultfont;
strsize = stringsize(f, s);
w = Dx(screen->r);
h = Dy(screen->r) - ButtonSize - 4 - strsize.y;
csize = (w < h ? w : h) / size;
w = size*csize;
left = screen->r.min.x + (Dx(screen->r) - w)/2;
/* buttons top */
y = screen->r.min.y + h + 2 + strsize.y;
if(full){
/* background */
draw(screen, screen->r, colora, nil, ZP);
/* buttons */
x = left + (w - NumColors*ButtonSize)/2;
for(i = 0; i < NumColors; i++, x += ButtonSize){
buttons[i] = Rect(x, y, x+ButtonSize, y+ButtonSize);
draw(screen, buttons[i], colors[i], nil, ZP);
}
}
/* caption */
p.x = left + w/2 - strsize.x/2;
p.y = y - strsize.y;
string(screen, p, colorb, sp, f, s);
/* cells */
c = cells;
for(x = 0; x < size; x++){
for(y = 0; y < size; y++, c++){
if((*c & Redraw) != 0 || full){
*c &= ~Redraw;
r.min.x = left + x*csize;
r.min.y = screen->r.min.y + 2 + y*csize;
r.max.x = r.min.x + csize;
r.max.y = r.min.y + csize;
draw(screen, r, colors[*c & ColorMask], nil, ZP);
}
}
}
flushimage(display, 1);
}
static void
floodrecurse(uchar color, int x, int y)
{
uchar *c;
c = &cells[x + y*size];
if((*c & Flood) == 0 && (*c & ColorMask) == color){
floodneighbours(color, x, y);
}
}
static void
floodneighbours(uchar color, int x, int y)
{
cells[x + y*size] = color|Flood|Redraw;
if(x > 0)
floodrecurse(color, x-1, y);
if(x < size-1)
floodrecurse(color, x+1, y);
if(y > 0)
floodrecurse(color, x, y-1);
if(y < size-1)
floodrecurse(color, x, y+1);
}
static int
reflood(uchar color)
{
int n, x, y;
color &= ColorMask;
n = 0;
for(x = 0; x < size; x++){
for(y = 0; y < size; y++){
if(cells[x + y*size] & Flood){
floodneighbours(color, x, y);
n++;
}
}
}
return n;
}
static void
flood(uchar color)
{
int n;
if((cells[0] & Flood) != 0 && (cells[0] & ColorMask) == color)
return;
if(!turnsleft)
return;
turnsleft--;
n = reflood(color);
if(n == size*size){
state = Twin;
clickwait = 1;
}else if(!turnsleft){
state = Tfail;
clickwait = 1;
}
redraw(screen, 0);
}
static void
newgame(int sid)
{
uchar *c;
int i, maxsize;
state = Tgame;
size = sizes[sid];
turnsleft = turns[sid];
clickwait = 0;
if(cells == nil){
maxsize = (size > sizes[Slarge]) ? size : sizes[Slarge];
cells = malloc(maxsize*maxsize);
}
/* randomize */
c = cells;
for(i = 0; i < size*size; i++){
*c++ = nrand(NumColors);
}
cells[0] |= Flood|Redraw;
reflood(cells[0]);
redraw(screen, 1);
}
void
eresized(int new)
{
if(new && getwindow(display, Refnone) < 0)
sysfatal("can't reattach to window: %r");
redraw(screen, 1);
}
static void
usage(void)
{
fprint(2, "usage: cflood [-b] [-s size] [-t turns]\n");
exits("usage");
}
void
main(int argc, char** argv)
{
int key, p, i, oldbuttons, sidmax, bw;
Event e;
Mouse m;
Menu menu;
Rectangle r;
sid = Ssmall;
bw = 0;
ARGBEGIN{
case 's':
sid = Scustom;
sizes[sid] = atoi(ARGF());
if(sizes[sid] < 1)
usage();
break;
case 't':
sid = Scustom;
turns[sid] = atoi(ARGF());
if(turns[sid] < 1)
usage();
break;
case 'b':
bw = 1;
break;
default:
usage();
}ARGEND
if(initdraw(nil, nil, "cflood") < 0)
sysfatal("initdraw failed");
r = Rect(0, 0, 1, 1);
for(i = 0; i < NumColors; i++)
colors[i] = allocimage(display, r, RGB24, 1, srccolors[i][bw]);
colora = display->white;
colorb = display->black;
einit(Emouse);
menu.item = mstr;
menu.lasthit = 0;
srand(time(0));
sidmax = (sid > Slarge) ? sid : Slarge;
if(sid == Scustom){
/* move "exit" and add "custom" size/turns */
sidmax = sid;
mstr[Scustom+1] = mstr[Scustom];
mstr[Scustom] = smprint("%dx%d / %d", sizes[sid], sizes[sid], turns[sid]);
}
newgame(sid);
for(oldbuttons = 0;;){
key = event(&e);
m = e.mouse;
if(key != Emouse)
continue;
if(m.buttons & 4){
p = emenuhit(3, &m, &menu);
if(p >= Ssmall && p <= sidmax)
newgame(sid = p);
else if(p > 0)
break;
}else if(clickwait && !oldbuttons && m.buttons){
newgame(sid);
}else if(m.buttons & 1){
for(i = 0; i < NumColors; i++){
if(ptinrect(m.xy, buttons[i])){
flood(i);
break;
}
}
}
oldbuttons = m.buttons;
}
exits(nil);
}