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