A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 3561 lines 113 kB view raw
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 * Further possible deduction types in the solver: 9 * 10 * * possibly an advanced form of deduce_parity via 2-connectedness. 11 * We currently deal with areas of the graph with exactly one way 12 * in and out; but if you have an area with exactly _two_ routes in 13 * and out of it, then you can at least decide on the _relative_ 14 * parity of the two (either 'these two edges both bisect dominoes 15 * or neither do', or 'exactly one of these edges bisects a 16 * domino'). And occasionally that can be enough to let you rule 17 * out one of the two remaining choices. 18 * + For example, if both those edges bisect a domino, then those 19 * two dominoes would also be both the same. 20 * + Or perhaps between them they rule out all possibilities for 21 * some other square. 22 * + Or perhaps they themselves would be duplicates! 23 * + Or perhaps, on purely geometric grounds, they would box in a 24 * square to the point where it ended up having to be an 25 * isolated singleton. 26 * + The tricky part of this is how you do the graph theory. 27 * Perhaps a modified form of Tarjan's bridge-finding algorithm 28 * would work, but I haven't thought through the details. 29 * 30 * * possibly an advanced version of set analysis which doesn't have 31 * to start from squares all having the same number? For example, 32 * if you have three mutually non-adjacent squares labelled 1,2,3 33 * such that the numbers adjacent to each are precisely the other 34 * two, then set analysis can work just fine in principle, and 35 * tells you that those three squares must overlap the three 36 * dominoes 1-2, 2-3 and 1-3 in some order, so you can rule out any 37 * placements of those elsewhere. 38 * + the difficulty with this is how you avoid it going painfully 39 * exponential-time. You can't iterate over all the subsets, so 40 * you'd need some kind of more sophisticated directed search. 41 * + and the adjacency allowance has to be similarly accounted 42 * for, which could get tricky to keep track of. 43 */ 44 45#include <stdio.h> 46#include <stdlib.h> 47#include <string.h> 48#include <assert.h> 49#include <ctype.h> 50#include <limits.h> 51#ifdef NO_TGMATH_H 52# include <math.h> 53#else 54# include <tgmath.h> 55#endif 56 57#include "puzzles.h" 58 59/* nth triangular number */ 60#define TRI(n) ( (n) * ((n) + 1) / 2 ) 61/* number of dominoes for value n */ 62#define DCOUNT(n) TRI((n)+1) 63/* map a pair of numbers to a unique domino index from 0 upwards. */ 64#define DINDEX(n1,n2) ( TRI(max(n1,n2)) + min(n1,n2) ) 65 66#define FLASH_TIME 0.13F 67 68/* 69 * Difficulty levels. I do some macro ickery here to ensure that my 70 * enum and the various forms of my name list always match up. 71 */ 72#define DIFFLIST(X) \ 73 X(TRIVIAL,Trivial,t) \ 74 X(BASIC,Basic,b) \ 75 X(HARD,Hard,h) \ 76 X(EXTREME,Extreme,e) \ 77 X(AMBIGUOUS,Ambiguous,a) \ 78 /* end of list */ 79#define ENUM(upper,title,lower) DIFF_ ## upper, 80#define TITLE(upper,title,lower) #title, 81#define ENCODE(upper,title,lower) #lower 82#define CONFIG(upper,title,lower) ":" #title 83enum { DIFFLIST(ENUM) DIFFCOUNT }; 84static char const *const dominosa_diffnames[] = { DIFFLIST(TITLE) }; 85static char const dominosa_diffchars[] = DIFFLIST(ENCODE); 86#define DIFFCONFIG DIFFLIST(CONFIG) 87 88enum { 89 COL_BACKGROUND, 90 COL_TEXT, 91 COL_DOMINO, 92 COL_DOMINOCLASH, 93 COL_DOMINOTEXT, 94 COL_EDGE, 95 COL_HIGHLIGHT_1, 96 COL_HIGHLIGHT_2, 97 NCOLOURS 98}; 99 100struct game_params { 101 int n; 102 int diff; 103}; 104 105struct game_numbers { 106 int refcount; 107 int *numbers; /* h x w */ 108}; 109 110#define EDGE_L 0x100 111#define EDGE_R 0x200 112#define EDGE_T 0x400 113#define EDGE_B 0x800 114 115struct game_state { 116 game_params params; 117 int w, h; 118 struct game_numbers *numbers; 119 int *grid; 120 unsigned short *edges; /* h x w */ 121 bool completed, cheated; 122}; 123 124static game_params *default_params(void) 125{ 126 game_params *ret = snew(game_params); 127 128 ret->n = 6; 129 ret->diff = DIFF_BASIC; 130 131 return ret; 132} 133 134static const struct game_params dominosa_presets[] = { 135 { 3, DIFF_TRIVIAL }, 136 { 4, DIFF_TRIVIAL }, 137 { 5, DIFF_TRIVIAL }, 138 { 6, DIFF_TRIVIAL }, 139 { 4, DIFF_BASIC }, 140 { 5, DIFF_BASIC }, 141 { 6, DIFF_BASIC }, 142 { 7, DIFF_BASIC }, 143 { 8, DIFF_BASIC }, 144 { 9, DIFF_BASIC }, 145 { 6, DIFF_HARD }, 146 { 6, DIFF_EXTREME }, 147}; 148 149static bool game_fetch_preset(int i, char **name, game_params **params_out) 150{ 151 game_params *params; 152 char buf[80]; 153 154 if (i < 0 || i >= lenof(dominosa_presets)) 155 return false; 156 157 params = snew(game_params); 158 *params = dominosa_presets[i]; /* structure copy */ 159 160 sprintf(buf, "Order %d, %s", params->n, dominosa_diffnames[params->diff]); 161 162 *name = dupstr(buf); 163 *params_out = params; 164 return true; 165} 166 167static void free_params(game_params *params) 168{ 169 sfree(params); 170} 171 172static game_params *dup_params(const game_params *params) 173{ 174 game_params *ret = snew(game_params); 175 *ret = *params; /* structure copy */ 176 return ret; 177} 178 179static void decode_params(game_params *params, char const *string) 180{ 181 const char *p = string; 182 183 params->n = atoi(p); 184 while (*p && isdigit((unsigned char)*p)) p++; 185 186 while (*p) { 187 char c = *p++; 188 if (c == 'a') { 189 /* Legacy encoding from before the difficulty system */ 190 params->diff = DIFF_AMBIGUOUS; 191 } else if (c == 'd') { 192 int i; 193 params->diff = DIFFCOUNT+1; /* ...which is invalid */ 194 if (*p) { 195 for (i = 0; i < DIFFCOUNT; i++) { 196 if (*p == dominosa_diffchars[i]) 197 params->diff = i; 198 } 199 p++; 200 } 201 } 202 } 203} 204 205static char *encode_params(const game_params *params, bool full) 206{ 207 char buf[80]; 208 int len = sprintf(buf, "%d", params->n); 209 if (full) 210 len += sprintf(buf + len, "d%c", dominosa_diffchars[params->diff]); 211 return dupstr(buf); 212} 213 214static config_item *game_configure(const game_params *params) 215{ 216 config_item *ret; 217 char buf[80]; 218 219 ret = snewn(3, config_item); 220 221 ret[0].name = "Maximum number on dominoes"; 222 ret[0].type = C_STRING; 223 sprintf(buf, "%d", params->n); 224 ret[0].u.string.sval = dupstr(buf); 225 226 ret[1].name = "Difficulty"; 227 ret[1].type = C_CHOICES; 228 ret[1].u.choices.choicenames = DIFFCONFIG; 229 ret[1].u.choices.selected = params->diff; 230 231 ret[2].name = NULL; 232 ret[2].type = C_END; 233 234 return ret; 235} 236 237static game_params *custom_params(const config_item *cfg) 238{ 239 game_params *ret = snew(game_params); 240 241 ret->n = atoi(cfg[0].u.string.sval); 242 ret->diff = cfg[1].u.choices.selected; 243 244 return ret; 245} 246 247static const char *validate_params(const game_params *params, bool full) 248{ 249 if (params->n < 1) 250 return "Maximum face number must be at least one"; 251 if (params->n > INT_MAX - 2 || 252 params->n + 2 > INT_MAX / (params->n + 1)) 253 return "Maximum face number must not be unreasonably large"; 254 255 if (params->diff >= DIFFCOUNT) 256 return "Unknown difficulty rating"; 257 return NULL; 258} 259 260/* ---------------------------------------------------------------------- 261 * Solver. 262 */ 263 264#ifdef STANDALONE_SOLVER 265#define SOLVER_DIAGNOSTICS 266static bool solver_diagnostics = false; 267#elif defined SOLVER_DIAGNOSTICS 268static const bool solver_diagnostics = true; 269#endif 270 271struct solver_domino; 272struct solver_placement; 273 274/* 275 * Information about a particular domino. 276 */ 277struct solver_domino { 278 /* The numbers on the domino, and its index in the dominoes array. */ 279 int lo, hi, index; 280 281 /* List of placements not yet ruled out for this domino. */ 282 int nplacements; 283 struct solver_placement **placements; 284 285#ifdef SOLVER_DIAGNOSTICS 286 /* A textual name we can easily reuse in solver diagnostics. */ 287 char *name; 288#endif 289}; 290 291/* 292 * Information about a particular 'placement' (i.e. specific location 293 * that a domino might go in). 294 */ 295struct solver_placement { 296 /* The index of this placement in sc->placements. */ 297 int index; 298 299 /* The two squares that make up this placement. */ 300 struct solver_square *squares[2]; 301 302 /* The domino that has to go in this position, if any. */ 303 struct solver_domino *domino; 304 305 /* The index of this placement in each square's placements array, 306 * and in that of the domino. */ 307 int spi[2], dpi; 308 309 /* Whether this is still considered a possible placement. */ 310 bool active; 311 312 /* Other domino placements that overlap with this one. (Maximum 6: 313 * three overlapping each square of the placement.) */ 314 int noverlaps; 315 struct solver_placement *overlaps[6]; 316 317#ifdef SOLVER_DIAGNOSTICS 318 /* A textual name we can easily reuse in solver diagnostics. */ 319 char *name; 320#endif 321}; 322 323/* 324 * Information about a particular solver square. 325 */ 326struct solver_square { 327 /* The coordinates of the square, and its index in a normal grid array. */ 328 int x, y, index; 329 330 /* List of domino placements not yet ruled out for this square. */ 331 int nplacements; 332 struct solver_placement *placements[4]; 333 334 /* The number in the square. */ 335 int number; 336 337#ifdef SOLVER_DIAGNOSTICS 338 /* A textual name we can easily reuse in solver diagnostics. */ 339 char *name; 340#endif 341}; 342 343struct solver_scratch { 344 int n, dc, pc, w, h, wh; 345 int max_diff_used; 346 struct solver_domino *dominoes; 347 struct solver_placement *placements; 348 struct solver_square *squares; 349 struct solver_placement **domino_placement_lists; 350 struct solver_square **squares_by_number; 351 struct findloopstate *fls; 352 bool squares_by_number_initialised; 353 int *wh_scratch, *pc_scratch, *pc_scratch2, *dc_scratch; 354 DSF *dsf_scratch; 355}; 356 357static struct solver_scratch *solver_make_scratch(int n) 358{ 359 int dc = DCOUNT(n), w = n+2, h = n+1, wh = w*h; 360 int pc = (w-1)*h + w*(h-1); 361 struct solver_scratch *sc = snew(struct solver_scratch); 362 int hi, lo, di, x, y, pi, si; 363 364 sc->n = n; 365 sc->dc = dc; 366 sc->pc = pc; 367 sc->w = w; 368 sc->h = h; 369 sc->wh = wh; 370 371 sc->dominoes = snewn(dc, struct solver_domino); 372 sc->placements = snewn(pc, struct solver_placement); 373 sc->squares = snewn(wh, struct solver_square); 374 sc->domino_placement_lists = snewn(pc, struct solver_placement *); 375 sc->fls = findloop_new_state(wh); 376 377 for (di = hi = 0; hi <= n; hi++) { 378 for (lo = 0; lo <= hi; lo++) { 379 assert(di == DINDEX(hi, lo)); 380 sc->dominoes[di].hi = hi; 381 sc->dominoes[di].lo = lo; 382 sc->dominoes[di].index = di; 383 384#ifdef SOLVER_DIAGNOSTICS 385 { 386 char buf[128]; 387 sprintf(buf, "%d-%d", hi, lo); 388 sc->dominoes[di].name = dupstr(buf); 389 } 390#endif 391 392 di++; 393 } 394 } 395 396 for (y = 0; y < h; y++) { 397 for (x = 0; x < w; x++) { 398 struct solver_square *sq = &sc->squares[y*w+x]; 399 sq->x = x; 400 sq->y = y; 401 sq->index = y * w + x; 402 sq->nplacements = 0; 403 404#ifdef SOLVER_DIAGNOSTICS 405 { 406 char buf[128]; 407 sprintf(buf, "(%d,%d)", x, y); 408 sq->name = dupstr(buf); 409 } 410#endif 411 } 412 } 413 414 pi = 0; 415 for (y = 0; y < h-1; y++) { 416 for (x = 0; x < w; x++) { 417 assert(pi < pc); 418 sc->placements[pi].squares[0] = &sc->squares[y*w+x]; 419 sc->placements[pi].squares[1] = &sc->squares[(y+1)*w+x]; 420#ifdef SOLVER_DIAGNOSTICS 421 { 422 char buf[128]; 423 sprintf(buf, "(%d,%d-%d)", x, y, y+1); 424 sc->placements[pi].name = dupstr(buf); 425 } 426#endif 427 pi++; 428 } 429 } 430 for (y = 0; y < h; y++) { 431 for (x = 0; x < w-1; x++) { 432 assert(pi < pc); 433 sc->placements[pi].squares[0] = &sc->squares[y*w+x]; 434 sc->placements[pi].squares[1] = &sc->squares[y*w+(x+1)]; 435#ifdef SOLVER_DIAGNOSTICS 436 { 437 char buf[128]; 438 sprintf(buf, "(%d-%d,%d)", x, x+1, y); 439 sc->placements[pi].name = dupstr(buf); 440 } 441#endif 442 pi++; 443 } 444 } 445 assert(pi == pc); 446 447 /* Set up the full placement lists for all squares, temporarily, 448 * so as to use them to calculate the overlap lists */ 449 for (si = 0; si < wh; si++) 450 sc->squares[si].nplacements = 0; 451 for (pi = 0; pi < pc; pi++) { 452 struct solver_placement *p = &sc->placements[pi]; 453 for (si = 0; si < 2; si++) { 454 struct solver_square *sq = p->squares[si]; 455 p->spi[si] = sq->nplacements; 456 sq->placements[sq->nplacements++] = p; 457 } 458 } 459 460 /* Actually calculate the overlap lists */ 461 for (pi = 0; pi < pc; pi++) { 462 struct solver_placement *p = &sc->placements[pi]; 463 p->noverlaps = 0; 464 for (si = 0; si < 2; si++) { 465 struct solver_square *sq = p->squares[si]; 466 int j; 467 for (j = 0; j < sq->nplacements; j++) { 468 struct solver_placement *q = sq->placements[j]; 469 if (q != p) 470 p->overlaps[p->noverlaps++] = q; 471 } 472 } 473 } 474 475 /* Fill in the index field of the placements */ 476 for (pi = 0; pi < pc; pi++) 477 sc->placements[pi].index = pi; 478 479 /* Lazily initialised by particular solver techniques that might 480 * never be needed */ 481 sc->squares_by_number = NULL; 482 sc->squares_by_number_initialised = false; 483 sc->wh_scratch = NULL; 484 sc->pc_scratch = sc->pc_scratch2 = NULL; 485 sc->dc_scratch = NULL; 486 sc->dsf_scratch = NULL; 487 488 return sc; 489} 490 491static void solver_free_scratch(struct solver_scratch *sc) 492{ 493#ifdef SOLVER_DIAGNOSTICS 494 { 495 int i; 496 for (i = 0; i < sc->dc; i++) 497 sfree(sc->dominoes[i].name); 498 for (i = 0; i < sc->pc; i++) 499 sfree(sc->placements[i].name); 500 for (i = 0; i < sc->wh; i++) 501 sfree(sc->squares[i].name); 502 } 503#endif 504 sfree(sc->dominoes); 505 sfree(sc->placements); 506 sfree(sc->squares); 507 sfree(sc->domino_placement_lists); 508 sfree(sc->squares_by_number); 509 findloop_free_state(sc->fls); 510 sfree(sc->wh_scratch); 511 sfree(sc->pc_scratch); 512 sfree(sc->pc_scratch2); 513 sfree(sc->dc_scratch); 514 dsf_free(sc->dsf_scratch); 515 sfree(sc); 516} 517 518static void solver_setup_grid(struct solver_scratch *sc, const int *numbers) 519{ 520 int i, j; 521 522 for (i = 0; i < sc->wh; i++) { 523 sc->squares[i].nplacements = 0; 524 sc->squares[i].number = numbers[sc->squares[i].index]; 525 } 526 527 for (i = 0; i < sc->pc; i++) { 528 struct solver_placement *p = &sc->placements[i]; 529 int di = DINDEX(p->squares[0]->number, p->squares[1]->number); 530 p->domino = &sc->dominoes[di]; 531 } 532 533 for (i = 0; i < sc->dc; i++) 534 sc->dominoes[i].nplacements = 0; 535 for (i = 0; i < sc->pc; i++) 536 sc->placements[i].domino->nplacements++; 537 for (i = j = 0; i < sc->dc; i++) { 538 sc->dominoes[i].placements = sc->domino_placement_lists + j; 539 j += sc->dominoes[i].nplacements; 540 sc->dominoes[i].nplacements = 0; 541 } 542 for (i = 0; i < sc->pc; i++) { 543 struct solver_placement *p = &sc->placements[i]; 544 p->dpi = p->domino->nplacements; 545 p->domino->placements[p->domino->nplacements++] = p; 546 p->active = true; 547 } 548 549 for (i = 0; i < sc->wh; i++) 550 sc->squares[i].nplacements = 0; 551 for (i = 0; i < sc->pc; i++) { 552 struct solver_placement *p = &sc->placements[i]; 553 for (j = 0; j < 2; j++) { 554 struct solver_square *sq = p->squares[j]; 555 p->spi[j] = sq->nplacements; 556 sq->placements[sq->nplacements++] = p; 557 } 558 } 559 560 sc->max_diff_used = DIFF_TRIVIAL; 561 sc->squares_by_number_initialised = false; 562} 563 564/* Given two placements p,q that overlap, returns si such that 565 * p->squares[si] is the square also in q */ 566static int common_square_index(struct solver_placement *p, 567 struct solver_placement *q) 568{ 569 return (p->squares[0] == q->squares[0] || 570 p->squares[0] == q->squares[1]) ? 0 : 1; 571} 572 573/* Sort function used to set up squares_by_number */ 574static int squares_by_number_cmpfn(const void *av, const void *bv) 575{ 576 struct solver_square *a = *(struct solver_square *const *)av; 577 struct solver_square *b = *(struct solver_square *const *)bv; 578 return (a->number < b->number ? -1 : a->number > b->number ? +1 : 579 a->index < b->index ? -1 : a->index > b->index ? +1 : 0); 580} 581 582static void rule_out_placement( 583 struct solver_scratch *sc, struct solver_placement *p) 584{ 585 struct solver_domino *d = p->domino; 586 int i, j, si; 587 588#ifdef SOLVER_DIAGNOSTICS 589 if (solver_diagnostics) 590 printf(" ruling out placement %s for domino %s\n", p->name, d->name); 591#endif 592 593 p->active = false; 594 595 i = p->dpi; 596 assert(d->placements[i] == p); 597 if (--d->nplacements != i) { 598 d->placements[i] = d->placements[d->nplacements]; 599 d->placements[i]->dpi = i; 600 } 601 602 for (si = 0; si < 2; si++) { 603 struct solver_square *sq = p->squares[si]; 604 i = p->spi[si]; 605 assert(sq->placements[i] == p); 606 if (--sq->nplacements != i) { 607 sq->placements[i] = sq->placements[sq->nplacements]; 608 j = (sq->placements[i]->squares[0] == sq ? 0 : 1); 609 sq->placements[i]->spi[j] = i; 610 } 611 } 612} 613 614/* 615 * If a domino has only one placement remaining, rule out all other 616 * placements that overlap it. 617 */ 618static bool deduce_domino_single_placement(struct solver_scratch *sc, int di) 619{ 620 struct solver_domino *d = &sc->dominoes[di]; 621 struct solver_placement *p, *q; 622 int oi; 623 bool done_something = false; 624 625 if (d->nplacements != 1) 626 return false; 627 p = d->placements[0]; 628 629 for (oi = 0; oi < p->noverlaps; oi++) { 630 q = p->overlaps[oi]; 631 if (q->active) { 632 if (!done_something) { 633 done_something = true; 634#ifdef SOLVER_DIAGNOSTICS 635 if (solver_diagnostics) 636 printf("domino %s has unique placement %s\n", 637 d->name, p->name); 638#endif 639 } 640 rule_out_placement(sc, q); 641 } 642 } 643 644 return done_something; 645} 646 647/* 648 * If a square has only one placement remaining, rule out all other 649 * placements of its domino. 650 */ 651static bool deduce_square_single_placement(struct solver_scratch *sc, int si) 652{ 653 struct solver_square *sq = &sc->squares[si]; 654 struct solver_placement *p; 655 struct solver_domino *d; 656 657 if (sq->nplacements != 1) 658 return false; 659 p = sq->placements[0]; 660 d = p->domino; 661 662 if (d->nplacements <= 1) 663 return false; /* we already knew everything this would tell us */ 664 665#ifdef SOLVER_DIAGNOSTICS 666 if (solver_diagnostics) 667 printf("square %s has unique placement %s (domino %s)\n", 668 sq->name, p->name, p->domino->name); 669#endif 670 671 while (d->nplacements > 1) 672 rule_out_placement( 673 sc, d->placements[0] == p ? d->placements[1] : d->placements[0]); 674 675 return true; 676} 677 678/* 679 * If all placements for a square involve the same domino, rule out 680 * all other placements of that domino. 681 */ 682static bool deduce_square_single_domino(struct solver_scratch *sc, int si) 683{ 684 struct solver_square *sq = &sc->squares[si]; 685 struct solver_domino *d; 686 int i; 687 688 /* 689 * We only bother with this if the square has at least _two_ 690 * placements. If it only has one, then a simpler deduction will 691 * have handled it already, or will do so the next time round the 692 * main solver loop - and we should let the simpler deduction do 693 * it, because that will give a less overblown diagnostic. 694 */ 695 if (sq->nplacements < 2) 696 return false; 697 698 d = sq->placements[0]->domino; 699 for (i = 1; i < sq->nplacements; i++) 700 if (sq->placements[i]->domino != d) 701 return false; /* not all the same domino */ 702 703 if (d->nplacements <= sq->nplacements) 704 return false; /* no other placements of d to rule out */ 705 706#ifdef SOLVER_DIAGNOSTICS 707 if (solver_diagnostics) 708 printf("square %s can only contain domino %s\n", sq->name, d->name); 709#endif 710 711 for (i = d->nplacements; i-- > 0 ;) { 712 struct solver_placement *p = d->placements[i]; 713 if (p->squares[0] != sq && p->squares[1] != sq) 714 rule_out_placement(sc, p); 715 } 716 717 return true; 718} 719 720/* 721 * If any placement is overlapped by _all_ possible placements of a 722 * given domino, rule that placement out. 723 */ 724static bool deduce_domino_must_overlap(struct solver_scratch *sc, int di) 725{ 726 struct solver_domino *d = &sc->dominoes[di]; 727 struct solver_placement *intersection[6], *p; 728 int nintersection = 0; 729 int i, j, k; 730 731 /* 732 * As in deduce_square_single_domino, we only bother with this 733 * deduction if the domino has at least two placements. 734 */ 735 if (d->nplacements < 2) 736 return false; 737 738 /* Initialise our set of overlapped placements with all the active 739 * ones overlapped by placements[0]. */ 740 p = d->placements[0]; 741 for (i = 0; i < p->noverlaps; i++) 742 if (p->overlaps[i]->active) 743 intersection[nintersection++] = p->overlaps[i]; 744 745 /* Now loop over the other placements of d, winnowing that set. */ 746 for (j = 1; j < d->nplacements; j++) { 747 int old_n; 748 749 p = d->placements[j]; 750 751 old_n = nintersection; 752 nintersection = 0; 753 754 for (k = 0; k < old_n; k++) { 755 for (i = 0; i < p->noverlaps; i++) 756 if (p->overlaps[i] == intersection[k]) 757 goto found; 758 /* If intersection[k] isn't in p->overlaps, exclude it 759 * from our set of placements overlapped by everything */ 760 continue; 761 found: 762 intersection[nintersection++] = intersection[k]; 763 } 764 } 765 766 if (nintersection == 0) 767 return false; /* no new exclusions */ 768 769 for (i = 0; i < nintersection; i++) { 770 p = intersection[i]; 771 772#ifdef SOLVER_DIAGNOSTICS 773 if (solver_diagnostics) { 774 printf("placement %s of domino %s overlaps all placements " 775 "of domino %s:", p->name, p->domino->name, d->name); 776 for (j = 0; j < d->nplacements; j++) 777 printf(" %s", d->placements[j]->name); 778 printf("\n"); 779 } 780#endif 781 rule_out_placement(sc, p); 782 } 783 784 return true; 785} 786 787/* 788 * If a placement of domino D overlaps the only remaining placement 789 * for some square S which is not also for domino D, then placing D 790 * here would require another copy of it in S, so we can rule it out. 791 */ 792static bool deduce_local_duplicate(struct solver_scratch *sc, int pi) 793{ 794 struct solver_placement *p = &sc->placements[pi]; 795 struct solver_domino *d = p->domino; 796 int i, j; 797 798 if (!p->active) 799 return false; 800 801 for (i = 0; i < p->noverlaps; i++) { 802 struct solver_placement *q = p->overlaps[i]; 803 struct solver_square *sq; 804 805 if (!q->active) 806 continue; 807 808 /* Find the square of q that _isn't_ part of p */ 809 sq = q->squares[1 - common_square_index(q, p)]; 810 811 for (j = 0; j < sq->nplacements; j++) 812 if (sq->placements[j] != q && sq->placements[j]->domino != d) 813 goto no; 814 815 /* If we get here, every possible placement for sq is either q 816 * itself, or another copy of d. Success! We can rule out p. */ 817#ifdef SOLVER_DIAGNOSTICS 818 if (solver_diagnostics) { 819 printf("placement %s of domino %s would force another copy of %s " 820 "in square %s\n", p->name, d->name, d->name, sq->name); 821 } 822#endif 823 824 rule_out_placement(sc, p); 825 return true; 826 827 no:; 828 } 829 830 return false; 831} 832 833/* 834 * If placement P overlaps one placement for each of two squares S,T 835 * such that all the remaining placements for both S and T are the 836 * same domino D (and none of those placements joins S and T to each 837 * other), then P can't be placed, because it would leave S,T each 838 * having to be a copy of D, i.e. duplicates. 839 */ 840static bool deduce_local_duplicate_2(struct solver_scratch *sc, int pi) 841{ 842 struct solver_placement *p = &sc->placements[pi]; 843 int i, j, k; 844 845 if (!p->active) 846 return false; 847 848 /* 849 * Iterate over pairs of placements qi,qj overlapping p. 850 */ 851 for (i = 0; i < p->noverlaps; i++) { 852 struct solver_placement *qi = p->overlaps[i]; 853 struct solver_square *sqi; 854 struct solver_domino *di = NULL; 855 856 if (!qi->active) 857 continue; 858 859 /* Find the square of qi that _isn't_ part of p */ 860 sqi = qi->squares[1 - common_square_index(qi, p)]; 861 862 /* 863 * Identify the unique domino involved in all possible 864 * placements of sqi other than qi. If there isn't a unique 865 * one (either too many or too few), move on and try the next 866 * qi. 867 */ 868 for (k = 0; k < sqi->nplacements; k++) { 869 struct solver_placement *pk = sqi->placements[k]; 870 if (sqi->placements[k] == qi) 871 continue; /* not counting qi itself */ 872 if (!di) 873 di = pk->domino; 874 else if (di != pk->domino) 875 goto done_qi; 876 } 877 if (!di) 878 goto done_qi; 879 880 /* 881 * Now find an appropriate qj != qi. 882 */ 883 for (j = 0; j < p->noverlaps; j++) { 884 struct solver_placement *qj = p->overlaps[j]; 885 struct solver_square *sqj; 886 bool found_di = false; 887 888 if (j == i || !qj->active) 889 continue; 890 891 sqj = qj->squares[1 - common_square_index(qj, p)]; 892 893 /* 894 * As above, we want the same domino di to be the only one 895 * sqj can be if placement qj is ruled out. But also we 896 * need no placement of sqj to overlap sqi. 897 */ 898 for (k = 0; k < sqj->nplacements; k++) { 899 struct solver_placement *pk = sqj->placements[k]; 900 if (pk == qj) 901 continue; /* not counting qj itself */ 902 if (pk->domino != di) 903 goto done_qj; /* found a different domino */ 904 if (pk->squares[0] == sqi || pk->squares[1] == sqi) 905 goto done_qj; /* sqi,sqj can be joined to each other */ 906 found_di = true; 907 } 908 if (!found_di) 909 goto done_qj; 910 911 /* If we get here, then every placement for either of sqi 912 * and sqj is a copy of di, except for the ones that 913 * overlap p. Success! We can rule out p. */ 914#ifdef SOLVER_DIAGNOSTICS 915 if (solver_diagnostics) { 916 printf("placement %s of domino %s would force squares " 917 "%s and %s to both be domino %s\n", 918 p->name, p->domino->name, 919 sqi->name, sqj->name, di->name); 920 } 921#endif 922 rule_out_placement(sc, p); 923 return true; 924 925 done_qj:; 926 } 927 928 done_qi:; 929 } 930 931 return false; 932} 933 934struct parity_findloop_ctx { 935 struct solver_scratch *sc; 936 struct solver_square *sq; 937 int i; 938}; 939 940static int parity_neighbour(int vertex, void *vctx) 941{ 942 struct parity_findloop_ctx *ctx = (struct parity_findloop_ctx *)vctx; 943 struct solver_placement *p; 944 945 if (vertex >= 0) { 946 ctx->sq = &ctx->sc->squares[vertex]; 947 ctx->i = 0; 948 } else { 949 assert(ctx->sq); 950 } 951 952 if (ctx->i >= ctx->sq->nplacements) { 953 ctx->sq = NULL; 954 return -1; 955 } 956 957 p = ctx->sq->placements[ctx->i++]; 958 return p->squares[0]->index + p->squares[1]->index - ctx->sq->index; 959} 960 961/* 962 * Look for dominoes whose placement would disconnect the unfilled 963 * area of the grid into pieces with odd area. Such a domino can't be 964 * placed, because then the area on each side of it would be 965 * untileable. 966 */ 967static bool deduce_parity(struct solver_scratch *sc) 968{ 969 struct parity_findloop_ctx pflctx; 970 bool done_something = false; 971 int pi; 972 973 /* 974 * Run findloop, aka Tarjan's bridge-finding algorithm, on the 975 * graph whose vertices are squares, with two vertices separated 976 * by an edge iff some not-yet-ruled-out domino placement covers 977 * them both. (So each edge itself corresponds to a domino 978 * placement.) 979 * 980 * The effect is that any bridge in this graph is a domino whose 981 * placement would separate two previously connected areas of the 982 * unfilled squares of the grid. 983 * 984 * Placing that domino would not just disconnect those areas from 985 * each other, but also use up one square of each. So if we want 986 * to avoid leaving two odd areas after placing the domino, it 987 * follows that we want to avoid the bridge having an _even_ 988 * number of vertices on each side. 989 */ 990 pflctx.sc = sc; 991 findloop_run(sc->fls, sc->wh, parity_neighbour, &pflctx); 992 993 for (pi = 0; pi < sc->pc; pi++) { 994 struct solver_placement *p = &sc->placements[pi]; 995 int size0, size1; 996 997 if (!p->active) 998 continue; 999 if (!findloop_is_bridge( 1000 sc->fls, p->squares[0]->index, p->squares[1]->index, 1001 &size0, &size1)) 1002 continue; 1003 /* To make a deduction, size0 and size1 must both be even, 1004 * i.e. after placing this domino decrements each by 1 they 1005 * would both become odd and untileable areas. */ 1006 if ((size0 | size1) & 1) 1007 continue; 1008 1009#ifdef SOLVER_DIAGNOSTICS 1010 if (solver_diagnostics) { 1011 printf("placement %s of domino %s would create two odd-sized " 1012 "areas\n", p->name, p->domino->name); 1013 } 1014#endif 1015 rule_out_placement(sc, p); 1016 done_something = true; 1017 } 1018 1019 return done_something; 1020} 1021 1022/* 1023 * Try to find a set of squares all containing the same number, such 1024 * that the set of possible dominoes for all the squares in that set 1025 * is small enough to let us rule out placements of those dominoes 1026 * elsewhere. 1027 */ 1028static bool deduce_set(struct solver_scratch *sc, bool doubles) 1029{ 1030 struct solver_square **sqs, **sqp, **sqe; 1031 int num, nsq, i, j; 1032 unsigned long domino_sets[16], adjacent[16]; 1033 struct solver_domino *ds[16]; 1034 bool done_something = false; 1035 1036 if (!sc->squares_by_number) 1037 sc->squares_by_number = snewn(sc->wh, struct solver_square *); 1038 if (!sc->wh_scratch) 1039 sc->wh_scratch = snewn(sc->wh, int); 1040 1041 if (!sc->squares_by_number_initialised) { 1042 /* 1043 * If this is the first call to this function for a given 1044 * grid, start by sorting the squares by their containing 1045 * number. 1046 */ 1047 for (i = 0; i < sc->wh; i++) 1048 sc->squares_by_number[i] = &sc->squares[i]; 1049 qsort(sc->squares_by_number, sc->wh, sizeof(*sc->squares_by_number), 1050 squares_by_number_cmpfn); 1051 } 1052 1053 sqp = sc->squares_by_number; 1054 sqe = sc->squares_by_number + sc->wh; 1055 for (num = 0; num <= sc->n; num++) { 1056 unsigned long squares; 1057 unsigned long squares_done; 1058 1059 /* Find the bounds of the subinterval of squares_by_number 1060 * containing squares with this particular number. */ 1061 sqs = sqp; 1062 while (sqp < sqe && (*sqp)->number == num) 1063 sqp++; 1064 nsq = sqp - sqs; 1065 1066 /* 1067 * Now sqs[0], ..., sqs[nsq-1] are the squares containing 'num'. 1068 */ 1069 1070 if (nsq > lenof(domino_sets)) { 1071 /* 1072 * Abort this analysis if we're trying to enumerate all 1073 * the subsets of a too-large base set. 1074 * 1075 * This _shouldn't_ happen, at the time of writing this 1076 * code, because the largest puzzle we support is only 1077 * supposed to have 10 instances of each number, and part 1078 * of our input grid validation checks that each number 1079 * does appear the right number of times. But just in case 1080 * weird test input makes its way to this function, or the 1081 * puzzle sizes are expanded later, it's easy enough to 1082 * just rule out doing this analysis for overlarge sets of 1083 * numbers. 1084 */ 1085 continue; 1086 } 1087 1088 /* 1089 * Index the squares in wh_scratch, which we're using as a 1090 * lookup table to map the official index of a square back to 1091 * its value in our local indexing scheme. 1092 */ 1093 for (i = 0; i < nsq; i++) 1094 sc->wh_scratch[sqs[i]->index] = i; 1095 1096 /* 1097 * For each square, make a bit mask of the dominoes that can 1098 * overlap it, by finding the number at the other end of each 1099 * one. 1100 * 1101 * Also, for each square, make a bit mask of other squares in 1102 * the current list that might occupy the _same_ domino 1103 * (because a possible placement of a double overlaps both). 1104 * We'll need that for evaluating whether sets are properly 1105 * exhaustive. 1106 */ 1107 for (i = 0; i < nsq; i++) 1108 adjacent[i] = 0; 1109 1110 for (i = 0; i < nsq; i++) { 1111 struct solver_square *sq = sqs[i]; 1112 unsigned long mask = 0; 1113 1114 for (j = 0; j < sq->nplacements; j++) { 1115 struct solver_placement *p = sq->placements[j]; 1116 int othernum = p->domino->lo + p->domino->hi - num; 1117 mask |= 1UL << othernum; 1118 ds[othernum] = p->domino; /* so we can find them later */ 1119 1120 if (othernum == num) { 1121 /* 1122 * Special case: this is a double, so it gives 1123 * rise to entries in adjacent[]. 1124 */ 1125 int i2 = sc->wh_scratch[p->squares[0]->index + 1126 p->squares[1]->index - sq->index]; 1127 adjacent[i] |= 1UL << i2; 1128 adjacent[i2] |= 1UL << i; 1129 } 1130 } 1131 1132 domino_sets[i] = mask; 1133 1134 } 1135 1136 squares_done = 0; 1137 1138 for (squares = 0; squares < (1UL << nsq); squares++) { 1139 unsigned long dominoes = 0; 1140 int bitpos, nsquares, ndominoes; 1141 bool got_adj_squares = false; 1142 bool reported = false; 1143 bool rule_out_nondoubles; 1144 int min_nused_for_double; 1145#ifdef SOLVER_DIAGNOSTICS 1146 const char *rule_out_text; 1147#endif 1148 1149 /* 1150 * We don't do set analysis on the same square of the grid 1151 * more than once in this loop. Otherwise you generate 1152 * pointlessly overcomplicated diagnostics for simpler 1153 * follow-up deductions. For example, suppose squares 1154 * {A,B} must go with dominoes {X,Y}. So you rule out X,Y 1155 * elsewhere, and then it turns out square C (from which 1156 * one of those was eliminated) has only one remaining 1157 * possibility Z. What you _don't_ want to do is 1158 * triumphantly report a second case of set elimination 1159 * where you say 'And also, squares {A,B,C} have to be 1160 * {X,Y,Z}!' You'd prefer to give 'now C has to be Z' as a 1161 * separate deduction later, more simply phrased. 1162 */ 1163 if (squares & squares_done) 1164 continue; 1165 1166 nsquares = 0; 1167 1168 /* Make the set of dominoes that these squares can inhabit. */ 1169 for (bitpos = 0; bitpos < nsq; bitpos++) { 1170 if (!(1 & (squares >> bitpos))) 1171 continue; /* this bit isn't set in the mask */ 1172 1173 if (adjacent[bitpos] & squares) 1174 got_adj_squares = true; 1175 1176 dominoes |= domino_sets[bitpos]; 1177 nsquares++; 1178 } 1179 1180 /* Count them. */ 1181 ndominoes = 0; 1182 for (bitpos = 0; bitpos < nsq; bitpos++) 1183 ndominoes += 1 & (dominoes >> bitpos); 1184 1185 /* 1186 * Do the two sets have the right relative size? 1187 */ 1188 if (!got_adj_squares) { 1189 /* 1190 * The normal case, in which every possible domino 1191 * placement involves at most _one_ of these squares. 1192 * 1193 * This is exactly analogous to the set analysis 1194 * deductions in many other puzzles: if our N squares 1195 * between them have to account for N distinct 1196 * dominoes, with exactly one of those dominoes to 1197 * each square, then all those dominoes correspond to 1198 * all those squares and we can rule out any 1199 * placements of the same dominoes appearing 1200 * elsewhere. 1201 */ 1202 if (ndominoes != nsquares) 1203 continue; 1204 rule_out_nondoubles = true; 1205 min_nused_for_double = 1; 1206#ifdef SOLVER_DIAGNOSTICS 1207 rule_out_text = "all of them elsewhere"; 1208#endif 1209 } else { 1210 if (!doubles) 1211 continue; /* not at this difficulty level */ 1212 1213 /* 1214 * But in Dominosa, there's a special case if _two_ 1215 * squares in this set can possibly both be covered by 1216 * the same double domino. (I.e. if they are adjacent, 1217 * and moreover, the double-domino placement 1218 * containing both is not yet ruled out.) 1219 * 1220 * In that situation, the simple argument doesn't hold 1221 * up, because the N squares might be covered by N-1 1222 * dominoes - or, put another way, if you list the 1223 * containing domino for each of the squares, they 1224 * might not be all distinct. 1225 * 1226 * In that situation, we can still do something, but 1227 * the details vary, and there are two further cases. 1228 */ 1229 if (ndominoes == nsquares-1) { 1230 /* 1231 * Suppose there is one _more_ square in our set 1232 * than there are dominoes it can involve. For 1233 * example, suppose we had four '0' squares which 1234 * between them could contain only the 0-0, 0-1 1235 * and 0-2 dominoes. 1236 * 1237 * Then that can only work at all if the 0-0 1238 * covers two of those squares - and in that 1239 * situation that _must_ be what's happened. 1240 * 1241 * So we can rule out the 0-1 and 0-2 dominoes (in 1242 * this example) in any placement that doesn't use 1243 * one of the squares in this set. And we can rule 1244 * out a placement of the 0-0 even if it uses 1245 * _one_ square from this set: in this situation, 1246 * we have to insist on it using _two_. 1247 */ 1248 rule_out_nondoubles = true; 1249 min_nused_for_double = 2; 1250#ifdef SOLVER_DIAGNOSTICS 1251 rule_out_text = "all of them elsewhere " 1252 "(including the double if it fails to use both)"; 1253#endif 1254 } else if (ndominoes == nsquares) { 1255 /* 1256 * A restricted form of the deduction is still 1257 * possible if we have the same number of dominoes 1258 * as squares. 1259 * 1260 * If we have _three_ '0' squares none of which 1261 * can be any domino other than 0-0, 0-1 and 0-2, 1262 * and there's still a possibility of an 0-0 1263 * domino using up two of them, then we can't rule 1264 * out 0-1 or 0-2 anywhere else, because it's 1265 * possible that these three squares only use two 1266 * of the dominoes between them. 1267 * 1268 * But we _can_ rule out the double 0-0, in any 1269 * placement that uses _none_ of our three 1270 * squares. Because we do know that _at least one_ 1271 * of our squares must be involved in the 0-0, or 1272 * else the three of them would only have the 1273 * other two dominoes left. 1274 */ 1275 rule_out_nondoubles = false; 1276 min_nused_for_double = 1; 1277#ifdef SOLVER_DIAGNOSTICS 1278 rule_out_text = "the double elsewhere"; 1279#endif 1280 } else { 1281 /* 1282 * If none of those cases has happened, then our 1283 * set admits no deductions at all. 1284 */ 1285 continue; 1286 } 1287 } 1288 1289 /* Skip sets of size 1, or whose complement has size 1. 1290 * Those can be handled by a simpler analysis, and should 1291 * be, for more sensible solver diagnostics. */ 1292 if (ndominoes <= 1 || ndominoes >= nsq-1) 1293 continue; 1294 1295 /* 1296 * We've found a set! That means we can rule out any 1297 * placement of any domino in that set which would leave 1298 * the squares in the set with too few dominoes between 1299 * them. 1300 * 1301 * We may or may not actually end up ruling anything out 1302 * here. But even if we don't, we should record that these 1303 * squares form a self-contained set, so that we don't 1304 * pointlessly report a superset of them later which could 1305 * instead be reported as just the other ones. 1306 * 1307 * Or rather, we do that for the main cases that let us 1308 * rule out lots of dominoes. We only do this with the 1309 * borderline case where we can only rule out a double if 1310 * we _actually_ rule something out. Otherwise we'll never 1311 * even _find_ a larger set with the same number of 1312 * dominoes! 1313 */ 1314 if (rule_out_nondoubles) 1315 squares_done |= squares; 1316 1317 for (bitpos = 0; bitpos < nsq; bitpos++) { 1318 struct solver_domino *d; 1319 1320 if (!(1 & (dominoes >> bitpos))) 1321 continue; 1322 d = ds[bitpos]; 1323 1324 for (i = d->nplacements; i-- > 0 ;) { 1325 struct solver_placement *p = d->placements[i]; 1326 int si, nused; 1327 1328 /* Count how many of our squares this placement uses. */ 1329 for (si = nused = 0; si < 2; si++) { 1330 struct solver_square *sq2 = p->squares[si]; 1331 if (sq2->number == num && 1332 (1 & (squares >> sc->wh_scratch[sq2->index]))) 1333 nused++; 1334 } 1335 1336 /* See if that's too many to rule it out. */ 1337 if (d->lo == d->hi) { 1338 if (nused >= min_nused_for_double) 1339 continue; 1340 } else { 1341 if (nused > 0 || !rule_out_nondoubles) 1342 continue; 1343 } 1344 1345 if (!reported) { 1346 reported = true; 1347 done_something = true; 1348 1349 /* In case we didn't do this above */ 1350 squares_done |= squares; 1351 1352#ifdef SOLVER_DIAGNOSTICS 1353 if (solver_diagnostics) { 1354 int b; 1355 const char *sep; 1356 printf("squares {"); 1357 for (sep = "", b = 0; b < nsq; b++) 1358 if (1 & (squares >> b)) { 1359 printf("%s%s", sep, sqs[b]->name); 1360 sep = ","; 1361 } 1362 printf("} can contain only dominoes {"); 1363 for (sep = "", b = 0; b < nsq; b++) 1364 if (1 & (dominoes >> b)) { 1365 printf("%s%s", sep, ds[b]->name); 1366 sep = ","; 1367 } 1368 printf("}, so rule out %s", rule_out_text); 1369 printf("\n"); 1370 } 1371#endif 1372 } 1373 1374 rule_out_placement(sc, p); 1375 } 1376 } 1377 } 1378 1379 } 1380 1381 return done_something; 1382} 1383 1384static int forcing_chain_dup_cmp(const void *av, const void *bv, void *ctx) 1385{ 1386 struct solver_scratch *sc = (struct solver_scratch *)ctx; 1387 int a = *(const int *)av, b = *(const int *)bv; 1388 int ac, bc; 1389 1390 ac = sc->pc_scratch[a]; 1391 bc = sc->pc_scratch[b]; 1392 if (ac != bc) return ac > bc ? +1 : -1; 1393 1394 ac = sc->placements[a].domino->index; 1395 bc = sc->placements[b].domino->index; 1396 if (ac != bc) return ac > bc ? +1 : -1; 1397 1398 return 0; 1399} 1400 1401static int forcing_chain_sq_cmp(const void *av, const void *bv, void *ctx) 1402{ 1403 struct solver_scratch *sc = (struct solver_scratch *)ctx; 1404 int a = *(const int *)av, b = *(const int *)bv; 1405 int ac, bc; 1406 1407 ac = sc->placements[a].domino->index; 1408 bc = sc->placements[b].domino->index; 1409 if (ac != bc) return ac > bc ? +1 : -1; 1410 1411 ac = sc->pc_scratch[a]; 1412 bc = sc->pc_scratch[b]; 1413 if (ac != bc) return ac > bc ? +1 : -1; 1414 1415 return 0; 1416} 1417 1418static bool deduce_forcing_chain(struct solver_scratch *sc) 1419{ 1420 int si, pi, di, j, k, m; 1421 bool done_something = false; 1422 1423 if (!sc->wh_scratch) 1424 sc->wh_scratch = snewn(sc->wh, int); 1425 if (!sc->pc_scratch) 1426 sc->pc_scratch = snewn(sc->pc, int); 1427 if (!sc->pc_scratch2) 1428 sc->pc_scratch2 = snewn(sc->pc, int); 1429 if (!sc->dc_scratch) 1430 sc->dc_scratch = snewn(sc->dc, int); 1431 if (!sc->dsf_scratch) 1432 sc->dsf_scratch = dsf_new_flip(sc->pc); 1433 1434 /* 1435 * Start by identifying chains of placements which must all occur 1436 * together if any of them occurs. We do this by making 1437 * dsf_scratch a flip dsf binding the placements into an equivalence 1438 * class for each entire forcing chain, with the two possible sets 1439 * of dominoes for the chain listed as inverses. 1440 */ 1441 dsf_reinit(sc->dsf_scratch); 1442 for (si = 0; si < sc->wh; si++) { 1443 struct solver_square *sq = &sc->squares[si]; 1444 if (sq->nplacements == 2) 1445 dsf_merge_flip(sc->dsf_scratch, 1446 sq->placements[0]->index, 1447 sq->placements[1]->index, true); 1448 } 1449 /* 1450 * Now read out the whole dsf into pc_scratch, flattening its 1451 * structured data into a simple integer id per chain of dominoes 1452 * that must occur together. 1453 * 1454 * The integer ids have the property that any two that differ only 1455 * in the lowest bit (i.e. of the form {2n,2n+1}) represent 1456 * complementary chains, each of which rules out the other. 1457 */ 1458 for (pi = 0; pi < sc->pc; pi++) { 1459 bool inv; 1460 int c = dsf_canonify_flip(sc->dsf_scratch, pi, &inv); 1461 sc->pc_scratch[pi] = c * 2 + (inv ? 1 : 0); 1462 } 1463 1464 /* 1465 * Identify chains that contain a duplicate domino, and rule them 1466 * out. We do this by making a list of the placement indices in 1467 * pc_scratch2, sorted by (chain id, domino id), so that dupes 1468 * become adjacent. 1469 */ 1470 for (pi = 0; pi < sc->pc; pi++) 1471 sc->pc_scratch2[pi] = pi; 1472 arraysort(sc->pc_scratch2, sc->pc, forcing_chain_dup_cmp, sc); 1473 1474 for (j = 0; j < sc->pc ;) { 1475 struct solver_domino *duplicated_domino = NULL; 1476 1477 /* 1478 * This loop iterates once per contiguous segment of the same 1479 * value in pc_scratch2, i.e. once per chain. 1480 */ 1481 int ci = sc->pc_scratch[sc->pc_scratch2[j]]; 1482 int climit, cstart = j; 1483 while (j < sc->pc && sc->pc_scratch[sc->pc_scratch2[j]] == ci) 1484 j++; 1485 climit = j; 1486 1487 /* 1488 * Now look for a duplicate domino within that chain. 1489 */ 1490 for (k = cstart; k + 1 < climit; k++) { 1491 struct solver_placement *p = &sc->placements[sc->pc_scratch2[k]]; 1492 struct solver_placement *q = &sc->placements[sc->pc_scratch2[k+1]]; 1493 if (p->domino == q->domino) { 1494 duplicated_domino = p->domino; 1495 break; 1496 } 1497 } 1498 1499 if (!duplicated_domino) 1500 continue; 1501 1502#ifdef SOLVER_DIAGNOSTICS 1503 if (solver_diagnostics) { 1504 printf("domino %s occurs more than once in forced chain:", 1505 duplicated_domino->name); 1506 for (k = cstart; k < climit; k++) 1507 printf(" %s", sc->placements[sc->pc_scratch2[k]].name); 1508 printf("\n"); 1509 } 1510#endif 1511 1512 for (k = cstart; k < climit; k++) 1513 rule_out_placement(sc, &sc->placements[sc->pc_scratch2[k]]); 1514 1515 done_something = true; 1516 } 1517 1518 if (done_something) 1519 return true; 1520 1521 /* 1522 * A second way in which a whole forcing chain can be ruled out is 1523 * if it contains all the dominoes that can occupy some other 1524 * square, so that if the domnioes in the chain were all laid, the 1525 * other square would be left without any choices. 1526 * 1527 * To detect this, we sort the placements again, this time by 1528 * (domino index, chain index), so that we can easily find a 1529 * sorted list of chains per domino. That allows us to iterate 1530 * over the squares and check for a chain id common to all the 1531 * placements of that square. 1532 */ 1533 for (pi = 0; pi < sc->pc; pi++) 1534 sc->pc_scratch2[pi] = pi; 1535 arraysort(sc->pc_scratch2, sc->pc, forcing_chain_sq_cmp, sc); 1536 1537 /* Store a lookup table of the first entry in pc_scratch2 1538 * corresponding to each domino. */ 1539 for (di = j = 0; j < sc->pc; j++) { 1540 while (di <= sc->placements[sc->pc_scratch2[j]].domino->index) { 1541 assert(di < sc->dc); 1542 sc->dc_scratch[di++] = j; 1543 } 1544 } 1545 assert(di == sc->dc); 1546 1547 for (si = 0; si < sc->wh; si++) { 1548 struct solver_square *sq = &sc->squares[si]; 1549 int listpos = 0, listsize = 0, listout = 0; 1550 int exclude[4]; 1551 int n_exclude; 1552 1553 if (sq->nplacements < 2) 1554 continue; /* too simple to be worth trying */ 1555 1556 /* 1557 * Start by checking for chains this square can actually form 1558 * part of. We won't consider those. (The aim is to find a 1559 * completely _different_ square whose placements are all 1560 * ruled out by a chain.) 1561 */ 1562 assert(sq->nplacements <= lenof(exclude)); 1563 for (j = n_exclude = 0; j < sq->nplacements; j++) 1564 exclude[n_exclude++] = sc->pc_scratch[sq->placements[j]->index]; 1565 1566 for (j = 0; j < sq->nplacements; j++) { 1567 struct solver_domino *d = sq->placements[j]->domino; 1568 1569 listout = listpos = 0; 1570 1571 for (k = sc->dc_scratch[d->index]; 1572 k < sc->pc && sc->placements[sc->pc_scratch2[k]].domino == d; 1573 k++) { 1574 int chain = sc->pc_scratch[sc->pc_scratch2[k]]; 1575 bool keep; 1576 1577 if (!sc->placements[sc->pc_scratch2[k]].active) 1578 continue; 1579 1580 if (j == 0) { 1581 keep = true; 1582 } else { 1583 while (listpos < listsize && 1584 sc->wh_scratch[listpos] < chain) 1585 listpos++; 1586 keep = (listpos < listsize && 1587 sc->wh_scratch[listpos] == chain); 1588 } 1589 1590 for (m = 0; m < n_exclude; m++) 1591 if (chain == exclude[m]) 1592 keep = false; 1593 1594 if (keep) 1595 sc->wh_scratch[listout++] = chain; 1596 } 1597 1598 listsize = listout; 1599 if (listsize == 0) 1600 break; /* ruled out all chains; terminate loop early */ 1601 } 1602 1603 for (listpos = 0; listpos < listsize; listpos++) { 1604 int chain = sc->wh_scratch[listpos]; 1605 1606 /* 1607 * We've found a chain we can rule out. 1608 */ 1609#ifdef SOLVER_DIAGNOSTICS 1610 if (solver_diagnostics) { 1611 printf("all choices for square %s would be ruled out " 1612 "by forced chain:", sq->name); 1613 for (pi = 0; pi < sc->pc; pi++) 1614 if (sc->pc_scratch[pi] == chain) 1615 printf(" %s", sc->placements[pi].name); 1616 printf("\n"); 1617 } 1618#endif 1619 1620 for (pi = 0; pi < sc->pc; pi++) 1621 if (sc->pc_scratch[pi] == chain) 1622 rule_out_placement(sc, &sc->placements[pi]); 1623 1624 done_something = true; 1625 } 1626 } 1627 1628 /* 1629 * Another thing you can do with forcing chains, besides ruling 1630 * out a whole one at a time, is to look at each pair of chains 1631 * that overlap each other. Each such pair gives you two sets of 1632 * domino placements, such that if either set is not placed, then 1633 * the other one must be. 1634 * 1635 * This means that any domino which has a placement in _both_ 1636 * chains of a pair must occupy one of those two placements, i.e. 1637 * we can rule that domino out anywhere else it might appear. 1638 */ 1639 for (di = 0; di < sc->dc; di++) { 1640 struct solver_domino *d = &sc->dominoes[di]; 1641 1642 if (d->nplacements <= 2) 1643 continue; /* not enough placements to rule one out */ 1644 1645 for (j = 0; j+1 < d->nplacements; j++) { 1646 int ij = d->placements[j]->index; 1647 int cj = sc->pc_scratch[ij]; 1648 for (k = j+1; k < d->nplacements; k++) { 1649 int ik = d->placements[k]->index; 1650 int ck = sc->pc_scratch[ik]; 1651 if ((cj ^ ck) == 1) { 1652 /* 1653 * Placements j,k of domino d are in complementary 1654 * chains, so we can rule out all the others. 1655 */ 1656 int i; 1657 1658#ifdef SOLVER_DIAGNOSTICS 1659 if (solver_diagnostics) { 1660 printf("domino %s occurs in both complementary " 1661 "forced chains:", d->name); 1662 for (i = 0; i < sc->pc; i++) 1663 if (sc->pc_scratch[i] == cj) 1664 printf(" %s", sc->placements[i].name); 1665 printf(" and"); 1666 for (i = 0; i < sc->pc; i++) 1667 if (sc->pc_scratch[i] == ck) 1668 printf(" %s", sc->placements[i].name); 1669 printf("\n"); 1670 } 1671#endif 1672 1673 for (i = d->nplacements; i-- > 0 ;) 1674 if (i != j && i != k) 1675 rule_out_placement(sc, d->placements[i]); 1676 1677 done_something = true; 1678 goto done_this_domino; 1679 } 1680 } 1681 } 1682 1683 done_this_domino:; 1684 } 1685 1686 return done_something; 1687} 1688 1689/* 1690 * Run the solver until it can't make any more progress. 1691 * 1692 * Return value is: 1693 * 0 = no solution exists (puzzle clues are unsatisfiable) 1694 * 1 = unique solution found (success!) 1695 * 2 = multiple possibilities remain (puzzle is ambiguous or solver is not 1696 * smart enough) 1697 */ 1698static int run_solver(struct solver_scratch *sc, int max_diff_allowed) 1699{ 1700 int di, si, pi; 1701 bool done_something; 1702 1703#ifdef SOLVER_DIAGNOSTICS 1704 if (solver_diagnostics) { 1705 int di, j; 1706 printf("Initial possible placements:\n"); 1707 for (di = 0; di < sc->dc; di++) { 1708 struct solver_domino *d = &sc->dominoes[di]; 1709 printf(" %s:", d->name); 1710 for (j = 0; j < d->nplacements; j++) 1711 printf(" %s", d->placements[j]->name); 1712 printf("\n"); 1713 } 1714 } 1715#endif 1716 1717 do { 1718 done_something = false; 1719 1720 for (di = 0; di < sc->dc; di++) 1721 if (deduce_domino_single_placement(sc, di)) 1722 done_something = true; 1723 if (done_something) { 1724 sc->max_diff_used = max(sc->max_diff_used, DIFF_TRIVIAL); 1725 continue; 1726 } 1727 1728 for (si = 0; si < sc->wh; si++) 1729 if (deduce_square_single_placement(sc, si)) 1730 done_something = true; 1731 if (done_something) { 1732 sc->max_diff_used = max(sc->max_diff_used, DIFF_TRIVIAL); 1733 continue; 1734 } 1735 1736 if (max_diff_allowed <= DIFF_TRIVIAL) 1737 continue; 1738 1739 for (si = 0; si < sc->wh; si++) 1740 if (deduce_square_single_domino(sc, si)) 1741 done_something = true; 1742 if (done_something) { 1743 sc->max_diff_used = max(sc->max_diff_used, DIFF_BASIC); 1744 continue; 1745 } 1746 1747 for (di = 0; di < sc->dc; di++) 1748 if (deduce_domino_must_overlap(sc, di)) 1749 done_something = true; 1750 if (done_something) { 1751 sc->max_diff_used = max(sc->max_diff_used, DIFF_BASIC); 1752 continue; 1753 } 1754 1755 for (pi = 0; pi < sc->pc; pi++) 1756 if (deduce_local_duplicate(sc, pi)) 1757 done_something = true; 1758 if (done_something) { 1759 sc->max_diff_used = max(sc->max_diff_used, DIFF_BASIC); 1760 continue; 1761 } 1762 1763 for (pi = 0; pi < sc->pc; pi++) 1764 if (deduce_local_duplicate_2(sc, pi)) 1765 done_something = true; 1766 if (done_something) { 1767 sc->max_diff_used = max(sc->max_diff_used, DIFF_BASIC); 1768 continue; 1769 } 1770 1771 if (deduce_parity(sc)) 1772 done_something = true; 1773 if (done_something) { 1774 sc->max_diff_used = max(sc->max_diff_used, DIFF_BASIC); 1775 continue; 1776 } 1777 1778 if (max_diff_allowed <= DIFF_BASIC) 1779 continue; 1780 1781 if (deduce_set(sc, false)) 1782 done_something = true; 1783 if (done_something) { 1784 sc->max_diff_used = max(sc->max_diff_used, DIFF_HARD); 1785 continue; 1786 } 1787 1788 if (max_diff_allowed <= DIFF_HARD) 1789 continue; 1790 1791 if (deduce_set(sc, true)) 1792 done_something = true; 1793 if (done_something) { 1794 sc->max_diff_used = max(sc->max_diff_used, DIFF_EXTREME); 1795 continue; 1796 } 1797 1798 if (deduce_forcing_chain(sc)) 1799 done_something = true; 1800 if (done_something) { 1801 sc->max_diff_used = max(sc->max_diff_used, DIFF_EXTREME); 1802 continue; 1803 } 1804 1805 } while (done_something); 1806 1807#ifdef SOLVER_DIAGNOSTICS 1808 if (solver_diagnostics) { 1809 int di, j; 1810 printf("Final possible placements:\n"); 1811 for (di = 0; di < sc->dc; di++) { 1812 struct solver_domino *d = &sc->dominoes[di]; 1813 printf(" %s:", d->name); 1814 for (j = 0; j < d->nplacements; j++) 1815 printf(" %s", d->placements[j]->name); 1816 printf("\n"); 1817 } 1818 } 1819#endif 1820 1821 for (di = 0; di < sc->dc; di++) 1822 if (sc->dominoes[di].nplacements == 0) 1823 return 0; 1824 for (di = 0; di < sc->dc; di++) 1825 if (sc->dominoes[di].nplacements > 1) 1826 return 2; 1827 return 1; 1828} 1829 1830/* ---------------------------------------------------------------------- 1831 * Functions for generating a candidate puzzle (before we run the 1832 * solver to check it's soluble at the right difficulty level). 1833 */ 1834 1835struct alloc_val; 1836struct alloc_loc; 1837 1838struct alloc_scratch { 1839 /* Game parameters. */ 1840 int n, w, h, wh, dc; 1841 1842 /* The domino layout. Indexed by squares in the usual y*w+x raster 1843 * order: layout[i] gives the index of the other square in the 1844 * same domino as square i. */ 1845 int *layout; 1846 1847 /* The output array, containing a number in every square. */ 1848 int *numbers; 1849 1850 /* List of domino values (i.e. number pairs), indexed by DINDEX. */ 1851 struct alloc_val *vals; 1852 1853 /* List of domino locations, indexed arbitrarily. */ 1854 struct alloc_loc *locs; 1855 1856 /* Preallocated scratch spaces. */ 1857 int *wh_scratch; /* size wh */ 1858 int *wh2_scratch; /* size 2*wh */ 1859}; 1860 1861struct alloc_val { 1862 int lo, hi; 1863 bool confounder; 1864}; 1865 1866struct alloc_loc { 1867 int sq[2]; 1868}; 1869 1870static struct alloc_scratch *alloc_make_scratch(int n) 1871{ 1872 struct alloc_scratch *as = snew(struct alloc_scratch); 1873 int lo, hi; 1874 1875 as->n = n; 1876 as->w = n+2; 1877 as->h = n+1; 1878 as->wh = as->w * as->h; 1879 as->dc = DCOUNT(n); 1880 1881 as->layout = snewn(as->wh, int); 1882 as->numbers = snewn(as->wh, int); 1883 as->vals = snewn(as->dc, struct alloc_val); 1884 as->locs = snewn(as->dc, struct alloc_loc); 1885 as->wh_scratch = snewn(as->wh, int); 1886 as->wh2_scratch = snewn(as->wh * 2, int); 1887 1888 for (hi = 0; hi <= n; hi++) 1889 for (lo = 0; lo <= hi; lo++) { 1890 struct alloc_val *v = &as->vals[DINDEX(hi, lo)]; 1891 v->lo = lo; 1892 v->hi = hi; 1893 } 1894 1895 return as; 1896} 1897 1898static void alloc_free_scratch(struct alloc_scratch *as) 1899{ 1900 sfree(as->layout); 1901 sfree(as->numbers); 1902 sfree(as->vals); 1903 sfree(as->locs); 1904 sfree(as->wh_scratch); 1905 sfree(as->wh2_scratch); 1906 sfree(as); 1907} 1908 1909static void alloc_make_layout(struct alloc_scratch *as, random_state *rs) 1910{ 1911 int i, pos; 1912 1913 domino_layout_prealloc(as->w, as->h, rs, 1914 as->layout, as->wh_scratch, as->wh2_scratch); 1915 1916 for (i = pos = 0; i < as->wh; i++) { 1917 if (as->layout[i] > i) { 1918 struct alloc_loc *loc; 1919 assert(pos < as->dc); 1920 1921 loc = &as->locs[pos++]; 1922 loc->sq[0] = i; 1923 loc->sq[1] = as->layout[i]; 1924 } 1925 } 1926 assert(pos == as->dc); 1927} 1928 1929static void alloc_trivial(struct alloc_scratch *as, random_state *rs) 1930{ 1931 int i; 1932 for (i = 0; i < as->dc; i++) 1933 as->wh_scratch[i] = i; 1934 shuffle(as->wh_scratch, as->dc, sizeof(*as->wh_scratch), rs); 1935 1936 for (i = 0; i < as->dc; i++) { 1937 struct alloc_val *val = &as->vals[as->wh_scratch[i]]; 1938 struct alloc_loc *loc = &as->locs[i]; 1939 int which_lo = random_upto(rs, 2), which_hi = 1 - which_lo; 1940 as->numbers[loc->sq[which_lo]] = val->lo; 1941 as->numbers[loc->sq[which_hi]] = val->hi; 1942 } 1943} 1944 1945/* 1946 * Given a domino location in the form of two square indices, compute 1947 * the square indices of the domino location that would lie on one 1948 * side of it. Returns false if the location would be outside the 1949 * grid, or if it isn't actually a domino in the layout. 1950 */ 1951static bool alloc_find_neighbour( 1952 struct alloc_scratch *as, int p0, int p1, int *n0, int *n1) 1953{ 1954 int x0 = p0 % as->w, y0 = p0 / as->w, x1 = p1 % as->w, y1 = p1 / as->w; 1955 int dy = y1-y0, dx = x1-x0; 1956 int nx0 = x0 + dy, ny0 = y0 - dx, nx1 = x1 + dy, ny1 = y1 - dx; 1957 int np0, np1; 1958 1959 if (!(nx0 >= 0 && nx0 < as->w && ny0 >= 0 && ny0 < as->h && 1960 nx1 >= 1 && nx1 < as->w && ny1 >= 1 && ny1 < as->h)) 1961 return false; /* out of bounds */ 1962 1963 np0 = ny0 * as->w + nx0; 1964 np1 = ny1 * as->w + nx1; 1965 if (as->layout[np0] != np1) 1966 return false; /* not a domino */ 1967 1968 *n0 = np0; 1969 *n1 = np1; 1970 return true; 1971} 1972 1973static bool alloc_try_unique(struct alloc_scratch *as, random_state *rs) 1974{ 1975 int i; 1976 for (i = 0; i < as->dc; i++) 1977 as->wh_scratch[i] = i; 1978 shuffle(as->wh_scratch, as->dc, sizeof(*as->wh_scratch), rs); 1979 for (i = 0; i < as->dc; i++) 1980 as->wh2_scratch[i] = i; 1981 shuffle(as->wh2_scratch, as->dc, sizeof(*as->wh2_scratch), rs); 1982 1983 for (i = 0; i < as->wh; i++) 1984 as->numbers[i] = -1; 1985 1986 for (i = 0; i < as->dc; i++) { 1987 struct alloc_val *val = &as->vals[as->wh_scratch[i]]; 1988 struct alloc_loc *loc = &as->locs[as->wh2_scratch[i]]; 1989 int which_lo, which_hi; 1990 bool can_lo_0 = true, can_lo_1 = true; 1991 int n0, n1; 1992 1993 /* 1994 * This is basically the same strategy as alloc_trivial: 1995 * simply iterate through the locations and values in random 1996 * relative order and pair them up. But we make sure to avoid 1997 * the most common, and also simplest, cause of a non-unique 1998 * solution:two dominoes side by side, sharing a number at 1999 * opposite ends. Any section of that form automatically leads 2000 * to an alternative solution: 2001 * 2002 * +-------+ +---+---+ 2003 * | 1 2 | | 1 | 2 | 2004 * +-------+ <-> | | | 2005 * | 2 3 | | 2 | 3 | 2006 * +-------+ +---+---+ 2007 * 2008 * So as we place each domino, we check for a neighbouring 2009 * domino on each side, and if there is one, rule out any 2010 * placement of _this_ domino that places a number diagonally 2011 * opposite the same number in the neighbour. 2012 * 2013 * Sometimes this can fail completely, if a domino on each 2014 * side is already placed and between them they rule out all 2015 * placements of this one. But it happens rarely enough that 2016 * it's fine to just abort and try the layout again. 2017 */ 2018 2019 if (alloc_find_neighbour(as, loc->sq[0], loc->sq[1], &n0, &n1) && 2020 (as->numbers[n0] == val->hi || as->numbers[n1] == val->lo)) 2021 can_lo_0 = false; 2022 if (alloc_find_neighbour(as, loc->sq[1], loc->sq[0], &n0, &n1) && 2023 (as->numbers[n0] == val->hi || as->numbers[n1] == val->lo)) 2024 can_lo_1 = false; 2025 2026 if (!can_lo_0 && !can_lo_1) 2027 return false; /* layout failed */ 2028 else if (can_lo_0 && can_lo_1) 2029 which_lo = random_upto(rs, 2); 2030 else 2031 which_lo = can_lo_0 ? 0 : 1; 2032 2033 which_hi = 1 - which_lo; 2034 as->numbers[loc->sq[which_lo]] = val->lo; 2035 as->numbers[loc->sq[which_hi]] = val->hi; 2036 } 2037 2038 return true; 2039} 2040 2041static bool alloc_try_hard(struct alloc_scratch *as, random_state *rs) 2042{ 2043 int i, x, y, hi, lo, vals, locs, confounders_needed; 2044 bool ok; 2045 2046 for (i = 0; i < as->wh; i++) 2047 as->numbers[i] = -1; 2048 2049 /* 2050 * Shuffle the location indices. 2051 */ 2052 for (i = 0; i < as->dc; i++) 2053 as->wh2_scratch[i] = i; 2054 shuffle(as->wh2_scratch, as->dc, sizeof(*as->wh2_scratch), rs); 2055 2056 /* 2057 * Start by randomly placing the double dominoes, to give a 2058 * starting instance of every number to try to put other things 2059 * next to. 2060 */ 2061 for (i = 0; i <= as->n; i++) 2062 as->wh_scratch[i] = DINDEX(i, i); 2063 shuffle(as->wh_scratch, i, sizeof(*as->wh_scratch), rs); 2064 for (i = 0; i <= as->n; i++) { 2065 struct alloc_loc *loc = &as->locs[as->wh2_scratch[i]]; 2066 as->numbers[loc->sq[0]] = as->numbers[loc->sq[1]] = i; 2067 } 2068 2069 /* 2070 * Find all the dominoes that don't yet have a _wrong_ placement 2071 * somewhere in the grid. 2072 */ 2073 for (i = 0; i < as->dc; i++) 2074 as->vals[i].confounder = false; 2075 for (y = 0; y < as->h; y++) { 2076 for (x = 0; x < as->w; x++) { 2077 int p = y * as->w + x; 2078 if (as->numbers[p] == -1) 2079 continue; 2080 2081 if (x+1 < as->w) { 2082 int p1 = y * as->w + (x+1); 2083 if (as->layout[p] != p1 && as->numbers[p1] != -1) 2084 as->vals[DINDEX(as->numbers[p], as->numbers[p1])] 2085 .confounder = true; 2086 } 2087 if (y+1 < as->h) { 2088 int p1 = (y+1) * as->w + x; 2089 if (as->layout[p] != p1 && as->numbers[p1] != -1) 2090 as->vals[DINDEX(as->numbers[p], as->numbers[p1])] 2091 .confounder = true; 2092 } 2093 } 2094 } 2095 2096 for (i = confounders_needed = 0; i < as->dc; i++) 2097 if (!as->vals[i].confounder) 2098 confounders_needed++; 2099 2100 /* 2101 * Make a shuffled list of all the unplaced dominoes, and go 2102 * through it trying to find a placement for each one that also 2103 * fills in at least one of the needed confounders. 2104 */ 2105 vals = 0; 2106 for (hi = 0; hi <= as->n; hi++) 2107 for (lo = 0; lo < hi; lo++) 2108 as->wh_scratch[vals++] = DINDEX(hi, lo); 2109 shuffle(as->wh_scratch, vals, sizeof(*as->wh_scratch), rs); 2110 2111 locs = as->dc; 2112 2113 while (vals > 0) { 2114 int valpos, valout, oldvals = vals; 2115 2116 for (valpos = valout = 0; valpos < vals; valpos++) { 2117 int validx = as->wh_scratch[valpos]; 2118 struct alloc_val *val = &as->vals[validx]; 2119 struct alloc_loc *loc; 2120 int locpos, si, which_lo; 2121 2122 for (locpos = 0; locpos < locs; locpos++) { 2123 int locidx = as->wh2_scratch[locpos]; 2124 int wi, flip; 2125 2126 loc = &as->locs[locidx]; 2127 if (as->numbers[loc->sq[0]] != -1) 2128 continue; /* this location is already filled */ 2129 2130 flip = random_upto(rs, 2); 2131 2132 /* Try this location both ways round. */ 2133 for (wi = 0; wi < 2; wi++) { 2134 int n0, n1; 2135 2136 which_lo = wi ^ flip; 2137 2138 /* First, do the same check as in alloc_try_unique, to 2139 * avoid making an obviously insoluble puzzle. */ 2140 if (alloc_find_neighbour(as, loc->sq[which_lo], 2141 loc->sq[1-which_lo], &n0, &n1) && 2142 (as->numbers[n0] == val->hi || 2143 as->numbers[n1] == val->lo)) 2144 break; /* can't place it this way round */ 2145 2146 if (confounders_needed == 0) 2147 goto place_ok; 2148 2149 /* Look to see if we're adding at least one 2150 * previously absent confounder. */ 2151 for (si = 0; si < 2; si++) { 2152 int x = loc->sq[si] % as->w, y = loc->sq[si] / as->w; 2153 int n = (si == which_lo ? val->lo : val->hi); 2154 int d; 2155 for (d = 0; d < 4; d++) { 2156 int dx = d==0 ? +1 : d==2 ? -1 : 0; 2157 int dy = d==1 ? +1 : d==3 ? -1 : 0; 2158 int x1 = x+dx, y1 = y+dy, p1 = y1 * as->w + x1; 2159 if (x1 >= 0 && x1 < as->w && 2160 y1 >= 0 && y1 < as->h && 2161 as->numbers[p1] != -1 && 2162 !(as->vals[DINDEX(n, as->numbers[p1])] 2163 .confounder)) { 2164 /* 2165 * Place this domino. 2166 */ 2167 goto place_ok; 2168 } 2169 } 2170 } 2171 } 2172 } 2173 2174 /* If we get here without executing 'goto place_ok', we 2175 * didn't find anywhere useful to put this domino. Put it 2176 * back on the list for the next pass. */ 2177 as->wh_scratch[valout++] = validx; 2178 continue; 2179 2180 place_ok:; 2181 2182 /* We've found a domino to place. Place it, and fill in 2183 * all the confounders it adds. */ 2184 as->numbers[loc->sq[which_lo]] = val->lo; 2185 as->numbers[loc->sq[1 - which_lo]] = val->hi; 2186 2187 for (si = 0; si < 2; si++) { 2188 int p = loc->sq[si]; 2189 int n = as->numbers[p]; 2190 int x = p % as->w, y = p / as->w; 2191 int d; 2192 for (d = 0; d < 4; d++) { 2193 int dx = d==0 ? +1 : d==2 ? -1 : 0; 2194 int dy = d==1 ? +1 : d==3 ? -1 : 0; 2195 int x1 = x+dx, y1 = y+dy, p1 = y1 * as->w + x1; 2196 2197 if (x1 >= 0 && x1 < as->w && y1 >= 0 && y1 < as->h && 2198 p1 != loc->sq[1-si] && as->numbers[p1] != -1) { 2199 int di = DINDEX(n, as->numbers[p1]); 2200 if (!as->vals[di].confounder) 2201 confounders_needed--; 2202 as->vals[di].confounder = true; 2203 } 2204 } 2205 } 2206 } 2207 2208 vals = valout; 2209 2210 if (oldvals == vals) 2211 break; 2212 } 2213 2214 ok = true; 2215 2216 for (i = 0; i < as->dc; i++) 2217 if (!as->vals[i].confounder) 2218 ok = false; 2219 for (i = 0; i < as->wh; i++) 2220 if (as->numbers[i] == -1) 2221 ok = false; 2222 2223 return ok; 2224} 2225 2226static char *new_game_desc(const game_params *params, random_state *rs, 2227 char **aux, bool interactive) 2228{ 2229 int n = params->n, w = n+2, h = n+1, wh = w*h, diff = params->diff; 2230 struct solver_scratch *sc; 2231 struct alloc_scratch *as; 2232 int i, j, k, len; 2233 char *ret; 2234 2235#ifndef OMIT_DIFFICULTY_CAP 2236 /* 2237 * Cap the difficulty level for small puzzles which would 2238 * otherwise become impossible to generate. 2239 * 2240 * Under an #ifndef, to make it easy to remove this cap for the 2241 * purpose of re-testing what it ought to be. 2242 */ 2243 if (diff != DIFF_AMBIGUOUS) { 2244 if (n == 1 && diff > DIFF_TRIVIAL) 2245 diff = DIFF_TRIVIAL; 2246 if (n == 2 && diff > DIFF_BASIC) 2247 diff = DIFF_BASIC; 2248 } 2249#endif /* OMIT_DIFFICULTY_CAP */ 2250 2251 /* 2252 * Allocate space in which to lay the grid out. 2253 */ 2254 sc = solver_make_scratch(n); 2255 as = alloc_make_scratch(n); 2256 2257 /* 2258 * I haven't been able to think of any particularly clever 2259 * techniques for generating instances of Dominosa with a 2260 * unique solution. Many of the deductions used in this puzzle 2261 * are based on information involving half the grid at a time 2262 * (`of all the 6s, exactly one is next to a 3'), so a strategy 2263 * of partially solving the grid and then perturbing the place 2264 * where the solver got stuck seems particularly likely to 2265 * accidentally destroy the information which the solver had 2266 * used in getting that far. (Contrast with, say, Mines, in 2267 * which most deductions are local so this is an excellent 2268 * strategy.) 2269 * 2270 * Therefore I resort to the basest of brute force methods: 2271 * generate a random grid, see if it's solvable, throw it away 2272 * and try again if not. My only concession to sophistication 2273 * and cleverness is to at least _try_ not to generate obvious 2274 * 2x2 ambiguous sections (see comment below in the domino- 2275 * flipping section). 2276 * 2277 * During tests performed on 2005-07-15, I found that the brute 2278 * force approach without that tweak had to throw away about 87 2279 * grids on average (at the default n=6) before finding a 2280 * unique one, or a staggering 379 at n=9; good job the 2281 * generator and solver are fast! When I added the 2282 * ambiguous-section avoidance, those numbers came down to 19 2283 * and 26 respectively, which is a lot more sensible. 2284 */ 2285 2286 while (1) { 2287 alloc_make_layout(as, rs); 2288 2289 if (diff == DIFF_AMBIGUOUS) { 2290 /* Just assign numbers to each domino completely at random. */ 2291 alloc_trivial(as, rs); 2292 } else if (diff < DIFF_HARD) { 2293 /* Try to rule out the most common case of a non-unique solution */ 2294 if (!alloc_try_unique(as, rs)) 2295 continue; 2296 } else { 2297 /* 2298 * For Hard puzzles and above, we'd like there not to be 2299 * any easy toehold to start with. 2300 * 2301 * Mostly, that's arranged by alloc_try_hard, which will 2302 * ensure that no domino starts off with only one 2303 * potential placement. But a few other deductions 2304 * possible at Basic level can still sneak through the 2305 * cracks - for example, if the only two placements of one 2306 * domino overlap in a square, and you therefore rule out 2307 * some other domino that can use that square, you might 2308 * then find that _that_ domino now has only one 2309 * placement, and you've made a start. 2310 * 2311 * Of course, the main difficulty-level check will still 2312 * guarantee that you have to do a harder deduction 2313 * _somewhere_ in the grid. But it's more elegant if 2314 * there's nowhere obvious to get started at all. 2315 */ 2316 int di; 2317 bool ok; 2318 2319 if (!alloc_try_hard(as, rs)) 2320 continue; 2321 2322 solver_setup_grid(sc, as->numbers); 2323 if (run_solver(sc, DIFF_BASIC) < 2) 2324 continue; 2325 2326 ok = true; 2327 for (di = 0; di < sc->dc; di++) 2328 if (sc->dominoes[di].nplacements <= 1) { 2329 ok = false; 2330 break; 2331 } 2332 2333 if (!ok) { 2334 continue; 2335 } 2336 } 2337 2338 if (diff != DIFF_AMBIGUOUS) { 2339 int solver_result; 2340 solver_setup_grid(sc, as->numbers); 2341 solver_result = run_solver(sc, diff); 2342 if (solver_result > 1) 2343 continue; /* puzzle couldn't be solved at this difficulty */ 2344 if (sc->max_diff_used < diff) 2345 continue; /* puzzle _could_ be solved at easier difficulty */ 2346 } 2347 2348 break; 2349 } 2350 2351#ifdef GENERATION_DIAGNOSTICS 2352 for (j = 0; j < h; j++) { 2353 for (i = 0; i < w; i++) { 2354 putchar('0' + as->numbers[j*w+i]); 2355 } 2356 putchar('\n'); 2357 } 2358 putchar('\n'); 2359#endif 2360 2361 /* 2362 * Encode the resulting game state. 2363 * 2364 * Our encoding is a string of digits. Any number greater than 2365 * 9 is represented by a decimal integer within square 2366 * brackets. We know there are n+2 of every number (it's paired 2367 * with each number from 0 to n inclusive, and one of those is 2368 * itself so that adds another occurrence), so we can work out 2369 * the string length in advance. 2370 */ 2371 2372 /* 2373 * To work out the total length of the decimal encodings of all 2374 * the numbers from 0 to n inclusive: 2375 * - every number has a units digit; total is n+1. 2376 * - all numbers above 9 have a tens digit; total is max(n+1-10,0). 2377 * - all numbers above 99 have a hundreds digit; total is max(n+1-100,0). 2378 * - and so on. 2379 */ 2380 len = n+1; 2381 for (i = 10; i <= n; i *= 10) 2382 len += max(n + 1 - i, 0); 2383 /* Now add two square brackets for each number above 9. */ 2384 len += 2 * max(n + 1 - 10, 0); 2385 /* And multiply by n+2 for the repeated occurrences of each number. */ 2386 len *= n+2; 2387 2388 /* 2389 * Now actually encode the string. 2390 */ 2391 ret = snewn(len+1, char); 2392 j = 0; 2393 for (i = 0; i < wh; i++) { 2394 k = as->numbers[i]; 2395 if (k < 10) 2396 ret[j++] = '0' + k; 2397 else 2398 j += sprintf(ret+j, "[%d]", k); 2399 assert(j <= len); 2400 } 2401 assert(j == len); 2402 ret[j] = '\0'; 2403 2404 /* 2405 * Encode the solved state as an aux_info. 2406 */ 2407 { 2408 char *auxinfo = snewn(wh+1, char); 2409 2410 for (i = 0; i < wh; i++) { 2411 int v = as->layout[i]; 2412 auxinfo[i] = (v == i+1 ? 'L' : v == i-1 ? 'R' : 2413 v == i+w ? 'T' : v == i-w ? 'B' : '.'); 2414 } 2415 auxinfo[wh] = '\0'; 2416 2417 *aux = auxinfo; 2418 } 2419 2420 solver_free_scratch(sc); 2421 alloc_free_scratch(as); 2422 2423 return ret; 2424} 2425 2426static const char *validate_desc(const game_params *params, const char *desc) 2427{ 2428 int n = params->n, w = n+2, h = n+1, wh = w*h; 2429 int *occurrences; 2430 int i, j; 2431 const char *ret; 2432 2433 ret = NULL; 2434 occurrences = snewn(n+1, int); 2435 for (i = 0; i <= n; i++) 2436 occurrences[i] = 0; 2437 2438 for (i = 0; i < wh; i++) { 2439 if (!*desc) { 2440 ret = ret ? ret : "Game description is too short"; 2441 } else { 2442 if (*desc >= '0' && *desc <= '9') 2443 j = *desc++ - '0'; 2444 else if (*desc == '[') { 2445 desc++; 2446 j = atoi(desc); 2447 while (*desc && isdigit((unsigned char)*desc)) desc++; 2448 if (*desc != ']') 2449 ret = ret ? ret : "Missing ']' in game description"; 2450 else 2451 desc++; 2452 } else { 2453 j = -1; 2454 ret = ret ? ret : "Invalid syntax in game description"; 2455 } 2456 if (j < 0 || j > n) 2457 ret = ret ? ret : "Number out of range in game description"; 2458 else 2459 occurrences[j]++; 2460 } 2461 } 2462 2463 if (*desc) 2464 ret = ret ? ret : "Game description is too long"; 2465 2466 if (!ret) { 2467 for (i = 0; i <= n; i++) 2468 if (occurrences[i] != n+2) 2469 ret = "Incorrect number balance in game description"; 2470 } 2471 2472 sfree(occurrences); 2473 2474 return ret; 2475} 2476 2477static game_state *new_game(midend *me, const game_params *params, 2478 const char *desc) 2479{ 2480 int n = params->n, w = n+2, h = n+1, wh = w*h; 2481 game_state *state = snew(game_state); 2482 int i, j; 2483 2484 state->params = *params; 2485 state->w = w; 2486 state->h = h; 2487 2488 state->grid = snewn(wh, int); 2489 for (i = 0; i < wh; i++) 2490 state->grid[i] = i; 2491 2492 state->edges = snewn(wh, unsigned short); 2493 for (i = 0; i < wh; i++) 2494 state->edges[i] = 0; 2495 2496 state->numbers = snew(struct game_numbers); 2497 state->numbers->refcount = 1; 2498 state->numbers->numbers = snewn(wh, int); 2499 2500 for (i = 0; i < wh; i++) { 2501 assert(*desc); 2502 if (*desc >= '0' && *desc <= '9') 2503 j = *desc++ - '0'; 2504 else { 2505 assert(*desc == '['); 2506 desc++; 2507 j = atoi(desc); 2508 while (*desc && isdigit((unsigned char)*desc)) desc++; 2509 assert(*desc == ']'); 2510 desc++; 2511 } 2512 assert(j >= 0 && j <= n); 2513 state->numbers->numbers[i] = j; 2514 } 2515 2516 state->completed = false; 2517 state->cheated = false; 2518 2519 return state; 2520} 2521 2522static game_state *dup_game(const game_state *state) 2523{ 2524 int n = state->params.n, w = n+2, h = n+1, wh = w*h; 2525 game_state *ret = snew(game_state); 2526 2527 ret->params = state->params; 2528 ret->w = state->w; 2529 ret->h = state->h; 2530 ret->grid = snewn(wh, int); 2531 memcpy(ret->grid, state->grid, wh * sizeof(int)); 2532 ret->edges = snewn(wh, unsigned short); 2533 memcpy(ret->edges, state->edges, wh * sizeof(unsigned short)); 2534 ret->numbers = state->numbers; 2535 ret->numbers->refcount++; 2536 ret->completed = state->completed; 2537 ret->cheated = state->cheated; 2538 2539 return ret; 2540} 2541 2542static void free_game(game_state *state) 2543{ 2544 sfree(state->grid); 2545 sfree(state->edges); 2546 if (--state->numbers->refcount <= 0) { 2547 sfree(state->numbers->numbers); 2548 sfree(state->numbers); 2549 } 2550 sfree(state); 2551} 2552 2553static char *solution_move_string(struct solver_scratch *sc) 2554{ 2555 char *ret; 2556 int retlen, retsize; 2557 int i, pass; 2558 2559 /* 2560 * First make a pass putting in edges for -1, then make a pass 2561 * putting in dominoes for +1. 2562 */ 2563 retsize = 256; 2564 ret = snewn(retsize, char); 2565 retlen = sprintf(ret, "S"); 2566 2567 for (pass = 0; pass < 2; pass++) { 2568 char type = "ED"[pass]; 2569 2570 for (i = 0; i < sc->pc; i++) { 2571 struct solver_placement *p = &sc->placements[i]; 2572 char buf[80]; 2573 int extra; 2574 2575 if (pass == 0) { 2576 /* Emit a barrier if this placement is ruled out for 2577 * the domino. */ 2578 if (p->active) 2579 continue; 2580 } else { 2581 /* Emit a domino if this placement is the only one not 2582 * ruled out. */ 2583 if (!p->active || p->domino->nplacements > 1) 2584 continue; 2585 } 2586 2587 extra = sprintf(buf, ";%c%d,%d", type, 2588 p->squares[0]->index, p->squares[1]->index); 2589 2590 if (retlen + extra + 1 >= retsize) { 2591 retsize = retlen + extra + 256; 2592 ret = sresize(ret, retsize, char); 2593 } 2594 strcpy(ret + retlen, buf); 2595 retlen += extra; 2596 } 2597 } 2598 2599 return ret; 2600} 2601 2602static char *solve_game(const game_state *state, const game_state *currstate, 2603 const char *aux, const char **error) 2604{ 2605 int n = state->params.n, w = n+2, h = n+1, wh = w*h; 2606 char *ret; 2607 int retlen, retsize; 2608 int i; 2609 char buf[80]; 2610 int extra; 2611 2612 if (aux) { 2613 retsize = 256; 2614 ret = snewn(retsize, char); 2615 retlen = sprintf(ret, "S"); 2616 2617 for (i = 0; i < wh; i++) { 2618 if (aux[i] == 'L') 2619 extra = sprintf(buf, ";D%d,%d", i, i+1); 2620 else if (aux[i] == 'T') 2621 extra = sprintf(buf, ";D%d,%d", i, i+w); 2622 else 2623 continue; 2624 2625 if (retlen + extra + 1 >= retsize) { 2626 retsize = retlen + extra + 256; 2627 ret = sresize(ret, retsize, char); 2628 } 2629 strcpy(ret + retlen, buf); 2630 retlen += extra; 2631 } 2632 2633 } else { 2634 struct solver_scratch *sc = solver_make_scratch(n); 2635 solver_setup_grid(sc, state->numbers->numbers); 2636 run_solver(sc, DIFFCOUNT); 2637 ret = solution_move_string(sc); 2638 solver_free_scratch(sc); 2639 } 2640 2641 return ret; 2642} 2643 2644static bool game_can_format_as_text_now(const game_params *params) 2645{ 2646 return params->n < 1000; 2647} 2648 2649static void draw_domino(char *board, int start, char corner, 2650 int dshort, int nshort, char cshort, 2651 int dlong, int nlong, char clong) 2652{ 2653 int go_short = nshort*dshort, go_long = nlong*dlong, i; 2654 2655 board[start] = corner; 2656 board[start + go_short] = corner; 2657 board[start + go_long] = corner; 2658 board[start + go_short + go_long] = corner; 2659 2660 for (i = 1; i < nshort; ++i) { 2661 int j = start + i*dshort, k = start + i*dshort + go_long; 2662 if (board[j] != corner) board[j] = cshort; 2663 if (board[k] != corner) board[k] = cshort; 2664 } 2665 2666 for (i = 1; i < nlong; ++i) { 2667 int j = start + i*dlong, k = start + i*dlong + go_short; 2668 if (board[j] != corner) board[j] = clong; 2669 if (board[k] != corner) board[k] = clong; 2670 } 2671} 2672 2673static char *game_text_format(const game_state *state) 2674{ 2675 int w = state->w, h = state->h, r, c; 2676 int cw = 4, ch = 2, gw = cw*w + 2, gh = ch * h + 1, len = gw * gh; 2677 char *board = snewn(len + 1, char); 2678 2679 memset(board, ' ', len); 2680 2681 for (r = 0; r < h; ++r) { 2682 for (c = 0; c < w; ++c) { 2683 int cell = r*ch*gw + cw*c, center = cell + gw*ch/2 + cw/2; 2684 int i = r*w + c, num = state->numbers->numbers[i]; 2685 2686 if (num < 100) { 2687 board[center] = '0' + num % 10; 2688 if (num >= 10) board[center - 1] = '0' + num / 10; 2689 } else { 2690 board[center+1] = '0' + num % 10; 2691 board[center] = '0' + num / 10 % 10; 2692 board[center-1] = '0' + num / 100; 2693 } 2694 2695 if (state->edges[i] & EDGE_L) board[center - cw/2] = '|'; 2696 if (state->edges[i] & EDGE_R) board[center + cw/2] = '|'; 2697 if (state->edges[i] & EDGE_T) board[center - gw] = '-'; 2698 if (state->edges[i] & EDGE_B) board[center + gw] = '-'; 2699 2700 if (state->grid[i] == i) continue; /* no domino pairing */ 2701 if (state->grid[i] < i) continue; /* already done */ 2702 assert (state->grid[i] == i + 1 || state->grid[i] == i + w); 2703 if (state->grid[i] == i + 1) 2704 draw_domino(board, cell, '+', gw, ch, '|', +1, 2*cw, '-'); 2705 else if (state->grid[i] == i + w) 2706 draw_domino(board, cell, '+', +1, cw, '-', gw, 2*ch, '|'); 2707 } 2708 board[r*ch*gw + gw - 1] = '\n'; 2709 board[r*ch*gw + gw + gw - 1] = '\n'; 2710 } 2711 board[len - 1] = '\n'; 2712 board[len] = '\0'; 2713 return board; 2714} 2715 2716struct game_ui { 2717 int cur_x, cur_y, highlight_1, highlight_2; 2718 bool cur_visible; 2719}; 2720 2721static game_ui *new_ui(const game_state *state) 2722{ 2723 game_ui *ui = snew(game_ui); 2724 ui->cur_x = ui->cur_y = 0; 2725 ui->cur_visible = getenv_bool("PUZZLES_SHOW_CURSOR", false); 2726 ui->highlight_1 = ui->highlight_2 = -1; 2727 return ui; 2728} 2729 2730static void free_ui(game_ui *ui) 2731{ 2732 sfree(ui); 2733} 2734 2735static void game_changed_state(game_ui *ui, const game_state *oldstate, 2736 const game_state *newstate) 2737{ 2738 if (!oldstate->completed && newstate->completed) 2739 ui->cur_visible = false; 2740} 2741 2742static const char *current_key_label(const game_ui *ui, 2743 const game_state *state, int button) 2744{ 2745 if (IS_CURSOR_SELECT(button)) { 2746 int d1, d2, w = state->w; 2747 2748 if (!((ui->cur_x ^ ui->cur_y) & 1)) 2749 return ""; /* must have exactly one dimension odd */ 2750 d1 = (ui->cur_y / 2) * w + (ui->cur_x / 2); 2751 d2 = ((ui->cur_y+1) / 2) * w + ((ui->cur_x+1) / 2); 2752 2753 /* We can't mark an edge next to any domino. */ 2754 if (button == CURSOR_SELECT2 && 2755 (state->grid[d1] != d1 || state->grid[d2] != d2)) 2756 return ""; 2757 if (button == CURSOR_SELECT) { 2758 if (state->grid[d1] == d2) return "Remove"; 2759 return "Place"; 2760 } else { 2761 int edge = d2 == d1 + 1 ? EDGE_R : EDGE_B; 2762 if (state->edges[d1] & edge) return "Remove"; 2763 return "Line"; 2764 } 2765 } 2766 return ""; 2767} 2768 2769#define PREFERRED_TILESIZE 32 2770#define TILESIZE (ds->tilesize) 2771#define BORDER (TILESIZE * 3 / 4) 2772#define DOMINO_GUTTER (TILESIZE / 16) 2773#define DOMINO_RADIUS (TILESIZE / 8) 2774#define DOMINO_COFFSET (DOMINO_GUTTER + DOMINO_RADIUS) 2775#define CURSOR_RADIUS (TILESIZE / 4) 2776 2777#define COORD(x) ( (x) * TILESIZE + BORDER ) 2778#define FROMCOORD(x) ( ((x) - BORDER + TILESIZE) / TILESIZE - 1 ) 2779 2780struct game_drawstate { 2781 int w, h, tilesize; 2782 unsigned long *visible; 2783}; 2784 2785static char *interpret_move(const game_state *state, game_ui *ui, 2786 const game_drawstate *ds, 2787 int x, int y, int button) 2788{ 2789 int w = state->w, h = state->h; 2790 char buf[80]; 2791 2792 /* 2793 * A left-click between two numbers toggles a domino covering 2794 * them. A right-click toggles an edge. 2795 */ 2796 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) { 2797 int tx = FROMCOORD(x), ty = FROMCOORD(y), t = ty*w+tx; 2798 int dx, dy; 2799 int d1, d2; 2800 2801 if (tx < 0 || tx >= w || ty < 0 || ty >= h) 2802 return MOVE_UNUSED; 2803 2804 /* 2805 * Now we know which square the click was in, decide which 2806 * edge of the square it was closest to. 2807 */ 2808 dx = 2 * (x - COORD(tx)) - TILESIZE; 2809 dy = 2 * (y - COORD(ty)) - TILESIZE; 2810 2811 if (abs(dx) > abs(dy) && dx < 0 && tx > 0) 2812 d1 = t - 1, d2 = t; /* clicked in right side of domino */ 2813 else if (abs(dx) > abs(dy) && dx > 0 && tx+1 < w) 2814 d1 = t, d2 = t + 1; /* clicked in left side of domino */ 2815 else if (abs(dy) > abs(dx) && dy < 0 && ty > 0) 2816 d1 = t - w, d2 = t; /* clicked in bottom half of domino */ 2817 else if (abs(dy) > abs(dx) && dy > 0 && ty+1 < h) 2818 d1 = t, d2 = t + w; /* clicked in top half of domino */ 2819 else 2820 return MOVE_NO_EFFECT; /* clicked precisely on a diagonal */ 2821 2822 /* 2823 * We can't mark an edge next to any domino. 2824 */ 2825 if (button == RIGHT_BUTTON && 2826 (state->grid[d1] != d1 || state->grid[d2] != d2)) 2827 return MOVE_NO_EFFECT; 2828 2829 ui->cur_visible = false; 2830 sprintf(buf, "%c%d,%d", (int)(button == RIGHT_BUTTON ? 'E' : 'D'), d1, d2); 2831 return dupstr(buf); 2832 } else if (IS_CURSOR_MOVE(button)) { 2833 return move_cursor(button, &ui->cur_x, &ui->cur_y, 2*w-1, 2*h-1, false, 2834 &ui->cur_visible); 2835 } else if (IS_CURSOR_SELECT(button)) { 2836 int d1, d2; 2837 2838 if (!((ui->cur_x ^ ui->cur_y) & 1)) 2839 return MOVE_NO_EFFECT; /* must have exactly one dimension odd */ 2840 d1 = (ui->cur_y / 2) * w + (ui->cur_x / 2); 2841 d2 = ((ui->cur_y+1) / 2) * w + ((ui->cur_x+1) / 2); 2842 2843 /* 2844 * We can't mark an edge next to any domino. 2845 */ 2846 if (button == CURSOR_SELECT2 && 2847 (state->grid[d1] != d1 || state->grid[d2] != d2)) 2848 return MOVE_NO_EFFECT; 2849 2850 sprintf(buf, "%c%d,%d", (int)(button == CURSOR_SELECT2 ? 'E' : 'D'), d1, d2); 2851 return dupstr(buf); 2852 } else if (isdigit(button & ~MOD_NUM_KEYPAD)) { 2853 int n = state->params.n, num = (button & ~MOD_NUM_KEYPAD) - '0'; 2854 if (num > n) { 2855 return MOVE_UNUSED; 2856 } else if (ui->highlight_1 == num) { 2857 ui->highlight_1 = -1; 2858 } else if (ui->highlight_2 == num) { 2859 ui->highlight_2 = -1; 2860 } else if (ui->highlight_1 == -1) { 2861 ui->highlight_1 = num; 2862 } else if (ui->highlight_2 == -1) { 2863 ui->highlight_2 = num; 2864 } else { 2865 return MOVE_NO_EFFECT; 2866 } 2867 return MOVE_UI_UPDATE; 2868 } 2869 2870 return MOVE_UNUSED; 2871} 2872 2873static game_state *execute_move(const game_state *state, const char *move) 2874{ 2875 int n = state->params.n, w = n+2, h = n+1, wh = w*h; 2876 int d1, d2, d3, p; 2877 game_state *ret = dup_game(state); 2878 2879 while (*move) { 2880 if (move[0] == 'S') { 2881 int i; 2882 2883 ret->cheated = true; 2884 2885 /* 2886 * Clear the existing edges and domino placements. We 2887 * expect the S to be followed by other commands. 2888 */ 2889 for (i = 0; i < wh; i++) { 2890 ret->grid[i] = i; 2891 ret->edges[i] = 0; 2892 } 2893 move++; 2894 } else if (move[0] == 'D' && 2895 sscanf(move+1, "%d,%d%n", &d1, &d2, &p) == 2 && 2896 d1 >= 0 && d1 < wh && d2 >= 0 && d2 < wh && d1 < d2 && 2897 (d2 - d1 == 1 || d2 - d1 == w)) { 2898 2899 /* 2900 * Toggle domino presence between d1 and d2. 2901 */ 2902 if (ret->grid[d1] == d2) { 2903 assert(ret->grid[d2] == d1); 2904 ret->grid[d1] = d1; 2905 ret->grid[d2] = d2; 2906 } else { 2907 /* 2908 * Erase any dominoes that might overlap the new one. 2909 */ 2910 d3 = ret->grid[d1]; 2911 if (d3 != d1) 2912 ret->grid[d3] = d3; 2913 d3 = ret->grid[d2]; 2914 if (d3 != d2) 2915 ret->grid[d3] = d3; 2916 /* 2917 * Place the new one. 2918 */ 2919 ret->grid[d1] = d2; 2920 ret->grid[d2] = d1; 2921 2922 /* 2923 * Destroy any edges lurking around it. 2924 */ 2925 if (ret->edges[d1] & EDGE_L) { 2926 assert(d1 - 1 >= 0); 2927 ret->edges[d1 - 1] &= ~EDGE_R; 2928 } 2929 if (ret->edges[d1] & EDGE_R) { 2930 assert(d1 + 1 < wh); 2931 ret->edges[d1 + 1] &= ~EDGE_L; 2932 } 2933 if (ret->edges[d1] & EDGE_T) { 2934 assert(d1 - w >= 0); 2935 ret->edges[d1 - w] &= ~EDGE_B; 2936 } 2937 if (ret->edges[d1] & EDGE_B) { 2938 assert(d1 + 1 < wh); 2939 ret->edges[d1 + w] &= ~EDGE_T; 2940 } 2941 ret->edges[d1] = 0; 2942 if (ret->edges[d2] & EDGE_L) { 2943 assert(d2 - 1 >= 0); 2944 ret->edges[d2 - 1] &= ~EDGE_R; 2945 } 2946 if (ret->edges[d2] & EDGE_R) { 2947 assert(d2 + 1 < wh); 2948 ret->edges[d2 + 1] &= ~EDGE_L; 2949 } 2950 if (ret->edges[d2] & EDGE_T) { 2951 assert(d2 - w >= 0); 2952 ret->edges[d2 - w] &= ~EDGE_B; 2953 } 2954 if (ret->edges[d2] & EDGE_B) { 2955 assert(d2 + 1 < wh); 2956 ret->edges[d2 + w] &= ~EDGE_T; 2957 } 2958 ret->edges[d2] = 0; 2959 } 2960 2961 move += p+1; 2962 } else if (move[0] == 'E' && 2963 sscanf(move+1, "%d,%d%n", &d1, &d2, &p) == 2 && 2964 d1 >= 0 && d1 < wh && d2 >= 0 && d2 < wh && d1 < d2 && 2965 ret->grid[d1] == d1 && ret->grid[d2] == d2 && 2966 (d2 - d1 == 1 || d2 - d1 == w)) { 2967 2968 /* 2969 * Toggle edge presence between d1 and d2. 2970 */ 2971 if (d2 == d1 + 1) { 2972 ret->edges[d1] ^= EDGE_R; 2973 ret->edges[d2] ^= EDGE_L; 2974 } else { 2975 ret->edges[d1] ^= EDGE_B; 2976 ret->edges[d2] ^= EDGE_T; 2977 } 2978 2979 move += p+1; 2980 } else { 2981 free_game(ret); 2982 return NULL; 2983 } 2984 2985 if (*move) { 2986 if (*move != ';') { 2987 free_game(ret); 2988 return NULL; 2989 } 2990 move++; 2991 } 2992 } 2993 2994 /* 2995 * After modifying the grid, check completion. 2996 */ 2997 if (!ret->completed) { 2998 int i, ok = 0; 2999 bool *used = snewn(TRI(n+1), bool); 3000 3001 memset(used, 0, TRI(n+1)); 3002 for (i = 0; i < wh; i++) 3003 if (ret->grid[i] > i) { 3004 int n1, n2, di; 3005 3006 n1 = ret->numbers->numbers[i]; 3007 n2 = ret->numbers->numbers[ret->grid[i]]; 3008 3009 di = DINDEX(n1, n2); 3010 assert(di >= 0 && di < TRI(n+1)); 3011 3012 if (!used[di]) { 3013 used[di] = true; 3014 ok++; 3015 } 3016 } 3017 3018 sfree(used); 3019 if (ok == DCOUNT(n)) 3020 ret->completed = true; 3021 } 3022 3023 return ret; 3024} 3025 3026/* ---------------------------------------------------------------------- 3027 * Drawing routines. 3028 */ 3029 3030static void game_compute_size(const game_params *params, int tilesize, 3031 const game_ui *ui, int *x, int *y) 3032{ 3033 int n = params->n, w = n+2, h = n+1; 3034 3035 /* Ick: fake up `ds->tilesize' for macro expansion purposes */ 3036 struct { int tilesize; } ads, *ds = &ads; 3037 ads.tilesize = tilesize; 3038 3039 *x = w * TILESIZE + 2*BORDER; 3040 *y = h * TILESIZE + 2*BORDER; 3041} 3042 3043static void game_set_size(drawing *dr, game_drawstate *ds, 3044 const game_params *params, int tilesize) 3045{ 3046 ds->tilesize = tilesize; 3047} 3048 3049static float *game_colours(frontend *fe, int *ncolours) 3050{ 3051 float *ret = snewn(3 * NCOLOURS, float); 3052 3053 frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]); 3054 3055 ret[COL_TEXT * 3 + 0] = 0.0F; 3056 ret[COL_TEXT * 3 + 1] = 0.0F; 3057 ret[COL_TEXT * 3 + 2] = 0.0F; 3058 3059 ret[COL_DOMINO * 3 + 0] = 0.0F; 3060 ret[COL_DOMINO * 3 + 1] = 0.0F; 3061 ret[COL_DOMINO * 3 + 2] = 0.0F; 3062 3063 ret[COL_DOMINOCLASH * 3 + 0] = 0.5F; 3064 ret[COL_DOMINOCLASH * 3 + 1] = 0.0F; 3065 ret[COL_DOMINOCLASH * 3 + 2] = 0.0F; 3066 3067 ret[COL_DOMINOTEXT * 3 + 0] = 1.0F; 3068 ret[COL_DOMINOTEXT * 3 + 1] = 1.0F; 3069 ret[COL_DOMINOTEXT * 3 + 2] = 1.0F; 3070 3071 ret[COL_EDGE * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 2 / 3; 3072 ret[COL_EDGE * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 2 / 3; 3073 ret[COL_EDGE * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] * 2 / 3; 3074 3075 ret[COL_HIGHLIGHT_1 * 3 + 0] = 0.85; 3076 ret[COL_HIGHLIGHT_1 * 3 + 1] = 0.20; 3077 ret[COL_HIGHLIGHT_1 * 3 + 2] = 0.20; 3078 3079 ret[COL_HIGHLIGHT_2 * 3 + 0] = 0.30; 3080 ret[COL_HIGHLIGHT_2 * 3 + 1] = 0.85; 3081 ret[COL_HIGHLIGHT_2 * 3 + 2] = 0.20; 3082 3083 *ncolours = NCOLOURS; 3084 return ret; 3085} 3086 3087static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) 3088{ 3089 struct game_drawstate *ds = snew(struct game_drawstate); 3090 int i; 3091 3092 ds->w = state->w; 3093 ds->h = state->h; 3094 ds->visible = snewn(ds->w * ds->h, unsigned long); 3095 ds->tilesize = 0; /* not decided yet */ 3096 for (i = 0; i < ds->w * ds->h; i++) 3097 ds->visible[i] = 0xFFFF; 3098 3099 return ds; 3100} 3101 3102static void game_free_drawstate(drawing *dr, game_drawstate *ds) 3103{ 3104 sfree(ds->visible); 3105 sfree(ds); 3106} 3107 3108enum { 3109 TYPE_L, 3110 TYPE_R, 3111 TYPE_T, 3112 TYPE_B, 3113 TYPE_BLANK, 3114 TYPE_MASK = 0x0F 3115}; 3116 3117/* These flags must be disjoint with: 3118 * the above enum (TYPE_*) [0x000 -- 0x00F] 3119 * EDGE_* [0x100 -- 0xF00] 3120 * and must fit into an unsigned long (32 bits). 3121 */ 3122#define DF_HIGHLIGHT_1 0x10 3123#define DF_HIGHLIGHT_2 0x20 3124#define DF_FLASH 0x40 3125#define DF_CLASH 0x80 3126 3127#define DF_CURSOR 0x01000 3128#define DF_CURSOR_USEFUL 0x02000 3129#define DF_CURSOR_XBASE 0x10000 3130#define DF_CURSOR_XMASK 0x30000 3131#define DF_CURSOR_YBASE 0x40000 3132#define DF_CURSOR_YMASK 0xC0000 3133 3134#define CEDGE_OFF (TILESIZE / 8) 3135#define IS_EMPTY(s,x,y) ((s)->grid[(y)*(s)->w+(x)] == ((y)*(s)->w+(x))) 3136 3137static void draw_tile(drawing *dr, game_drawstate *ds, const game_state *state, 3138 int x, int y, int type, int highlight_1, int highlight_2) 3139{ 3140 int w = state->w /*, h = state->h */; 3141 int cx = COORD(x), cy = COORD(y); 3142 int nc; 3143 char str[80]; 3144 int flags; 3145 3146 clip(dr, cx, cy, TILESIZE, TILESIZE); 3147 draw_rect(dr, cx, cy, TILESIZE, TILESIZE, COL_BACKGROUND); 3148 3149 flags = type &~ TYPE_MASK; 3150 type &= TYPE_MASK; 3151 3152 if (type != TYPE_BLANK) { 3153 int i, bg; 3154 3155 /* 3156 * Draw one end of a domino. This is composed of: 3157 * 3158 * - two filled circles (rounded corners) 3159 * - two rectangles 3160 * - a slight shift in the number 3161 */ 3162 3163 if (flags & DF_CLASH) 3164 bg = COL_DOMINOCLASH; 3165 else 3166 bg = COL_DOMINO; 3167 nc = COL_DOMINOTEXT; 3168 3169 if (flags & DF_FLASH) { 3170 int tmp = nc; 3171 nc = bg; 3172 bg = tmp; 3173 } 3174 3175 if (type == TYPE_L || type == TYPE_T) 3176 draw_circle(dr, cx+DOMINO_COFFSET, cy+DOMINO_COFFSET, 3177 DOMINO_RADIUS, bg, bg); 3178 if (type == TYPE_R || type == TYPE_T) 3179 draw_circle(dr, cx+TILESIZE-1-DOMINO_COFFSET, cy+DOMINO_COFFSET, 3180 DOMINO_RADIUS, bg, bg); 3181 if (type == TYPE_L || type == TYPE_B) 3182 draw_circle(dr, cx+DOMINO_COFFSET, cy+TILESIZE-1-DOMINO_COFFSET, 3183 DOMINO_RADIUS, bg, bg); 3184 if (type == TYPE_R || type == TYPE_B) 3185 draw_circle(dr, cx+TILESIZE-1-DOMINO_COFFSET, 3186 cy+TILESIZE-1-DOMINO_COFFSET, 3187 DOMINO_RADIUS, bg, bg); 3188 3189 for (i = 0; i < 2; i++) { 3190 int x1, y1, x2, y2; 3191 3192 x1 = cx + (i ? DOMINO_GUTTER : DOMINO_COFFSET); 3193 y1 = cy + (i ? DOMINO_COFFSET : DOMINO_GUTTER); 3194 x2 = cx + TILESIZE-1 - (i ? DOMINO_GUTTER : DOMINO_COFFSET); 3195 y2 = cy + TILESIZE-1 - (i ? DOMINO_COFFSET : DOMINO_GUTTER); 3196 if (type == TYPE_L) 3197 x2 = cx + TILESIZE + TILESIZE/16; 3198 else if (type == TYPE_R) 3199 x1 = cx - TILESIZE/16; 3200 else if (type == TYPE_T) 3201 y2 = cy + TILESIZE + TILESIZE/16; 3202 else if (type == TYPE_B) 3203 y1 = cy - TILESIZE/16; 3204 3205 draw_rect(dr, x1, y1, x2-x1+1, y2-y1+1, bg); 3206 } 3207 } else { 3208 if (flags & EDGE_T) 3209 draw_rect(dr, cx+DOMINO_GUTTER, cy, 3210 TILESIZE-2*DOMINO_GUTTER, 1, COL_EDGE); 3211 if (flags & EDGE_B) 3212 draw_rect(dr, cx+DOMINO_GUTTER, cy+TILESIZE-1, 3213 TILESIZE-2*DOMINO_GUTTER, 1, COL_EDGE); 3214 if (flags & EDGE_L) 3215 draw_rect(dr, cx, cy+DOMINO_GUTTER, 3216 1, TILESIZE-2*DOMINO_GUTTER, COL_EDGE); 3217 if (flags & EDGE_R) 3218 draw_rect(dr, cx+TILESIZE-1, cy+DOMINO_GUTTER, 3219 1, TILESIZE-2*DOMINO_GUTTER, COL_EDGE); 3220 nc = COL_TEXT; 3221 } 3222 3223 if (flags & DF_CURSOR) { 3224 int curx = ((flags & DF_CURSOR_XMASK) / DF_CURSOR_XBASE) & 3; 3225 int cury = ((flags & DF_CURSOR_YMASK) / DF_CURSOR_YBASE) & 3; 3226 int ox = cx + curx*TILESIZE/2; 3227 int oy = cy + cury*TILESIZE/2; 3228 3229 draw_rect_corners(dr, ox, oy, CURSOR_RADIUS, nc); 3230 if (flags & DF_CURSOR_USEFUL) 3231 draw_rect_corners(dr, ox, oy, CURSOR_RADIUS+1, nc); 3232 } 3233 3234 if (flags & DF_HIGHLIGHT_1) { 3235 nc = COL_HIGHLIGHT_1; 3236 } else if (flags & DF_HIGHLIGHT_2) { 3237 nc = COL_HIGHLIGHT_2; 3238 } 3239 3240 sprintf(str, "%d", state->numbers->numbers[y*w+x]); 3241 draw_text(dr, cx+TILESIZE/2, cy+TILESIZE/2, FONT_VARIABLE, TILESIZE/2, 3242 ALIGN_HCENTRE | ALIGN_VCENTRE, nc, str); 3243 3244 draw_update(dr, cx, cy, TILESIZE, TILESIZE); 3245 unclip(dr); 3246} 3247 3248static void game_redraw(drawing *dr, game_drawstate *ds, 3249 const game_state *oldstate, const game_state *state, 3250 int dir, const game_ui *ui, 3251 float animtime, float flashtime) 3252{ 3253 int n = state->params.n, w = state->w, h = state->h, wh = w*h; 3254 int x, y, i; 3255 unsigned char *used; 3256 3257 /* 3258 * See how many dominoes of each type there are, so we can 3259 * highlight clashes in red. 3260 */ 3261 used = snewn(TRI(n+1), unsigned char); 3262 memset(used, 0, TRI(n+1)); 3263 for (i = 0; i < wh; i++) 3264 if (state->grid[i] > i) { 3265 int n1, n2, di; 3266 3267 n1 = state->numbers->numbers[i]; 3268 n2 = state->numbers->numbers[state->grid[i]]; 3269 3270 di = DINDEX(n1, n2); 3271 assert(di >= 0 && di < TRI(n+1)); 3272 3273 if (used[di] < 2) 3274 used[di]++; 3275 } 3276 3277 for (y = 0; y < h; y++) 3278 for (x = 0; x < w; x++) { 3279 int n = y*w+x; 3280 int n1, n2, di; 3281 unsigned long c; 3282 3283 if (state->grid[n] == n-1) 3284 c = TYPE_R; 3285 else if (state->grid[n] == n+1) 3286 c = TYPE_L; 3287 else if (state->grid[n] == n-w) 3288 c = TYPE_B; 3289 else if (state->grid[n] == n+w) 3290 c = TYPE_T; 3291 else 3292 c = TYPE_BLANK; 3293 3294 n1 = state->numbers->numbers[n]; 3295 if (c != TYPE_BLANK) { 3296 n2 = state->numbers->numbers[state->grid[n]]; 3297 di = DINDEX(n1, n2); 3298 if (used[di] > 1) 3299 c |= DF_CLASH; /* highlight a clash */ 3300 } else { 3301 c |= state->edges[n]; 3302 } 3303 3304 if (n1 == ui->highlight_1) 3305 c |= DF_HIGHLIGHT_1; 3306 if (n1 == ui->highlight_2) 3307 c |= DF_HIGHLIGHT_2; 3308 3309 if (flashtime != 0) 3310 c |= DF_FLASH; /* we're flashing */ 3311 3312 if (ui->cur_visible) { 3313 unsigned curx = (unsigned)(ui->cur_x - (2*x-1)); 3314 unsigned cury = (unsigned)(ui->cur_y - (2*y-1)); 3315 if (curx < 3 && cury < 3) { 3316 c |= (DF_CURSOR | 3317 (curx * DF_CURSOR_XBASE) | 3318 (cury * DF_CURSOR_YBASE)); 3319 if ((ui->cur_x ^ ui->cur_y) & 1) 3320 c |= DF_CURSOR_USEFUL; 3321 } 3322 } 3323 3324 if (ds->visible[n] != c) { 3325 draw_tile(dr, ds, state, x, y, c, 3326 ui->highlight_1, ui->highlight_2); 3327 ds->visible[n] = c; 3328 } 3329 } 3330 3331 sfree(used); 3332} 3333 3334static float game_anim_length(const game_state *oldstate, 3335 const game_state *newstate, int dir, game_ui *ui) 3336{ 3337 return 0.0F; 3338} 3339 3340static float game_flash_length(const game_state *oldstate, 3341 const game_state *newstate, int dir, game_ui *ui) 3342{ 3343 if (!oldstate->completed && newstate->completed && 3344 !oldstate->cheated && !newstate->cheated) 3345 { 3346 ui->highlight_1 = ui->highlight_2 = -1; 3347 return FLASH_TIME; 3348 } 3349 return 0.0F; 3350} 3351 3352static void game_get_cursor_location(const game_ui *ui, 3353 const game_drawstate *ds, 3354 const game_state *state, 3355 const game_params *params, 3356 int *x, int *y, int *w, int *h) 3357{ 3358 if(ui->cur_visible) 3359 { 3360 *x = BORDER + ((2 * ui->cur_x + 1) * TILESIZE) / 4; 3361 *y = BORDER + ((2 * ui->cur_y + 1) * TILESIZE) / 4; 3362 *w = *h = TILESIZE / 2 + 2; 3363 } 3364} 3365 3366static int game_status(const game_state *state) 3367{ 3368 return state->completed ? +1 : 0; 3369} 3370 3371static void game_print_size(const game_params *params, const game_ui *ui, 3372 float *x, float *y) 3373{ 3374 int pw, ph; 3375 3376 /* 3377 * I'll use 6mm squares by default. 3378 */ 3379 game_compute_size(params, 600, ui, &pw, &ph); 3380 *x = pw / 100.0F; 3381 *y = ph / 100.0F; 3382} 3383 3384static void game_print(drawing *dr, const game_state *state, const game_ui *ui, 3385 int tilesize) 3386{ 3387 int w = state->w, h = state->h; 3388 int c, x, y; 3389 3390 /* Ick: fake up `ds->tilesize' for macro expansion purposes */ 3391 game_drawstate ads, *ds = &ads; 3392 game_set_size(dr, ds, NULL, tilesize); 3393 3394 c = print_mono_colour(dr, 1); assert(c == COL_BACKGROUND); 3395 c = print_mono_colour(dr, 0); assert(c == COL_TEXT); 3396 c = print_mono_colour(dr, 0); assert(c == COL_DOMINO); 3397 c = print_mono_colour(dr, 0); assert(c == COL_DOMINOCLASH); 3398 c = print_mono_colour(dr, 1); assert(c == COL_DOMINOTEXT); 3399 c = print_mono_colour(dr, 0); assert(c == COL_EDGE); 3400 3401 for (y = 0; y < h; y++) 3402 for (x = 0; x < w; x++) { 3403 int n = y*w+x; 3404 unsigned long c; 3405 3406 if (state->grid[n] == n-1) 3407 c = TYPE_R; 3408 else if (state->grid[n] == n+1) 3409 c = TYPE_L; 3410 else if (state->grid[n] == n-w) 3411 c = TYPE_B; 3412 else if (state->grid[n] == n+w) 3413 c = TYPE_T; 3414 else 3415 c = TYPE_BLANK; 3416 3417 draw_tile(dr, ds, state, x, y, c, -1, -1); 3418 } 3419} 3420 3421#ifdef COMBINED 3422#define thegame dominosa 3423#endif 3424 3425const struct game thegame = { 3426 "Dominosa", "games.dominosa", "dominosa", 3427 default_params, 3428 game_fetch_preset, NULL, 3429 decode_params, 3430 encode_params, 3431 free_params, 3432 dup_params, 3433 true, game_configure, custom_params, 3434 validate_params, 3435 new_game_desc, 3436 validate_desc, 3437 new_game, 3438 dup_game, 3439 free_game, 3440 true, solve_game, 3441 true, game_can_format_as_text_now, game_text_format, 3442 NULL, NULL, /* get_prefs, set_prefs */ 3443 new_ui, 3444 free_ui, 3445 NULL, /* encode_ui */ 3446 NULL, /* decode_ui */ 3447 NULL, /* game_request_keys */ 3448 game_changed_state, 3449 current_key_label, 3450 interpret_move, 3451 execute_move, 3452 PREFERRED_TILESIZE, game_compute_size, game_set_size, 3453 game_colours, 3454 game_new_drawstate, 3455 game_free_drawstate, 3456 game_redraw, 3457 game_anim_length, 3458 game_flash_length, 3459 game_get_cursor_location, 3460 game_status, 3461 true, false, game_print_size, game_print, 3462 false, /* wants_statusbar */ 3463 false, NULL, /* timing_state */ 3464 0, /* flags */ 3465}; 3466 3467#ifdef STANDALONE_SOLVER 3468 3469int main(int argc, char **argv) 3470{ 3471 game_params *p; 3472 game_state *s, *s2; 3473 char *id = NULL, *desc; 3474 int maxdiff = DIFFCOUNT; 3475 const char *err; 3476 bool grade = false, diagnostics = false; 3477 struct solver_scratch *sc; 3478 int retd; 3479 3480 while (--argc > 0) { 3481 char *p = *++argv; 3482 if (!strcmp(p, "-v")) { 3483 diagnostics = true; 3484 } else if (!strcmp(p, "-g")) { 3485 grade = true; 3486 } else if (!strncmp(p, "-d", 2) && p[2] && !p[3]) { 3487 int i; 3488 bool bad = true; 3489 for (i = 0; i < lenof(dominosa_diffchars); i++) 3490 if (dominosa_diffchars[i] != DIFF_AMBIGUOUS && 3491 dominosa_diffchars[i] == p[2]) { 3492 bad = false; 3493 maxdiff = i; 3494 break; 3495 } 3496 if (bad) { 3497 fprintf(stderr, "%s: unrecognised difficulty `%c'\n", 3498 argv[0], p[2]); 3499 return 1; 3500 } 3501 } else if (*p == '-') { 3502 fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p); 3503 return 1; 3504 } else { 3505 id = p; 3506 } 3507 } 3508 3509 if (!id) { 3510 fprintf(stderr, "usage: %s [-v | -g] <game_id>\n", argv[0]); 3511 return 1; 3512 } 3513 3514 desc = strchr(id, ':'); 3515 if (!desc) { 3516 fprintf(stderr, "%s: game id expects a colon in it\n", argv[0]); 3517 return 1; 3518 } 3519 *desc++ = '\0'; 3520 3521 p = default_params(); 3522 decode_params(p, id); 3523 err = validate_desc(p, desc); 3524 if (err) { 3525 fprintf(stderr, "%s: %s\n", argv[0], err); 3526 return 1; 3527 } 3528 s = new_game(NULL, p, desc); 3529 3530 solver_diagnostics = diagnostics; 3531 sc = solver_make_scratch(p->n); 3532 solver_setup_grid(sc, s->numbers->numbers); 3533 retd = run_solver(sc, maxdiff); 3534 if (retd == 0) { 3535 printf("Puzzle is inconsistent\n"); 3536 } else if (grade) { 3537 printf("Difficulty rating: %s\n", 3538 dominosa_diffnames[sc->max_diff_used]); 3539 } else { 3540 char *move, *text; 3541 move = solution_move_string(sc); 3542 s2 = execute_move(s, move); 3543 text = game_text_format(s2); 3544 sfree(move); 3545 fputs(text, stdout); 3546 sfree(text); 3547 free_game(s2); 3548 if (retd > 1) 3549 printf("Could not deduce a unique solution\n"); 3550 } 3551 solver_free_scratch(sc); 3552 free_game(s); 3553 free_params(p); 3554 3555 return 0; 3556} 3557 3558#endif 3559 3560/* vim: set shiftwidth=4 :set textwidth=80: */ 3561