ref: 47de140b5fbf8498bf8da6f43cbfd50227f04d7e
dir: /games/cflood.c/
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
enum {
Ssmall,
Snormal,
Slarge,
Tgame,
Twin,
Tfail,
Flood = 1<<7,
ColorMask = 0x0f,
NumColors = 6,
ButtonSize = 32
};
const int sizes[] = {14, 21, 28};
const int turns[] = {25, 35, 50};
const ulong srccolors[NumColors] = {
0x6060a8ff,
0xf6f61dff,
0x46b0e0ff,
0x7ea020ff,
0xf070a0ff,
0xdc4a20ff
};
char *mstr[] = {
"14x14 / 25",
"25x25 / 35",
"28x28 / 50",
"exit",
0
};
static int sid;
static int size;
static int type;
static int wait4click;
static int turnsleft;
static uchar cells[28*28]; // enough for maximal size
static Image *colors[NumColors];
static Rectangle buttons[NumColors];
static void
floodneighbours(uchar color, int x, int y);
static void
redraw(Image *screen) {
Rectangle r = screen->r;
draw(screen, r, display->white, nil, ZP);
char s[64];
if(type == Tgame)
sprint(s, "%d", turnsleft);
else if(type == Twin)
sprint(s, "You won using %d turns (of %d)",
turns[sid]-turnsleft,
turns[sid]);
else if(type == Tfail)
sprint(s, "You failed");
Font *f = display->defaultfont;
Point strsize = stringsize(f, s);
const uchar *cell = &cells[0];
int w = Dx(r);
int h = Dy(r) - ButtonSize - 2 - strsize.y;
int c = (w < h ? w : h) / size;
w = c*size;
int left = r.min.x + (Dx(r) - w)/2;
// cells
for(int x = left; x < left+size*c; x+=c) {
for(int y = r.min.y; y < r.min.y+size*c; y+=c) {
Rectangle r = Rect(x, y, x+c, y+c);
draw(screen, r, colors[*cell & ColorMask], nil, ZP);
cell++;
}
}
// buttons
int x = left + (w/2) - NumColors*ButtonSize/2;
int y = r.min.y + h + strsize.y;
for(int 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
Point p = {left + w/2 - strsize.x/2, y - strsize.y};
Point sp = {0, 0};
string(screen, p, display->black, sp, f, s);
flushimage(display, 1);
}
static void
floodrecurse(uchar color, int x, int y) {
uchar *c = &cells[x + y*size];
if((*c & Flood) == 0 && (*c & ColorMask) == color) {
*c = color | Flood;
floodneighbours(color, x, y);
}
}
static void
floodneighbours(uchar color, int x, int y) {
cells[x + y*size] = color | Flood;
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) {
color &= ColorMask;
int n = 0;
for(int x = 0; x < size; x++)
for(int y = 0; y < size; y++)
if(cells[x + y*size] & Flood) {
floodneighbours(color, x, y);
n++;
}
return n;
}
static void
flood(uchar color) {
if((cells[0] & Flood) != 0 && (cells[0] & ColorMask) == color)
return;
if(!turnsleft)
return;
turnsleft--;
int n = reflood(color);
if(n == size*size) {
type = Twin;
wait4click = 1;
} else if(!turnsleft) {
type = Tfail;
wait4click = 1;
}
redraw(screen);
}
static void
newgame(int sid) {
type = Tgame;
size = sizes[sid];
turnsleft = turns[sid];
// randomize
uchar *c = &cells[0];
for(int i = 0; i < size*size; i++) {
*c++ = nrand(NumColors);
}
cells[0] |= Flood;
reflood(cells[0]);
redraw(screen);
}
void
eresized(int new)
{
if(new && getwindow(display, Refnone) < 0)
sysfatal("can't reattach to window: %r");
redraw(screen);
}
void main(int, char**) {
Menu menu;
if(initdraw(0, 0, "cflood") < 0)
sysfatal("initdraw failed");
Rectangle r = Rect(0, 0, 1, 1);
for(int i = 0; i < NumColors; i++)
colors[i] = allocimage(display, r, CMAP8, 1, srccolors[i]);
einit(Emouse);
menu.item = mstr;
menu.lasthit = 0;
srand(time(0));
sid = Snormal;
newgame(sid);
for(int mold = 0;;) {
Event e;
int key = event(&e);
Mouse m = e.mouse;
if(key != Emouse)
continue;
if(m.buttons & 4) {
int p = emenuhit(3, &m, &menu);
if(p >= Ssmall && p <= Slarge)
newgame(sid = p);
else if(p > 0)
break;
} else if(wait4click && !mold && m.buttons != mold) {
wait4click = 0;
newgame(sid);
} else if(m.buttons & 1) {
for(int i = 0; i < NumColors; i++) {
if(ptinrect(m.xy, buttons[i])) {
flood(i);
break;
}
}
}
mold = m.buttons;
}
}