sudoku.c 7.6 KB

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