#include #include #include #include #include char *data = "/sys/games/lib/fillomino"; char *fontpath = "/lib/font/bit/pelm/ascii.16.font"; ulong rgb[] = {DRed, DGreen, DBlue, DYellow, DCyan, DMagenta}; Image *hue[nelem(rgb)], *bad; enum{MAXDAT = 1024, CLUE = 0x40, FLOOD = 0x20, SQ = 32}; char sol[MAXDAT * 2], *puz, *vis; int x, y, cur; void flood(int i, int n){ vis[i] |= FLOOD; if(i >= x && (vis[i - x] & ~CLUE) == n) flood(i - x, n); if((i + 1) % x && (vis[i + 1] & ~CLUE) == n) flood(i + 1, n); if(i + x < x * y && (vis[i + x] & ~CLUE) == n) flood(i + x, n); if(i % x && (vis[i - 1] & ~CLUE) == n) flood(i - 1, n); } void redraw(void){ Rectangle r; char s[2]; int i, j, n, c; s[1] = '\0'; for(i = 0; i < x * y; i++){ r.min = Pt(screen->r.min.x + i % x * SQ, screen->r.min.y + i / x * SQ); r.max = addpt(r.min, Pt(SQ, SQ)); if(vis[i] == 0){ draw(screen, r, display->white, nil, ZP); continue; } n = vis[i] & ~CLUE; s[0] = n <= 9 ? n + '0' : n - 10 + 'A'; flood(i, n); for(j = c = 0; j < x * y; j++) if(vis[j] & FLOOD){ vis[j] ^= FLOOD; c++; } draw(screen, r, c == n ? hue[n % nelem(hue)] : vis[i] & CLUE ? display->white : bad, nil, ZP); r.min.x += SQ - stringwidth(font, s) >> 1; r.min.y += SQ - font->height >> 1; string(screen, r.min, vis[i] & CLUE ? display->black : display->white, ZP, font, s); } r.min = Pt(screen->r.min.x + cur % x * SQ, screen->r.min.y + cur / x * SQ); r.max = addpt(r.min, Pt(SQ, SQ)); border(screen, r, 4, display->black, ZP); } void eresized(int i){ if(i && getwindow(display, Refnone) < 0) sysfatal("can't reattach to window"); if((!i || Dx(screen->r) != x * SQ || Dy(screen->r) != y * SQ) && (i = open("/dev/wctl", OWRITE)) >= 0){ fprint(i, "resize -dx %d -dy %d", x * SQ + 2 * Borderwidth, y * SQ + 2 * Borderwidth); close(i); } redraw(); } void main(int, char **argv){ vlong n; int fd; if(initdraw(nil, nil, *argv) < 0) sysfatal("initdraw: %r"); if((font = openfont(display, fontpath)) == nil) sysfatal("openfont: %r"); for(n = 0; n < nelem(hue); n++) if((hue[n] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, rgb[n])) == nil) sysfatal("allocimage: %r"); if((bad = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x777777FF)) == nil) sysfatal("allocimage: %r"); einit(Ekeyboard | Emouse); srand(truerand()); if((fd = open(data, OREAD)) < 0) sysfatal("open: %r"); n = nrand(seek(fd, -MAXDAT, 2)); /* there's a big one at the end */ seek(fd, n, 0); /* so everyone gets a chance */ read(fd, sol, MAXDAT * 2 - 1); close(fd); for(vis = sol; *vis; vis++) if(vis[0] == '\n' && vis[1] == '\n'){ vis += 2; break; } for(puz = sol; ; vis++){ if(*vis == '\n'){ y++; if(vis[1] == '\n') break; if(!x) x = puz - sol; } else if(*vis >= 'A' && *vis <= 'Z') *puz++ = *vis - 'A' | CLUE; else if(*vis >= 'a' && *vis <= 'z') *puz++ = *vis - 'a'; else sysfatal("bad game data: %s:#%lld", data, n); } if(puz - sol != x * y) sysfatal("bad dimensions: %d x %d != %lld: %s:#%lld", x, y, puz - sol, data, n); for(n = 0, vis = puz; n < puz - sol; n++) *vis++ = sol[n] & CLUE ? sol[n] : 0; vis = puz; eresized(0); for(;;){ switch(n = ekbd()){ case Kdel: exits(nil); case Kesc: vis = vis == puz ? sol : puz; break; case Kup: if((cur -= x) < 0) cur += x * y; break; case Kright: if(++cur % x == 0) cur -= x; break; case Kdown: cur = (cur + x) % (x * y); break; case Kleft: if(cur-- % x == 0) cur += x; break; case Kbs: case ' ': if(vis == sol || puz[cur] & CLUE) continue; puz[cur] = 0; break; default: if(vis == sol || puz[cur] & CLUE) continue; if(n >= '1' && n <= '9') puz[cur] = n - '0'; else if(n >= 'a' && n <= 'p') puz[cur] = n - 'a' + 10; else continue; break; } redraw(); } }