/* code from mark huckvale: http://www.phon.ucl.ac.uk/home/mark/sudoku/ */ #include #include #include #include #include "sudoku.h" char *imgdir = "/sys/games/lib/sudoku/images"; char *lvldir = "/sys/games/lib/sudoku/boards"; /* level library dir */ int selected; /* which digit do we have selected? */ Image *background; /* DPaleyellow */ Image *backselect; /* DPalebluegreen */ Image *blink; /* DDarkyellow */ Image *brdr; /* 0x55555555 */ Image *fixed; /* DBlue */ Image *wrong; /* DRed */ Image *dig[10]; /* digit masks */ Dir *dir; int numlevels; int curlevel; char *buttons[] = { "new", "check", "solve", "clear", "save", "load", "print", "offline", "exit", 0 }; Menu menu = { buttons }; Menu lmenu = { nil, genlevels, 0, }; int readlevels(char *leveldir) { int fd, n; if((fd = open(leveldir, OREAD)) < 0) return -1; n = dirreadall(fd, &dir); close(fd); return n; } char * genlevels(int i) { if(numlevels == 0) numlevels = readlevels(lvldir); if(numlevels > 0 && i < numlevels) return (dir+i)->name; return nil; } void convert(Cell *brd, int *board) { int i; for(i = 0; i < Psize; i++) { brd[i].digit = board[i] & Digit; if(brd[i].digit < 0 || brd[i].digit > 9) brd[i].digit = -1; brd[i].solve = (board[i] & Solve) >> 4; brd[i].locked = board[i] & MLock; } memcpy(obrd, brd, Psize * sizeof(Cell)); } Image * eallocimage(Rectangle r, int repl, uint color) { Image *tmp; tmp = allocimage(display, r, screen->chan, repl, color); if(tmp == nil) sysfatal("cannot allocate buffer image: %r"); return tmp; } Image * eloadfile(char *path) { Image *img; int fd; fd = open(path, OREAD); if(fd < 0) { fprint(2, "cannot open image file %s: %r\n", path); exits("image"); } img = readimage(display, fd, 0); if(img == nil) sysfatal("cannot load image: %r"); close(fd); return img; } void clearboard(Cell *board) { int i; for(i = 0; i < Psize; i++) { board[i].digit = -1; board[i].solve = 0; board[i].locked = 0; } } void solveboard(Cell *board) { int i; for(i = 0; i < Psize; i++) { board[i].digit = board[i].solve; } } int checkpossible(Cell *board, int x, int y, int num) { int i, j; for(i = 0; i < Brdsize; i++) { if(board[i*Brdsize + y].digit == num && i != x) return 0; if(board[x*Brdsize + i].digit == num && i != y) return 0; } for(i = x - (x%3); i < x - (x%3) + 3; i++) for(j = y - (y%3); j < y - (y%3) + 3; j++) if((i != x && j != y) && board[i*Brdsize + j].digit == num) return 0; return 1; } void resize(void) { int fd; fd = open("/dev/wctl", OWRITE); if(fd >= 0){ fprint(fd, "resize -dx %d -dy %d", Maxx, Maxy); close(fd); } } void drawcell(int x, int y, int num, Image *col) { Rectangle r = Rect(x*Square, y*Square, (x+1)*Square, (y+1)*Square); if(num < 0 || num > 9) return; r = insetrect(r, Border); r = rectaddpt(r, Pt(0, Square)); r.max = addpt(r.max, Pt(2, 2)); draw(screen, rectaddpt(r, screen->r.min), col, dig[num], ZP); } void drawboard(void) { int i; for(i = 0; i < Psize; i++) { drawcell(i / Brdsize, i % Brdsize, brd[i].digit, brd[i].locked ? fixed : display->black); } } void drawchecked(Cell *brd) { int i; for(i = 0; i < Psize; i++) { if(brd[i].locked) drawcell(i / Brdsize, i % Brdsize, brd[i].digit, fixed); else drawcell(i / Brdsize, i % Brdsize, brd[i].digit, checkpossible(brd, i / Brdsize, i % Brdsize, brd[i].digit) ? display->black : wrong); } } void drawscreen(void) { Point l1, l2; int i; draw(screen, screen->r, brdr, nil, ZP); draw(screen, insetrect(screen->r, Border), background, nil, ZP); for(i = 0; i < Brdsize; i++) { l1 = addpt(screen->r.min, Pt(i*Square, Square)); l2 = addpt(screen->r.min, Pt(i*Square, Maxy)); line(screen, l1, l2, Endsquare, Endsquare, (i%3) == 0 ? Thickline : Line, brdr, ZP); l1 = addpt(screen->r.min, Pt(0, (i+1)*Square)); l2 = addpt(screen->r.min, Pt(Maxx, (i+1)*Square)); line(screen, l1, l2, Endsquare, Endsquare, (i%3) == 0 ? Thickline : Line, brdr, ZP); } for(i = 1; i < 10; i++) { drawbar(i, (selected == i) ? 1 : 0); } drawboard(); flushimage(display, 1); } void drawbar(int digit, int selected) { Rectangle r = Rect((digit - 1)*Square, 0, digit*Square, Square); if(digit < 1 || digit > 9) return; r = insetrect(r, Border); r.max = addpt(r.max, Pt(2, 2)); draw(screen, rectaddpt(r, screen->r.min), selected ? backselect : background, nil, ZP); draw(screen, rectaddpt(r, screen->r.min), display->black, dig[digit-1], ZP); } void eresized(int new) { Point p; char path[256]; int i; if(new && getwindow(display, Refnone) < 0) sysfatal("can't reattach to window"); if(background == nil) background = eallocimage(Rect(0, 0, 1, 1), 1, DPaleyellow); if(backselect == nil) backselect = eallocimage(Rect(0, 0, 1, 1), 1, DPalebluegreen); if(blink == nil) blink = eallocimage(Rect(0, 0, 1, 1), 1, DDarkyellow); if(brdr == nil) brdr = eallocimage(Rect(0, 0, 1, 1), 1, 0x55555555); if(fixed == nil) fixed = eallocimage(Rect(0, 0, 1, 1), 1, DBlue); if(wrong == nil) wrong = eallocimage(Rect(0, 0, 1, 1), 1, DRed); if(dig[0] == nil) { for(i = 0; i < 9; i++) { snprint(path, 256, "%s/%d.bit", imgdir, i+1); dig[i] = eloadfile(path); } } p = Pt(Dx(screen->r), Dy(screen->r)); if(!new || !eqpt(p, Pt(Maxx - 8, Maxy - 8))) resize(); drawscreen(); } void main(int argc, char *argv[]) { Mouse m; Event e; Point p; int last1 = 0; /* was the button clicked last time? */ USED(argc, argv); if(initdraw(nil, nil, "sudoku") < 0) sysfatal("initdraw failed: %r"); einit(Emouse|Ekeyboard); clearboard(brd); eresized(0); srand(time(0)*getpid()); makep(); convert(brd, board); drawscreen(); for(;;) { switch(event(&e)) { case Emouse: m = e.mouse; if(m.buttons&1) { if(last1 == 0) { last1 = 1; p = subpt(m.xy, screen->r.min); if(ptinrect(p, Rect(0, 0, Maxx, Square+Border))) { if(p.x/Square == selected - 1) { drawbar(selected, 0); selected = 0; } else { selected = p.x/Square + 1; } } else { Point lp = divpt(p, Square); lp.y--; if(brd[lp.x * Brdsize + lp.y].locked) break; if(selected) { brd[lp.x * Brdsize + lp.y].digit = selected - 1; } else { brd[lp.x * Brdsize + lp.y].digit = -1; } } drawscreen(); } } else { last1 = 0; } if(m.buttons&2) { char *str; int l; /* levels start from 1 */ lmenu.lasthit = curlevel; l = emenuhit(2, &m, &lmenu); if(l >= 0){ curlevel = l; str = smprint("%s/%s", lvldir, (dir+curlevel)->name); if(loadlevel(str, brd) < 0) clearboard(brd); memcpy(obrd, brd, Psize * sizeof(Cell)); free(str); } drawscreen(); } if(m.buttons&4) { switch(emenuhit(3, &m, &menu)) { case 0: /* new */ makep(); convert(brd, board); drawscreen(); break; case 1: /* solve */ drawchecked(brd); break; case 2: /* solve */ solveboard(brd); drawscreen(); break; case 3: /* clear */ memcpy(brd, obrd, Psize * sizeof(Cell)); drawscreen(); break; case 4: /* save */ savegame(brd); drawscreen(); break; case 5: /* load */ if(loadgame(brd) < 0) { clearboard(brd); } memcpy(obrd, brd, Psize * sizeof(Cell)); drawscreen(); break; case 6: /* print */ printboard(brd); break; case 7: /* offline */ fprettyprintbrd(brd); break; case 8: /* exit */ exits(nil); } } break; case Ekeyboard: switch(e.kbdc) { case 127: case 'q': case 'Q': exits(nil); default: break; } break; } } }