Bug fix from James H: solve_game() was returning error messages in
[sgt/puzzles] / slant.c
1 /*
2 * slant.c: Puzzle from nikoli.co.jp involving drawing a diagonal
3 * line through each square of a grid.
4 */
5
6 /*
7 * In this puzzle you have a grid of squares, each of which must
8 * contain a diagonal line; you also have clue numbers placed at
9 * _points_ of that grid, which means there's a (w+1) x (h+1) array
10 * of possible clue positions.
11 *
12 * I'm therefore going to adopt a rigid convention throughout this
13 * source file of using w and h for the dimensions of the grid of
14 * squares, and W and H for the dimensions of the grid of points.
15 * Thus, W == w+1 and H == h+1 always.
16 *
17 * Clue arrays will be W*H `signed char's, and the clue at each
18 * point will be a number from 0 to 4, or -1 if there's no clue.
19 *
20 * Solution arrays will be W*H `signed char's, and the number at
21 * each point will be +1 for a forward slash (/), -1 for a
22 * backslash (\), and 0 for unknown.
23 */
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <assert.h>
29 #include <ctype.h>
30 #include <math.h>
31
32 #include "puzzles.h"
33
34 enum {
35 COL_BACKGROUND,
36 COL_GRID,
37 COL_INK,
38 NCOLOURS
39 };
40
41 struct game_params {
42 int w, h;
43 };
44
45 typedef struct game_clues {
46 int w, h;
47 signed char *clues;
48 int *dsf; /* scratch space for completion check */
49 int refcount;
50 } game_clues;
51
52 struct game_state {
53 struct game_params p;
54 game_clues *clues;
55 signed char *soln;
56 int completed;
57 int used_solve; /* used to suppress completion flash */
58 };
59
60 static game_params *default_params(void)
61 {
62 game_params *ret = snew(game_params);
63
64 ret->w = ret->h = 8;
65
66 return ret;
67 }
68
69 static const struct game_params slant_presets[] = {
70 {5, 5},
71 {8, 8},
72 {12, 10},
73 };
74
75 static int game_fetch_preset(int i, char **name, game_params **params)
76 {
77 game_params *ret;
78 char str[80];
79
80 if (i < 0 || i >= lenof(slant_presets))
81 return FALSE;
82
83 ret = snew(game_params);
84 *ret = slant_presets[i];
85
86 sprintf(str, "%dx%d", ret->w, ret->h);
87
88 *name = dupstr(str);
89 *params = ret;
90 return TRUE;
91 }
92
93 static void free_params(game_params *params)
94 {
95 sfree(params);
96 }
97
98 static game_params *dup_params(game_params *params)
99 {
100 game_params *ret = snew(game_params);
101 *ret = *params; /* structure copy */
102 return ret;
103 }
104
105 static void decode_params(game_params *ret, char const *string)
106 {
107 ret->w = ret->h = atoi(string);
108 while (*string && isdigit((unsigned char)*string)) string++;
109 if (*string == 'x') {
110 string++;
111 ret->h = atoi(string);
112 }
113 }
114
115 static char *encode_params(game_params *params, int full)
116 {
117 char data[256];
118
119 sprintf(data, "%dx%d", params->w, params->h);
120
121 return dupstr(data);
122 }
123
124 static config_item *game_configure(game_params *params)
125 {
126 config_item *ret;
127 char buf[80];
128
129 ret = snewn(3, config_item);
130
131 ret[0].name = "Width";
132 ret[0].type = C_STRING;
133 sprintf(buf, "%d", params->w);
134 ret[0].sval = dupstr(buf);
135 ret[0].ival = 0;
136
137 ret[1].name = "Height";
138 ret[1].type = C_STRING;
139 sprintf(buf, "%d", params->h);
140 ret[1].sval = dupstr(buf);
141 ret[1].ival = 0;
142
143 ret[2].name = NULL;
144 ret[2].type = C_END;
145 ret[2].sval = NULL;
146 ret[2].ival = 0;
147
148 return ret;
149 }
150
151 static game_params *custom_params(config_item *cfg)
152 {
153 game_params *ret = snew(game_params);
154
155 ret->w = atoi(cfg[0].sval);
156 ret->h = atoi(cfg[1].sval);
157
158 return ret;
159 }
160
161 static char *validate_params(game_params *params, int full)
162 {
163 /*
164 * (At least at the time of writing this comment) The grid
165 * generator is actually capable of handling even zero grid
166 * dimensions without crashing. Puzzles with a zero-area grid
167 * are a bit boring, though, because they're already solved :-)
168 */
169
170 if (params->w < 1 || params->h < 1)
171 return "Width and height must both be at least one";
172
173 return NULL;
174 }
175
176 /*
177 * Utility function used by both the solver and the filled-grid
178 * generator.
179 */
180
181 static void fill_square(int w, int h, int y, int x, int v,
182 signed char *soln, int *dsf)
183 {
184 int W = w+1 /*, H = h+1 */;
185
186 soln[y*w+x] = v;
187
188 if (v < 0)
189 dsf_merge(dsf, y*W+x, (y+1)*W+(x+1));
190 else
191 dsf_merge(dsf, y*W+(x+1), (y+1)*W+x);
192 }
193
194 /*
195 * Scratch space for solver.
196 */
197 struct solver_scratch {
198 int *dsf;
199 };
200
201 static struct solver_scratch *new_scratch(int w, int h)
202 {
203 int W = w+1, H = h+1;
204 struct solver_scratch *ret = snew(struct solver_scratch);
205 ret->dsf = snewn(W*H, int);
206 return ret;
207 }
208
209 static void free_scratch(struct solver_scratch *sc)
210 {
211 sfree(sc->dsf);
212 sfree(sc);
213 }
214
215 /*
216 * Solver. Returns 0 for impossibility, 1 for success, 2 for
217 * ambiguity or failure to converge.
218 */
219 static int slant_solve(int w, int h, const signed char *clues,
220 signed char *soln, struct solver_scratch *sc)
221 {
222 int W = w+1, H = h+1;
223 int x, y, i;
224 int done_something;
225
226 /*
227 * Clear the output.
228 */
229 memset(soln, 0, w*h);
230
231 /*
232 * Establish a disjoint set forest for tracking connectedness
233 * between grid points.
234 */
235 for (i = 0; i < W*H; i++)
236 sc->dsf[i] = i; /* initially all distinct */
237
238 /*
239 * Repeatedly try to deduce something until we can't.
240 */
241 do {
242 done_something = FALSE;
243
244 /*
245 * Any clue point with the number of remaining lines equal
246 * to zero or to the number of remaining undecided
247 * neighbouring squares can be filled in completely.
248 */
249 for (y = 0; y < H; y++)
250 for (x = 0; x < W; x++) {
251 int nu, nl, v, c;
252
253 if ((c = clues[y*W+x]) < 0)
254 continue;
255
256 /*
257 * We have a clue point. Count up the number of
258 * undecided neighbours, and also the number of
259 * lines already present.
260 */
261 nu = 0;
262 nl = c;
263 if (x > 0 && y > 0 && (v = soln[(y-1)*w+(x-1)]) != +1)
264 v == 0 ? nu++ : nl--;
265 if (x > 0 && y < h && (v = soln[y*w+(x-1)]) != -1)
266 v == 0 ? nu++ : nl--;
267 if (x < w && y > 0 && (v = soln[(y-1)*w+x]) != -1)
268 v == 0 ? nu++ : nl--;
269 if (x < w && y < h && (v = soln[y*w+x]) != +1)
270 v == 0 ? nu++ : nl--;
271
272 /*
273 * Check the counts.
274 */
275 if (nl < 0 || nl > nu) {
276 /*
277 * No consistent value for this at all!
278 */
279 return 0; /* impossible */
280 }
281
282 if (nu > 0 && (nl == 0 || nl == nu)) {
283 #ifdef SOLVER_DIAGNOSTICS
284 printf("%s around clue point at %d,%d\n",
285 nl ? "filling" : "emptying", x, y);
286 #endif
287 if (x > 0 && y > 0 && soln[(y-1)*w+(x-1)] == 0)
288 fill_square(w, h, y-1, x-1, (nl ? -1 : +1), soln,
289 sc->dsf);
290 if (x > 0 && y < h && soln[y*w+(x-1)] == 0)
291 fill_square(w, h, y, x-1, (nl ? +1 : -1), soln,
292 sc->dsf);
293 if (x < w && y > 0 && soln[(y-1)*w+x] == 0)
294 fill_square(w, h, y-1, x, (nl ? +1 : -1), soln,
295 sc->dsf);
296 if (x < w && y < h && soln[y*w+x] == 0)
297 fill_square(w, h, y, x, (nl ? -1 : +1), soln,
298 sc->dsf);
299
300 done_something = TRUE;
301 }
302 }
303
304 if (done_something)
305 continue;
306
307 /*
308 * Failing that, we now apply the second condition, which
309 * is that no square may be filled in such a way as to form
310 * a loop.
311 */
312 for (y = 0; y < h; y++)
313 for (x = 0; x < w; x++) {
314 int fs, bs;
315
316 if (soln[y*w+x])
317 continue; /* got this one already */
318
319 fs = (dsf_canonify(sc->dsf, y*W+x) ==
320 dsf_canonify(sc->dsf, (y+1)*W+(x+1)));
321 bs = (dsf_canonify(sc->dsf, (y+1)*W+x) ==
322 dsf_canonify(sc->dsf, y*W+(x+1)));
323
324 if (fs && bs) {
325 /*
326 * Loop avoidance leaves no consistent value
327 * for this at all!
328 */
329 return 0; /* impossible */
330 }
331
332 if (fs) {
333 /*
334 * Top left and bottom right corners of this
335 * square are already connected, which means we
336 * aren't allowed to put a backslash in here.
337 */
338 #ifdef SOLVER_DIAGNOSTICS
339 printf("placing / in %d,%d by loop avoidance\n", x, y);
340 #endif
341 fill_square(w, h, y, x, +1, soln, sc->dsf);
342 done_something = TRUE;
343 } else if (bs) {
344 /*
345 * Top right and bottom left corners of this
346 * square are already connected, which means we
347 * aren't allowed to put a forward slash in
348 * here.
349 */
350 #ifdef SOLVER_DIAGNOSTICS
351 printf("placing \\ in %d,%d by loop avoidance\n", x, y);
352 #endif
353 fill_square(w, h, y, x, -1, soln, sc->dsf);
354 done_something = TRUE;
355 }
356 }
357
358 } while (done_something);
359
360 /*
361 * Solver can make no more progress. See if the grid is full.
362 */
363 for (i = 0; i < w*h; i++)
364 if (!soln[i])
365 return 2; /* failed to converge */
366 return 1; /* success */
367 }
368
369 /*
370 * Filled-grid generator.
371 */
372 static void slant_generate(int w, int h, signed char *soln, random_state *rs)
373 {
374 int W = w+1, H = h+1;
375 int x, y, i;
376 int *dsf, *indices;
377
378 /*
379 * Clear the output.
380 */
381 memset(soln, 0, w*h);
382
383 /*
384 * Establish a disjoint set forest for tracking connectedness
385 * between grid points.
386 */
387 dsf = snewn(W*H, int);
388 for (i = 0; i < W*H; i++)
389 dsf[i] = i; /* initially all distinct */
390
391 /*
392 * Prepare a list of the squares in the grid, and fill them in
393 * in a random order.
394 */
395 indices = snewn(w*h, int);
396 for (i = 0; i < w*h; i++)
397 indices[i] = i;
398 shuffle(indices, w*h, sizeof(*indices), rs);
399
400 /*
401 * Fill in each one in turn.
402 */
403 for (i = 0; i < w*h; i++) {
404 int fs, bs, v;
405
406 y = indices[i] / w;
407 x = indices[i] % w;
408
409 fs = (dsf_canonify(dsf, y*W+x) ==
410 dsf_canonify(dsf, (y+1)*W+(x+1)));
411 bs = (dsf_canonify(dsf, (y+1)*W+x) ==
412 dsf_canonify(dsf, y*W+(x+1)));
413
414 /*
415 * It isn't possible to get into a situation where we
416 * aren't allowed to place _either_ type of slash in a
417 * square.
418 *
419 * Proof (thanks to Gareth Taylor):
420 *
421 * If it were possible, it would have to be because there
422 * was an existing path (not using this square) between the
423 * top-left and bottom-right corners of this square, and
424 * another between the other two. These two paths would
425 * have to cross at some point.
426 *
427 * Obviously they can't cross in the middle of a square, so
428 * they must cross by sharing a point in common. But this
429 * isn't possible either: if you chessboard-colour all the
430 * points on the grid, you find that any continuous
431 * diagonal path is entirely composed of points of the same
432 * colour. And one of our two hypothetical paths is between
433 * two black points, and the other is between two white
434 * points - therefore they can have no point in common. []
435 */
436 assert(!(fs && bs));
437
438 v = fs ? +1 : bs ? -1 : 2 * random_upto(rs, 2) - 1;
439 fill_square(w, h, y, x, v, soln, dsf);
440 }
441
442 sfree(indices);
443 sfree(dsf);
444 }
445
446 static char *new_game_desc(game_params *params, random_state *rs,
447 char **aux, int interactive)
448 {
449 int w = params->w, h = params->h, W = w+1, H = h+1;
450 signed char *soln, *tmpsoln, *clues;
451 int *clueindices;
452 struct solver_scratch *sc;
453 int x, y, v, i;
454 char *desc;
455
456 soln = snewn(w*h, signed char);
457 tmpsoln = snewn(w*h, signed char);
458 clues = snewn(W*H, signed char);
459 clueindices = snewn(W*H, int);
460 sc = new_scratch(w, h);
461
462 do {
463 /*
464 * Create the filled grid.
465 */
466 slant_generate(w, h, soln, rs);
467
468 /*
469 * Fill in the complete set of clues.
470 */
471 for (y = 0; y < H; y++)
472 for (x = 0; x < W; x++) {
473 v = 0;
474
475 if (x > 0 && y > 0 && soln[(y-1)*w+(x-1)] == -1) v++;
476 if (x > 0 && y < h && soln[y*w+(x-1)] == +1) v++;
477 if (x < w && y > 0 && soln[(y-1)*w+x] == +1) v++;
478 if (x < w && y < h && soln[y*w+x] == -1) v++;
479
480 clues[y*W+x] = v;
481 }
482 } while (slant_solve(w, h, clues, tmpsoln, sc) != 1);
483
484 /*
485 * Remove as many clues as possible while retaining solubility.
486 */
487 for (i = 0; i < W*H; i++)
488 clueindices[i] = i;
489 shuffle(clueindices, W*H, sizeof(*clueindices), rs);
490 for (i = 0; i < W*H; i++) {
491 y = clueindices[i] / W;
492 x = clueindices[i] % W;
493 v = clues[y*W+x];
494 clues[y*W+x] = -1;
495 if (slant_solve(w, h, clues, tmpsoln, sc) != 1)
496 clues[y*W+x] = v; /* put it back */
497 }
498
499 /*
500 * Now we have the clue set as it will be presented to the
501 * user. Encode it in a game desc.
502 */
503 {
504 char *p;
505 int run, i;
506
507 desc = snewn(W*H+1, char);
508 p = desc;
509 run = 0;
510 for (i = 0; i <= W*H; i++) {
511 int n = (i < W*H ? clues[i] : -2);
512
513 if (n == -1)
514 run++;
515 else {
516 if (run) {
517 while (run > 0) {
518 int c = 'a' - 1 + run;
519 if (run > 26)
520 c = 'z';
521 *p++ = c;
522 run -= c - ('a' - 1);
523 }
524 }
525 if (n >= 0)
526 *p++ = '0' + n;
527 run = 0;
528 }
529 }
530 assert(p - desc <= W*H);
531 *p++ = '\0';
532 desc = sresize(desc, p - desc, char);
533 }
534
535 /*
536 * Encode the solution as an aux_info.
537 */
538 {
539 char *auxbuf;
540 *aux = auxbuf = snewn(w*h+1, char);
541 for (i = 0; i < w*h; i++)
542 auxbuf[i] = soln[i] < 0 ? '\\' : '/';
543 auxbuf[w*h] = '\0';
544 }
545
546 free_scratch(sc);
547 sfree(clueindices);
548 sfree(clues);
549 sfree(tmpsoln);
550 sfree(soln);
551
552 return desc;
553 }
554
555 static char *validate_desc(game_params *params, char *desc)
556 {
557 int w = params->w, h = params->h, W = w+1, H = h+1;
558 int area = W*H;
559 int squares = 0;
560
561 while (*desc) {
562 int n = *desc++;
563 if (n >= 'a' && n <= 'z') {
564 squares += n - 'a' + 1;
565 } else if (n >= '0' && n <= '4') {
566 squares++;
567 } else
568 return "Invalid character in game description";
569 }
570
571 if (squares < area)
572 return "Not enough data to fill grid";
573
574 if (squares > area)
575 return "Too much data to fit in grid";
576
577 return NULL;
578 }
579
580 static game_state *new_game(midend_data *me, game_params *params, char *desc)
581 {
582 int w = params->w, h = params->h, W = w+1, H = h+1;
583 game_state *state = snew(game_state);
584 int area = W*H;
585 int squares = 0;
586
587 state->p = *params;
588 state->soln = snewn(w*h, signed char);
589 memset(state->soln, 0, w*h);
590 state->completed = state->used_solve = FALSE;
591
592 state->clues = snew(game_clues);
593 state->clues->w = w;
594 state->clues->h = h;
595 state->clues->clues = snewn(W*H, signed char);
596 state->clues->refcount = 1;
597 state->clues->dsf = snewn(W*H, int);
598 memset(state->clues->clues, -1, W*H);
599 while (*desc) {
600 int n = *desc++;
601 if (n >= 'a' && n <= 'z') {
602 squares += n - 'a' + 1;
603 } else if (n >= '0' && n <= '4') {
604 state->clues->clues[squares++] = n - '0';
605 } else
606 assert(!"can't get here");
607 }
608 assert(squares == area);
609
610 return state;
611 }
612
613 static game_state *dup_game(game_state *state)
614 {
615 int w = state->p.w, h = state->p.h;
616 game_state *ret = snew(game_state);
617
618 ret->p = state->p;
619 ret->clues = state->clues;
620 ret->clues->refcount++;
621 ret->completed = state->completed;
622 ret->used_solve = state->used_solve;
623
624 ret->soln = snewn(w*h, signed char);
625 memcpy(ret->soln, state->soln, w*h);
626
627 return ret;
628 }
629
630 static void free_game(game_state *state)
631 {
632 sfree(state->soln);
633 assert(state->clues);
634 if (--state->clues->refcount <= 0) {
635 sfree(state->clues->clues);
636 sfree(state->clues->dsf);
637 sfree(state->clues);
638 }
639 sfree(state);
640 }
641
642 static int check_completion(game_state *state)
643 {
644 int w = state->p.w, h = state->p.h, W = w+1, H = h+1;
645 int i, x, y;
646
647 /*
648 * Establish a disjoint set forest for tracking connectedness
649 * between grid points. Use the dsf scratch space in the shared
650 * clues structure, to avoid mallocing too often.
651 */
652 for (i = 0; i < W*H; i++)
653 state->clues->dsf[i] = i; /* initially all distinct */
654
655 /*
656 * Now go through the grid checking connectedness. While we're
657 * here, also check that everything is filled in.
658 */
659 for (y = 0; y < h; y++)
660 for (x = 0; x < w; x++) {
661 int i1, i2;
662
663 if (state->soln[y*w+x] == 0)
664 return FALSE;
665 if (state->soln[y*w+x] < 0) {
666 i1 = y*W+x;
667 i2 = (y+1)*W+(x+1);
668 } else {
669 i1 = (y+1)*W+x;
670 i2 = y*W+(x+1);
671 }
672
673 /*
674 * Our edge connects i1 with i2. If they're already
675 * connected, return failure. Otherwise, link them.
676 */
677 if (dsf_canonify(state->clues->dsf, i1) ==
678 dsf_canonify(state->clues->dsf, i2))
679 return FALSE;
680 else
681 dsf_merge(state->clues->dsf, i1, i2);
682 }
683
684 /*
685 * The grid is _a_ valid grid; let's see if it matches the
686 * clues.
687 */
688 for (y = 0; y < H; y++)
689 for (x = 0; x < W; x++) {
690 int v, c;
691
692 if ((c = state->clues->clues[y*W+x]) < 0)
693 continue;
694
695 v = 0;
696
697 if (x > 0 && y > 0 && state->soln[(y-1)*w+(x-1)] == -1) v++;
698 if (x > 0 && y < h && state->soln[y*w+(x-1)] == +1) v++;
699 if (x < w && y > 0 && state->soln[(y-1)*w+x] == +1) v++;
700 if (x < w && y < h && state->soln[y*w+x] == -1) v++;
701
702 if (c != v)
703 return FALSE;
704 }
705
706 return TRUE;
707 }
708
709 static char *solve_game(game_state *state, game_state *currstate,
710 char *aux, char **error)
711 {
712 int w = state->p.w, h = state->p.h;
713 signed char *soln;
714 int bs, ret;
715 int free_soln = FALSE;
716 char *move, buf[80];
717 int movelen, movesize;
718 int x, y;
719
720 if (aux) {
721 /*
722 * If we already have the solution, save ourselves some
723 * time.
724 */
725 soln = (signed char *)aux;
726 bs = (signed char)'\\';
727 free_soln = FALSE;
728 } else {
729 struct solver_scratch *sc = new_scratch(w, h);
730 soln = snewn(w*h, signed char);
731 bs = -1;
732 ret = slant_solve(w, h, state->clues->clues, soln, sc);
733 free_scratch(sc);
734 if (ret != 1) {
735 sfree(soln);
736 if (ret == 0)
737 *error = "This puzzle is not self-consistent";
738 else
739 *error = "Unable to find a unique solution for this puzzle";
740 return NULL;
741 }
742 free_soln = TRUE;
743 }
744
745 /*
746 * Construct a move string which turns the current state into
747 * the solved state.
748 */
749 movesize = 256;
750 move = snewn(movesize, char);
751 movelen = 0;
752 move[movelen++] = 'S';
753 move[movelen] = '\0';
754 for (y = 0; y < h; y++)
755 for (x = 0; x < w; x++) {
756 int v = (soln[y*w+x] == bs ? -1 : +1);
757 if (state->soln[y*w+x] != v) {
758 int len = sprintf(buf, ";%c%d,%d", (int)(v < 0 ? '\\' : '/'), x, y);
759 if (movelen + len >= movesize) {
760 movesize = movelen + len + 256;
761 move = sresize(move, movesize, char);
762 }
763 strcpy(move + movelen, buf);
764 movelen += len;
765 }
766 }
767
768 if (free_soln)
769 sfree(soln);
770
771 return move;
772 }
773
774 static char *game_text_format(game_state *state)
775 {
776 int w = state->p.w, h = state->p.h, W = w+1, H = h+1;
777 int x, y, len;
778 char *ret, *p;
779
780 /*
781 * There are h+H rows of w+W columns.
782 */
783 len = (h+H) * (w+W+1) + 1;
784 ret = snewn(len, char);
785 p = ret;
786
787 for (y = 0; y < H; y++) {
788 for (x = 0; x < W; x++) {
789 if (state->clues->clues[y*W+x] >= 0)
790 *p++ = state->clues->clues[y*W+x] + '0';
791 else
792 *p++ = '+';
793 if (x < w)
794 *p++ = '-';
795 }
796 *p++ = '\n';
797 if (y < h) {
798 for (x = 0; x < W; x++) {
799 *p++ = '|';
800 if (x < w) {
801 if (state->soln[y*w+x] != 0)
802 *p++ = (state->soln[y*w+x] < 0 ? '\\' : '/');
803 else
804 *p++ = ' ';
805 }
806 }
807 *p++ = '\n';
808 }
809 }
810 *p++ = '\0';
811
812 assert(p - ret == len);
813 return ret;
814 }
815
816 static game_ui *new_ui(game_state *state)
817 {
818 return NULL;
819 }
820
821 static void free_ui(game_ui *ui)
822 {
823 }
824
825 static char *encode_ui(game_ui *ui)
826 {
827 return NULL;
828 }
829
830 static void decode_ui(game_ui *ui, char *encoding)
831 {
832 }
833
834 static void game_changed_state(game_ui *ui, game_state *oldstate,
835 game_state *newstate)
836 {
837 }
838
839 #define PREFERRED_TILESIZE 32
840 #define TILESIZE (ds->tilesize)
841 #define BORDER TILESIZE
842 #define CLUE_RADIUS (TILESIZE / 3)
843 #define CLUE_TEXTSIZE (TILESIZE / 2)
844 #define COORD(x) ( (x) * TILESIZE + BORDER )
845 #define FROMCOORD(x) ( ((x) - BORDER + TILESIZE) / TILESIZE - 1 )
846
847 #define FLASH_TIME 0.30F
848
849 /*
850 * Bit fields in the `grid' and `todraw' elements of the drawstate.
851 */
852 #define BACKSLASH 0x0001
853 #define FORWSLASH 0x0002
854 #define L_T 0x0004
855 #define L_B 0x0008
856 #define T_L 0x0010
857 #define T_R 0x0020
858 #define R_T 0x0040
859 #define R_B 0x0080
860 #define B_L 0x0100
861 #define B_R 0x0200
862 #define C_TL 0x0400
863 #define C_TR 0x0800
864 #define C_BL 0x1000
865 #define C_BR 0x2000
866 #define FLASH 0x4000
867
868 struct game_drawstate {
869 int tilesize;
870 int started;
871 int *grid;
872 int *todraw;
873 };
874
875 static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds,
876 int x, int y, int button)
877 {
878 int w = state->p.w, h = state->p.h;
879
880 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
881 int v;
882 char buf[80];
883
884 x = FROMCOORD(x);
885 y = FROMCOORD(y);
886 if (x < 0 || y < 0 || x >= w || y >= h)
887 return NULL;
888
889 if (button == LEFT_BUTTON) {
890 /*
891 * Left-clicking cycles blank -> \ -> / -> blank.
892 */
893 v = state->soln[y*w+x] - 1;
894 if (v == -2)
895 v = +1;
896 } else {
897 /*
898 * Right-clicking cycles blank -> / -> \ -> blank.
899 */
900 v = state->soln[y*w+x] + 1;
901 if (v == +2)
902 v = -1;
903 }
904
905 sprintf(buf, "%c%d,%d", (int)(v==-1 ? '\\' : v==+1 ? '/' : 'C'), x, y);
906 return dupstr(buf);
907 }
908
909 return NULL;
910 }
911
912 static game_state *execute_move(game_state *state, char *move)
913 {
914 int w = state->p.w, h = state->p.h;
915 char c;
916 int x, y, n;
917 game_state *ret = dup_game(state);
918
919 while (*move) {
920 c = *move;
921 if (c == 'S') {
922 ret->used_solve = TRUE;
923 move++;
924 } else if (c == '\\' || c == '/' || c == 'C') {
925 move++;
926 if (sscanf(move, "%d,%d%n", &x, &y, &n) != 2 ||
927 x < 0 || y < 0 || x >= w || y >= h) {
928 free_game(ret);
929 return NULL;
930 }
931 ret->soln[y*w+x] = (c == '\\' ? -1 : c == '/' ? +1 : 0);
932 move += n;
933 } else {
934 free_game(ret);
935 return NULL;
936 }
937 if (*move == ';')
938 move++;
939 else if (*move) {
940 free_game(ret);
941 return NULL;
942 }
943 }
944
945 if (!ret->completed)
946 ret->completed = check_completion(ret);
947
948 return ret;
949 }
950
951 /* ----------------------------------------------------------------------
952 * Drawing routines.
953 */
954
955 static void game_compute_size(game_params *params, int tilesize,
956 int *x, int *y)
957 {
958 /* fool the macros */
959 struct dummy { int tilesize; } dummy = { tilesize }, *ds = &dummy;
960
961 *x = 2 * BORDER + params->w * TILESIZE + 1;
962 *y = 2 * BORDER + params->h * TILESIZE + 1;
963 }
964
965 static void game_set_size(game_drawstate *ds, game_params *params,
966 int tilesize)
967 {
968 ds->tilesize = tilesize;
969 }
970
971 static float *game_colours(frontend *fe, game_state *state, int *ncolours)
972 {
973 float *ret = snewn(3 * NCOLOURS, float);
974
975 frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
976
977 ret[COL_GRID * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 0.7F;
978 ret[COL_GRID * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 0.7F;
979 ret[COL_GRID * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] * 0.7F;
980
981 ret[COL_INK * 3 + 0] = 0.0F;
982 ret[COL_INK * 3 + 1] = 0.0F;
983 ret[COL_INK * 3 + 2] = 0.0F;
984
985 *ncolours = NCOLOURS;
986 return ret;
987 }
988
989 static game_drawstate *game_new_drawstate(game_state *state)
990 {
991 int w = state->p.w, h = state->p.h;
992 int i;
993 struct game_drawstate *ds = snew(struct game_drawstate);
994
995 ds->tilesize = 0;
996 ds->started = FALSE;
997 ds->grid = snewn(w*h, int);
998 ds->todraw = snewn(w*h, int);
999 for (i = 0; i < w*h; i++)
1000 ds->grid[i] = ds->todraw[i] = -1;
1001
1002 return ds;
1003 }
1004
1005 static void game_free_drawstate(game_drawstate *ds)
1006 {
1007 sfree(ds->todraw);
1008 sfree(ds->grid);
1009 sfree(ds);
1010 }
1011
1012 static void draw_clue(frontend *fe, game_drawstate *ds,
1013 int x, int y, int v)
1014 {
1015 char p[2];
1016
1017 if (v < 0)
1018 return;
1019
1020 p[0] = v + '0';
1021 p[1] = '\0';
1022 draw_circle(fe, COORD(x), COORD(y), CLUE_RADIUS,
1023 COL_BACKGROUND, COL_INK);
1024 draw_text(fe, COORD(x), COORD(y), FONT_VARIABLE,
1025 CLUE_TEXTSIZE, ALIGN_VCENTRE|ALIGN_HCENTRE,
1026 COL_INK, p);
1027 }
1028
1029 static void draw_tile(frontend *fe, game_drawstate *ds, game_clues *clues,
1030 int x, int y, int v)
1031 {
1032 int w = clues->w /*, h = clues->h*/, W = w+1 /*, H = h+1 */;
1033 int xx, yy;
1034
1035 clip(fe, COORD(x), COORD(y), TILESIZE+1, TILESIZE+1);
1036
1037 draw_rect(fe, COORD(x), COORD(y), TILESIZE, TILESIZE,
1038 (v & FLASH) ? COL_GRID : COL_BACKGROUND);
1039
1040 /*
1041 * Draw the grid lines.
1042 */
1043 draw_line(fe, COORD(x), COORD(y), COORD(x+1), COORD(y), COL_GRID);
1044 draw_line(fe, COORD(x), COORD(y+1), COORD(x+1), COORD(y+1), COL_GRID);
1045 draw_line(fe, COORD(x), COORD(y), COORD(x), COORD(y+1), COL_GRID);
1046 draw_line(fe, COORD(x+1), COORD(y), COORD(x+1), COORD(y+1), COL_GRID);
1047
1048 /*
1049 * Draw the slash.
1050 */
1051 if (v & BACKSLASH) {
1052 draw_line(fe, COORD(x), COORD(y), COORD(x+1), COORD(y+1), COL_INK);
1053 draw_line(fe, COORD(x)+1, COORD(y), COORD(x+1), COORD(y+1)-1,
1054 COL_INK);
1055 draw_line(fe, COORD(x), COORD(y)+1, COORD(x+1)-1, COORD(y+1),
1056 COL_INK);
1057 } else if (v & FORWSLASH) {
1058 draw_line(fe, COORD(x+1), COORD(y), COORD(x), COORD(y+1), COL_INK);
1059 draw_line(fe, COORD(x+1)-1, COORD(y), COORD(x), COORD(y+1)-1,
1060 COL_INK);
1061 draw_line(fe, COORD(x+1), COORD(y)+1, COORD(x)+1, COORD(y+1),
1062 COL_INK);
1063 }
1064
1065 /*
1066 * Draw dots on the grid corners that appear if a slash is in a
1067 * neighbouring cell.
1068 */
1069 if (v & L_T)
1070 draw_rect(fe, COORD(x), COORD(y)+1, 1, 1, COL_INK);
1071 if (v & L_B)
1072 draw_rect(fe, COORD(x), COORD(y+1)-1, 1, 1, COL_INK);
1073 if (v & R_T)
1074 draw_rect(fe, COORD(x+1), COORD(y)+1, 1, 1, COL_INK);
1075 if (v & R_B)
1076 draw_rect(fe, COORD(x+1), COORD(y+1)-1, 1, 1, COL_INK);
1077 if (v & T_L)
1078 draw_rect(fe, COORD(x)+1, COORD(y), 1, 1, COL_INK);
1079 if (v & T_R)
1080 draw_rect(fe, COORD(x+1)-1, COORD(y), 1, 1, COL_INK);
1081 if (v & B_L)
1082 draw_rect(fe, COORD(x)+1, COORD(y+1), 1, 1, COL_INK);
1083 if (v & B_R)
1084 draw_rect(fe, COORD(x+1)-1, COORD(y+1), 1, 1, COL_INK);
1085 if (v & C_TL)
1086 draw_rect(fe, COORD(x), COORD(y), 1, 1, COL_INK);
1087 if (v & C_TR)
1088 draw_rect(fe, COORD(x+1), COORD(y), 1, 1, COL_INK);
1089 if (v & C_BL)
1090 draw_rect(fe, COORD(x), COORD(y+1), 1, 1, COL_INK);
1091 if (v & C_BR)
1092 draw_rect(fe, COORD(x+1), COORD(y+1), 1, 1, COL_INK);
1093
1094 /*
1095 * And finally the clues at the corners.
1096 */
1097 for (xx = x; xx <= x+1; xx++)
1098 for (yy = y; yy <= y+1; yy++)
1099 draw_clue(fe, ds, xx, yy, clues->clues[yy*W+xx]);
1100
1101 unclip(fe);
1102 draw_update(fe, COORD(x), COORD(y), TILESIZE+1, TILESIZE+1);
1103 }
1104
1105 static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
1106 game_state *state, int dir, game_ui *ui,
1107 float animtime, float flashtime)
1108 {
1109 int w = state->p.w, h = state->p.h, W = w+1, H = h+1;
1110 int x, y;
1111 int flashing;
1112
1113 if (flashtime > 0)
1114 flashing = (int)(flashtime * 3 / FLASH_TIME) != 1;
1115 else
1116 flashing = FALSE;
1117
1118 if (!ds->started) {
1119 int ww, wh;
1120 game_compute_size(&state->p, TILESIZE, &ww, &wh);
1121 draw_rect(fe, 0, 0, ww, wh, COL_BACKGROUND);
1122 draw_update(fe, 0, 0, ww, wh);
1123
1124 /*
1125 * Draw any clues on the very edges (since normal tile
1126 * redraw won't draw the bits outside the grid boundary).
1127 */
1128 for (y = 0; y < H; y++) {
1129 draw_clue(fe, ds, 0, y, state->clues->clues[y*W+0]);
1130 draw_clue(fe, ds, w, y, state->clues->clues[y*W+w]);
1131 }
1132 for (x = 0; x < W; x++) {
1133 draw_clue(fe, ds, x, 0, state->clues->clues[0*W+x]);
1134 draw_clue(fe, ds, x, h, state->clues->clues[h*W+x]);
1135 }
1136
1137 ds->started = TRUE;
1138 }
1139
1140 /*
1141 * Loop over the grid and work out where all the slashes are.
1142 * We need to do this because a slash in one square affects the
1143 * drawing of the next one along.
1144 */
1145 for (y = 0; y < h; y++)
1146 for (x = 0; x < w; x++)
1147 ds->todraw[y*w+x] = flashing ? FLASH : 0;
1148
1149 for (y = 0; y < h; y++) {
1150 for (x = 0; x < w; x++) {
1151 if (state->soln[y*w+x] < 0) {
1152 ds->todraw[y*w+x] |= BACKSLASH;
1153 if (x > 0)
1154 ds->todraw[y*w+(x-1)] |= R_T | C_TR;
1155 if (x+1 < w)
1156 ds->todraw[y*w+(x+1)] |= L_B | C_BL;
1157 if (y > 0)
1158 ds->todraw[(y-1)*w+x] |= B_L | C_BL;
1159 if (y+1 < h)
1160 ds->todraw[(y+1)*w+x] |= T_R | C_TR;
1161 if (x > 0 && y > 0)
1162 ds->todraw[(y-1)*w+(x-1)] |= C_BR;
1163 if (x+1 < w && y+1 < h)
1164 ds->todraw[(y+1)*w+(x+1)] |= C_TL;
1165 } else if (state->soln[y*w+x] > 0) {
1166 ds->todraw[y*w+x] |= FORWSLASH;
1167 if (x > 0)
1168 ds->todraw[y*w+(x-1)] |= R_B | C_BR;
1169 if (x+1 < w)
1170 ds->todraw[y*w+(x+1)] |= L_T | C_TL;
1171 if (y > 0)
1172 ds->todraw[(y-1)*w+x] |= B_R | C_BR;
1173 if (y+1 < h)
1174 ds->todraw[(y+1)*w+x] |= T_L | C_TL;
1175 if (x > 0 && y+1 < h)
1176 ds->todraw[(y+1)*w+(x-1)] |= C_TR;
1177 if (x+1 < w && y > 0)
1178 ds->todraw[(y-1)*w+(x+1)] |= C_BL;
1179 }
1180 }
1181 }
1182
1183 /*
1184 * Now go through and draw the grid squares.
1185 */
1186 for (y = 0; y < h; y++) {
1187 for (x = 0; x < w; x++) {
1188 if (ds->todraw[y*w+x] != ds->grid[y*w+x]) {
1189 draw_tile(fe, ds, state->clues, x, y, ds->todraw[y*w+x]);
1190 ds->grid[y*w+x] = ds->todraw[y*w+x];
1191 }
1192 }
1193 }
1194 }
1195
1196 static float game_anim_length(game_state *oldstate, game_state *newstate,
1197 int dir, game_ui *ui)
1198 {
1199 return 0.0F;
1200 }
1201
1202 static float game_flash_length(game_state *oldstate, game_state *newstate,
1203 int dir, game_ui *ui)
1204 {
1205 if (!oldstate->completed && newstate->completed &&
1206 !oldstate->used_solve && !newstate->used_solve)
1207 return FLASH_TIME;
1208
1209 return 0.0F;
1210 }
1211
1212 static int game_wants_statusbar(void)
1213 {
1214 return FALSE;
1215 }
1216
1217 static int game_timing_state(game_state *state, game_ui *ui)
1218 {
1219 return TRUE;
1220 }
1221
1222 #ifdef COMBINED
1223 #define thegame slant
1224 #endif
1225
1226 const struct game thegame = {
1227 "Slant", "games.slant",
1228 default_params,
1229 game_fetch_preset,
1230 decode_params,
1231 encode_params,
1232 free_params,
1233 dup_params,
1234 TRUE, game_configure, custom_params,
1235 validate_params,
1236 new_game_desc,
1237 validate_desc,
1238 new_game,
1239 dup_game,
1240 free_game,
1241 TRUE, solve_game,
1242 TRUE, game_text_format,
1243 new_ui,
1244 free_ui,
1245 encode_ui,
1246 decode_ui,
1247 game_changed_state,
1248 interpret_move,
1249 execute_move,
1250 PREFERRED_TILESIZE, game_compute_size, game_set_size,
1251 game_colours,
1252 game_new_drawstate,
1253 game_free_drawstate,
1254 game_redraw,
1255 game_anim_length,
1256 game_flash_length,
1257 game_wants_statusbar,
1258 FALSE, game_timing_state,
1259 0, /* mouse_priorities */
1260 };