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; } }