ref: a0ae550d621a0403b3a2ce9ac1e2d1c71c2ae741
dir: /cflood.c/
#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] = { 0x6060a8ff, 0xf6f61dff, 0x46b0e0ff, 0x7ea020ff, 0xf070a0ff, 0xdc4a20ff }; 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 [-s size] [-t turns]\n"); exits("usage"); } void main(int argc, char** argv) { int key, p, i, oldbuttons, sidmax; Event e; Mouse m; Menu menu; Rectangle r; sid = Ssmall; 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; 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, CMAP8, 1, srccolors[i]); 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); }