shithub: puzzles

Download patch

ref: f1c8e4092cf1f31bbf5a3889bd47cbe1c5955f87
parent: 68363231f062192156799af7a153fc3ab3a0f5ed
author: Simon Tatham <anakin@pobox.com>
date: Tue Apr 2 14:42:01 EDT 2019

Dominosa: add a command-line solver.

I've made the existing optional solver diagnostics appear as the
verbose output of the solver program. They're not particularly legible
at the moment, but they're better than nothing.

--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,7 @@
 /bridges
 /cube
 /dominosa
+/dominosasolver
 /fifteen
 /fifteensolver
 /filling
--- a/dominosa.R
+++ b/dominosa.R
@@ -8,6 +8,9 @@
 
 ALL += dominosa[COMBINED] DOMINOSA_EXTRA
 
+dominosasolver :   [U] dominosa[STANDALONE_SOLVER] DOMINOSA_EXTRA STANDALONE
+dominosasolver :   [C] dominosa[STANDALONE_SOLVER] DOMINOSA_EXTRA STANDALONE
+
 !begin am gtk
 GAMES += dominosa
 !end
--- a/dominosa.c
+++ b/dominosa.c
@@ -229,6 +229,11 @@
  * Solver.
  */
 
+#ifdef STANDALONE_SOLVER
+#define SOLVER_DIAGNOSTICS
+bool solver_diagnostics = false;
+#endif
+
 static int find_overlaps(int w, int h, int placement, int *set)
 {
     int x, y, n;
@@ -339,16 +344,17 @@
         }
 
 #ifdef SOLVER_DIAGNOSTICS
-    printf("before solver:\n");
-    for (i = 0; i <= n; i++)
-        for (j = 0; j <= i; j++) {
-            int k, m;
-            m = 0;
-            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");
-        }
+    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");
+            }
+    }
 #endif
 
     while (1) {
@@ -412,8 +418,10 @@
                     p2 = (j & 1) ? p1 + 1 : p1 + w;
                     di = DINDEX(grid[p1], grid[p2]);
 #ifdef SOLVER_DIAGNOSTICS
-                    printf("considering domino %d: ruling out placement %d"
-                           " for %d\n", i, j, di);
+                    if (solver_diagnostics) {
+                        printf("considering domino %d: ruling out placement %d"
+                               " for %d\n", i, j, di);
+                    }
 #endif
 
                     /*
@@ -493,8 +501,10 @@
                 if (nn > n) {
                     done_something = true;
 #ifdef SOLVER_DIAGNOSTICS
-                    printf("considering square %d,%d: reducing placements "
-                           "of domino %d\n", x, y, adi);
+                    if (solver_diagnostics) {
+                        printf("considering square %d,%d: reducing placements "
+                               "of domino %d\n", x, y, adi);
+                    }
 #endif
                     /*
                      * Set all other placements on the list to
@@ -521,16 +531,17 @@
     }
 
 #ifdef SOLVER_DIAGNOSTICS
-    printf("after solver:\n");
-    for (i = 0; i <= n; i++)
-        for (j = 0; j <= i; j++) {
-            int k, m;
-            m = 0;
-            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");
-        }
+    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");
+            }
+    }
 #endif
 
     ret = 1;
@@ -898,6 +909,58 @@
     sfree(state);
 }
 
+static int *solution_placements(int n, int *numbers, int *solver_retval)
+{
+    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;
+
+    /*
+     * First make a pass putting in edges for -1, then make a pass
+     * putting in dominoes for +1.
+     */
+    retsize = 256;
+    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];
+
+                int extra = sprintf(buf, ";%c%d,%d",
+                                    (int)(v==-1 ? 'E' : 'D'), p1, p2);
+
+                if (retlen + extra + 1 >= retsize) {
+                    retsize = retlen + extra + 256;
+                    ret = sresize(ret, retsize, char);
+                }
+                strcpy(ret + retlen, buf);
+                retlen += extra;
+            }
+
+    return ret;
+}
+
 static char *solve_game(const game_state *state, const game_state *currstate,
                         const char *aux, const char **error)
 {
@@ -905,7 +968,7 @@
     int *placements;
     char *ret;
     int retlen, retsize;
-    int i, v;
+    int i;
     char buf[80];
     int extra;
 
@@ -931,37 +994,8 @@
 	}
 
     } else {
-
-	placements = snewn(wh*2, int);
-	for (i = 0; i < wh*2; i++)
-	    placements[i] = -3;
-	solver(w, h, n, state->numbers->numbers, placements);
-
-	/*
-	 * First make a pass putting in edges for -1, then make a pass
-	 * putting in dominoes for +1.
-	 */
-	retsize = 256;
-	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;
-
-		    extra = sprintf(buf, ";%c%d,%d",
-				    (int)(v==-1 ? 'E' : 'D'), p1, p2);
-
-		    if (retlen + extra + 1 >= retsize) {
-			retsize = retlen + extra + 256;
-			ret = sresize(ret, retsize, char);
-		    }
-		    strcpy(ret + retlen, buf);
-		    retlen += extra;
-		}
-
+        placements = solution_placements(n, state->numbers->numbers, NULL);
+        ret = solution_move_string(n, placements);
 	sfree(placements);
     }
 
@@ -1769,6 +1803,75 @@
     false, game_timing_state,
     0,				       /* flags */
 };
+
+#ifdef STANDALONE_SOLVER
+
+int main(int argc, char **argv)
+{
+    game_params *p;
+    game_state *s, *s2;
+    char *id = NULL, *desc;
+    const char *err;
+    bool diagnostics = false;
+    int retd;
+
+    while (--argc > 0) {
+        char *p = *++argv;
+        if (!strcmp(p, "-v")) {
+            diagnostics = true;
+        } else if (*p == '-') {
+            fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p);
+            return 1;
+        } else {
+            id = p;
+        }
+    }
+
+    if (!id) {
+        fprintf(stderr, "usage: %s [-v] <game_id>\n", argv[0]);
+        return 1;
+    }
+
+    desc = strchr(id, ':');
+    if (!desc) {
+        fprintf(stderr, "%s: game id expects a colon in it\n", argv[0]);
+        return 1;
+    }
+    *desc++ = '\0';
+
+    p = default_params();
+    decode_params(p, id);
+    err = validate_desc(p, desc);
+    if (err) {
+        fprintf(stderr, "%s: %s\n", argv[0], err);
+        return 1;
+    }
+    s = new_game(NULL, p, desc);
+
+    solver_diagnostics = diagnostics;
+    int *placements = solution_placements(p->n, s->numbers->numbers, &retd);
+    if (retd == 0) {
+        printf("Puzzle is inconsistent\n");
+    } else {
+        char *move, *text;
+        move = solution_move_string(p->n, placements);
+        s2 = execute_move(s, move);
+        text = game_text_format(s2);
+        sfree(move);
+        fputs(text, stdout);
+        sfree(text);
+        free_game(s2);
+        if (retd > 1)
+            printf("Could not deduce a unique solution\n");
+    }
+    sfree(placements);
+    free_game(s);
+    free_params(p);
+
+    return 0;
+}
+
+#endif
 
 /* vim: set shiftwidth=4 :set textwidth=80: */