]> git.lizzy.rs Git - plan9front.git/blob - sys/src/games/sudoku/sudoku.c
mkfiles: do not rely on path containing the . element
[plan9front.git] / sys / src / games / sudoku / sudoku.c
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 }