ref: 82ee3d42a4fd4907b8a4c98579d934176654eb7d
parent: f1c8e4092cf1f31bbf5a3889bd47cbe1c5955f87
author: Simon Tatham <anakin@pobox.com>
date: Tue Apr 2 17:08:43 EDT 2019
Dominosa: rewrite the solver. The new solver should be equivalent to the previous solver's intelligence level, but it's more usefully split up into basic data-structure maintenance and separate deduction routines that you can omit some of. So it's a better basis to build on when adding further deductions or dividing the existing ones into tiers. The new solver also produces much more legible diagnostics, when the command-line solver is run in -v mode.
--- a/dominosa.c
+++ b/dominosa.c
@@ -234,347 +234,550 @@
bool solver_diagnostics = false;
#endif
-static int find_overlaps(int w, int h, int placement, int *set)
-{
- int x, y, n;
+struct solver_domino;
+struct solver_placement;
- n = 0; /* number of returned placements */
+/*
+ * Information about a particular domino.
+ */
+struct solver_domino {
+ /* The numbers on the domino. */
+ int lo, hi;
- x = placement / 2;
- y = x / w;
- x %= w;
+ /* List of placements not yet ruled out for this domino. */
+ int nplacements;
+ struct solver_placement **placements;
- if (placement & 1) {
- /*
- * Horizontal domino, indexed by its left end.
- */
- if (x > 0)
- set[n++] = placement-2; /* horizontal domino to the left */
- if (y > 0)
- set[n++] = placement-2*w-1;/* vertical domino above left side */
- if (y+1 < h)
- set[n++] = placement-1; /* vertical domino below left side */
- if (x+2 < w)
- set[n++] = placement+2; /* horizontal domino to the right */
- if (y > 0)
- set[n++] = placement-2*w+2-1;/* vertical domino above right side */
- if (y+1 < h)
- set[n++] = placement+2-1; /* vertical domino below right side */
- } else {
- /*
- * Vertical domino, indexed by its top end.
- */
- if (y > 0)
- set[n++] = placement-2*w; /* vertical domino above */
- if (x > 0)
- set[n++] = placement-2+1; /* horizontal domino left of top */
- if (x+1 < w)
- set[n++] = placement+1; /* horizontal domino right of top */
- if (y+2 < h)
- set[n++] = placement+2*w; /* vertical domino below */
- if (x > 0)
- set[n++] = placement-2+2*w+1;/* horizontal domino left of bottom */
- if (x+1 < w)
- set[n++] = placement+2*w+1;/* horizontal domino right of bottom */
- }
+#ifdef SOLVER_DIAGNOSTICS
+ /* A textual name we can easily reuse in solver diagnostics. */
+ char *name;
+#endif
+};
- return n;
-}
+/*
+ * Information about a particular 'placement' (i.e. specific location
+ * that a domino might go in).
+ */
+struct solver_placement {
+ /* The two squares that make up this placement. */
+ struct solver_square *squares[2];
+ /* The domino that has to go in this position, if any. */
+ struct solver_domino *domino;
+
+ /* The index of this placement in each square's placements array,
+ * and in that of the domino. */
+ int spi[2], dpi;
+
+ /* Whether this is still considered a possible placement. */
+ bool active;
+
+ /* Other domino placements that overlap with this one. (Maximum 6:
+ * three overlapping each square of the placement.) */
+ int noverlaps;
+ struct solver_placement *overlaps[6];
+
+#ifdef SOLVER_DIAGNOSTICS
+ /* A textual name we can easily reuse in solver diagnostics. */
+ char *name;
+#endif
+};
+
/*
- * Returns 0, 1 or 2 for number of solutions. 2 means `any number
- * more than one', or more accurately `we were unable to prove
- * there was only one'.
- *
- * Outputs in a `placements' array, indexed the same way as the one
- * within this function (see below); entries in there are <0 for a
- * placement ruled out, 0 for an uncertain placement, and 1 for a
- * definite one.
+ * Information about a particular solver square.
*/
-static int solver(int w, int h, int n, int *grid, int *output)
+struct solver_square {
+ /* The coordinates of the square, and its index in a normal grid array. */
+ int x, y, index;
+
+ /* List of domino placements not yet ruled out for this square. */
+ int nplacements;
+ struct solver_placement *placements[4];
+
+ /* The number in the square. */
+ int number;
+
+#ifdef SOLVER_DIAGNOSTICS
+ /* A textual name we can easily reuse in solver diagnostics. */
+ char *name;
+#endif
+};
+
+struct solver_scratch {
+ int n, dc, pc, w, h, wh;
+ struct solver_domino *dominoes;
+ struct solver_placement *placements;
+ struct solver_square *squares;
+ struct solver_placement **domino_placement_lists;
+};
+
+static struct solver_scratch *solver_make_scratch(int n)
{
- int wh = w*h, dc = DCOUNT(n);
- int *placements, *heads;
- int i, j, x, y, ret;
+ int dc = DCOUNT(n), w = n+2, h = n+1, wh = w*h;
+ int pc = (w-1)*h + w*(h-1);
+ struct solver_scratch *sc = snew(struct solver_scratch);
+ int hi, lo, di, x, y, pi, si;
- /*
- * This array has one entry for every possible domino
- * placement. Vertical placements are indexed by their top
- * half, at (y*w+x)*2; horizontal placements are indexed by
- * their left half at (y*w+x)*2+1.
- *
- * This array is used to link domino placements together into
- * linked lists, so that we can track all the possible
- * placements of each different domino. It's also used as a
- * quick means of looking up an individual placement to see
- * whether we still think it's possible. Actual values stored
- * in this array are -2 (placement not possible at all), -1
- * (end of list), or the array index of the next item.
- *
- * Oh, and -3 for `not even valid', used for array indices
- * which don't even represent a plausible placement.
- */
- placements = snewn(2*wh, int);
- for (i = 0; i < 2*wh; i++)
- placements[i] = -3; /* not even valid */
+ sc->n = n;
+ sc->dc = dc;
+ sc->pc = pc;
+ sc->w = w;
+ sc->h = h;
+ sc->wh = wh;
- /*
- * This array has one entry for every domino, and it is an
- * index into `placements' denoting the head of the placement
- * list for that domino.
- */
- heads = snewn(dc, int);
- for (i = 0; i < dc; i++)
- heads[i] = -1;
+ sc->dominoes = snewn(dc, struct solver_domino);
+ sc->placements = snewn(pc, struct solver_placement);
+ sc->squares = snewn(wh, struct solver_square);
+ sc->domino_placement_lists = snewn(pc, struct solver_placement *);
- /*
- * Set up the initial possibility lists by scanning the grid.
- */
- for (y = 0; y < h-1; y++)
- for (x = 0; x < w; x++) {
- int di = DINDEX(grid[y*w+x], grid[(y+1)*w+x]);
- placements[(y*w+x)*2] = heads[di];
- heads[di] = (y*w+x)*2;
- }
- for (y = 0; y < h; y++)
- for (x = 0; x < w-1; x++) {
- int di = DINDEX(grid[y*w+x], grid[y*w+(x+1)]);
- placements[(y*w+x)*2+1] = heads[di];
- heads[di] = (y*w+x)*2+1;
- }
+ for (di = hi = 0; hi <= n; hi++) {
+ for (lo = 0; lo <= hi; lo++) {
+ assert(di == DINDEX(hi, lo));
+ sc->dominoes[di].hi = hi;
+ sc->dominoes[di].lo = lo;
#ifdef SOLVER_DIAGNOSTICS
- if (solver_diagnostics) {
- printf("before solver:\n");
- for (i = 0; i <= n; i++)
- for (j = 0; j <= i; j++) {
- int k;
- printf("%2d [%d %d]:", DINDEX(i, j), i, j);
- for (k = heads[DINDEX(i,j)]; k >= 0; k = placements[k])
- printf(" %3d [%d,%d,%c]", k, k/2%w, k/2/w, k%2?'h':'v');
- printf("\n");
+ {
+ char buf[128];
+ sprintf(buf, "%d-%d", hi, lo);
+ sc->dominoes[di].name = dupstr(buf);
}
- }
#endif
- while (1) {
- bool done_something = false;
+ di++;
+ }
+ }
- /*
- * For each domino, look at its possible placements, and
- * for each placement consider the placements (of any
- * domino) it overlaps. Any placement overlapped by all
- * placements of this domino can be ruled out.
- *
- * Each domino placement overlaps only six others, so we
- * need not do serious set theory to work this out.
- */
- for (i = 0; i < dc; i++) {
- int permset[6], permlen = 0, p;
-
+ for (y = 0; y < h; y++) {
+ for (x = 0; x < w; x++) {
+ struct solver_square *sq = &sc->squares[y*w+x];
+ sq->x = x;
+ sq->y = y;
+ sq->index = y * w + x;
+ sq->nplacements = 0;
- if (heads[i] == -1) { /* no placement for this domino */
- ret = 0; /* therefore puzzle is impossible */
- goto done;
+#ifdef SOLVER_DIAGNOSTICS
+ {
+ char buf[128];
+ sprintf(buf, "(%d,%d)", x, y);
+ sq->name = dupstr(buf);
}
- for (j = heads[i]; j >= 0; j = placements[j]) {
- assert(placements[j] != -2);
+#endif
+ }
+ }
- if (j == heads[i]) {
- permlen = find_overlaps(w, h, j, permset);
- } else {
- int tempset[6], templen, m, n, k;
+ pi = 0;
+ for (y = 0; y < h-1; y++) {
+ for (x = 0; x < w; x++) {
+ assert(pi < pc);
+ sc->placements[pi].squares[0] = &sc->squares[y*w+x];
+ sc->placements[pi].squares[1] = &sc->squares[(y+1)*w+x];
+#ifdef SOLVER_DIAGNOSTICS
+ {
+ char buf[128];
+ sprintf(buf, "(%d,%d-%d)", x, y, y+1);
+ sc->placements[pi].name = dupstr(buf);
+ }
+#endif
+ pi++;
+ }
+ }
+ for (y = 0; y < h; y++) {
+ for (x = 0; x < w-1; x++) {
+ assert(pi < pc);
+ sc->placements[pi].squares[0] = &sc->squares[y*w+x];
+ sc->placements[pi].squares[1] = &sc->squares[y*w+(x+1)];
+#ifdef SOLVER_DIAGNOSTICS
+ {
+ char buf[128];
+ sprintf(buf, "(%d-%d,%d)", x, x+1, y);
+ sc->placements[pi].name = dupstr(buf);
+ }
+#endif
+ pi++;
+ }
+ }
+ assert(pi == pc);
- templen = find_overlaps(w, h, j, tempset);
+ /* Set up the full placement lists for all squares, temporarily,
+ * so as to use them to calculate the overlap lists */
+ for (si = 0; si < wh; si++)
+ sc->squares[si].nplacements = 0;
+ for (pi = 0; pi < pc; pi++) {
+ struct solver_placement *p = &sc->placements[pi];
+ for (si = 0; si < 2; si++) {
+ struct solver_square *sq = p->squares[si];
+ p->spi[si] = sq->nplacements;
+ sq->placements[sq->nplacements++] = p;
+ }
+ }
- /*
- * Pathetically primitive set intersection
- * algorithm, which I'm only getting away with
- * because I know my sets are bounded by a very
- * small size.
- */
- for (m = n = 0; m < permlen; m++) {
- for (k = 0; k < templen; k++)
- if (tempset[k] == permset[m])
- break;
- if (k < templen)
- permset[n++] = permset[m];
- }
- permlen = n;
- }
- }
- for (p = 0; p < permlen; p++) {
- j = permset[p];
- if (placements[j] != -2) {
- int p1, p2, di;
+ /* Actually calculate the overlap lists */
+ for (pi = 0; pi < pc; pi++) {
+ struct solver_placement *p = &sc->placements[pi];
+ p->noverlaps = 0;
+ for (si = 0; si < 2; si++) {
+ struct solver_square *sq = p->squares[si];
+ int j;
+ for (j = 0; j < sq->nplacements; j++) {
+ struct solver_placement *q = sq->placements[j];
+ if (q != p)
+ p->overlaps[p->noverlaps++] = q;
+ }
+ }
+ }
+
+ return sc;
+}
- done_something = true;
-
- /*
- * Rule out this placement. First find what
- * domino it is...
- */
- p1 = j / 2;
- p2 = (j & 1) ? p1 + 1 : p1 + w;
- di = DINDEX(grid[p1], grid[p2]);
+static void solver_free_scratch(struct solver_scratch *sc)
+{
#ifdef SOLVER_DIAGNOSTICS
- if (solver_diagnostics) {
- printf("considering domino %d: ruling out placement %d"
- " for %d\n", i, j, di);
- }
+ {
+ int i;
+ for (i = 0; i < sc->dc; i++)
+ sfree(sc->dominoes[i].name);
+ for (i = 0; i < sc->pc; i++)
+ sfree(sc->placements[i].name);
+ for (i = 0; i < sc->wh; i++)
+ sfree(sc->squares[i].name);
+ }
#endif
+ sfree(sc->dominoes);
+ sfree(sc->placements);
+ sfree(sc->squares);
+ sfree(sc->domino_placement_lists);
+ sfree(sc);
+}
- /*
- * ... then walk that domino's placement list,
- * removing this placement when we find it.
- */
- if (heads[di] == j)
- heads[di] = placements[j];
- else {
- int k = heads[di];
- while (placements[k] != -1 && placements[k] != j)
- k = placements[k];
- assert(placements[k] == j);
- placements[k] = placements[j];
- }
- placements[j] = -2;
- }
- }
+static void solver_setup_grid(struct solver_scratch *sc, const int *numbers)
+{
+ int i, j;
+
+ for (i = 0; i < sc->wh; i++) {
+ sc->squares[i].nplacements = 0;
+ sc->squares[i].number = numbers[sc->squares[i].index];
+ }
+
+ for (i = 0; i < sc->pc; i++) {
+ struct solver_placement *p = &sc->placements[i];
+ int di = DINDEX(p->squares[0]->number, p->squares[1]->number);
+ p->domino = &sc->dominoes[di];
+ }
+
+ for (i = 0; i < sc->dc; i++)
+ sc->dominoes[i].nplacements = 0;
+ for (i = 0; i < sc->pc; i++)
+ sc->placements[i].domino->nplacements++;
+ for (i = j = 0; i < sc->dc; i++) {
+ sc->dominoes[i].placements = sc->domino_placement_lists + j;
+ j += sc->dominoes[i].nplacements;
+ sc->dominoes[i].nplacements = 0;
+ }
+ for (i = 0; i < sc->pc; i++) {
+ struct solver_placement *p = &sc->placements[i];
+ p->dpi = p->domino->nplacements;
+ p->domino->placements[p->domino->nplacements++] = p;
+ p->active = true;
+ }
+
+ for (i = 0; i < sc->wh; i++)
+ sc->squares[i].nplacements = 0;
+ for (i = 0; i < sc->pc; i++) {
+ struct solver_placement *p = &sc->placements[i];
+ for (j = 0; j < 2; j++) {
+ struct solver_square *sq = p->squares[j];
+ p->spi[j] = sq->nplacements;
+ sq->placements[sq->nplacements++] = p;
}
+ }
+}
- /*
- * For each square, look at the available placements
- * involving that square. If all of them are for the same
- * domino, then rule out any placements for that domino
- * _not_ involving this square.
- */
- for (i = 0; i < wh; i++) {
- int list[4], k, n, adi;
+static void rule_out_placement(
+ struct solver_scratch *sc, struct solver_placement *p)
+{
+ struct solver_domino *d = p->domino;
+ int i, j, si;
- x = i % w;
- y = i / w;
+#ifdef SOLVER_DIAGNOSTICS
+ if (solver_diagnostics)
+ printf(" ruling out placement %s for domino %s\n", p->name, d->name);
+#endif
- j = 0;
- if (x > 0)
- list[j++] = 2*(i-1)+1;
- if (x+1 < w)
- list[j++] = 2*i+1;
- if (y > 0)
- list[j++] = 2*(i-w);
- if (y+1 < h)
- list[j++] = 2*i;
+ p->active = false;
- for (n = k = 0; k < j; k++)
- if (placements[list[k]] >= -1)
- list[n++] = list[k];
+ i = p->dpi;
+ assert(d->placements[i] == p);
+ if (--d->nplacements != i) {
+ d->placements[i] = d->placements[d->nplacements];
+ d->placements[i]->dpi = i;
+ }
- adi = -1;
+ for (si = 0; si < 2; si++) {
+ struct solver_square *sq = p->squares[si];
+ i = p->spi[si];
+ assert(sq->placements[i] == p);
+ if (--sq->nplacements != i) {
+ sq->placements[i] = sq->placements[sq->nplacements];
+ j = (sq->placements[i]->squares[0] == sq ? 0 : 1);
+ sq->placements[i]->spi[j] = i;
+ }
+ }
+}
- for (j = 0; j < n; j++) {
- int p1, p2, di;
- k = list[j];
+/*
+ * If a domino has only one placement remaining, rule out all other
+ * placements that overlap it.
+ */
+static bool deduce_domino_single_placement(struct solver_scratch *sc, int di)
+{
+ struct solver_domino *d = &sc->dominoes[di];
+ struct solver_placement *p, *q;
+ int oi;
+ bool done_something = false;
- p1 = k / 2;
- p2 = (k & 1) ? p1 + 1 : p1 + w;
- di = DINDEX(grid[p1], grid[p2]);
+ if (d->nplacements != 1)
+ return false;
+ p = d->placements[0];
- if (adi == -1)
- adi = di;
- if (adi != di)
- break;
+ for (oi = 0; oi < p->noverlaps; oi++) {
+ q = p->overlaps[oi];
+ if (q->active) {
+ if (!done_something) {
+ done_something = true;
+#ifdef SOLVER_DIAGNOSTICS
+ if (solver_diagnostics)
+ printf("domino %s has unique placement %s\n",
+ d->name, p->name);
+#endif
}
+ rule_out_placement(sc, q);
+ }
+ }
- if (j == n) {
- int nn;
+ return done_something;
+}
- assert(adi >= 0);
- /*
- * We've found something. All viable placements
- * involving this square are for domino `adi'. If
- * the current placement list for that domino is
- * longer than n, reduce it to precisely this
- * placement list and we've done something.
- */
- nn = 0;
- for (k = heads[adi]; k >= 0; k = placements[k])
- nn++;
- if (nn > n) {
- done_something = true;
+/*
+ * If a square has only one placement remaining, rule out all other
+ * placements of its domino.
+ */
+static bool deduce_square_single_placement(struct solver_scratch *sc, int si)
+{
+ struct solver_square *sq = &sc->squares[si];
+ struct solver_placement *p;
+ struct solver_domino *d;
+
+ if (sq->nplacements != 1)
+ return false;
+ p = sq->placements[0];
+ d = p->domino;
+
+ if (d->nplacements <= 1)
+ return false; /* we already knew everything this would tell us */
+
#ifdef SOLVER_DIAGNOSTICS
- if (solver_diagnostics) {
- printf("considering square %d,%d: reducing placements "
- "of domino %d\n", x, y, adi);
- }
+ if (solver_diagnostics)
+ printf("square %s has unique placement %s (domino %s)\n",
+ sq->name, p->name, p->domino->name);
#endif
- /*
- * Set all other placements on the list to
- * impossible.
- */
- k = heads[adi];
- while (k >= 0) {
- int tmp = placements[k];
- placements[k] = -2;
- k = tmp;
- }
- /*
- * Set up the new list.
- */
- heads[adi] = list[0];
- for (k = 0; k < n; k++)
- placements[list[k]] = (k+1 == n ? -1 : list[k+1]);
- }
- }
+
+ while (d->nplacements > 1)
+ rule_out_placement(
+ sc, d->placements[0] == p ? d->placements[1] : d->placements[0]);
+
+ return true;
+}
+
+/*
+ * If all placements for a square involve the same domino, rule out
+ * all other placements of that domino.
+ */
+static bool deduce_square_single_domino(struct solver_scratch *sc, int si)
+{
+ struct solver_square *sq = &sc->squares[si];
+ struct solver_domino *d;
+ int i;
+
+ /*
+ * We only bother with this if the square has at least _two_
+ * placements. If it only has one, then a simpler deduction will
+ * have handled it already, or will do so the next time round the
+ * main solver loop - and we should let the simpler deduction do
+ * it, because that will give a less overblown diagnostic.
+ */
+ if (sq->nplacements < 2)
+ return false;
+
+ d = sq->placements[0]->domino;
+ for (i = 1; i < sq->nplacements; i++)
+ if (sq->placements[i]->domino != d)
+ return false; /* not all the same domino */
+
+ if (d->nplacements <= sq->nplacements)
+ return false; /* no other placements of d to rule out */
+
+#ifdef SOLVER_DIAGNOSTICS
+ if (solver_diagnostics)
+ printf("square %s can only contain domino %s\n", sq->name, d->name);
+#endif
+
+ for (i = d->nplacements; i-- > 0 ;) {
+ struct solver_placement *p = d->placements[i];
+ if (p->squares[0] != sq && p->squares[1] != sq)
+ rule_out_placement(sc, p);
+ }
+
+ return true;
+}
+
+/*
+ * If any placement is overlapped by _all_ possible placements of a
+ * given domino, rule that placement out.
+ */
+static bool deduce_domino_must_overlap(struct solver_scratch *sc, int di)
+{
+ struct solver_domino *d = &sc->dominoes[di];
+ struct solver_placement *intersection[6], *p;
+ int nintersection = 0;
+ int i, j, k;
+
+ /*
+ * As in deduce_square_single_domino, we only bother with this
+ * deduction if the domino has at least two placements.
+ */
+ if (d->nplacements < 2)
+ return false;
+
+ /* Initialise our set of overlapped placements with all the active
+ * ones overlapped by placements[0]. */
+ p = d->placements[0];
+ for (i = 0; i < p->noverlaps; i++)
+ if (p->overlaps[i]->active)
+ intersection[nintersection++] = p->overlaps[i];
+
+ /* Now loop over the other placements of d, winnowing that set. */
+ for (j = 1; j < d->nplacements; j++) {
+ int old_n;
+
+ p = d->placements[j];
+
+ old_n = nintersection;
+ nintersection = 0;
+
+ for (k = 0; k < old_n; k++) {
+ for (i = 0; i < p->noverlaps; i++)
+ if (p->overlaps[i] == intersection[k])
+ goto found;
+ /* If intersection[k] isn't in p->overlaps, exclude it
+ * from our set of placements overlapped by everything */
+ continue;
+ found:
+ intersection[nintersection++] = intersection[k];
}
+ }
- if (!done_something)
- break;
+ if (nintersection == 0)
+ return false; /* no new exclusions */
+
+ for (i = 0; i < nintersection; i++) {
+ p = intersection[i];
+
+#ifdef SOLVER_DIAGNOSTICS
+ if (solver_diagnostics) {
+ printf("placement %s of domino %s overlaps all placements "
+ "of domino %s:", p->name, p->domino->name, d->name);
+ for (j = 0; j < d->nplacements; j++)
+ printf(" %s", d->placements[j]->name);
+ printf("\n");
+ }
+#endif
+ rule_out_placement(sc, p);
}
+ return true;
+}
+
+/*
+ * Run the solver until it can't make any more progress.
+ *
+ * Return value is:
+ * 0 = no solution exists (puzzle clues are unsatisfiable)
+ * 1 = unique solution found (success!)
+ * 2 = multiple possibilities remain (puzzle is ambiguous or solver is not
+ * smart enough)
+ */
+static int run_solver(struct solver_scratch *sc)
+{
+ int di, si;
+ bool done_something;
+
#ifdef SOLVER_DIAGNOSTICS
if (solver_diagnostics) {
- printf("after solver:\n");
- for (i = 0; i <= n; i++)
- for (j = 0; j <= i; j++) {
- int k;
- printf("%2d [%d %d]:", DINDEX(i, j), i, j);
- for (k = heads[DINDEX(i,j)]; k >= 0; k = placements[k])
- printf(" %3d [%d,%d,%c]", k, k/2%w, k/2/w, k%2?'h':'v');
- printf("\n");
- }
+ int di, j;
+ printf("Initial possible placements:\n");
+ for (di = 0; di < sc->dc; di++) {
+ struct solver_domino *d = &sc->dominoes[di];
+ printf(" %s:", d->name);
+ for (j = 0; j < d->nplacements; j++)
+ printf(" %s", d->placements[j]->name);
+ printf("\n");
+ }
}
#endif
- ret = 1;
- for (i = 0; i < wh*2; i++) {
- if (placements[i] == -2) {
- if (output)
- output[i] = -1; /* ruled out */
- } else if (placements[i] != -3) {
- int p1, p2, di;
+ do {
+ done_something = false;
- p1 = i / 2;
- p2 = (i & 1) ? p1 + 1 : p1 + w;
- di = DINDEX(grid[p1], grid[p2]);
+ for (di = 0; di < sc->dc; di++)
+ if (deduce_domino_single_placement(sc, di))
+ done_something = true;
+ if (done_something)
+ continue;
- if (i == heads[di] && placements[i] == -1) {
- if (output)
- output[i] = 1; /* certain */
- } else {
- if (output)
- output[i] = 0; /* uncertain */
- ret = 2;
- }
+ for (si = 0; si < sc->wh; si++)
+ if (deduce_square_single_placement(sc, si))
+ done_something = true;
+ if (done_something)
+ continue;
+
+ /* FIXME: Trivial difficulty ends here */
+
+ for (si = 0; si < sc->wh; si++)
+ if (deduce_square_single_domino(sc, si))
+ done_something = true;
+ if (done_something)
+ continue;
+
+ for (di = 0; di < sc->dc; di++)
+ if (deduce_domino_must_overlap(sc, di))
+ done_something = true;
+ if (done_something)
+ continue;
+
+ } while (done_something);
+
+#ifdef SOLVER_DIAGNOSTICS
+ if (solver_diagnostics) {
+ int di, j;
+ printf("Final possible placements:\n");
+ for (di = 0; di < sc->dc; di++) {
+ struct solver_domino *d = &sc->dominoes[di];
+ printf(" %s:", d->name);
+ for (j = 0; j < d->nplacements; j++)
+ printf(" %s", d->placements[j]->name);
+ printf("\n");
}
}
+#endif
- done:
- /*
- * Free working data.
- */
- sfree(placements);
- sfree(heads);
-
- return ret;
+ for (di = 0; di < sc->dc; di++)
+ if (sc->dominoes[di].nplacements == 0)
+ return 0;
+ for (di = 0; di < sc->dc; di++)
+ if (sc->dominoes[di].nplacements > 1)
+ return 2;
+ return 1;
}
/* ----------------------------------------------------------------------
@@ -586,6 +789,7 @@
{
int n = params->n, w = n+2, h = n+1, wh = w*h;
int *grid, *grid2, *list;
+ struct solver_scratch *sc;
int i, j, k, len;
char *ret;
@@ -595,6 +799,7 @@
grid = snewn(wh, int);
grid2 = snewn(wh, int);
list = snewn(2*wh, int);
+ sc = solver_make_scratch(n);
/*
* I haven't been able to think of any particularly clever
@@ -704,8 +909,11 @@
j += 2;
}
assert(j == k);
- } while (params->unique && solver(w, h, n, grid2, NULL) > 1);
+ solver_setup_grid(sc, grid2);
+ } while (params->unique && run_solver(sc) > 1);
+ solver_free_scratch(sc);
+
#ifdef GENERATION_DIAGNOSTICS
for (j = 0; j < h; j++) {
for (i = 0; i < w; i++) {
@@ -909,28 +1117,11 @@
sfree(state);
}
-static int *solution_placements(int n, int *numbers, int *solver_retval)
+static char *solution_move_string(struct solver_scratch *sc)
{
- int w = n+2, h = n+1, wh = w*h;
- int i, retd;
- int *placements = snewn(wh*2, int);
-
- for (i = 0; i < wh*2; i++)
- placements[i] = -3;
-
- retd = solver(w, h, n, numbers, placements);
-
- if (solver_retval)
- *solver_retval = retd;
- return placements;
-}
-
-static char *solution_move_string(int n, int *placements)
-{
- int w = n+2, h = n+1, wh = w*h;
char *ret;
int retlen, retsize;
- int i, v;
+ int i, pass;
/*
* First make a pass putting in edges for -1, then make a pass
@@ -940,24 +1131,38 @@
ret = snewn(retsize, char);
retlen = sprintf(ret, "S");
- for (v = -1; v <= +1; v += 2)
- for (i = 0; i < wh*2; i++)
- if (placements[i] == v) {
- int p1 = i / 2;
- int p2 = (i & 1) ? p1+1 : p1+w;
- char buf[80];
+ for (pass = 0; pass < 2; pass++) {
+ char type = "ED"[pass];
- int extra = sprintf(buf, ";%c%d,%d",
- (int)(v==-1 ? 'E' : 'D'), p1, p2);
+ for (i = 0; i < sc->pc; i++) {
+ struct solver_placement *p = &sc->placements[i];
+ char buf[80];
+ int extra;
- if (retlen + extra + 1 >= retsize) {
- retsize = retlen + extra + 256;
- ret = sresize(ret, retsize, char);
- }
- strcpy(ret + retlen, buf);
- retlen += extra;
+ if (pass == 0) {
+ /* Emit a barrier if this placement is ruled out for
+ * the domino. */
+ if (p->active)
+ continue;
+ } else {
+ /* Emit a domino if this placement is the only one not
+ * ruled out. */
+ if (!p->active || p->domino->nplacements > 1)
+ continue;
}
+ extra = sprintf(buf, ";%c%d,%d", type,
+ p->squares[0]->index, p->squares[1]->index);
+
+ if (retlen + extra + 1 >= retsize) {
+ retsize = retlen + extra + 256;
+ ret = sresize(ret, retsize, char);
+ }
+ strcpy(ret + retlen, buf);
+ retlen += extra;
+ }
+ }
+
return ret;
}
@@ -965,7 +1170,6 @@
const char *aux, const char **error)
{
int n = state->params.n, w = n+2, h = n+1, wh = w*h;
- int *placements;
char *ret;
int retlen, retsize;
int i;
@@ -994,9 +1198,11 @@
}
} else {
- placements = solution_placements(n, state->numbers->numbers, NULL);
- ret = solution_move_string(n, placements);
- sfree(placements);
+ struct solver_scratch *sc = solver_make_scratch(n);
+ solver_setup_grid(sc, state->numbers->numbers);
+ run_solver(sc);
+ ret = solution_move_string(sc);
+ solver_free_scratch(sc);
}
return ret;
@@ -1813,6 +2019,7 @@
char *id = NULL, *desc;
const char *err;
bool diagnostics = false;
+ struct solver_scratch *sc;
int retd;
while (--argc > 0) {
@@ -1849,12 +2056,14 @@
s = new_game(NULL, p, desc);
solver_diagnostics = diagnostics;
- int *placements = solution_placements(p->n, s->numbers->numbers, &retd);
+ sc = solver_make_scratch(p->n);
+ solver_setup_grid(sc, s->numbers->numbers);
+ retd = run_solver(sc);
if (retd == 0) {
printf("Puzzle is inconsistent\n");
} else {
char *move, *text;
- move = solution_move_string(p->n, placements);
+ move = solution_move_string(sc);
s2 = execute_move(s, move);
text = game_text_format(s2);
sfree(move);
@@ -1864,7 +2073,7 @@
if (retd > 1)
printf("Could not deduce a unique solution\n");
}
- sfree(placements);
+ solver_free_scratch(sc);
free_game(s);
free_params(p);