-
Notifications
You must be signed in to change notification settings - Fork 6
/
dominosa.c
3531 lines (3040 loc) · 109 KB
/
dominosa.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* dominosa.c: Domino jigsaw puzzle. Aim to place one of every
* possible domino within a rectangle in such a way that the number
* on each square matches the provided clue.
*/
/*
* Further possible deduction types in the solver:
*
* * possibly an advanced form of deduce_parity via 2-connectedness.
* We currently deal with areas of the graph with exactly one way
* in and out; but if you have an area with exactly _two_ routes in
* and out of it, then you can at least decide on the _relative_
* parity of the two (either 'these two edges both bisect dominoes
* or neither do', or 'exactly one of these edges bisects a
* domino'). And occasionally that can be enough to let you rule
* out one of the two remaining choices.
* + For example, if both those edges bisect a domino, then those
* two dominoes would also be both the same.
* + Or perhaps between them they rule out all possibilities for
* some other square.
* + Or perhaps they themselves would be duplicates!
* + Or perhaps, on purely geometric grounds, they would box in a
* square to the point where it ended up having to be an
* isolated singleton.
* + The tricky part of this is how you do the graph theory.
* Perhaps a modified form of Tarjan's bridge-finding algorithm
* would work, but I haven't thought through the details.
*
* * possibly an advanced version of set analysis which doesn't have
* to start from squares all having the same number? For example,
* if you have three mutually non-adjacent squares labelled 1,2,3
* such that the numbers adjacent to each are precisely the other
* two, then set analysis can work just fine in principle, and
* tells you that those three squares must overlap the three
* dominoes 1-2, 2-3 and 1-3 in some order, so you can rule out any
* placements of those elsewhere.
* + the difficulty with this is how you avoid it going painfully
* exponential-time. You can't iterate over all the subsets, so
* you'd need some kind of more sophisticated directed search.
* + and the adjacency allowance has to be similarly accounted
* for, which could get tricky to keep track of.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <ctype.h>
#include <math.h>
#include "puzzles.h"
/* nth triangular number */
#define TRI(n) ( (n) * ((n) + 1) / 2 )
/* number of dominoes for value n */
#define DCOUNT(n) TRI((n)+1)
/* map a pair of numbers to a unique domino index from 0 upwards. */
#define DINDEX(n1,n2) ( TRI(max(n1,n2)) + min(n1,n2) )
#define FLASH_TIME 0.13F
/*
* Difficulty levels. I do some macro ickery here to ensure that my
* enum and the various forms of my name list always match up.
*/
#define DIFFLIST(X) \
X(TRIVIAL,Trivial,t) \
X(BASIC,Basic,b) \
X(HARD,Hard,h) \
X(EXTREME,Extreme,e) \
X(AMBIGUOUS,Ambiguous,a) \
/* end of list */
#define ENUM(upper,title,lower) DIFF_ ## upper,
#define TITLE(upper,title,lower) #title,
#define ENCODE(upper,title,lower) #lower
#define CONFIG(upper,title,lower) ":" #title
enum { DIFFLIST(ENUM) DIFFCOUNT };
static char const *const dominosa_diffnames[] = { DIFFLIST(TITLE) };
static char const dominosa_diffchars[] = DIFFLIST(ENCODE);
#define DIFFCONFIG DIFFLIST(CONFIG)
enum {
COL_BACKGROUND,
COL_TEXT,
COL_DOMINO,
COL_DOMINOCLASH,
COL_DOMINOTEXT,
COL_EDGE,
COL_HIGHLIGHT_1,
COL_HIGHLIGHT_2,
NCOLOURS
};
struct game_params {
int n;
int diff;
};
struct game_numbers {
int refcount;
int *numbers; /* h x w */
};
#define EDGE_L 0x100
#define EDGE_R 0x200
#define EDGE_T 0x400
#define EDGE_B 0x800
struct game_state {
game_params params;
int w, h;
struct game_numbers *numbers;
int *grid;
unsigned short *edges; /* h x w */
bool completed, cheated;
};
static game_params *default_params(void)
{
game_params *ret = snew(game_params);
ret->n = 6;
ret->diff = DIFF_BASIC;
return ret;
}
static const struct game_params dominosa_presets[] = {
{ 3, DIFF_TRIVIAL },
{ 4, DIFF_TRIVIAL },
{ 5, DIFF_TRIVIAL },
{ 6, DIFF_TRIVIAL },
{ 4, DIFF_BASIC },
{ 5, DIFF_BASIC },
{ 6, DIFF_BASIC },
{ 7, DIFF_BASIC },
{ 8, DIFF_BASIC },
{ 9, DIFF_BASIC },
{ 6, DIFF_HARD },
{ 6, DIFF_EXTREME },
};
static bool game_fetch_preset(int i, char **name, game_params **params_out)
{
game_params *params;
char buf[80];
if (i < 0 || i >= lenof(dominosa_presets))
return false;
params = snew(game_params);
*params = dominosa_presets[i]; /* structure copy */
sprintf(buf, "Order %d, %s", params->n, dominosa_diffnames[params->diff]);
*name = dupstr(buf);
*params_out = params;
return true;
}
static void free_params(game_params *params)
{
sfree(params);
}
static game_params *dup_params(const game_params *params)
{
game_params *ret = snew(game_params);
*ret = *params; /* structure copy */
return ret;
}
static void decode_params(game_params *params, char const *string)
{
const char *p = string;
params->n = atoi(p);
while (*p && isdigit((unsigned char)*p)) p++;
while (*p) {
char c = *p++;
if (c == 'a') {
/* Legacy encoding from before the difficulty system */
params->diff = DIFF_AMBIGUOUS;
} else if (c == 'd') {
int i;
params->diff = DIFFCOUNT+1; /* ...which is invalid */
if (*p) {
for (i = 0; i < DIFFCOUNT; i++) {
if (*p == dominosa_diffchars[i])
params->diff = i;
}
p++;
}
}
}
}
static char *encode_params(const game_params *params, bool full)
{
char buf[80];
int len = sprintf(buf, "%d", params->n);
if (full)
len += sprintf(buf + len, "d%c", dominosa_diffchars[params->diff]);
return dupstr(buf);
}
static config_item *game_configure(const game_params *params)
{
config_item *ret;
char buf[80];
ret = snewn(3, config_item);
ret[0].name = "Maximum number on dominoes";
ret[0].type = C_STRING;
sprintf(buf, "%d", params->n);
ret[0].u.string.sval = dupstr(buf);
ret[1].name = "Difficulty";
ret[1].type = C_CHOICES;
ret[1].u.choices.choicenames = DIFFCONFIG;
ret[1].u.choices.selected = params->diff;
ret[2].name = NULL;
ret[2].type = C_END;
return ret;
}
static game_params *custom_params(const config_item *cfg)
{
game_params *ret = snew(game_params);
ret->n = atoi(cfg[0].u.string.sval);
ret->diff = cfg[1].u.choices.selected;
return ret;
}
static const char *validate_params(const game_params *params, bool full)
{
if (params->n < 1)
return "Maximum face number must be at least one";
if (params->diff >= DIFFCOUNT)
return "Unknown difficulty rating";
return NULL;
}
/* ----------------------------------------------------------------------
* Solver.
*/
#ifdef STANDALONE_SOLVER
#define SOLVER_DIAGNOSTICS
bool solver_diagnostics = false;
#elif defined SOLVER_DIAGNOSTICS
const bool solver_diagnostics = true;
#endif
struct solver_domino;
struct solver_placement;
/*
* Information about a particular domino.
*/
struct solver_domino {
/* The numbers on the domino, and its index in the dominoes array. */
int lo, hi, index;
/* List of placements not yet ruled out for this domino. */
int nplacements;
struct solver_placement **placements;
#ifdef SOLVER_DIAGNOSTICS
/* A textual name we can easily reuse in solver diagnostics. */
char *name;
#endif
};
/*
* Information about a particular 'placement' (i.e. specific location
* that a domino might go in).
*/
struct solver_placement {
/* The index of this placement in sc->placements. */
int index;
/* The two squares that make up this placement. */
struct solver_square *squares[2];
/* The domino that has to go in this position, if any. */
struct solver_domino *domino;
/* The index of this placement in each square's placements array,
* and in that of the domino. */
int spi[2], dpi;
/* Whether this is still considered a possible placement. */
bool active;
/* Other domino placements that overlap with this one. (Maximum 6:
* three overlapping each square of the placement.) */
int noverlaps;
struct solver_placement *overlaps[6];
#ifdef SOLVER_DIAGNOSTICS
/* A textual name we can easily reuse in solver diagnostics. */
char *name;
#endif
};
/*
* Information about a particular solver square.
*/
struct solver_square {
/* The coordinates of the square, and its index in a normal grid array. */
int x, y, index;
/* List of domino placements not yet ruled out for this square. */
int nplacements;
struct solver_placement *placements[4];
/* The number in the square. */
int number;
#ifdef SOLVER_DIAGNOSTICS
/* A textual name we can easily reuse in solver diagnostics. */
char *name;
#endif
};
struct solver_scratch {
int n, dc, pc, w, h, wh;
int max_diff_used;
struct solver_domino *dominoes;
struct solver_placement *placements;
struct solver_square *squares;
struct solver_placement **domino_placement_lists;
struct solver_square **squares_by_number;
struct findloopstate *fls;
bool squares_by_number_initialised;
int *wh_scratch, *pc_scratch, *pc_scratch2, *dc_scratch;
};
static struct solver_scratch *solver_make_scratch(int n)
{
int dc = DCOUNT(n), w = n+2, h = n+1, wh = w*h;
int pc = (w-1)*h + w*(h-1);
struct solver_scratch *sc = snew(struct solver_scratch);
int hi, lo, di, x, y, pi, si;
sc->n = n;
sc->dc = dc;
sc->pc = pc;
sc->w = w;
sc->h = h;
sc->wh = wh;
sc->dominoes = snewn(dc, struct solver_domino);
sc->placements = snewn(pc, struct solver_placement);
sc->squares = snewn(wh, struct solver_square);
sc->domino_placement_lists = snewn(pc, struct solver_placement *);
sc->fls = findloop_new_state(wh);
for (di = hi = 0; hi <= n; hi++) {
for (lo = 0; lo <= hi; lo++) {
assert(di == DINDEX(hi, lo));
sc->dominoes[di].hi = hi;
sc->dominoes[di].lo = lo;
sc->dominoes[di].index = di;
#ifdef SOLVER_DIAGNOSTICS
{
char buf[128];
sprintf(buf, "%d-%d", hi, lo);
sc->dominoes[di].name = dupstr(buf);
}
#endif
di++;
}
}
for (y = 0; y < h; y++) {
for (x = 0; x < w; x++) {
struct solver_square *sq = &sc->squares[y*w+x];
sq->x = x;
sq->y = y;
sq->index = y * w + x;
sq->nplacements = 0;
#ifdef SOLVER_DIAGNOSTICS
{
char buf[128];
sprintf(buf, "(%d,%d)", x, y);
sq->name = dupstr(buf);
}
#endif
}
}
pi = 0;
for (y = 0; y < h-1; y++) {
for (x = 0; x < w; x++) {
assert(pi < pc);
sc->placements[pi].squares[0] = &sc->squares[y*w+x];
sc->placements[pi].squares[1] = &sc->squares[(y+1)*w+x];
#ifdef SOLVER_DIAGNOSTICS
{
char buf[128];
sprintf(buf, "(%d,%d-%d)", x, y, y+1);
sc->placements[pi].name = dupstr(buf);
}
#endif
pi++;
}
}
for (y = 0; y < h; y++) {
for (x = 0; x < w-1; x++) {
assert(pi < pc);
sc->placements[pi].squares[0] = &sc->squares[y*w+x];
sc->placements[pi].squares[1] = &sc->squares[y*w+(x+1)];
#ifdef SOLVER_DIAGNOSTICS
{
char buf[128];
sprintf(buf, "(%d-%d,%d)", x, x+1, y);
sc->placements[pi].name = dupstr(buf);
}
#endif
pi++;
}
}
assert(pi == pc);
/* Set up the full placement lists for all squares, temporarily,
* so as to use them to calculate the overlap lists */
for (si = 0; si < wh; si++)
sc->squares[si].nplacements = 0;
for (pi = 0; pi < pc; pi++) {
struct solver_placement *p = &sc->placements[pi];
for (si = 0; si < 2; si++) {
struct solver_square *sq = p->squares[si];
p->spi[si] = sq->nplacements;
sq->placements[sq->nplacements++] = p;
}
}
/* Actually calculate the overlap lists */
for (pi = 0; pi < pc; pi++) {
struct solver_placement *p = &sc->placements[pi];
p->noverlaps = 0;
for (si = 0; si < 2; si++) {
struct solver_square *sq = p->squares[si];
int j;
for (j = 0; j < sq->nplacements; j++) {
struct solver_placement *q = sq->placements[j];
if (q != p)
p->overlaps[p->noverlaps++] = q;
}
}
}
/* Fill in the index field of the placements */
for (pi = 0; pi < pc; pi++)
sc->placements[pi].index = pi;
/* Lazily initialised by particular solver techniques that might
* never be needed */
sc->squares_by_number = NULL;
sc->squares_by_number_initialised = false;
sc->wh_scratch = NULL;
sc->pc_scratch = sc->pc_scratch2 = NULL;
sc->dc_scratch = NULL;
return sc;
}
static void solver_free_scratch(struct solver_scratch *sc)
{
#ifdef SOLVER_DIAGNOSTICS
{
int i;
for (i = 0; i < sc->dc; i++)
sfree(sc->dominoes[i].name);
for (i = 0; i < sc->pc; i++)
sfree(sc->placements[i].name);
for (i = 0; i < sc->wh; i++)
sfree(sc->squares[i].name);
}
#endif
sfree(sc->dominoes);
sfree(sc->placements);
sfree(sc->squares);
sfree(sc->domino_placement_lists);
sfree(sc->squares_by_number);
findloop_free_state(sc->fls);
sfree(sc->wh_scratch);
sfree(sc->pc_scratch);
sfree(sc->pc_scratch2);
sfree(sc->dc_scratch);
sfree(sc);
}
static void solver_setup_grid(struct solver_scratch *sc, const int *numbers)
{
int i, j;
for (i = 0; i < sc->wh; i++) {
sc->squares[i].nplacements = 0;
sc->squares[i].number = numbers[sc->squares[i].index];
}
for (i = 0; i < sc->pc; i++) {
struct solver_placement *p = &sc->placements[i];
int di = DINDEX(p->squares[0]->number, p->squares[1]->number);
p->domino = &sc->dominoes[di];
}
for (i = 0; i < sc->dc; i++)
sc->dominoes[i].nplacements = 0;
for (i = 0; i < sc->pc; i++)
sc->placements[i].domino->nplacements++;
for (i = j = 0; i < sc->dc; i++) {
sc->dominoes[i].placements = sc->domino_placement_lists + j;
j += sc->dominoes[i].nplacements;
sc->dominoes[i].nplacements = 0;
}
for (i = 0; i < sc->pc; i++) {
struct solver_placement *p = &sc->placements[i];
p->dpi = p->domino->nplacements;
p->domino->placements[p->domino->nplacements++] = p;
p->active = true;
}
for (i = 0; i < sc->wh; i++)
sc->squares[i].nplacements = 0;
for (i = 0; i < sc->pc; i++) {
struct solver_placement *p = &sc->placements[i];
for (j = 0; j < 2; j++) {
struct solver_square *sq = p->squares[j];
p->spi[j] = sq->nplacements;
sq->placements[sq->nplacements++] = p;
}
}
sc->max_diff_used = DIFF_TRIVIAL;
sc->squares_by_number_initialised = false;
}
/* Given two placements p,q that overlap, returns si such that
* p->squares[si] is the square also in q */
static int common_square_index(struct solver_placement *p,
struct solver_placement *q)
{
return (p->squares[0] == q->squares[0] ||
p->squares[0] == q->squares[1]) ? 0 : 1;
}
/* Sort function used to set up squares_by_number */
static int squares_by_number_cmpfn(const void *av, const void *bv)
{
struct solver_square *a = *(struct solver_square *const *)av;
struct solver_square *b = *(struct solver_square *const *)bv;
return (a->number < b->number ? -1 : a->number > b->number ? +1 :
a->index < b->index ? -1 : a->index > b->index ? +1 : 0);
}
static void rule_out_placement(
struct solver_scratch *sc, struct solver_placement *p)
{
struct solver_domino *d = p->domino;
int i, j, si;
#ifdef SOLVER_DIAGNOSTICS
if (solver_diagnostics)
printf(" ruling out placement %s for domino %s\n", p->name, d->name);
#endif
p->active = false;
i = p->dpi;
assert(d->placements[i] == p);
if (--d->nplacements != i) {
d->placements[i] = d->placements[d->nplacements];
d->placements[i]->dpi = i;
}
for (si = 0; si < 2; si++) {
struct solver_square *sq = p->squares[si];
i = p->spi[si];
assert(sq->placements[i] == p);
if (--sq->nplacements != i) {
sq->placements[i] = sq->placements[sq->nplacements];
j = (sq->placements[i]->squares[0] == sq ? 0 : 1);
sq->placements[i]->spi[j] = i;
}
}
}
/*
* If a domino has only one placement remaining, rule out all other
* placements that overlap it.
*/
static bool deduce_domino_single_placement(struct solver_scratch *sc, int di)
{
struct solver_domino *d = &sc->dominoes[di];
struct solver_placement *p, *q;
int oi;
bool done_something = false;
if (d->nplacements != 1)
return false;
p = d->placements[0];
for (oi = 0; oi < p->noverlaps; oi++) {
q = p->overlaps[oi];
if (q->active) {
if (!done_something) {
done_something = true;
#ifdef SOLVER_DIAGNOSTICS
if (solver_diagnostics)
printf("domino %s has unique placement %s\n",
d->name, p->name);
#endif
}
rule_out_placement(sc, q);
}
}
return done_something;
}
/*
* If a square has only one placement remaining, rule out all other
* placements of its domino.
*/
static bool deduce_square_single_placement(struct solver_scratch *sc, int si)
{
struct solver_square *sq = &sc->squares[si];
struct solver_placement *p;
struct solver_domino *d;
if (sq->nplacements != 1)
return false;
p = sq->placements[0];
d = p->domino;
if (d->nplacements <= 1)
return false; /* we already knew everything this would tell us */
#ifdef SOLVER_DIAGNOSTICS
if (solver_diagnostics)
printf("square %s has unique placement %s (domino %s)\n",
sq->name, p->name, p->domino->name);
#endif
while (d->nplacements > 1)
rule_out_placement(
sc, d->placements[0] == p ? d->placements[1] : d->placements[0]);
return true;
}
/*
* If all placements for a square involve the same domino, rule out
* all other placements of that domino.
*/
static bool deduce_square_single_domino(struct solver_scratch *sc, int si)
{
struct solver_square *sq = &sc->squares[si];
struct solver_domino *d;
int i;
/*
* We only bother with this if the square has at least _two_
* placements. If it only has one, then a simpler deduction will
* have handled it already, or will do so the next time round the
* main solver loop - and we should let the simpler deduction do
* it, because that will give a less overblown diagnostic.
*/
if (sq->nplacements < 2)
return false;
d = sq->placements[0]->domino;
for (i = 1; i < sq->nplacements; i++)
if (sq->placements[i]->domino != d)
return false; /* not all the same domino */
if (d->nplacements <= sq->nplacements)
return false; /* no other placements of d to rule out */
#ifdef SOLVER_DIAGNOSTICS
if (solver_diagnostics)
printf("square %s can only contain domino %s\n", sq->name, d->name);
#endif
for (i = d->nplacements; i-- > 0 ;) {
struct solver_placement *p = d->placements[i];
if (p->squares[0] != sq && p->squares[1] != sq)
rule_out_placement(sc, p);
}
return true;
}
/*
* If any placement is overlapped by _all_ possible placements of a
* given domino, rule that placement out.
*/
static bool deduce_domino_must_overlap(struct solver_scratch *sc, int di)
{
struct solver_domino *d = &sc->dominoes[di];
struct solver_placement *intersection[6], *p;
int nintersection = 0;
int i, j, k;
/*
* As in deduce_square_single_domino, we only bother with this
* deduction if the domino has at least two placements.
*/
if (d->nplacements < 2)
return false;
/* Initialise our set of overlapped placements with all the active
* ones overlapped by placements[0]. */
p = d->placements[0];
for (i = 0; i < p->noverlaps; i++)
if (p->overlaps[i]->active)
intersection[nintersection++] = p->overlaps[i];
/* Now loop over the other placements of d, winnowing that set. */
for (j = 1; j < d->nplacements; j++) {
int old_n;
p = d->placements[j];
old_n = nintersection;
nintersection = 0;
for (k = 0; k < old_n; k++) {
for (i = 0; i < p->noverlaps; i++)
if (p->overlaps[i] == intersection[k])
goto found;
/* If intersection[k] isn't in p->overlaps, exclude it
* from our set of placements overlapped by everything */
continue;
found:
intersection[nintersection++] = intersection[k];
}
}
if (nintersection == 0)
return false; /* no new exclusions */
for (i = 0; i < nintersection; i++) {
p = intersection[i];
#ifdef SOLVER_DIAGNOSTICS
if (solver_diagnostics) {
printf("placement %s of domino %s overlaps all placements "
"of domino %s:", p->name, p->domino->name, d->name);
for (j = 0; j < d->nplacements; j++)
printf(" %s", d->placements[j]->name);
printf("\n");
}
#endif
rule_out_placement(sc, p);
}
return true;
}
/*
* If a placement of domino D overlaps the only remaining placement
* for some square S which is not also for domino D, then placing D
* here would require another copy of it in S, so we can rule it out.
*/
static bool deduce_local_duplicate(struct solver_scratch *sc, int pi)
{
struct solver_placement *p = &sc->placements[pi];
struct solver_domino *d = p->domino;
int i, j;
if (!p->active)
return false;
for (i = 0; i < p->noverlaps; i++) {
struct solver_placement *q = p->overlaps[i];
struct solver_square *sq;
if (!q->active)
continue;
/* Find the square of q that _isn't_ part of p */
sq = q->squares[1 - common_square_index(q, p)];
for (j = 0; j < sq->nplacements; j++)
if (sq->placements[j] != q && sq->placements[j]->domino != d)
goto no;
/* If we get here, every possible placement for sq is either q
* itself, or another copy of d. Success! We can rule out p. */
#ifdef SOLVER_DIAGNOSTICS
if (solver_diagnostics) {
printf("placement %s of domino %s would force another copy of %s "
"in square %s\n", p->name, d->name, d->name, sq->name);
}
#endif
rule_out_placement(sc, p);
return true;
no:;
}
return false;
}
/*
* If placement P overlaps one placement for each of two squares S,T
* such that all the remaining placements for both S and T are the
* same domino D (and none of those placements joins S and T to each
* other), then P can't be placed, because it would leave S,T each
* having to be a copy of D, i.e. duplicates.
*/
static bool deduce_local_duplicate_2(struct solver_scratch *sc, int pi)
{
struct solver_placement *p = &sc->placements[pi];
int i, j, k;
if (!p->active)
return false;
/*
* Iterate over pairs of placements qi,qj overlapping p.
*/
for (i = 0; i < p->noverlaps; i++) {
struct solver_placement *qi = p->overlaps[i];
struct solver_square *sqi;
struct solver_domino *di = NULL;
if (!qi->active)
continue;
/* Find the square of qi that _isn't_ part of p */
sqi = qi->squares[1 - common_square_index(qi, p)];
/*
* Identify the unique domino involved in all possible
* placements of sqi other than qi. If there isn't a unique
* one (either too many or too few), move on and try the next
* qi.
*/
for (k = 0; k < sqi->nplacements; k++) {
struct solver_placement *pk = sqi->placements[k];
if (sqi->placements[k] == qi)
continue; /* not counting qi itself */
if (!di)
di = pk->domino;
else if (di != pk->domino)
goto done_qi;
}
if (!di)
goto done_qi;
/*
* Now find an appropriate qj != qi.
*/
for (j = 0; j < p->noverlaps; j++) {
struct solver_placement *qj = p->overlaps[j];
struct solver_square *sqj;
bool found_di = false;
if (j == i || !qj->active)
continue;
sqj = qj->squares[1 - common_square_index(qj, p)];
/*
* As above, we want the same domino di to be the only one
* sqj can be if placement qj is ruled out. But also we
* need no placement of sqj to overlap sqi.
*/
for (k = 0; k < sqj->nplacements; k++) {
struct solver_placement *pk = sqj->placements[k];
if (pk == qj)
continue; /* not counting qj itself */
if (pk->domino != di)
goto done_qj; /* found a different domino */
if (pk->squares[0] == sqi || pk->squares[1] == sqi)
goto done_qj; /* sqi,sqj can be joined to each other */
found_di = true;
}
if (!found_di)
goto done_qj;
/* If we get here, then every placement for either of sqi
* and sqj is a copy of di, except for the ones that
* overlap p. Success! We can rule out p. */
#ifdef SOLVER_DIAGNOSTICS
if (solver_diagnostics) {
printf("placement %s of domino %s would force squares "
"%s and %s to both be domino %s\n",
p->name, p->domino->name,
sqi->name, sqj->name, di->name);
}
#endif
rule_out_placement(sc, p);
return true;
done_qj:;
}
done_qi:;
}
return false;
}
struct parity_findloop_ctx {
struct solver_scratch *sc;
struct solver_square *sq;
int i;
};
int parity_neighbour(int vertex, void *vctx)
{
struct parity_findloop_ctx *ctx = (struct parity_findloop_ctx *)vctx;
struct solver_placement *p;
if (vertex >= 0) {
ctx->sq = &ctx->sc->squares[vertex];
ctx->i = 0;
} else {
assert(ctx->sq);
}
if (ctx->i >= ctx->sq->nplacements) {
ctx->sq = NULL;
return -1;
}
p = ctx->sq->placements[ctx->i++];
return p->squares[0]->index + p->squares[1]->index - ctx->sq->index;
}
/*
* Look for dominoes whose placement would disconnect the unfilled
* area of the grid into pieces with odd area. Such a domino can't be
* placed, because then the area on each side of it would be
* untileable.
*/
static bool deduce_parity(struct solver_scratch *sc)
{
struct parity_findloop_ctx pflctx;
bool done_something = false;
int pi;
/*
* Run findloop, aka Tarjan's bridge-finding algorithm, on the
* graph whose vertices are squares, with two vertices separated
* by an edge iff some not-yet-ruled-out domino placement covers
* them both. (So each edge itself corresponds to a domino
* placement.)
*
* The effect is that any bridge in this graph is a domino whose
* placement would separate two previously connected areas of the
* unfilled squares of the grid.
*
* Placing that domino would not just disconnect those areas from
* each other, but also use up one square of each. So if we want
* to avoid leaving two odd areas after placing the domino, it
* follows that we want to avoid the bridge having an _even_
* number of vertices on each side.
*/
pflctx.sc = sc;
findloop_run(sc->fls, sc->wh, parity_neighbour, &pflctx);
for (pi = 0; pi < sc->pc; pi++) {
struct solver_placement *p = &sc->placements[pi];
int size0, size1;
if (!p->active)
continue;
if (!findloop_is_bridge(
sc->fls, p->squares[0]->index, p->squares[1]->index,
&size0, &size1))
continue;
/* To make a deduction, size0 and size1 must both be even,
* i.e. after placing this domino decrements each by 1 they
* would both become odd and untileable areas. */
if ((size0 | size1) & 1)
continue;
#ifdef SOLVER_DIAGNOSTICS
if (solver_diagnostics) {
printf("placement %s of domino %s would create two odd-sized "
"areas\n", p->name, p->domino->name);