shithub: puzzles

Download patch

ref: 2ec6daee32ad7b2967141aa52ee3327af8055e67
parent: 6235f7fb3d1fd85c53ac7da9f461a6299c64ad5b
author: Simon Tatham <anakin@pobox.com>
date: Tue Apr 2 17:32:29 EDT 2019

Dominosa: new deduction deduce_local_duplicate().

This is a reasonably simple local deduction I've been using myself for
ages, and feel comfortable adding to the existing Basic difficulty
level.

--- a/dominosa.c
+++ b/dominosa.c
@@ -42,17 +42,6 @@
  *             careful that we don't rule out precisely the domino
  *             placement that was _included_ in our set!
  *
- *     * playing off the two ends of one potential domino, by
- *       considering the alternatives to that domino that each end
- *       might otherwise be part of.
- *        + if not playing this domino would require each end to be
- *          part of an identical domino, play it. (e.g. the middle of
- *          5-4-4-5)
- *        + if not playing this domino would guarantee that the two
- *          ends between them used up all of some other square's
- *          choices, play it. (e.g. the middle of 2-3-3-1 if another 3
- *          cell can only link to a 2 or a 1)
- *
  *     * identify 'forcing chains', in the sense of any path of cells
  *       each of which has only two possible dominoes to be part of,
  *       and each of those rules out one of the choices for the next
@@ -62,8 +51,6 @@
  *       the chain, or using up all of some other square's choices),
  *       then the whole set can be ruled out, and the other set played
  *       immediately.
- *        + this is of course a generalisation of the previous idea,
- *          which is simply a forcing chain of length 3.
  */
 
 #include <stdio.h>
@@ -539,6 +526,15 @@
     sc->max_diff_used = DIFF_TRIVIAL;
 }
 
+/* Given two placements p,q that overlap, returns si such that
+ * p->squares[si] is the square also in q */
+static int common_square_index(struct solver_placement *p,
+                               struct solver_placement *q)
+{
+    return (p->squares[0] == q->squares[0] ||
+            p->squares[0] == q->squares[1]) ? 0 : 1;
+}
+
 static void rule_out_placement(
     struct solver_scratch *sc, struct solver_placement *p)
 {
@@ -744,7 +740,54 @@
     return true;
 }
 
+
 /*
+ * If a placement of domino D overlaps the only remaining placement
+ * for some square S which is not also for domino D, then placing D
+ * here would require another copy of it in S, so we can rule it out.
+ */
+static bool deduce_local_duplicate(struct solver_scratch *sc, int pi)
+{
+    struct solver_placement *p = &sc->placements[pi];
+    struct solver_domino *d = p->domino;
+    int i, j;
+
+    if (!p->active)
+        return false;
+
+    for (i = 0; i < p->noverlaps; i++) {
+        struct solver_placement *q = p->overlaps[i];
+        struct solver_square *sq;
+
+        if (!q->active)
+            continue;
+
+        /* Find the square of q that _isn't_ part of p */
+        sq = q->squares[1 - common_square_index(q, p)];
+
+        for (j = 0; j < sq->nplacements; j++)
+            if (sq->placements[j] != q && sq->placements[j]->domino != d)
+                goto no;
+
+        /* If we get here, every possible placement for sq is either q
+         * itself, or another copy of d. Success! We can rule out p. */
+#ifdef SOLVER_DIAGNOSTICS
+        if (solver_diagnostics) {
+            printf("placement %s of domino %s would force another copy of %s "
+                   "in square %s\n", p->name, d->name, d->name, sq->name);
+        }
+#endif
+
+        rule_out_placement(sc, p);
+        return true;
+
+      no:;
+    }
+
+    return false;
+}
+
+/*
  * Run the solver until it can't make any more progress.
  *
  * Return value is:
@@ -755,7 +798,7 @@
  */
 static int run_solver(struct solver_scratch *sc, int max_diff_allowed)
 {
-    int di, si;
+    int di, si, pi;
     bool done_something;
 
 #ifdef SOLVER_DIAGNOSTICS
@@ -804,6 +847,14 @@
 
         for (di = 0; di < sc->dc; di++)
             if (deduce_domino_must_overlap(sc, di))
+                done_something = true;
+        if (done_something) {
+            sc->max_diff_used = max(sc->max_diff_used, DIFF_BASIC);
+            continue;
+        }
+
+        for (pi = 0; pi < sc->pc; pi++)
+            if (deduce_local_duplicate(sc, pi))
                 done_something = true;
         if (done_something) {
             sc->max_diff_used = max(sc->max_diff_used, DIFF_BASIC);