WIP: hack to avoid install attempts of nullgame on Qtopia.
[sgt-puzzles/ydirson.git] / dominosa.c
blob02e276c9d5cfc0465b2e51b0e1f84a538c35ce8a
1 /*
2 * dominosa.c: Domino jigsaw puzzle. Aim to place one of every
3 * possible domino within a rectangle in such a way that the number
4 * on each square matches the provided clue.
5 */
7 /*
8 * TODO:
9 *
10 * - improve solver so as to use more interesting forms of
11 * deduction
13 * * rule out a domino placement if it would divide an unfilled
14 * region such that at least one resulting region had an odd
15 * area
16 * + use b.f.s. to determine the area of an unfilled region
17 * + a square is unfilled iff it has at least two possible
18 * placements, and two adjacent unfilled squares are part
19 * of the same region iff the domino placement joining
20 * them is possible
22 * * perhaps set analysis
23 * + look at all unclaimed squares containing a given number
24 * + for each one, find the set of possible numbers that it
25 * can connect to (i.e. each neighbouring tile such that
26 * the placement between it and that neighbour has not yet
27 * been ruled out)
28 * + now proceed similarly to Solo set analysis: try to find
29 * a subset of the squares such that the union of their
30 * possible numbers is the same size as the subset. If so,
31 * rule out those possible numbers for all other squares.
32 * * important wrinkle: the double dominoes complicate
33 * matters. Connecting a number to itself uses up _two_
34 * of the unclaimed squares containing a number. Thus,
35 * when finding the initial subset we must never
36 * include two adjacent squares; and also, when ruling
37 * things out after finding the subset, we must be
38 * careful that we don't rule out precisely the domino
39 * placement that was _included_ in our set!
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <assert.h>
46 #include <ctype.h>
47 #include <math.h>
49 #include "puzzles.h"
51 /* nth triangular number */
52 #define TRI(n) ( (n) * ((n) + 1) / 2 )
53 /* number of dominoes for value n */
54 #define DCOUNT(n) TRI((n)+1)
55 /* map a pair of numbers to a unique domino index from 0 upwards. */
56 #define DINDEX(n1,n2) ( TRI(max(n1,n2)) + min(n1,n2) )
58 #define FLASH_TIME 0.13F
60 enum {
61 COL_BACKGROUND,
62 COL_TEXT,
63 COL_DOMINO,
64 COL_DOMINOCLASH,
65 COL_DOMINOTEXT,
66 COL_EDGE,
67 NCOLOURS
70 struct game_params {
71 int n;
72 int unique;
75 struct game_numbers {
76 int refcount;
77 int *numbers; /* h x w */
80 #define EDGE_L 0x100
81 #define EDGE_R 0x200
82 #define EDGE_T 0x400
83 #define EDGE_B 0x800
85 struct game_state {
86 game_params params;
87 int w, h;
88 struct game_numbers *numbers;
89 int *grid;
90 unsigned short *edges; /* h x w */
91 int completed, cheated;
94 static game_params *default_params(void)
96 game_params *ret = snew(game_params);
98 ret->n = 6;
99 ret->unique = TRUE;
101 return ret;
104 static int game_fetch_preset(int i, char **name, game_params **params)
106 game_params *ret;
107 int n;
108 char buf[80];
110 switch (i) {
111 case 0: n = 3; break;
112 case 1: n = 4; break;
113 case 2: n = 5; break;
114 case 3: n = 6; break;
115 case 4: n = 7; break;
116 case 5: n = 8; break;
117 case 6: n = 9; break;
118 default: return FALSE;
121 sprintf(buf, "Up to double-%d", n);
122 *name = dupstr(buf);
124 *params = ret = snew(game_params);
125 ret->n = n;
126 ret->unique = TRUE;
128 return TRUE;
131 static void free_params(game_params *params)
133 sfree(params);
136 static game_params *dup_params(game_params *params)
138 game_params *ret = snew(game_params);
139 *ret = *params; /* structure copy */
140 return ret;
143 static void decode_params(game_params *params, char const *string)
145 params->n = atoi(string);
146 while (*string && isdigit((unsigned char)*string)) string++;
147 if (*string == 'a')
148 params->unique = FALSE;
151 static char *encode_params(game_params *params, int full)
153 char buf[80];
154 sprintf(buf, "%d", params->n);
155 if (full && !params->unique)
156 strcat(buf, "a");
157 return dupstr(buf);
160 static config_item *game_configure(game_params *params)
162 config_item *ret;
163 char buf[80];
165 ret = snewn(3, config_item);
167 ret[0].name = "Maximum number on dominoes";
168 ret[0].type = C_STRING;
169 sprintf(buf, "%d", params->n);
170 ret[0].sval = dupstr(buf);
171 ret[0].ival = 0;
173 ret[1].name = "Ensure unique solution";
174 ret[1].type = C_BOOLEAN;
175 ret[1].sval = NULL;
176 ret[1].ival = params->unique;
178 ret[2].name = NULL;
179 ret[2].type = C_END;
180 ret[2].sval = NULL;
181 ret[2].ival = 0;
183 return ret;
186 static game_params *custom_params(config_item *cfg)
188 game_params *ret = snew(game_params);
190 ret->n = atoi(cfg[0].sval);
191 ret->unique = cfg[1].ival;
193 return ret;
196 static char *validate_params(game_params *params, int full)
198 if (params->n < 1)
199 return "Maximum face number must be at least one";
200 return NULL;
203 /* ----------------------------------------------------------------------
204 * Solver.
207 static int find_overlaps(int w, int h, int placement, int *set)
209 int x, y, n;
211 n = 0; /* number of returned placements */
213 x = placement / 2;
214 y = x / w;
215 x %= w;
217 if (placement & 1) {
219 * Horizontal domino, indexed by its left end.
221 if (x > 0)
222 set[n++] = placement-2; /* horizontal domino to the left */
223 if (y > 0)
224 set[n++] = placement-2*w-1;/* vertical domino above left side */
225 if (y+1 < h)
226 set[n++] = placement-1; /* vertical domino below left side */
227 if (x+2 < w)
228 set[n++] = placement+2; /* horizontal domino to the right */
229 if (y > 0)
230 set[n++] = placement-2*w+2-1;/* vertical domino above right side */
231 if (y+1 < h)
232 set[n++] = placement+2-1; /* vertical domino below right side */
233 } else {
235 * Vertical domino, indexed by its top end.
237 if (y > 0)
238 set[n++] = placement-2*w; /* vertical domino above */
239 if (x > 0)
240 set[n++] = placement-2+1; /* horizontal domino left of top */
241 if (x+1 < w)
242 set[n++] = placement+1; /* horizontal domino right of top */
243 if (y+2 < h)
244 set[n++] = placement+2*w; /* vertical domino below */
245 if (x > 0)
246 set[n++] = placement-2+2*w+1;/* horizontal domino left of bottom */
247 if (x+1 < w)
248 set[n++] = placement+2*w+1;/* horizontal domino right of bottom */
251 return n;
255 * Returns 0, 1 or 2 for number of solutions. 2 means `any number
256 * more than one', or more accurately `we were unable to prove
257 * there was only one'.
259 * Outputs in a `placements' array, indexed the same way as the one
260 * within this function (see below); entries in there are <0 for a
261 * placement ruled out, 0 for an uncertain placement, and 1 for a
262 * definite one.
264 static int solver(int w, int h, int n, int *grid, int *output)
266 int wh = w*h, dc = DCOUNT(n);
267 int *placements, *heads;
268 int i, j, x, y, ret;
271 * This array has one entry for every possible domino
272 * placement. Vertical placements are indexed by their top
273 * half, at (y*w+x)*2; horizontal placements are indexed by
274 * their left half at (y*w+x)*2+1.
276 * This array is used to link domino placements together into
277 * linked lists, so that we can track all the possible
278 * placements of each different domino. It's also used as a
279 * quick means of looking up an individual placement to see
280 * whether we still think it's possible. Actual values stored
281 * in this array are -2 (placement not possible at all), -1
282 * (end of list), or the array index of the next item.
284 * Oh, and -3 for `not even valid', used for array indices
285 * which don't even represent a plausible placement.
287 placements = snewn(2*wh, int);
288 for (i = 0; i < 2*wh; i++)
289 placements[i] = -3; /* not even valid */
292 * This array has one entry for every domino, and it is an
293 * index into `placements' denoting the head of the placement
294 * list for that domino.
296 heads = snewn(dc, int);
297 for (i = 0; i < dc; i++)
298 heads[i] = -1;
301 * Set up the initial possibility lists by scanning the grid.
303 for (y = 0; y < h-1; y++)
304 for (x = 0; x < w; x++) {
305 int di = DINDEX(grid[y*w+x], grid[(y+1)*w+x]);
306 placements[(y*w+x)*2] = heads[di];
307 heads[di] = (y*w+x)*2;
309 for (y = 0; y < h; y++)
310 for (x = 0; x < w-1; x++) {
311 int di = DINDEX(grid[y*w+x], grid[y*w+(x+1)]);
312 placements[(y*w+x)*2+1] = heads[di];
313 heads[di] = (y*w+x)*2+1;
316 #ifdef SOLVER_DIAGNOSTICS
317 printf("before solver:\n");
318 for (i = 0; i <= n; i++)
319 for (j = 0; j <= i; j++) {
320 int k, m;
321 m = 0;
322 printf("%2d [%d %d]:", DINDEX(i, j), i, j);
323 for (k = heads[DINDEX(i,j)]; k >= 0; k = placements[k])
324 printf(" %3d [%d,%d,%c]", k, k/2%w, k/2/w, k%2?'h':'v');
325 printf("\n");
327 #endif
329 while (1) {
330 int done_something = FALSE;
333 * For each domino, look at its possible placements, and
334 * for each placement consider the placements (of any
335 * domino) it overlaps. Any placement overlapped by all
336 * placements of this domino can be ruled out.
338 * Each domino placement overlaps only six others, so we
339 * need not do serious set theory to work this out.
341 for (i = 0; i < dc; i++) {
342 int permset[6], permlen = 0, p;
345 if (heads[i] == -1) { /* no placement for this domino */
346 ret = 0; /* therefore puzzle is impossible */
347 goto done;
349 for (j = heads[i]; j >= 0; j = placements[j]) {
350 assert(placements[j] != -2);
352 if (j == heads[i]) {
353 permlen = find_overlaps(w, h, j, permset);
354 } else {
355 int tempset[6], templen, m, n, k;
357 templen = find_overlaps(w, h, j, tempset);
360 * Pathetically primitive set intersection
361 * algorithm, which I'm only getting away with
362 * because I know my sets are bounded by a very
363 * small size.
365 for (m = n = 0; m < permlen; m++) {
366 for (k = 0; k < templen; k++)
367 if (tempset[k] == permset[m])
368 break;
369 if (k < templen)
370 permset[n++] = permset[m];
372 permlen = n;
375 for (p = 0; p < permlen; p++) {
376 j = permset[p];
377 if (placements[j] != -2) {
378 int p1, p2, di;
380 done_something = TRUE;
383 * Rule out this placement. First find what
384 * domino it is...
386 p1 = j / 2;
387 p2 = (j & 1) ? p1 + 1 : p1 + w;
388 di = DINDEX(grid[p1], grid[p2]);
389 #ifdef SOLVER_DIAGNOSTICS
390 printf("considering domino %d: ruling out placement %d"
391 " for %d\n", i, j, di);
392 #endif
395 * ... then walk that domino's placement list,
396 * removing this placement when we find it.
398 if (heads[di] == j)
399 heads[di] = placements[j];
400 else {
401 int k = heads[di];
402 while (placements[k] != -1 && placements[k] != j)
403 k = placements[k];
404 assert(placements[k] == j);
405 placements[k] = placements[j];
407 placements[j] = -2;
413 * For each square, look at the available placements
414 * involving that square. If all of them are for the same
415 * domino, then rule out any placements for that domino
416 * _not_ involving this square.
418 for (i = 0; i < wh; i++) {
419 int list[4], k, n, adi;
421 x = i % w;
422 y = i / w;
424 j = 0;
425 if (x > 0)
426 list[j++] = 2*(i-1)+1;
427 if (x+1 < w)
428 list[j++] = 2*i+1;
429 if (y > 0)
430 list[j++] = 2*(i-w);
431 if (y+1 < h)
432 list[j++] = 2*i;
434 for (n = k = 0; k < j; k++)
435 if (placements[list[k]] >= -1)
436 list[n++] = list[k];
438 adi = -1;
440 for (j = 0; j < n; j++) {
441 int p1, p2, di;
442 k = list[j];
444 p1 = k / 2;
445 p2 = (k & 1) ? p1 + 1 : p1 + w;
446 di = DINDEX(grid[p1], grid[p2]);
448 if (adi == -1)
449 adi = di;
450 if (adi != di)
451 break;
454 if (j == n) {
455 int nn;
457 assert(adi >= 0);
459 * We've found something. All viable placements
460 * involving this square are for domino `adi'. If
461 * the current placement list for that domino is
462 * longer than n, reduce it to precisely this
463 * placement list and we've done something.
465 nn = 0;
466 for (k = heads[adi]; k >= 0; k = placements[k])
467 nn++;
468 if (nn > n) {
469 done_something = TRUE;
470 #ifdef SOLVER_DIAGNOSTICS
471 printf("considering square %d,%d: reducing placements "
472 "of domino %d\n", x, y, adi);
473 #endif
475 * Set all other placements on the list to
476 * impossible.
478 k = heads[adi];
479 while (k >= 0) {
480 int tmp = placements[k];
481 placements[k] = -2;
482 k = tmp;
485 * Set up the new list.
487 heads[adi] = list[0];
488 for (k = 0; k < n; k++)
489 placements[list[k]] = (k+1 == n ? -1 : list[k+1]);
494 if (!done_something)
495 break;
498 #ifdef SOLVER_DIAGNOSTICS
499 printf("after solver:\n");
500 for (i = 0; i <= n; i++)
501 for (j = 0; j <= i; j++) {
502 int k, m;
503 m = 0;
504 printf("%2d [%d %d]:", DINDEX(i, j), i, j);
505 for (k = heads[DINDEX(i,j)]; k >= 0; k = placements[k])
506 printf(" %3d [%d,%d,%c]", k, k/2%w, k/2/w, k%2?'h':'v');
507 printf("\n");
509 #endif
511 ret = 1;
512 for (i = 0; i < wh*2; i++) {
513 if (placements[i] == -2) {
514 if (output)
515 output[i] = -1; /* ruled out */
516 } else if (placements[i] != -3) {
517 int p1, p2, di;
519 p1 = i / 2;
520 p2 = (i & 1) ? p1 + 1 : p1 + w;
521 di = DINDEX(grid[p1], grid[p2]);
523 if (i == heads[di] && placements[i] == -1) {
524 if (output)
525 output[i] = 1; /* certain */
526 } else {
527 if (output)
528 output[i] = 0; /* uncertain */
529 ret = 2;
534 done:
536 * Free working data.
538 sfree(placements);
539 sfree(heads);
541 return ret;
544 /* ----------------------------------------------------------------------
545 * End of solver code.
548 static char *new_game_desc(game_params *params, random_state *rs,
549 char **aux, int interactive)
551 int n = params->n, w = n+2, h = n+1, wh = w*h;
552 int *grid, *grid2, *list;
553 int i, j, k, len;
554 char *ret;
557 * Allocate space in which to lay the grid out.
559 grid = snewn(wh, int);
560 grid2 = snewn(wh, int);
561 list = snewn(2*wh, int);
564 * I haven't been able to think of any particularly clever
565 * techniques for generating instances of Dominosa with a
566 * unique solution. Many of the deductions used in this puzzle
567 * are based on information involving half the grid at a time
568 * (`of all the 6s, exactly one is next to a 3'), so a strategy
569 * of partially solving the grid and then perturbing the place
570 * where the solver got stuck seems particularly likely to
571 * accidentally destroy the information which the solver had
572 * used in getting that far. (Contrast with, say, Mines, in
573 * which most deductions are local so this is an excellent
574 * strategy.)
576 * Therefore I resort to the basest of brute force methods:
577 * generate a random grid, see if it's solvable, throw it away
578 * and try again if not. My only concession to sophistication
579 * and cleverness is to at least _try_ not to generate obvious
580 * 2x2 ambiguous sections (see comment below in the domino-
581 * flipping section).
583 * During tests performed on 2005-07-15, I found that the brute
584 * force approach without that tweak had to throw away about 87
585 * grids on average (at the default n=6) before finding a
586 * unique one, or a staggering 379 at n=9; good job the
587 * generator and solver are fast! When I added the
588 * ambiguous-section avoidance, those numbers came down to 19
589 * and 26 respectively, which is a lot more sensible.
592 do {
593 domino_layout_prealloc(w, h, rs, grid, grid2, list);
596 * Now we have a complete layout covering the whole
597 * rectangle with dominoes. So shuffle the actual domino
598 * values and fill the rectangle with numbers.
600 k = 0;
601 for (i = 0; i <= params->n; i++)
602 for (j = 0; j <= i; j++) {
603 list[k++] = i;
604 list[k++] = j;
606 shuffle(list, k/2, 2*sizeof(*list), rs);
607 j = 0;
608 for (i = 0; i < wh; i++)
609 if (grid[i] > i) {
610 /* Optionally flip the domino round. */
611 int flip = -1;
613 if (params->unique) {
614 int t1, t2;
616 * If we're after a unique solution, we can do
617 * something here to improve the chances. If
618 * we're placing a domino so that it forms a
619 * 2x2 rectangle with one we've already placed,
620 * and if that domino and this one share a
621 * number, we can try not to put them so that
622 * the identical numbers are diagonally
623 * separated, because that automatically causes
624 * non-uniqueness:
626 * +---+ +-+-+
627 * |2 3| |2|3|
628 * +---+ -> | | |
629 * |4 2| |4|2|
630 * +---+ +-+-+
632 t1 = i;
633 t2 = grid[i];
634 if (t2 == t1 + w) { /* this domino is vertical */
635 if (t1 % w > 0 &&/* and not on the left hand edge */
636 grid[t1-1] == t2-1 &&/* alongside one to left */
637 (grid2[t1-1] == list[j] || /* and has a number */
638 grid2[t1-1] == list[j+1] || /* in common */
639 grid2[t2-1] == list[j] ||
640 grid2[t2-1] == list[j+1])) {
641 if (grid2[t1-1] == list[j] ||
642 grid2[t2-1] == list[j+1])
643 flip = 0;
644 else
645 flip = 1;
647 } else { /* this domino is horizontal */
648 if (t1 / w > 0 &&/* and not on the top edge */
649 grid[t1-w] == t2-w &&/* alongside one above */
650 (grid2[t1-w] == list[j] || /* and has a number */
651 grid2[t1-w] == list[j+1] || /* in common */
652 grid2[t2-w] == list[j] ||
653 grid2[t2-w] == list[j+1])) {
654 if (grid2[t1-w] == list[j] ||
655 grid2[t2-w] == list[j+1])
656 flip = 0;
657 else
658 flip = 1;
663 if (flip < 0)
664 flip = random_upto(rs, 2);
666 grid2[i] = list[j + flip];
667 grid2[grid[i]] = list[j + 1 - flip];
668 j += 2;
670 assert(j == k);
671 } while (params->unique && solver(w, h, n, grid2, NULL) > 1);
673 #ifdef GENERATION_DIAGNOSTICS
674 for (j = 0; j < h; j++) {
675 for (i = 0; i < w; i++) {
676 putchar('0' + grid2[j*w+i]);
678 putchar('\n');
680 putchar('\n');
681 #endif
684 * Encode the resulting game state.
686 * Our encoding is a string of digits. Any number greater than
687 * 9 is represented by a decimal integer within square
688 * brackets. We know there are n+2 of every number (it's paired
689 * with each number from 0 to n inclusive, and one of those is
690 * itself so that adds another occurrence), so we can work out
691 * the string length in advance.
695 * To work out the total length of the decimal encodings of all
696 * the numbers from 0 to n inclusive:
697 * - every number has a units digit; total is n+1.
698 * - all numbers above 9 have a tens digit; total is max(n+1-10,0).
699 * - all numbers above 99 have a hundreds digit; total is max(n+1-100,0).
700 * - and so on.
702 len = n+1;
703 for (i = 10; i <= n; i *= 10)
704 len += max(n + 1 - i, 0);
705 /* Now add two square brackets for each number above 9. */
706 len += 2 * max(n + 1 - 10, 0);
707 /* And multiply by n+2 for the repeated occurrences of each number. */
708 len *= n+2;
711 * Now actually encode the string.
713 ret = snewn(len+1, char);
714 j = 0;
715 for (i = 0; i < wh; i++) {
716 k = grid2[i];
717 if (k < 10)
718 ret[j++] = '0' + k;
719 else
720 j += sprintf(ret+j, "[%d]", k);
721 assert(j <= len);
723 assert(j == len);
724 ret[j] = '\0';
727 * Encode the solved state as an aux_info.
730 char *auxinfo = snewn(wh+1, char);
732 for (i = 0; i < wh; i++) {
733 int v = grid[i];
734 auxinfo[i] = (v == i+1 ? 'L' : v == i-1 ? 'R' :
735 v == i+w ? 'T' : v == i-w ? 'B' : '.');
737 auxinfo[wh] = '\0';
739 *aux = auxinfo;
742 sfree(list);
743 sfree(grid2);
744 sfree(grid);
746 return ret;
749 static char *validate_desc(game_params *params, char *desc)
751 int n = params->n, w = n+2, h = n+1, wh = w*h;
752 int *occurrences;
753 int i, j;
754 char *ret;
756 ret = NULL;
757 occurrences = snewn(n+1, int);
758 for (i = 0; i <= n; i++)
759 occurrences[i] = 0;
761 for (i = 0; i < wh; i++) {
762 if (!*desc) {
763 ret = ret ? ret : "Game description is too short";
764 } else {
765 if (*desc >= '0' && *desc <= '9')
766 j = *desc++ - '0';
767 else if (*desc == '[') {
768 desc++;
769 j = atoi(desc);
770 while (*desc && isdigit((unsigned char)*desc)) desc++;
771 if (*desc != ']')
772 ret = ret ? ret : "Missing ']' in game description";
773 else
774 desc++;
775 } else {
776 j = -1;
777 ret = ret ? ret : "Invalid syntax in game description";
779 if (j < 0 || j > n)
780 ret = ret ? ret : "Number out of range in game description";
781 else
782 occurrences[j]++;
786 if (*desc)
787 ret = ret ? ret : "Game description is too long";
789 if (!ret) {
790 for (i = 0; i <= n; i++)
791 if (occurrences[i] != n+2)
792 ret = "Incorrect number balance in game description";
795 sfree(occurrences);
797 return ret;
800 static game_state *new_game(midend *me, game_params *params, char *desc)
802 int n = params->n, w = n+2, h = n+1, wh = w*h;
803 game_state *state = snew(game_state);
804 int i, j;
806 state->params = *params;
807 state->w = w;
808 state->h = h;
810 state->grid = snewn(wh, int);
811 for (i = 0; i < wh; i++)
812 state->grid[i] = i;
814 state->edges = snewn(wh, unsigned short);
815 for (i = 0; i < wh; i++)
816 state->edges[i] = 0;
818 state->numbers = snew(struct game_numbers);
819 state->numbers->refcount = 1;
820 state->numbers->numbers = snewn(wh, int);
822 for (i = 0; i < wh; i++) {
823 assert(*desc);
824 if (*desc >= '0' && *desc <= '9')
825 j = *desc++ - '0';
826 else {
827 assert(*desc == '[');
828 desc++;
829 j = atoi(desc);
830 while (*desc && isdigit((unsigned char)*desc)) desc++;
831 assert(*desc == ']');
832 desc++;
834 assert(j >= 0 && j <= n);
835 state->numbers->numbers[i] = j;
838 state->completed = state->cheated = FALSE;
840 return state;
843 static game_state *dup_game(game_state *state)
845 int n = state->params.n, w = n+2, h = n+1, wh = w*h;
846 game_state *ret = snew(game_state);
848 ret->params = state->params;
849 ret->w = state->w;
850 ret->h = state->h;
851 ret->grid = snewn(wh, int);
852 memcpy(ret->grid, state->grid, wh * sizeof(int));
853 ret->edges = snewn(wh, unsigned short);
854 memcpy(ret->edges, state->edges, wh * sizeof(unsigned short));
855 ret->numbers = state->numbers;
856 ret->numbers->refcount++;
857 ret->completed = state->completed;
858 ret->cheated = state->cheated;
860 return ret;
863 static void free_game(game_state *state)
865 sfree(state->grid);
866 sfree(state->edges);
867 if (--state->numbers->refcount <= 0) {
868 sfree(state->numbers->numbers);
869 sfree(state->numbers);
871 sfree(state);
874 static char *solve_game(game_state *state, game_state *currstate,
875 char *aux, char **error)
877 int n = state->params.n, w = n+2, h = n+1, wh = w*h;
878 int *placements;
879 char *ret;
880 int retlen, retsize;
881 int i, v;
882 char buf[80];
883 int extra;
885 if (aux) {
886 retsize = 256;
887 ret = snewn(retsize, char);
888 retlen = sprintf(ret, "S");
890 for (i = 0; i < wh; i++) {
891 if (aux[i] == 'L')
892 extra = sprintf(buf, ";D%d,%d", i, i+1);
893 else if (aux[i] == 'T')
894 extra = sprintf(buf, ";D%d,%d", i, i+w);
895 else
896 continue;
898 if (retlen + extra + 1 >= retsize) {
899 retsize = retlen + extra + 256;
900 ret = sresize(ret, retsize, char);
902 strcpy(ret + retlen, buf);
903 retlen += extra;
906 } else {
908 placements = snewn(wh*2, int);
909 for (i = 0; i < wh*2; i++)
910 placements[i] = -3;
911 solver(w, h, n, state->numbers->numbers, placements);
914 * First make a pass putting in edges for -1, then make a pass
915 * putting in dominoes for +1.
917 retsize = 256;
918 ret = snewn(retsize, char);
919 retlen = sprintf(ret, "S");
921 for (v = -1; v <= +1; v += 2)
922 for (i = 0; i < wh*2; i++)
923 if (placements[i] == v) {
924 int p1 = i / 2;
925 int p2 = (i & 1) ? p1+1 : p1+w;
927 extra = sprintf(buf, ";%c%d,%d",
928 (int)(v==-1 ? 'E' : 'D'), p1, p2);
930 if (retlen + extra + 1 >= retsize) {
931 retsize = retlen + extra + 256;
932 ret = sresize(ret, retsize, char);
934 strcpy(ret + retlen, buf);
935 retlen += extra;
938 sfree(placements);
941 return ret;
944 static int game_can_format_as_text_now(game_params *params)
946 return TRUE;
949 static char *game_text_format(game_state *state)
951 return NULL;
954 struct game_ui {
955 int cur_x, cur_y, cur_visible;
958 static game_ui *new_ui(game_state *state)
960 game_ui *ui = snew(game_ui);
961 ui->cur_x = ui->cur_y = 0;
962 ui->cur_visible = 0;
963 return ui;
966 static void free_ui(game_ui *ui)
968 sfree(ui);
971 static char *encode_ui(game_ui *ui)
973 return NULL;
976 static void decode_ui(game_ui *ui, char *encoding)
980 static void game_changed_state(game_ui *ui, game_state *oldstate,
981 game_state *newstate)
983 if (!oldstate->completed && newstate->completed)
984 ui->cur_visible = 0;
987 #define PREFERRED_TILESIZE 32
988 #define TILESIZE (ds->tilesize)
989 #define BORDER (TILESIZE * 3 / 4)
990 #define DOMINO_GUTTER (TILESIZE / 16)
991 #define DOMINO_RADIUS (TILESIZE / 8)
992 #define DOMINO_COFFSET (DOMINO_GUTTER + DOMINO_RADIUS)
993 #define CURSOR_RADIUS (TILESIZE / 4)
995 #define COORD(x) ( (x) * TILESIZE + BORDER )
996 #define FROMCOORD(x) ( ((x) - BORDER + TILESIZE) / TILESIZE - 1 )
998 struct game_drawstate {
999 int started;
1000 int w, h, tilesize;
1001 unsigned long *visible;
1004 static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds,
1005 int x, int y, int button)
1007 int w = state->w, h = state->h;
1008 char buf[80];
1011 * A left-click between two numbers toggles a domino covering
1012 * them. A right-click toggles an edge.
1014 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
1015 int tx = FROMCOORD(x), ty = FROMCOORD(y), t = ty*w+tx;
1016 int dx, dy;
1017 int d1, d2;
1019 if (tx < 0 || tx >= w || ty < 0 || ty >= h)
1020 return NULL;
1023 * Now we know which square the click was in, decide which
1024 * edge of the square it was closest to.
1026 dx = 2 * (x - COORD(tx)) - TILESIZE;
1027 dy = 2 * (y - COORD(ty)) - TILESIZE;
1029 if (abs(dx) > abs(dy) && dx < 0 && tx > 0)
1030 d1 = t - 1, d2 = t; /* clicked in right side of domino */
1031 else if (abs(dx) > abs(dy) && dx > 0 && tx+1 < w)
1032 d1 = t, d2 = t + 1; /* clicked in left side of domino */
1033 else if (abs(dy) > abs(dx) && dy < 0 && ty > 0)
1034 d1 = t - w, d2 = t; /* clicked in bottom half of domino */
1035 else if (abs(dy) > abs(dx) && dy > 0 && ty+1 < h)
1036 d1 = t, d2 = t + w; /* clicked in top half of domino */
1037 else
1038 return NULL;
1041 * We can't mark an edge next to any domino.
1043 if (button == RIGHT_BUTTON &&
1044 (state->grid[d1] != d1 || state->grid[d2] != d2))
1045 return NULL;
1047 ui->cur_visible = 0;
1048 sprintf(buf, "%c%d,%d", (int)(button == RIGHT_BUTTON ? 'E' : 'D'), d1, d2);
1049 return dupstr(buf);
1050 } else if (IS_CURSOR_MOVE(button)) {
1051 ui->cur_visible = 1;
1053 move_cursor(button, &ui->cur_x, &ui->cur_y, 2*w-1, 2*h-1, 0);
1055 return "";
1056 } else if (IS_CURSOR_SELECT(button)) {
1057 int d1, d2;
1059 if (!((ui->cur_x ^ ui->cur_y) & 1))
1060 return NULL; /* must have exactly one dimension odd */
1061 d1 = (ui->cur_y / 2) * w + (ui->cur_x / 2);
1062 d2 = ((ui->cur_y+1) / 2) * w + ((ui->cur_x+1) / 2);
1065 * We can't mark an edge next to any domino.
1067 if (button == CURSOR_SELECT2 &&
1068 (state->grid[d1] != d1 || state->grid[d2] != d2))
1069 return NULL;
1071 sprintf(buf, "%c%d,%d", (int)(button == CURSOR_SELECT2 ? 'E' : 'D'), d1, d2);
1072 return dupstr(buf);
1075 return NULL;
1078 static game_state *execute_move(game_state *state, char *move)
1080 int n = state->params.n, w = n+2, h = n+1, wh = w*h;
1081 int d1, d2, d3, p;
1082 game_state *ret = dup_game(state);
1084 while (*move) {
1085 if (move[0] == 'S') {
1086 int i;
1088 ret->cheated = TRUE;
1091 * Clear the existing edges and domino placements. We
1092 * expect the S to be followed by other commands.
1094 for (i = 0; i < wh; i++) {
1095 ret->grid[i] = i;
1096 ret->edges[i] = 0;
1098 move++;
1099 } else if (move[0] == 'D' &&
1100 sscanf(move+1, "%d,%d%n", &d1, &d2, &p) == 2 &&
1101 d1 >= 0 && d1 < wh && d2 >= 0 && d2 < wh && d1 < d2) {
1104 * Toggle domino presence between d1 and d2.
1106 if (ret->grid[d1] == d2) {
1107 assert(ret->grid[d2] == d1);
1108 ret->grid[d1] = d1;
1109 ret->grid[d2] = d2;
1110 } else {
1112 * Erase any dominoes that might overlap the new one.
1114 d3 = ret->grid[d1];
1115 if (d3 != d1)
1116 ret->grid[d3] = d3;
1117 d3 = ret->grid[d2];
1118 if (d3 != d2)
1119 ret->grid[d3] = d3;
1121 * Place the new one.
1123 ret->grid[d1] = d2;
1124 ret->grid[d2] = d1;
1127 * Destroy any edges lurking around it.
1129 if (ret->edges[d1] & EDGE_L) {
1130 assert(d1 - 1 >= 0);
1131 ret->edges[d1 - 1] &= ~EDGE_R;
1133 if (ret->edges[d1] & EDGE_R) {
1134 assert(d1 + 1 < wh);
1135 ret->edges[d1 + 1] &= ~EDGE_L;
1137 if (ret->edges[d1] & EDGE_T) {
1138 assert(d1 - w >= 0);
1139 ret->edges[d1 - w] &= ~EDGE_B;
1141 if (ret->edges[d1] & EDGE_B) {
1142 assert(d1 + 1 < wh);
1143 ret->edges[d1 + w] &= ~EDGE_T;
1145 ret->edges[d1] = 0;
1146 if (ret->edges[d2] & EDGE_L) {
1147 assert(d2 - 1 >= 0);
1148 ret->edges[d2 - 1] &= ~EDGE_R;
1150 if (ret->edges[d2] & EDGE_R) {
1151 assert(d2 + 1 < wh);
1152 ret->edges[d2 + 1] &= ~EDGE_L;
1154 if (ret->edges[d2] & EDGE_T) {
1155 assert(d2 - w >= 0);
1156 ret->edges[d2 - w] &= ~EDGE_B;
1158 if (ret->edges[d2] & EDGE_B) {
1159 assert(d2 + 1 < wh);
1160 ret->edges[d2 + w] &= ~EDGE_T;
1162 ret->edges[d2] = 0;
1165 move += p+1;
1166 } else if (move[0] == 'E' &&
1167 sscanf(move+1, "%d,%d%n", &d1, &d2, &p) == 2 &&
1168 d1 >= 0 && d1 < wh && d2 >= 0 && d2 < wh && d1 < d2 &&
1169 ret->grid[d1] == d1 && ret->grid[d2] == d2) {
1172 * Toggle edge presence between d1 and d2.
1174 if (d2 == d1 + 1) {
1175 ret->edges[d1] ^= EDGE_R;
1176 ret->edges[d2] ^= EDGE_L;
1177 } else {
1178 ret->edges[d1] ^= EDGE_B;
1179 ret->edges[d2] ^= EDGE_T;
1182 move += p+1;
1183 } else {
1184 free_game(ret);
1185 return NULL;
1188 if (*move) {
1189 if (*move != ';') {
1190 free_game(ret);
1191 return NULL;
1193 move++;
1198 * After modifying the grid, check completion.
1200 if (!ret->completed) {
1201 int i, ok = 0;
1202 unsigned char *used = snewn(TRI(n+1), unsigned char);
1204 memset(used, 0, TRI(n+1));
1205 for (i = 0; i < wh; i++)
1206 if (ret->grid[i] > i) {
1207 int n1, n2, di;
1209 n1 = ret->numbers->numbers[i];
1210 n2 = ret->numbers->numbers[ret->grid[i]];
1212 di = DINDEX(n1, n2);
1213 assert(di >= 0 && di < TRI(n+1));
1215 if (!used[di]) {
1216 used[di] = 1;
1217 ok++;
1221 sfree(used);
1222 if (ok == DCOUNT(n))
1223 ret->completed = TRUE;
1226 return ret;
1229 /* ----------------------------------------------------------------------
1230 * Drawing routines.
1233 static void game_compute_size(game_params *params, int tilesize,
1234 int *x, int *y)
1236 int n = params->n, w = n+2, h = n+1;
1238 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1239 struct { int tilesize; } ads, *ds = &ads;
1240 ads.tilesize = tilesize;
1242 *x = w * TILESIZE + 2*BORDER;
1243 *y = h * TILESIZE + 2*BORDER;
1246 static void game_set_size(drawing *dr, game_drawstate *ds,
1247 game_params *params, int tilesize)
1249 ds->tilesize = tilesize;
1252 static float *game_colours(frontend *fe, int *ncolours)
1254 float *ret = snewn(3 * NCOLOURS, float);
1256 frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
1258 ret[COL_TEXT * 3 + 0] = 0.0F;
1259 ret[COL_TEXT * 3 + 1] = 0.0F;
1260 ret[COL_TEXT * 3 + 2] = 0.0F;
1262 ret[COL_DOMINO * 3 + 0] = 0.0F;
1263 ret[COL_DOMINO * 3 + 1] = 0.0F;
1264 ret[COL_DOMINO * 3 + 2] = 0.0F;
1266 ret[COL_DOMINOCLASH * 3 + 0] = 0.5F;
1267 ret[COL_DOMINOCLASH * 3 + 1] = 0.0F;
1268 ret[COL_DOMINOCLASH * 3 + 2] = 0.0F;
1270 ret[COL_DOMINOTEXT * 3 + 0] = 1.0F;
1271 ret[COL_DOMINOTEXT * 3 + 1] = 1.0F;
1272 ret[COL_DOMINOTEXT * 3 + 2] = 1.0F;
1274 ret[COL_EDGE * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 2 / 3;
1275 ret[COL_EDGE * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 2 / 3;
1276 ret[COL_EDGE * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] * 2 / 3;
1278 *ncolours = NCOLOURS;
1279 return ret;
1282 static game_drawstate *game_new_drawstate(drawing *dr, game_state *state)
1284 struct game_drawstate *ds = snew(struct game_drawstate);
1285 int i;
1287 ds->started = FALSE;
1288 ds->w = state->w;
1289 ds->h = state->h;
1290 ds->visible = snewn(ds->w * ds->h, unsigned long);
1291 ds->tilesize = 0; /* not decided yet */
1292 for (i = 0; i < ds->w * ds->h; i++)
1293 ds->visible[i] = 0xFFFF;
1295 return ds;
1298 static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1300 sfree(ds->visible);
1301 sfree(ds);
1304 enum {
1305 TYPE_L,
1306 TYPE_R,
1307 TYPE_T,
1308 TYPE_B,
1309 TYPE_BLANK,
1310 TYPE_MASK = 0x0F
1313 /* These flags must be disjoint with:
1314 * the above enum (TYPE_*) [0x000 -- 0x00F]
1315 * EDGE_* [0x100 -- 0xF00]
1316 * and must fit into an unsigned long (32 bits).
1318 #define DF_FLASH 0x40
1319 #define DF_CLASH 0x80
1321 #define DF_CURSOR 0x01000
1322 #define DF_CURSOR_USEFUL 0x02000
1323 #define DF_CURSOR_XBASE 0x10000
1324 #define DF_CURSOR_XMASK 0x30000
1325 #define DF_CURSOR_YBASE 0x40000
1326 #define DF_CURSOR_YMASK 0xC0000
1328 #define CEDGE_OFF (TILESIZE / 8)
1329 #define IS_EMPTY(s,x,y) ((s)->grid[(y)*(s)->w+(x)] == ((y)*(s)->w+(x)))
1331 static void draw_tile(drawing *dr, game_drawstate *ds, game_state *state,
1332 int x, int y, int type)
1334 int w = state->w /*, h = state->h */;
1335 int cx = COORD(x), cy = COORD(y);
1336 int nc;
1337 char str[80];
1338 int flags;
1340 clip(dr, cx, cy, TILESIZE, TILESIZE);
1341 draw_rect(dr, cx, cy, TILESIZE, TILESIZE, COL_BACKGROUND);
1343 flags = type &~ TYPE_MASK;
1344 type &= TYPE_MASK;
1346 if (type != TYPE_BLANK) {
1347 int i, bg;
1350 * Draw one end of a domino. This is composed of:
1352 * - two filled circles (rounded corners)
1353 * - two rectangles
1354 * - a slight shift in the number
1357 if (flags & DF_CLASH)
1358 bg = COL_DOMINOCLASH;
1359 else
1360 bg = COL_DOMINO;
1361 nc = COL_DOMINOTEXT;
1363 if (flags & DF_FLASH) {
1364 int tmp = nc;
1365 nc = bg;
1366 bg = tmp;
1369 if (type == TYPE_L || type == TYPE_T)
1370 draw_circle(dr, cx+DOMINO_COFFSET, cy+DOMINO_COFFSET,
1371 DOMINO_RADIUS, bg, bg);
1372 if (type == TYPE_R || type == TYPE_T)
1373 draw_circle(dr, cx+TILESIZE-1-DOMINO_COFFSET, cy+DOMINO_COFFSET,
1374 DOMINO_RADIUS, bg, bg);
1375 if (type == TYPE_L || type == TYPE_B)
1376 draw_circle(dr, cx+DOMINO_COFFSET, cy+TILESIZE-1-DOMINO_COFFSET,
1377 DOMINO_RADIUS, bg, bg);
1378 if (type == TYPE_R || type == TYPE_B)
1379 draw_circle(dr, cx+TILESIZE-1-DOMINO_COFFSET,
1380 cy+TILESIZE-1-DOMINO_COFFSET,
1381 DOMINO_RADIUS, bg, bg);
1383 for (i = 0; i < 2; i++) {
1384 int x1, y1, x2, y2;
1386 x1 = cx + (i ? DOMINO_GUTTER : DOMINO_COFFSET);
1387 y1 = cy + (i ? DOMINO_COFFSET : DOMINO_GUTTER);
1388 x2 = cx + TILESIZE-1 - (i ? DOMINO_GUTTER : DOMINO_COFFSET);
1389 y2 = cy + TILESIZE-1 - (i ? DOMINO_COFFSET : DOMINO_GUTTER);
1390 if (type == TYPE_L)
1391 x2 = cx + TILESIZE + TILESIZE/16;
1392 else if (type == TYPE_R)
1393 x1 = cx - TILESIZE/16;
1394 else if (type == TYPE_T)
1395 y2 = cy + TILESIZE + TILESIZE/16;
1396 else if (type == TYPE_B)
1397 y1 = cy - TILESIZE/16;
1399 draw_rect(dr, x1, y1, x2-x1+1, y2-y1+1, bg);
1401 } else {
1402 if (flags & EDGE_T)
1403 draw_rect(dr, cx+DOMINO_GUTTER, cy,
1404 TILESIZE-2*DOMINO_GUTTER, 1, COL_EDGE);
1405 if (flags & EDGE_B)
1406 draw_rect(dr, cx+DOMINO_GUTTER, cy+TILESIZE-1,
1407 TILESIZE-2*DOMINO_GUTTER, 1, COL_EDGE);
1408 if (flags & EDGE_L)
1409 draw_rect(dr, cx, cy+DOMINO_GUTTER,
1410 1, TILESIZE-2*DOMINO_GUTTER, COL_EDGE);
1411 if (flags & EDGE_R)
1412 draw_rect(dr, cx+TILESIZE-1, cy+DOMINO_GUTTER,
1413 1, TILESIZE-2*DOMINO_GUTTER, COL_EDGE);
1414 nc = COL_TEXT;
1417 if (flags & DF_CURSOR) {
1418 int curx = ((flags & DF_CURSOR_XMASK) / DF_CURSOR_XBASE) & 3;
1419 int cury = ((flags & DF_CURSOR_YMASK) / DF_CURSOR_YBASE) & 3;
1420 int ox = cx + curx*TILESIZE/2;
1421 int oy = cy + cury*TILESIZE/2;
1423 draw_rect_corners(dr, ox, oy, CURSOR_RADIUS, nc);
1424 if (flags & DF_CURSOR_USEFUL)
1425 draw_rect_corners(dr, ox, oy, CURSOR_RADIUS+1, nc);
1428 sprintf(str, "%d", state->numbers->numbers[y*w+x]);
1429 draw_text(dr, cx+TILESIZE/2, cy+TILESIZE/2, FONT_VARIABLE, TILESIZE/2,
1430 ALIGN_HCENTRE | ALIGN_VCENTRE, nc, str);
1432 draw_update(dr, cx, cy, TILESIZE, TILESIZE);
1433 unclip(dr);
1436 static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
1437 game_state *state, int dir, game_ui *ui,
1438 float animtime, float flashtime)
1440 int n = state->params.n, w = state->w, h = state->h, wh = w*h;
1441 int x, y, i;
1442 unsigned char *used;
1444 if (!ds->started) {
1445 int pw, ph;
1446 game_compute_size(&state->params, TILESIZE, &pw, &ph);
1447 draw_rect(dr, 0, 0, pw, ph, COL_BACKGROUND);
1448 draw_update(dr, 0, 0, pw, ph);
1449 ds->started = TRUE;
1453 * See how many dominoes of each type there are, so we can
1454 * highlight clashes in red.
1456 used = snewn(TRI(n+1), unsigned char);
1457 memset(used, 0, TRI(n+1));
1458 for (i = 0; i < wh; i++)
1459 if (state->grid[i] > i) {
1460 int n1, n2, di;
1462 n1 = state->numbers->numbers[i];
1463 n2 = state->numbers->numbers[state->grid[i]];
1465 di = DINDEX(n1, n2);
1466 assert(di >= 0 && di < TRI(n+1));
1468 if (used[di] < 2)
1469 used[di]++;
1472 for (y = 0; y < h; y++)
1473 for (x = 0; x < w; x++) {
1474 int n = y*w+x;
1475 int n1, n2, di;
1476 unsigned long c;
1478 if (state->grid[n] == n-1)
1479 c = TYPE_R;
1480 else if (state->grid[n] == n+1)
1481 c = TYPE_L;
1482 else if (state->grid[n] == n-w)
1483 c = TYPE_B;
1484 else if (state->grid[n] == n+w)
1485 c = TYPE_T;
1486 else
1487 c = TYPE_BLANK;
1489 if (c != TYPE_BLANK) {
1490 n1 = state->numbers->numbers[n];
1491 n2 = state->numbers->numbers[state->grid[n]];
1492 di = DINDEX(n1, n2);
1493 if (used[di] > 1)
1494 c |= DF_CLASH; /* highlight a clash */
1495 } else {
1496 c |= state->edges[n];
1499 if (flashtime != 0)
1500 c |= DF_FLASH; /* we're flashing */
1502 if (ui->cur_visible) {
1503 unsigned curx = (unsigned)(ui->cur_x - (2*x-1));
1504 unsigned cury = (unsigned)(ui->cur_y - (2*y-1));
1505 if (curx < 3 && cury < 3) {
1506 c |= (DF_CURSOR |
1507 (curx * DF_CURSOR_XBASE) |
1508 (cury * DF_CURSOR_YBASE));
1509 if ((ui->cur_x ^ ui->cur_y) & 1)
1510 c |= DF_CURSOR_USEFUL;
1514 if (ds->visible[n] != c) {
1515 draw_tile(dr, ds, state, x, y, c);
1516 ds->visible[n] = c;
1520 sfree(used);
1523 static float game_anim_length(game_state *oldstate, game_state *newstate,
1524 int dir, game_ui *ui)
1526 return 0.0F;
1529 static float game_flash_length(game_state *oldstate, game_state *newstate,
1530 int dir, game_ui *ui)
1532 if (!oldstate->completed && newstate->completed &&
1533 !oldstate->cheated && !newstate->cheated)
1534 return FLASH_TIME;
1535 return 0.0F;
1538 static int game_status(game_state *state)
1540 return state->completed ? +1 : 0;
1543 static int game_timing_state(game_state *state, game_ui *ui)
1545 return TRUE;
1548 static void game_print_size(game_params *params, float *x, float *y)
1550 int pw, ph;
1553 * I'll use 6mm squares by default.
1555 game_compute_size(params, 600, &pw, &ph);
1556 *x = pw / 100.0F;
1557 *y = ph / 100.0F;
1560 static void game_print(drawing *dr, game_state *state, int tilesize)
1562 int w = state->w, h = state->h;
1563 int c, x, y;
1565 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1566 game_drawstate ads, *ds = &ads;
1567 game_set_size(dr, ds, NULL, tilesize);
1569 c = print_mono_colour(dr, 1); assert(c == COL_BACKGROUND);
1570 c = print_mono_colour(dr, 0); assert(c == COL_TEXT);
1571 c = print_mono_colour(dr, 0); assert(c == COL_DOMINO);
1572 c = print_mono_colour(dr, 0); assert(c == COL_DOMINOCLASH);
1573 c = print_mono_colour(dr, 1); assert(c == COL_DOMINOTEXT);
1574 c = print_mono_colour(dr, 0); assert(c == COL_EDGE);
1576 for (y = 0; y < h; y++)
1577 for (x = 0; x < w; x++) {
1578 int n = y*w+x;
1579 unsigned long c;
1581 if (state->grid[n] == n-1)
1582 c = TYPE_R;
1583 else if (state->grid[n] == n+1)
1584 c = TYPE_L;
1585 else if (state->grid[n] == n-w)
1586 c = TYPE_B;
1587 else if (state->grid[n] == n+w)
1588 c = TYPE_T;
1589 else
1590 c = TYPE_BLANK;
1592 draw_tile(dr, ds, state, x, y, c);
1596 #ifdef COMBINED
1597 #define thegame dominosa
1598 #endif
1600 const struct game thegame = {
1601 "Dominosa", "games.dominosa", "dominosa",
1602 default_params,
1603 game_fetch_preset,
1604 decode_params,
1605 encode_params,
1606 free_params,
1607 dup_params,
1608 TRUE, game_configure, custom_params,
1609 validate_params,
1610 new_game_desc,
1611 validate_desc,
1612 new_game,
1613 dup_game,
1614 free_game,
1615 TRUE, solve_game,
1616 FALSE, game_can_format_as_text_now, game_text_format,
1617 new_ui,
1618 free_ui,
1619 encode_ui,
1620 decode_ui,
1621 game_changed_state,
1622 interpret_move,
1623 execute_move,
1624 PREFERRED_TILESIZE, game_compute_size, game_set_size,
1625 game_colours,
1626 game_new_drawstate,
1627 game_free_drawstate,
1628 game_redraw,
1629 game_anim_length,
1630 game_flash_length,
1631 game_status,
1632 TRUE, FALSE, game_print_size, game_print,
1633 FALSE, /* wants_statusbar */
1634 FALSE, game_timing_state,
1635 0, /* flags */
1638 /* vim: set shiftwidth=4 :set textwidth=80: */