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
readlevels(char * leveldir)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 *
genlevels(int i)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
convert(Cell * brd,int * board)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 *
eallocimage(Rectangle r,int repl,uint color)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 *
eloadfile(char * path)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
clearboard(Cell * board)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
solveboard(Cell * board)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
checkpossible(Cell * board,int x,int y,int num)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
resize(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
drawcell(int x,int y,int num,Image * col)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
drawboard(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
drawchecked(Cell * brd)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
drawscreen(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
drawbar(int digit,int selected)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
eresized(int new)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
main(int argc,char * argv[])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