1 /* code from mark huckvale: http://www.phon.ucl.ac.uk/home/mark/sudoku/ */ 2 3 #include <u.h> 4 #include <libc.h> 5 #include <draw.h> 6 #include <event.h> 7 8 #include "sudoku.h" 9 10 char *imgdir = "/sys/games/lib/sudoku/images"; 11 char *lvldir = "/sys/games/lib/sudoku/boards"; /* level library dir */ 12 13 int selected; /* which digit do we have selected? */ 14 15 Image *background; /* DPaleyellow */ 16 Image *backselect; /* DPalebluegreen */ 17 Image *blink; /* DDarkyellow */ 18 Image *brdr; /* 0x55555555 */ 19 Image *fixed; /* DBlue */ 20 Image *wrong; /* DRed */ 21 Image *dig[10]; /* digit masks */ 22 23 Dir *dir; 24 int numlevels; 25 int curlevel; 26 27 char *buttons[] = 28 { 29 "new", 30 "check", 31 "solve", 32 "clear", 33 "save", 34 "load", 35 "print", 36 "offline", 37 "exit", 38 0 39 }; 40 41 Menu menu = 42 { 43 buttons 44 }; 45 46 Menu lmenu = 47 { 48 nil, 49 genlevels, 50 0, 51 }; 52 53 int 54 readlevels(char *leveldir) 55 { 56 int fd, n; 57 58 if((fd = open(leveldir, OREAD)) < 0) 59 return -1; 60 61 n = dirreadall(fd, &dir); 62 close(fd); 63 64 return n; 65 } 66 67 char * 68 genlevels(int i) 69 { 70 if(numlevels == 0) 71 numlevels = readlevels(lvldir); 72 73 if(numlevels > 0 && i < numlevels) 74 return (dir+i)->name; 75 76 return nil; 77 } 78 79 void 80 convert(Cell *brd, int *board) 81 { 82 int i; 83 84 for(i = 0; i < Psize; i++) { 85 brd[i].digit = board[i] & Digit; 86 if(brd[i].digit < 0 || brd[i].digit > 9) 87 brd[i].digit = -1; 88 brd[i].solve = (board[i] & Solve) >> 4; 89 brd[i].locked = board[i] & MLock; 90 } 91 memcpy(obrd, brd, Psize * sizeof(Cell)); 92 } 93 94 Image * 95 eallocimage(Rectangle r, int repl, uint color) 96 { 97 Image *tmp; 98 99 tmp = allocimage(display, r, screen->chan, repl, color); 100 if(tmp == nil) 101 sysfatal("cannot allocate buffer image: %r"); 102 103 return tmp; 104 } 105 106 Image * 107 eloadfile(char *path) 108 { 109 Image *img; 110 int fd; 111 112 fd = open(path, OREAD); 113 if(fd < 0) { 114 fprint(2, "cannot open image file %s: %r\n", path); 115 exits("image"); 116 } 117 img = readimage(display, fd, 0); 118 if(img == nil) 119 sysfatal("cannot load image: %r"); 120 close(fd); 121 122 return img; 123 } 124 125 126 void 127 clearboard(Cell *board) 128 { 129 int i; 130 131 for(i = 0; i < Psize; i++) { 132 board[i].digit = -1; 133 board[i].solve = 0; 134 board[i].locked = 0; 135 } 136 } 137 138 void 139 solveboard(Cell *board) 140 { 141 int i; 142 143 for(i = 0; i < Psize; i++) { 144 board[i].digit = board[i].solve; 145 } 146 } 147 148 149 int 150 checkpossible(Cell *board, int x, int y, int num) 151 { 152 int i, j; 153 154 for(i = 0; i < Brdsize; i++) { 155 if(board[i*Brdsize + y].digit == num && i != x) 156 return 0; 157 if(board[x*Brdsize + i].digit == num && i != y) 158 return 0; 159 } 160 161 for(i = x - (x%3); i < x - (x%3) + 3; i++) 162 for(j = y - (y%3); j < y - (y%3) + 3; j++) 163 if((i != x && j != y) && board[i*Brdsize + j].digit == num) 164 return 0; 165 166 return 1; 167 } 168 169 void 170 resize(void) 171 { 172 int fd; 173 174 fd = open("/dev/wctl", OWRITE); 175 if(fd >= 0){ 176 fprint(fd, "resize -dx %d -dy %d", Maxx, Maxy); 177 close(fd); 178 } 179 180 } 181 182 void 183 drawcell(int x, int y, int num, Image *col) 184 { 185 Rectangle r = Rect(x*Square, y*Square, (x+1)*Square, (y+1)*Square); 186 187 if(num < 0 || num > 9) 188 return; 189 190 r = insetrect(r, Border); 191 r = rectaddpt(r, Pt(0, Square)); 192 r.max = addpt(r.max, Pt(2, 2)); 193 194 draw(screen, rectaddpt(r, screen->r.min), col, dig[num], ZP); 195 } 196 197 void 198 drawboard(void) 199 { 200 int i; 201 202 for(i = 0; i < Psize; i++) { 203 drawcell(i / Brdsize, i % Brdsize, brd[i].digit, brd[i].locked ? fixed : display->black); 204 } 205 } 206 207 void 208 drawchecked(Cell *brd) 209 { 210 int i; 211 212 for(i = 0; i < Psize; i++) { 213 if(brd[i].locked) 214 drawcell(i / Brdsize, i % Brdsize, brd[i].digit, fixed); 215 else 216 drawcell(i / Brdsize, i % Brdsize, brd[i].digit, 217 checkpossible(brd, i / Brdsize, i % Brdsize, brd[i].digit) ? display->black : wrong); 218 } 219 } 220 221 void 222 drawscreen(void) 223 { 224 Point l1, l2; 225 int i; 226 227 draw(screen, screen->r, brdr, nil, ZP); 228 draw(screen, insetrect(screen->r, Border), background, nil, ZP); 229 for(i = 0; i < Brdsize; i++) { 230 l1 = addpt(screen->r.min, Pt(i*Square, Square)); 231 l2 = addpt(screen->r.min, Pt(i*Square, Maxy)); 232 line(screen, l1, l2, Endsquare, Endsquare, (i%3) == 0 ? Thickline : Line, brdr, ZP); 233 l1 = addpt(screen->r.min, Pt(0, (i+1)*Square)); 234 l2 = addpt(screen->r.min, Pt(Maxx, (i+1)*Square)); 235 line(screen, l1, l2, Endsquare, Endsquare, (i%3) == 0 ? Thickline : Line, brdr, ZP); 236 } 237 for(i = 1; i < 10; i++) { 238 drawbar(i, (selected == i) ? 1 : 0); 239 } 240 drawboard(); 241 flushimage(display, 1); 242 } 243 244 245 void 246 drawbar(int digit, int selected) 247 { 248 Rectangle r = Rect((digit - 1)*Square, 0, digit*Square, Square); 249 250 if(digit < 1 || digit > 9) 251 return; 252 253 r = insetrect(r, Border); 254 r.max = addpt(r.max, Pt(2, 2)); 255 draw(screen, rectaddpt(r, screen->r.min), selected ? backselect : background, nil, ZP); 256 draw(screen, rectaddpt(r, screen->r.min), display->black, dig[digit-1], ZP); 257 } 258 259 void 260 eresized(int new) 261 { 262 Point p; 263 char path[256]; 264 int i; 265 266 if(new && getwindow(display, Refnone) < 0) 267 sysfatal("can't reattach to window"); 268 269 if(background == nil) 270 background = eallocimage(Rect(0, 0, 1, 1), 1, DPaleyellow); 271 if(backselect == nil) 272 backselect = eallocimage(Rect(0, 0, 1, 1), 1, DPalebluegreen); 273 if(blink == nil) 274 blink = eallocimage(Rect(0, 0, 1, 1), 1, DDarkyellow); 275 if(brdr == nil) 276 brdr = eallocimage(Rect(0, 0, 1, 1), 1, 0x55555555); 277 if(fixed == nil) 278 fixed = eallocimage(Rect(0, 0, 1, 1), 1, DBlue); 279 if(wrong == nil) 280 wrong = eallocimage(Rect(0, 0, 1, 1), 1, DRed); 281 if(dig[0] == nil) { 282 for(i = 0; i < 9; i++) { 283 snprint(path, 256, "%s/%d.bit", imgdir, i+1); 284 dig[i] = eloadfile(path); 285 } 286 } 287 288 p = Pt(Dx(screen->r), Dy(screen->r)); 289 if(!new || !eqpt(p, Pt(Maxx - 8, Maxy - 8))) 290 resize(); 291 292 drawscreen(); 293 } 294 295 void 296 main(int argc, char *argv[]) 297 { 298 Mouse m; 299 Event e; 300 Point p; 301 int last1 = 0; /* was the button clicked last time? */ 302 303 USED(argc, argv); 304 305 if(initdraw(nil, nil, "sudoku") < 0) 306 sysfatal("initdraw failed: %r"); 307 308 einit(Emouse|Ekeyboard); 309 310 311 clearboard(brd); 312 eresized(0); 313 314 srand(time(0)*getpid()); 315 makep(); 316 convert(brd, board); 317 318 drawscreen(); 319 for(;;) { 320 switch(event(&e)) { 321 case Emouse: 322 m = e.mouse; 323 if(m.buttons&1) { 324 if(last1 == 0) { 325 last1 = 1; 326 p = subpt(m.xy, screen->r.min); 327 if(ptinrect(p, Rect(0, 0, Maxx, Square+Border))) { 328 if(p.x/Square == selected - 1) { 329 drawbar(selected, 0); 330 selected = 0; 331 } else { 332 selected = p.x/Square + 1; 333 } 334 } else { 335 Point lp = divpt(p, Square); 336 lp.y--; 337 338 if(brd[lp.x * Brdsize + lp.y].locked) 339 break; 340 341 if(selected) { 342 brd[lp.x * Brdsize + lp.y].digit = selected - 1; 343 } else { 344 brd[lp.x * Brdsize + lp.y].digit = -1; 345 } 346 } 347 drawscreen(); 348 } 349 } else { 350 last1 = 0; 351 } 352 353 if(m.buttons&2) { 354 char *str; 355 int l; 356 /* levels start from 1 */ 357 lmenu.lasthit = curlevel; 358 l = emenuhit(2, &m, &lmenu); 359 if(l >= 0){ 360 curlevel = l; 361 str = smprint("%s/%s", lvldir, (dir+curlevel)->name); 362 if(loadlevel(str, brd) < 0) 363 clearboard(brd); 364 memcpy(obrd, brd, Psize * sizeof(Cell)); 365 free(str); 366 } 367 drawscreen(); 368 } 369 if(m.buttons&4) { 370 switch(emenuhit(3, &m, &menu)) { 371 case 0: /* new */ 372 makep(); 373 convert(brd, board); 374 drawscreen(); 375 break; 376 case 1: /* solve */ 377 drawchecked(brd); 378 break; 379 case 2: /* solve */ 380 solveboard(brd); 381 drawscreen(); 382 break; 383 case 3: /* clear */ 384 memcpy(brd, obrd, Psize * sizeof(Cell)); 385 drawscreen(); 386 break; 387 case 4: /* save */ 388 savegame(brd); 389 drawscreen(); 390 break; 391 case 5: /* load */ 392 if(loadgame(brd) < 0) { 393 clearboard(brd); 394 } 395 memcpy(obrd, brd, Psize * sizeof(Cell)); 396 drawscreen(); 397 break; 398 case 6: /* print */ 399 printboard(brd); 400 break; 401 case 7: /* offline */ 402 fprettyprintbrd(brd); 403 break; 404 case 8: /* exit */ 405 exits(nil); 406 } 407 } 408 break; 409 410 case Ekeyboard: 411 switch(e.kbdc) { 412 case 127: 413 case 'q': 414 case 'Q': 415 exits(nil); 416 default: 417 break; 418 } 419 break; 420 } 421 } 422 } 423