Make menus accessible from the keyboard in the Gtk front-end.
[sgt/puzzles] / dominosa.c
CommitLineData
6c04c334 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 */
6
7/*
8 * TODO:
9 *
10 * - improve solver so as to use more interesting forms of
11 * deduction
8bba8910 12 *
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
21 *
6c04c334 22 * * perhaps set analysis
8bba8910 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!
6c04c334 40 */
41
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45#include <assert.h>
46#include <ctype.h>
47#include <math.h>
48
49#include "puzzles.h"
50
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) )
57
58#define FLASH_TIME 0.13F
59
60enum {
61 COL_BACKGROUND,
62 COL_TEXT,
63 COL_DOMINO,
64 COL_DOMINOCLASH,
65 COL_DOMINOTEXT,
66 COL_EDGE,
67 NCOLOURS
68};
69
70struct game_params {
71 int n;
72 int unique;
73};
74
75struct game_numbers {
76 int refcount;
77 int *numbers; /* h x w */
78};
79
80#define EDGE_L 0x100
81#define EDGE_R 0x200
82#define EDGE_T 0x400
83#define EDGE_B 0x800
84
85struct 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;
92};
93
94static game_params *default_params(void)
95{
96 game_params *ret = snew(game_params);
97
98 ret->n = 6;
99 ret->unique = TRUE;
100
101 return ret;
102}
103
104static int game_fetch_preset(int i, char **name, game_params **params)
105{
106 game_params *ret;
107 int n;
108 char buf[80];
109
110 switch (i) {
111 case 0: n = 3; break;
effa9923 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;
6c04c334 118 default: return FALSE;
119 }
120
121 sprintf(buf, "Up to double-%d", n);
122 *name = dupstr(buf);
123
124 *params = ret = snew(game_params);
125 ret->n = n;
126 ret->unique = TRUE;
127
128 return TRUE;
129}
130
131static void free_params(game_params *params)
132{
133 sfree(params);
134}
135
136static game_params *dup_params(game_params *params)
137{
138 game_params *ret = snew(game_params);
139 *ret = *params; /* structure copy */
140 return ret;
141}
142
143static void decode_params(game_params *params, char const *string)
144{
145 params->n = atoi(string);
146 while (*string && isdigit((unsigned char)*string)) string++;
147 if (*string == 'a')
148 params->unique = FALSE;
149}
150
151static char *encode_params(game_params *params, int full)
152{
153 char buf[80];
154 sprintf(buf, "%d", params->n);
155 if (full && !params->unique)
156 strcat(buf, "a");
157 return dupstr(buf);
158}
159
160static config_item *game_configure(game_params *params)
161{
162 config_item *ret;
163 char buf[80];
164
165 ret = snewn(3, config_item);
166
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;
172
173 ret[1].name = "Ensure unique solution";
174 ret[1].type = C_BOOLEAN;
175 ret[1].sval = NULL;
176 ret[1].ival = params->unique;
177
178 ret[2].name = NULL;
179 ret[2].type = C_END;
180 ret[2].sval = NULL;
181 ret[2].ival = 0;
182
183 return ret;
184}
185
186static game_params *custom_params(config_item *cfg)
187{
188 game_params *ret = snew(game_params);
189
190 ret->n = atoi(cfg[0].sval);
191 ret->unique = cfg[1].ival;
192
193 return ret;
194}
195
196static char *validate_params(game_params *params, int full)
197{
198 if (params->n < 1)
199 return "Maximum face number must be at least one";
200 return NULL;
201}
202
203/* ----------------------------------------------------------------------
204 * Solver.
205 */
206
207static int find_overlaps(int w, int h, int placement, int *set)
208{
209 int x, y, n;
210
211 n = 0; /* number of returned placements */
212
213 x = placement / 2;
214 y = x / w;
215 x %= w;
216
217 if (placement & 1) {
218 /*
219 * Horizontal domino, indexed by its left end.
220 */
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 {
234 /*
235 * Vertical domino, indexed by its top end.
236 */
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 */
249 }
250
251 return n;
252}
253
254/*
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'.
258 *
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.
263 */
264static int solver(int w, int h, int n, int *grid, int *output)
265{
266 int wh = w*h, dc = DCOUNT(n);
267 int *placements, *heads;
268 int i, j, x, y, ret;
269
270 /*
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.
275 *
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.
283 *
284 * Oh, and -3 for `not even valid', used for array indices
285 * which don't even represent a plausible placement.
286 */
287 placements = snewn(2*wh, int);
288 for (i = 0; i < 2*wh; i++)
289 placements[i] = -3; /* not even valid */
290
291 /*
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.
295 */
296 heads = snewn(dc, int);
297 for (i = 0; i < dc; i++)
298 heads[i] = -1;
299
300 /*
301 * Set up the initial possibility lists by scanning the grid.
302 */
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;
308 }
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;
314 }
315
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");
326 }
327#endif
328
329 while (1) {
330 int done_something = FALSE;
331
332 /*
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.
337 *
338 * Each domino placement overlaps only six others, so we
339 * need not do serious set theory to work this out.
340 */
341 for (i = 0; i < dc; i++) {
342 int permset[6], permlen = 0, p;
343
344
345 if (heads[i] == -1) { /* no placement for this domino */
346 ret = 0; /* therefore puzzle is impossible */
347 goto done;
348 }
349 for (j = heads[i]; j >= 0; j = placements[j]) {
350 assert(placements[j] != -2);
351
352 if (j == heads[i]) {
353 permlen = find_overlaps(w, h, j, permset);
354 } else {
355 int tempset[6], templen, m, n, k;
356
357 templen = find_overlaps(w, h, j, tempset);
358
359 /*
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.
364 */
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];
371 }
372 permlen = n;
373 }
374 }
375 for (p = 0; p < permlen; p++) {
376 j = permset[p];
377 if (placements[j] != -2) {
378 int p1, p2, di;
379
380 done_something = TRUE;
381
382 /*
383 * Rule out this placement. First find what
384 * domino it is...
385 */
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
393
394 /*
395 * ... then walk that domino's placement list,
396 * removing this placement when we find it.
397 */
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];
406 }
407 placements[j] = -2;
408 }
409 }
410 }
411
412 /*
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.
417 */
418 for (i = 0; i < wh; i++) {
419 int list[4], k, n, adi;
420
421 x = i % w;
422 y = i / w;
423
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;
433
434 for (n = k = 0; k < j; k++)
435 if (placements[list[k]] >= -1)
436 list[n++] = list[k];
437
438 adi = -1;
439
440 for (j = 0; j < n; j++) {
441 int p1, p2, di;
442 k = list[j];
443
444 p1 = k / 2;
445 p2 = (k & 1) ? p1 + 1 : p1 + w;
446 di = DINDEX(grid[p1], grid[p2]);
447
448 if (adi == -1)
449 adi = di;
450 if (adi != di)
451 break;
452 }
453
454 if (j == n) {
455 int nn;
456
457 assert(adi >= 0);
458 /*
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.
464 */
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
474 /*
475 * Set all other placements on the list to
476 * impossible.
477 */
478 k = heads[adi];
479 while (k >= 0) {
480 int tmp = placements[k];
481 placements[k] = -2;
482 k = tmp;
483 }
484 /*
485 * Set up the new list.
486 */
487 heads[adi] = list[0];
488 for (k = 0; k < n; k++)
489 placements[list[k]] = (k+1 == n ? -1 : list[k+1]);
490 }
491 }
492 }
493
494 if (!done_something)
495 break;
496 }
497
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");
508 }
509#endif
510
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;
518
519 p1 = i / 2;
520 p2 = (i & 1) ? p1 + 1 : p1 + w;
521 di = DINDEX(grid[p1], grid[p2]);
522
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;
530 }
531 }
532 }
533
534 done:
535 /*
536 * Free working data.
537 */
538 sfree(placements);
539 sfree(heads);
540
541 return ret;
542}
543
544/* ----------------------------------------------------------------------
545 * End of solver code.
546 */
547
548static char *new_game_desc(game_params *params, random_state *rs,
549 char **aux, int interactive)
550{
551 int n = params->n, w = n+2, h = n+1, wh = w*h;
552 int *grid, *grid2, *list;
553 int i, j, k, m, todo, done, len;
554 char *ret;
555
556 /*
557 * Allocate space in which to lay the grid out.
558 */
559 grid = snewn(wh, int);
560 grid2 = snewn(wh, int);
561 list = snewn(2*wh, int);
562
8bba8910 563 /*
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.)
575 *
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).
582 *
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.
590 */
591
6c04c334 592 do {
593 /*
594 * To begin with, set grid[i] = i for all i to indicate
595 * that all squares are currently singletons. Later we'll
596 * set grid[i] to be the index of the other end of the
597 * domino on i.
598 */
599 for (i = 0; i < wh; i++)
600 grid[i] = i;
601
602 /*
603 * Now prepare a list of the possible domino locations. There
604 * are w*(h-1) possible vertical locations, and (w-1)*h
605 * horizontal ones, for a total of 2*wh - h - w.
606 *
607 * I'm going to denote the vertical domino placement with
608 * its top in square i as 2*i, and the horizontal one with
609 * its left half in square i as 2*i+1.
610 */
611 k = 0;
612 for (j = 0; j < h-1; j++)
613 for (i = 0; i < w; i++)
614 list[k++] = 2 * (j*w+i); /* vertical positions */
615 for (j = 0; j < h; j++)
616 for (i = 0; i < w-1; i++)
617 list[k++] = 2 * (j*w+i) + 1; /* horizontal positions */
618 assert(k == 2*wh - h - w);
619
620 /*
621 * Shuffle the list.
622 */
623 shuffle(list, k, sizeof(*list), rs);
624
625 /*
626 * Work down the shuffled list, placing a domino everywhere
627 * we can.
628 */
629 for (i = 0; i < k; i++) {
630 int horiz, xy, xy2;
631
632 horiz = list[i] % 2;
633 xy = list[i] / 2;
634 xy2 = xy + (horiz ? 1 : w);
635
636 if (grid[xy] == xy && grid[xy2] == xy2) {
637 /*
638 * We can place this domino. Do so.
639 */
640 grid[xy] = xy2;
641 grid[xy2] = xy;
642 }
643 }
644
645#ifdef GENERATION_DIAGNOSTICS
646 printf("generated initial layout\n");
647#endif
648
649 /*
650 * Now we've placed as many dominoes as we can immediately
651 * manage. There will be squares remaining, but they'll be
652 * singletons. So loop round and deal with the singletons
653 * two by two.
654 */
655 while (1) {
656#ifdef GENERATION_DIAGNOSTICS
657 for (j = 0; j < h; j++) {
658 for (i = 0; i < w; i++) {
659 int xy = j*w+i;
660 int v = grid[xy];
661 int c = (v == xy+1 ? '[' : v == xy-1 ? ']' :
662 v == xy+w ? 'n' : v == xy-w ? 'U' : '.');
663 putchar(c);
664 }
665 putchar('\n');
666 }
667 putchar('\n');
668#endif
669
670 /*
671 * Our strategy is:
672 *
673 * First find a singleton square.
674 *
675 * Then breadth-first search out from the starting
676 * square. From that square (and any others we reach on
677 * the way), examine all four neighbours of the square.
678 * If one is an end of a domino, we move to the _other_
679 * end of that domino before looking at neighbours
680 * again. When we encounter another singleton on this
681 * search, stop.
682 *
683 * This will give us a path of adjacent squares such
684 * that all but the two ends are covered in dominoes.
685 * So we can now shuffle every domino on the path up by
686 * one.
687 *
688 * (Chessboard colours are mathematically important
689 * here: we always end up pairing each singleton with a
690 * singleton of the other colour. However, we never
691 * have to track this manually, since it's
692 * automatically taken care of by the fact that we
693 * always make an even number of orthogonal moves.)
694 */
695 for (i = 0; i < wh; i++)
696 if (grid[i] == i)
697 break;
698 if (i == wh)
699 break; /* no more singletons; we're done. */
700
701#ifdef GENERATION_DIAGNOSTICS
702 printf("starting b.f.s. at singleton %d\n", i);
703#endif
704 /*
705 * Set grid2 to -1 everywhere. It will hold our
706 * distance-from-start values, and also our
707 * backtracking data, during the b.f.s.
708 */
709 for (j = 0; j < wh; j++)
710 grid2[j] = -1;
711 grid2[i] = 0; /* starting square has distance zero */
712
713 /*
714 * Start our to-do list of squares. It'll live in
715 * `list'; since the b.f.s can cover every square at
716 * most once there is no need for it to be circular.
717 * We'll just have two counters tracking the end of the
718 * list and the squares we've already dealt with.
719 */
720 done = 0;
721 todo = 1;
722 list[0] = i;
723
724 /*
725 * Now begin the b.f.s. loop.
726 */
727 while (done < todo) {
728 int d[4], nd, x, y;
729
730 i = list[done++];
731
732#ifdef GENERATION_DIAGNOSTICS
733 printf("b.f.s. iteration from %d\n", i);
734#endif
735 x = i % w;
736 y = i / w;
737 nd = 0;
738 if (x > 0)
739 d[nd++] = i - 1;
740 if (x+1 < w)
741 d[nd++] = i + 1;
742 if (y > 0)
743 d[nd++] = i - w;
744 if (y+1 < h)
745 d[nd++] = i + w;
746 /*
747 * To avoid directional bias, process the
748 * neighbours of this square in a random order.
749 */
750 shuffle(d, nd, sizeof(*d), rs);
751
752 for (j = 0; j < nd; j++) {
753 k = d[j];
754 if (grid[k] == k) {
755#ifdef GENERATION_DIAGNOSTICS
756 printf("found neighbouring singleton %d\n", k);
757#endif
758 grid2[k] = i;
759 break; /* found a target singleton! */
760 }
761
762 /*
763 * We're moving through a domino here, so we
764 * have two entries in grid2 to fill with
765 * useful data. In grid[k] - the square
766 * adjacent to where we came from - I'm going
767 * to put the address _of_ the square we came
768 * from. In the other end of the domino - the
769 * square from which we will continue the
770 * search - I'm going to put the distance.
771 */
772 m = grid[k];
773
774 if (grid2[m] < 0 || grid2[m] > grid2[i]+1) {
775#ifdef GENERATION_DIAGNOSTICS
776 printf("found neighbouring domino %d/%d\n", k, m);
777#endif
778 grid2[m] = grid2[i]+1;
779 grid2[k] = i;
780 /*
781 * And since we've now visited a new
782 * domino, add m to the to-do list.
783 */
784 assert(todo < wh);
785 list[todo++] = m;
786 }
787 }
788
789 if (j < nd) {
790 i = k;
791#ifdef GENERATION_DIAGNOSTICS
792 printf("terminating b.f.s. loop, i = %d\n", i);
793#endif
794 break;
795 }
796
797 i = -1; /* just in case the loop terminates */
798 }
799
800 /*
801 * We expect this b.f.s. to have found us a target
802 * square.
803 */
804 assert(i >= 0);
805
806 /*
807 * Now we can follow the trail back to our starting
808 * singleton, re-laying dominoes as we go.
809 */
810 while (1) {
811 j = grid2[i];
812 assert(j >= 0 && j < wh);
813 k = grid[j];
814
815 grid[i] = j;
816 grid[j] = i;
817#ifdef GENERATION_DIAGNOSTICS
818 printf("filling in domino %d/%d (next %d)\n", i, j, k);
819#endif
820 if (j == k)
821 break; /* we've reached the other singleton */
822 i = k;
823 }
824#ifdef GENERATION_DIAGNOSTICS
825 printf("fixup path completed\n");
826#endif
827 }
828
829 /*
830 * Now we have a complete layout covering the whole
831 * rectangle with dominoes. So shuffle the actual domino
832 * values and fill the rectangle with numbers.
833 */
834 k = 0;
835 for (i = 0; i <= params->n; i++)
836 for (j = 0; j <= i; j++) {
837 list[k++] = i;
838 list[k++] = j;
839 }
840 shuffle(list, k/2, 2*sizeof(*list), rs);
841 j = 0;
842 for (i = 0; i < wh; i++)
843 if (grid[i] > i) {
844 /* Optionally flip the domino round. */
8bba8910 845 int flip = -1;
846
847 if (params->unique) {
848 int t1, t2;
849 /*
850 * If we're after a unique solution, we can do
851 * something here to improve the chances. If
852 * we're placing a domino so that it forms a
853 * 2x2 rectangle with one we've already placed,
854 * and if that domino and this one share a
855 * number, we can try not to put them so that
856 * the identical numbers are diagonally
857 * separated, because that automatically causes
858 * non-uniqueness:
859 *
860 * +---+ +-+-+
861 * |2 3| |2|3|
862 * +---+ -> | | |
863 * |4 2| |4|2|
864 * +---+ +-+-+
865 */
866 t1 = i;
867 t2 = grid[i];
868 if (t2 == t1 + w) { /* this domino is vertical */
869 if (t1 % w > 0 &&/* and not on the left hand edge */
870 grid[t1-1] == t2-1 &&/* alongside one to left */
871 (grid2[t1-1] == list[j] || /* and has a number */
872 grid2[t1-1] == list[j+1] || /* in common */
873 grid2[t2-1] == list[j] ||
874 grid2[t2-1] == list[j+1])) {
875 if (grid2[t1-1] == list[j] ||
876 grid2[t2-1] == list[j+1])
877 flip = 0;
878 else
879 flip = 1;
880 }
881 } else { /* this domino is horizontal */
882 if (t1 / w > 0 &&/* and not on the top edge */
883 grid[t1-w] == t2-w &&/* alongside one above */
884 (grid2[t1-w] == list[j] || /* and has a number */
885 grid2[t1-w] == list[j+1] || /* in common */
886 grid2[t2-w] == list[j] ||
887 grid2[t2-w] == list[j+1])) {
888 if (grid2[t1-w] == list[j] ||
889 grid2[t2-w] == list[j+1])
890 flip = 0;
891 else
892 flip = 1;
893 }
894 }
895 }
896
897 if (flip < 0)
898 flip = random_upto(rs, 2);
899
6c04c334 900 grid2[i] = list[j + flip];
901 grid2[grid[i]] = list[j + 1 - flip];
902 j += 2;
903 }
904 assert(j == k);
905 } while (params->unique && solver(w, h, n, grid2, NULL) > 1);
906
907#ifdef GENERATION_DIAGNOSTICS
908 for (j = 0; j < h; j++) {
909 for (i = 0; i < w; i++) {
910 putchar('0' + grid2[j*w+i]);
911 }
912 putchar('\n');
913 }
914 putchar('\n');
915#endif
916
917 /*
918 * Encode the resulting game state.
919 *
920 * Our encoding is a string of digits. Any number greater than
921 * 9 is represented by a decimal integer within square
922 * brackets. We know there are n+2 of every number (it's paired
923 * with each number from 0 to n inclusive, and one of those is
924 * itself so that adds another occurrence), so we can work out
925 * the string length in advance.
926 */
927
928 /*
929 * To work out the total length of the decimal encodings of all
930 * the numbers from 0 to n inclusive:
931 * - every number has a units digit; total is n+1.
932 * - all numbers above 9 have a tens digit; total is max(n+1-10,0).
933 * - all numbers above 99 have a hundreds digit; total is max(n+1-100,0).
934 * - and so on.
935 */
936 len = n+1;
937 for (i = 10; i <= n; i *= 10)
938 len += max(n + 1 - i, 0);
939 /* Now add two square brackets for each number above 9. */
940 len += 2 * max(n + 1 - 10, 0);
941 /* And multiply by n+2 for the repeated occurrences of each number. */
942 len *= n+2;
943
944 /*
945 * Now actually encode the string.
946 */
947 ret = snewn(len+1, char);
948 j = 0;
949 for (i = 0; i < wh; i++) {
950 k = grid2[i];
951 if (k < 10)
952 ret[j++] = '0' + k;
953 else
954 j += sprintf(ret+j, "[%d]", k);
955 assert(j <= len);
956 }
957 assert(j == len);
958 ret[j] = '\0';
959
960 /*
961 * Encode the solved state as an aux_info.
962 */
963 {
964 char *auxinfo = snewn(wh+1, char);
965
966 for (i = 0; i < wh; i++) {
967 int v = grid[i];
968 auxinfo[i] = (v == i+1 ? 'L' : v == i-1 ? 'R' :
969 v == i+w ? 'T' : v == i-w ? 'B' : '.');
970 }
971 auxinfo[wh] = '\0';
972
973 *aux = auxinfo;
974 }
975
976 sfree(list);
977 sfree(grid2);
978 sfree(grid);
979
980 return ret;
981}
982
983static char *validate_desc(game_params *params, char *desc)
984{
985 int n = params->n, w = n+2, h = n+1, wh = w*h;
986 int *occurrences;
987 int i, j;
988 char *ret;
989
990 ret = NULL;
991 occurrences = snewn(n+1, int);
992 for (i = 0; i <= n; i++)
993 occurrences[i] = 0;
994
995 for (i = 0; i < wh; i++) {
996 if (!*desc) {
997 ret = ret ? ret : "Game description is too short";
998 } else {
999 if (*desc >= '0' && *desc <= '9')
1000 j = *desc++ - '0';
1001 else if (*desc == '[') {
1002 desc++;
1003 j = atoi(desc);
1004 while (*desc && isdigit((unsigned char)*desc)) desc++;
1005 if (*desc != ']')
1006 ret = ret ? ret : "Missing ']' in game description";
1007 else
1008 desc++;
1009 } else {
1010 j = -1;
1011 ret = ret ? ret : "Invalid syntax in game description";
1012 }
1013 if (j < 0 || j > n)
1014 ret = ret ? ret : "Number out of range in game description";
1015 else
1016 occurrences[j]++;
1017 }
1018 }
1019
1020 if (*desc)
1021 ret = ret ? ret : "Game description is too long";
1022
1023 if (!ret) {
1024 for (i = 0; i <= n; i++)
1025 if (occurrences[i] != n+2)
1026 ret = "Incorrect number balance in game description";
1027 }
1028
1029 sfree(occurrences);
1030
1031 return ret;
1032}
1033
dafd6cf6 1034static game_state *new_game(midend *me, game_params *params, char *desc)
6c04c334 1035{
1036 int n = params->n, w = n+2, h = n+1, wh = w*h;
1037 game_state *state = snew(game_state);
1038 int i, j;
1039
1040 state->params = *params;
1041 state->w = w;
1042 state->h = h;
1043
1044 state->grid = snewn(wh, int);
1045 for (i = 0; i < wh; i++)
1046 state->grid[i] = i;
1047
1048 state->edges = snewn(wh, unsigned short);
1049 for (i = 0; i < wh; i++)
1050 state->edges[i] = 0;
1051
1052 state->numbers = snew(struct game_numbers);
1053 state->numbers->refcount = 1;
1054 state->numbers->numbers = snewn(wh, int);
1055
1056 for (i = 0; i < wh; i++) {
1057 assert(*desc);
1058 if (*desc >= '0' && *desc <= '9')
1059 j = *desc++ - '0';
1060 else {
1061 assert(*desc == '[');
1062 desc++;
1063 j = atoi(desc);
1064 while (*desc && isdigit((unsigned char)*desc)) desc++;
1065 assert(*desc == ']');
1066 desc++;
1067 }
1068 assert(j >= 0 && j <= n);
1069 state->numbers->numbers[i] = j;
1070 }
1071
1072 state->completed = state->cheated = FALSE;
1073
1074 return state;
1075}
1076
1077static game_state *dup_game(game_state *state)
1078{
1079 int n = state->params.n, w = n+2, h = n+1, wh = w*h;
1080 game_state *ret = snew(game_state);
1081
1082 ret->params = state->params;
1083 ret->w = state->w;
1084 ret->h = state->h;
1085 ret->grid = snewn(wh, int);
1086 memcpy(ret->grid, state->grid, wh * sizeof(int));
1087 ret->edges = snewn(wh, unsigned short);
1088 memcpy(ret->edges, state->edges, wh * sizeof(unsigned short));
1089 ret->numbers = state->numbers;
1090 ret->numbers->refcount++;
1091 ret->completed = state->completed;
1092 ret->cheated = state->cheated;
1093
1094 return ret;
1095}
1096
1097static void free_game(game_state *state)
1098{
1099 sfree(state->grid);
963efbc8 1100 sfree(state->edges);
6c04c334 1101 if (--state->numbers->refcount <= 0) {
1102 sfree(state->numbers->numbers);
1103 sfree(state->numbers);
1104 }
1105 sfree(state);
1106}
1107
1108static char *solve_game(game_state *state, game_state *currstate,
1109 char *aux, char **error)
1110{
1111 int n = state->params.n, w = n+2, h = n+1, wh = w*h;
1112 int *placements;
1113 char *ret;
1114 int retlen, retsize;
1115 int i, v;
1116 char buf[80];
1117 int extra;
1118
1119 if (aux) {
1120 retsize = 256;
1121 ret = snewn(retsize, char);
1122 retlen = sprintf(ret, "S");
1123
1124 for (i = 0; i < wh; i++) {
1125 if (aux[i] == 'L')
1126 extra = sprintf(buf, ";D%d,%d", i, i+1);
1127 else if (aux[i] == 'T')
1128 extra = sprintf(buf, ";D%d,%d", i, i+w);
1129 else
1130 continue;
1131
1132 if (retlen + extra + 1 >= retsize) {
1133 retsize = retlen + extra + 256;
1134 ret = sresize(ret, retsize, char);
1135 }
1136 strcpy(ret + retlen, buf);
1137 retlen += extra;
1138 }
1139
1140 } else {
1141
1142 placements = snewn(wh*2, int);
1143 for (i = 0; i < wh*2; i++)
1144 placements[i] = -3;
1145 solver(w, h, n, state->numbers->numbers, placements);
1146
1147 /*
1148 * First make a pass putting in edges for -1, then make a pass
1149 * putting in dominoes for +1.
1150 */
1151 retsize = 256;
1152 ret = snewn(retsize, char);
1153 retlen = sprintf(ret, "S");
1154
1155 for (v = -1; v <= +1; v += 2)
1156 for (i = 0; i < wh*2; i++)
1157 if (placements[i] == v) {
1158 int p1 = i / 2;
1159 int p2 = (i & 1) ? p1+1 : p1+w;
1160
1161 extra = sprintf(buf, ";%c%d,%d",
963efbc8 1162 (int)(v==-1 ? 'E' : 'D'), p1, p2);
6c04c334 1163
1164 if (retlen + extra + 1 >= retsize) {
1165 retsize = retlen + extra + 256;
1166 ret = sresize(ret, retsize, char);
1167 }
1168 strcpy(ret + retlen, buf);
1169 retlen += extra;
1170 }
1171
1172 sfree(placements);
1173 }
1174
1175 return ret;
1176}
1177
fa3abef5 1178static int game_can_format_as_text_now(game_params *params)
1179{
1180 return TRUE;
1181}
1182
6c04c334 1183static char *game_text_format(game_state *state)
1184{
1185 return NULL;
1186}
1187
1188static game_ui *new_ui(game_state *state)
1189{
1190 return NULL;
1191}
1192
1193static void free_ui(game_ui *ui)
1194{
1195}
1196
1197static char *encode_ui(game_ui *ui)
1198{
1199 return NULL;
1200}
1201
1202static void decode_ui(game_ui *ui, char *encoding)
1203{
1204}
1205
1206static void game_changed_state(game_ui *ui, game_state *oldstate,
1207 game_state *newstate)
1208{
1209}
1210
1211#define PREFERRED_TILESIZE 32
1212#define TILESIZE (ds->tilesize)
1213#define BORDER (TILESIZE * 3 / 4)
1214#define DOMINO_GUTTER (TILESIZE / 16)
1215#define DOMINO_RADIUS (TILESIZE / 8)
1216#define DOMINO_COFFSET (DOMINO_GUTTER + DOMINO_RADIUS)
1217
1218#define COORD(x) ( (x) * TILESIZE + BORDER )
1219#define FROMCOORD(x) ( ((x) - BORDER + TILESIZE) / TILESIZE - 1 )
1220
1221struct game_drawstate {
1222 int started;
1223 int w, h, tilesize;
1224 unsigned long *visible;
1225};
1226
1227static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds,
1228 int x, int y, int button)
1229{
1230 int w = state->w, h = state->h;
1231 char buf[80];
1232
1233 /*
1234 * A left-click between two numbers toggles a domino covering
1235 * them. A right-click toggles an edge.
1236 */
1237 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
1238 int tx = FROMCOORD(x), ty = FROMCOORD(y), t = ty*w+tx;
1239 int dx, dy;
1240 int d1, d2;
1241
1242 if (tx < 0 || tx >= w || ty < 0 || ty >= h)
1243 return NULL;
1244
1245 /*
1246 * Now we know which square the click was in, decide which
1247 * edge of the square it was closest to.
1248 */
1249 dx = 2 * (x - COORD(tx)) - TILESIZE;
1250 dy = 2 * (y - COORD(ty)) - TILESIZE;
1251
1252 if (abs(dx) > abs(dy) && dx < 0 && tx > 0)
1253 d1 = t - 1, d2 = t; /* clicked in right side of domino */
1254 else if (abs(dx) > abs(dy) && dx > 0 && tx+1 < w)
1255 d1 = t, d2 = t + 1; /* clicked in left side of domino */
1256 else if (abs(dy) > abs(dx) && dy < 0 && ty > 0)
1257 d1 = t - w, d2 = t; /* clicked in bottom half of domino */
1258 else if (abs(dy) > abs(dx) && dy > 0 && ty+1 < h)
1259 d1 = t, d2 = t + w; /* clicked in top half of domino */
1260 else
1261 return NULL;
1262
1263 /*
1264 * We can't mark an edge next to any domino.
1265 */
1266 if (button == RIGHT_BUTTON &&
1267 (state->grid[d1] != d1 || state->grid[d2] != d2))
1268 return NULL;
1269
963efbc8 1270 sprintf(buf, "%c%d,%d", (int)(button == RIGHT_BUTTON ? 'E' : 'D'), d1, d2);
6c04c334 1271 return dupstr(buf);
1272 }
1273
1274 return NULL;
1275}
1276
1277static game_state *execute_move(game_state *state, char *move)
1278{
1279 int n = state->params.n, w = n+2, h = n+1, wh = w*h;
1280 int d1, d2, d3, p;
1281 game_state *ret = dup_game(state);
1282
1283 while (*move) {
1284 if (move[0] == 'S') {
1285 int i;
1286
1287 ret->cheated = TRUE;
1288
1289 /*
1290 * Clear the existing edges and domino placements. We
1291 * expect the S to be followed by other commands.
1292 */
1293 for (i = 0; i < wh; i++) {
1294 ret->grid[i] = i;
1295 ret->edges[i] = 0;
1296 }
1297 move++;
1298 } else if (move[0] == 'D' &&
1299 sscanf(move+1, "%d,%d%n", &d1, &d2, &p) == 2 &&
1300 d1 >= 0 && d1 < wh && d2 >= 0 && d2 < wh && d1 < d2) {
1301
1302 /*
1303 * Toggle domino presence between d1 and d2.
1304 */
1305 if (ret->grid[d1] == d2) {
1306 assert(ret->grid[d2] == d1);
1307 ret->grid[d1] = d1;
1308 ret->grid[d2] = d2;
1309 } else {
1310 /*
1311 * Erase any dominoes that might overlap the new one.
1312 */
1313 d3 = ret->grid[d1];
1314 if (d3 != d1)
1315 ret->grid[d3] = d3;
1316 d3 = ret->grid[d2];
1317 if (d3 != d2)
1318 ret->grid[d3] = d3;
1319 /*
1320 * Place the new one.
1321 */
1322 ret->grid[d1] = d2;
1323 ret->grid[d2] = d1;
1324
1325 /*
1326 * Destroy any edges lurking around it.
1327 */
1328 if (ret->edges[d1] & EDGE_L) {
1329 assert(d1 - 1 >= 0);
1330 ret->edges[d1 - 1] &= ~EDGE_R;
1331 }
1332 if (ret->edges[d1] & EDGE_R) {
1333 assert(d1 + 1 < wh);
1334 ret->edges[d1 + 1] &= ~EDGE_L;
1335 }
1336 if (ret->edges[d1] & EDGE_T) {
1337 assert(d1 - w >= 0);
1338 ret->edges[d1 - w] &= ~EDGE_B;
1339 }
1340 if (ret->edges[d1] & EDGE_B) {
1341 assert(d1 + 1 < wh);
1342 ret->edges[d1 + w] &= ~EDGE_T;
1343 }
1344 ret->edges[d1] = 0;
1345 if (ret->edges[d2] & EDGE_L) {
1346 assert(d2 - 1 >= 0);
1347 ret->edges[d2 - 1] &= ~EDGE_R;
1348 }
1349 if (ret->edges[d2] & EDGE_R) {
1350 assert(d2 + 1 < wh);
1351 ret->edges[d2 + 1] &= ~EDGE_L;
1352 }
1353 if (ret->edges[d2] & EDGE_T) {
1354 assert(d2 - w >= 0);
1355 ret->edges[d2 - w] &= ~EDGE_B;
1356 }
1357 if (ret->edges[d2] & EDGE_B) {
1358 assert(d2 + 1 < wh);
1359 ret->edges[d2 + w] &= ~EDGE_T;
1360 }
1361 ret->edges[d2] = 0;
1362 }
1363
1364 move += p+1;
1365 } else if (move[0] == 'E' &&
1366 sscanf(move+1, "%d,%d%n", &d1, &d2, &p) == 2 &&
1367 d1 >= 0 && d1 < wh && d2 >= 0 && d2 < wh && d1 < d2 &&
1368 ret->grid[d1] == d1 && ret->grid[d2] == d2) {
1369
1370 /*
1371 * Toggle edge presence between d1 and d2.
1372 */
1373 if (d2 == d1 + 1) {
1374 ret->edges[d1] ^= EDGE_R;
1375 ret->edges[d2] ^= EDGE_L;
1376 } else {
1377 ret->edges[d1] ^= EDGE_B;
1378 ret->edges[d2] ^= EDGE_T;
1379 }
1380
1381 move += p+1;
1382 } else {
1383 free_game(ret);
1384 return NULL;
1385 }
1386
1387 if (*move) {
1388 if (*move != ';') {
1389 free_game(ret);
1390 return NULL;
1391 }
1392 move++;
1393 }
1394 }
1395
1396 /*
1397 * After modifying the grid, check completion.
1398 */
1399 if (!ret->completed) {
1400 int i, ok = 0;
1401 unsigned char *used = snewn(TRI(n+1), unsigned char);
1402
1403 memset(used, 0, TRI(n+1));
1404 for (i = 0; i < wh; i++)
1405 if (ret->grid[i] > i) {
1406 int n1, n2, di;
1407
1408 n1 = ret->numbers->numbers[i];
1409 n2 = ret->numbers->numbers[ret->grid[i]];
1410
1411 di = DINDEX(n1, n2);
1412 assert(di >= 0 && di < TRI(n+1));
1413
1414 if (!used[di]) {
1415 used[di] = 1;
1416 ok++;
1417 }
1418 }
1419
1420 sfree(used);
1421 if (ok == DCOUNT(n))
1422 ret->completed = TRUE;
1423 }
1424
1425 return ret;
1426}
1427
1428/* ----------------------------------------------------------------------
1429 * Drawing routines.
1430 */
1431
1432static void game_compute_size(game_params *params, int tilesize,
1433 int *x, int *y)
1434{
1435 int n = params->n, w = n+2, h = n+1;
1436
1437 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1438 struct { int tilesize; } ads, *ds = &ads;
1439 ads.tilesize = tilesize;
1440
1441 *x = w * TILESIZE + 2*BORDER;
1442 *y = h * TILESIZE + 2*BORDER;
1443}
1444
dafd6cf6 1445static void game_set_size(drawing *dr, game_drawstate *ds,
1446 game_params *params, int tilesize)
6c04c334 1447{
1448 ds->tilesize = tilesize;
1449}
1450
8266f3fc 1451static float *game_colours(frontend *fe, int *ncolours)
6c04c334 1452{
1453 float *ret = snewn(3 * NCOLOURS, float);
1454
1455 frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
1456
1457 ret[COL_TEXT * 3 + 0] = 0.0F;
1458 ret[COL_TEXT * 3 + 1] = 0.0F;
1459 ret[COL_TEXT * 3 + 2] = 0.0F;
1460
1461 ret[COL_DOMINO * 3 + 0] = 0.0F;
1462 ret[COL_DOMINO * 3 + 1] = 0.0F;
1463 ret[COL_DOMINO * 3 + 2] = 0.0F;
1464
1465 ret[COL_DOMINOCLASH * 3 + 0] = 0.5F;
1466 ret[COL_DOMINOCLASH * 3 + 1] = 0.0F;
1467 ret[COL_DOMINOCLASH * 3 + 2] = 0.0F;
1468
1469 ret[COL_DOMINOTEXT * 3 + 0] = 1.0F;
1470 ret[COL_DOMINOTEXT * 3 + 1] = 1.0F;
1471 ret[COL_DOMINOTEXT * 3 + 2] = 1.0F;
1472
1473 ret[COL_EDGE * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 2 / 3;
1474 ret[COL_EDGE * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 2 / 3;
1475 ret[COL_EDGE * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] * 2 / 3;
1476
1477 *ncolours = NCOLOURS;
1478 return ret;
1479}
1480
dafd6cf6 1481static game_drawstate *game_new_drawstate(drawing *dr, game_state *state)
6c04c334 1482{
1483 struct game_drawstate *ds = snew(struct game_drawstate);
1484 int i;
1485
1486 ds->started = FALSE;
1487 ds->w = state->w;
1488 ds->h = state->h;
1489 ds->visible = snewn(ds->w * ds->h, unsigned long);
1490 ds->tilesize = 0; /* not decided yet */
1491 for (i = 0; i < ds->w * ds->h; i++)
1492 ds->visible[i] = 0xFFFF;
1493
1494 return ds;
1495}
1496
dafd6cf6 1497static void game_free_drawstate(drawing *dr, game_drawstate *ds)
6c04c334 1498{
1499 sfree(ds->visible);
1500 sfree(ds);
1501}
1502
1503enum {
1504 TYPE_L,
1505 TYPE_R,
1506 TYPE_T,
1507 TYPE_B,
1508 TYPE_BLANK,
1509 TYPE_MASK = 0x0F
1510};
1511
dafd6cf6 1512static void draw_tile(drawing *dr, game_drawstate *ds, game_state *state,
6c04c334 1513 int x, int y, int type)
1514{
1515 int w = state->w /*, h = state->h */;
1516 int cx = COORD(x), cy = COORD(y);
1517 int nc;
1518 char str[80];
1519 int flags;
1520
dafd6cf6 1521 draw_rect(dr, cx, cy, TILESIZE, TILESIZE, COL_BACKGROUND);
6c04c334 1522
1523 flags = type &~ TYPE_MASK;
1524 type &= TYPE_MASK;
1525
1526 if (type != TYPE_BLANK) {
1527 int i, bg;
1528
1529 /*
1530 * Draw one end of a domino. This is composed of:
1531 *
1532 * - two filled circles (rounded corners)
1533 * - two rectangles
1534 * - a slight shift in the number
1535 */
1536
1537 if (flags & 0x80)
1538 bg = COL_DOMINOCLASH;
1539 else
1540 bg = COL_DOMINO;
1541 nc = COL_DOMINOTEXT;
1542
1543 if (flags & 0x40) {
1544 int tmp = nc;
1545 nc = bg;
1546 bg = tmp;
1547 }
1548
1549 if (type == TYPE_L || type == TYPE_T)
dafd6cf6 1550 draw_circle(dr, cx+DOMINO_COFFSET, cy+DOMINO_COFFSET,
6c04c334 1551 DOMINO_RADIUS, bg, bg);
1552 if (type == TYPE_R || type == TYPE_T)
dafd6cf6 1553 draw_circle(dr, cx+TILESIZE-1-DOMINO_COFFSET, cy+DOMINO_COFFSET,
6c04c334 1554 DOMINO_RADIUS, bg, bg);
1555 if (type == TYPE_L || type == TYPE_B)
dafd6cf6 1556 draw_circle(dr, cx+DOMINO_COFFSET, cy+TILESIZE-1-DOMINO_COFFSET,
6c04c334 1557 DOMINO_RADIUS, bg, bg);
1558 if (type == TYPE_R || type == TYPE_B)
dafd6cf6 1559 draw_circle(dr, cx+TILESIZE-1-DOMINO_COFFSET,
6c04c334 1560 cy+TILESIZE-1-DOMINO_COFFSET,
1561 DOMINO_RADIUS, bg, bg);
1562
1563 for (i = 0; i < 2; i++) {
1564 int x1, y1, x2, y2;
1565
1566 x1 = cx + (i ? DOMINO_GUTTER : DOMINO_COFFSET);
1567 y1 = cy + (i ? DOMINO_COFFSET : DOMINO_GUTTER);
1568 x2 = cx + TILESIZE-1 - (i ? DOMINO_GUTTER : DOMINO_COFFSET);
1569 y2 = cy + TILESIZE-1 - (i ? DOMINO_COFFSET : DOMINO_GUTTER);
1570 if (type == TYPE_L)
dafd6cf6 1571 x2 = cx + TILESIZE + TILESIZE/16;
6c04c334 1572 else if (type == TYPE_R)
dafd6cf6 1573 x1 = cx - TILESIZE/16;
6c04c334 1574 else if (type == TYPE_T)
dafd6cf6 1575 y2 = cy + TILESIZE + TILESIZE/16;
6c04c334 1576 else if (type == TYPE_B)
dafd6cf6 1577 y1 = cy - TILESIZE/16;
6c04c334 1578
dafd6cf6 1579 draw_rect(dr, x1, y1, x2-x1+1, y2-y1+1, bg);
6c04c334 1580 }
1581 } else {
1582 if (flags & EDGE_T)
dafd6cf6 1583 draw_rect(dr, cx+DOMINO_GUTTER, cy,
6c04c334 1584 TILESIZE-2*DOMINO_GUTTER, 1, COL_EDGE);
1585 if (flags & EDGE_B)
dafd6cf6 1586 draw_rect(dr, cx+DOMINO_GUTTER, cy+TILESIZE-1,
6c04c334 1587 TILESIZE-2*DOMINO_GUTTER, 1, COL_EDGE);
1588 if (flags & EDGE_L)
dafd6cf6 1589 draw_rect(dr, cx, cy+DOMINO_GUTTER,
6c04c334 1590 1, TILESIZE-2*DOMINO_GUTTER, COL_EDGE);
1591 if (flags & EDGE_R)
dafd6cf6 1592 draw_rect(dr, cx+TILESIZE-1, cy+DOMINO_GUTTER,
6c04c334 1593 1, TILESIZE-2*DOMINO_GUTTER, COL_EDGE);
1594 nc = COL_TEXT;
1595 }
1596
1597 sprintf(str, "%d", state->numbers->numbers[y*w+x]);
dafd6cf6 1598 draw_text(dr, cx+TILESIZE/2, cy+TILESIZE/2, FONT_VARIABLE, TILESIZE/2,
6c04c334 1599 ALIGN_HCENTRE | ALIGN_VCENTRE, nc, str);
1600
dafd6cf6 1601 draw_update(dr, cx, cy, TILESIZE, TILESIZE);
6c04c334 1602}
1603
dafd6cf6 1604static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
6c04c334 1605 game_state *state, int dir, game_ui *ui,
1606 float animtime, float flashtime)
1607{
1608 int n = state->params.n, w = state->w, h = state->h, wh = w*h;
1609 int x, y, i;
1610 unsigned char *used;
1611
1612 if (!ds->started) {
1613 int pw, ph;
1614 game_compute_size(&state->params, TILESIZE, &pw, &ph);
dafd6cf6 1615 draw_rect(dr, 0, 0, pw, ph, COL_BACKGROUND);
1616 draw_update(dr, 0, 0, pw, ph);
6c04c334 1617 ds->started = TRUE;
1618 }
1619
1620 /*
1621 * See how many dominoes of each type there are, so we can
1622 * highlight clashes in red.
1623 */
1624 used = snewn(TRI(n+1), unsigned char);
1625 memset(used, 0, TRI(n+1));
1626 for (i = 0; i < wh; i++)
1627 if (state->grid[i] > i) {
1628 int n1, n2, di;
1629
1630 n1 = state->numbers->numbers[i];
1631 n2 = state->numbers->numbers[state->grid[i]];
1632
1633 di = DINDEX(n1, n2);
1634 assert(di >= 0 && di < TRI(n+1));
1635
1636 if (used[di] < 2)
1637 used[di]++;
1638 }
1639
1640 for (y = 0; y < h; y++)
1641 for (x = 0; x < w; x++) {
1642 int n = y*w+x;
1643 int n1, n2, di;
1644 unsigned long c;
1645
1646 if (state->grid[n] == n-1)
1647 c = TYPE_R;
1648 else if (state->grid[n] == n+1)
1649 c = TYPE_L;
1650 else if (state->grid[n] == n-w)
1651 c = TYPE_B;
1652 else if (state->grid[n] == n+w)
1653 c = TYPE_T;
1654 else
1655 c = TYPE_BLANK;
1656
1657 if (c != TYPE_BLANK) {
1658 n1 = state->numbers->numbers[n];
1659 n2 = state->numbers->numbers[state->grid[n]];
1660 di = DINDEX(n1, n2);
1661 if (used[di] > 1)
1662 c |= 0x80; /* highlight a clash */
1663 } else {
1664 c |= state->edges[n];
1665 }
1666
1667 if (flashtime != 0)
1668 c |= 0x40; /* we're flashing */
1669
1670 if (ds->visible[n] != c) {
dafd6cf6 1671 draw_tile(dr, ds, state, x, y, c);
6c04c334 1672 ds->visible[n] = c;
1673 }
1674 }
1675
1676 sfree(used);
1677}
1678
1679static float game_anim_length(game_state *oldstate, game_state *newstate,
1680 int dir, game_ui *ui)
1681{
1682 return 0.0F;
1683}
1684
1685static float game_flash_length(game_state *oldstate, game_state *newstate,
1686 int dir, game_ui *ui)
1687{
1688 if (!oldstate->completed && newstate->completed &&
1689 !oldstate->cheated && !newstate->cheated)
1690 return FLASH_TIME;
1691 return 0.0F;
1692}
1693
6c04c334 1694static int game_timing_state(game_state *state, game_ui *ui)
1695{
1696 return TRUE;
1697}
1698
dafd6cf6 1699static void game_print_size(game_params *params, float *x, float *y)
1700{
1701 int pw, ph;
1702
1703 /*
1704 * I'll use 6mm squares by default.
1705 */
1706 game_compute_size(params, 600, &pw, &ph);
1707 *x = pw / 100.0;
1708 *y = ph / 100.0;
1709}
1710
1711static void game_print(drawing *dr, game_state *state, int tilesize)
1712{
1713 int w = state->w, h = state->h;
1714 int c, x, y;
1715
1716 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1717 game_drawstate ads, *ds = &ads;
4413ef0f 1718 game_set_size(dr, ds, NULL, tilesize);
dafd6cf6 1719
1720 c = print_mono_colour(dr, 1); assert(c == COL_BACKGROUND);
1721 c = print_mono_colour(dr, 0); assert(c == COL_TEXT);
1722 c = print_mono_colour(dr, 0); assert(c == COL_DOMINO);
1723 c = print_mono_colour(dr, 0); assert(c == COL_DOMINOCLASH);
1724 c = print_mono_colour(dr, 1); assert(c == COL_DOMINOTEXT);
1725 c = print_mono_colour(dr, 0); assert(c == COL_EDGE);
1726
1727 for (y = 0; y < h; y++)
1728 for (x = 0; x < w; x++) {
1729 int n = y*w+x;
1730 unsigned long c;
1731
1732 if (state->grid[n] == n-1)
1733 c = TYPE_R;
1734 else if (state->grid[n] == n+1)
1735 c = TYPE_L;
1736 else if (state->grid[n] == n-w)
1737 c = TYPE_B;
1738 else if (state->grid[n] == n+w)
1739 c = TYPE_T;
1740 else
1741 c = TYPE_BLANK;
1742
1743 draw_tile(dr, ds, state, x, y, c);
1744 }
1745}
1746
6c04c334 1747#ifdef COMBINED
1748#define thegame dominosa
1749#endif
1750
1751const struct game thegame = {
750037d7 1752 "Dominosa", "games.dominosa", "dominosa",
6c04c334 1753 default_params,
1754 game_fetch_preset,
1755 decode_params,
1756 encode_params,
1757 free_params,
1758 dup_params,
1759 TRUE, game_configure, custom_params,
1760 validate_params,
1761 new_game_desc,
1762 validate_desc,
1763 new_game,
1764 dup_game,
1765 free_game,
1766 TRUE, solve_game,
fa3abef5 1767 FALSE, game_can_format_as_text_now, game_text_format,
6c04c334 1768 new_ui,
1769 free_ui,
1770 encode_ui,
1771 decode_ui,
1772 game_changed_state,
1773 interpret_move,
1774 execute_move,
1775 PREFERRED_TILESIZE, game_compute_size, game_set_size,
1776 game_colours,
1777 game_new_drawstate,
1778 game_free_drawstate,
1779 game_redraw,
1780 game_anim_length,
1781 game_flash_length,
dafd6cf6 1782 TRUE, FALSE, game_print_size, game_print,
ac9f41c4 1783 FALSE, /* wants_statusbar */
6c04c334 1784 FALSE, game_timing_state,
2705d374 1785 0, /* flags */
6c04c334 1786};