shithub: puzzles

Download patch

ref: 432590a05c17b8220efb97ffa038120f1102f5d8
parent: c9b3c3896ca54681d03bd6c25e433affd9809860
author: Simon Tatham <anakin@pobox.com>
date: Tue May 19 17:02:36 EDT 2020

Group: add a special deduction about the group identity.

In identity-hidden mode, as soon as you find any table entry that
matches the element indexing its row or column (i.e. a product of
group elements of the form ab=a or ab=b), then you immediately know
which element is the group identity, and you can fill in the rest of
its row and column trivially.

But the Group solver was not actually able to do this deduction.
Proof: it couldn't solve the game id 4i:1d1d1d1, which is trivial if
you take this into account. (a^2=a, so a is the identity, and once you
fill in ax=xa=x for all x, the rest of the grid is forced.)

So I've added dedicated piece of logic to spot the group identity as
soon as anything in its row and column is filled in. Now that puzzle
can be solved.

(A thing that I _haven't_ done here is the higher-level deduction of
determining the identity by elimination. Any table entry that
_doesn't_ match either its row or column rules out both the row and
the column from being the group identity - so as soon as you have
enough table entries to rule out all but one element, you know the
identity must be the remaining one. At the moment, this is just doing
the simple version of the deduction where a single table entry tells
you what _is_ the identity.)

One slightly tricky part is that because this new identity inference
deduction fills in multiple grid entries at a time, it has to be
careful to avoid triggering an assertion failure if the puzzle is
inconsistent - before filling in each entry, it has to make sure the
value it wants to fill in has not been ruled out by something it
filled in already. Without that care, an insoluble puzzle can cause
the solver to crash - and worse, the same can happen on an insoluble
_branch_ of the guesswork-and-backtracking tree in Unreasonable mode.
In both cases, the answer is to make sure you detect the problem
before hitting the assertion, and return -1 for 'inconsistent puzzle'
if you spot it. Then any guesswork branch that ends up in this
situation is cleanly abandoned, and we move on to another one.

--- a/unfinished/group.c
+++ b/unfinished/group.c
@@ -280,6 +280,23 @@
  * Solver.
  */
 
+static int find_identity(struct latin_solver *solver)
+{
+    int w = solver->o;
+    digit *grid = solver->grid;
+    int i, j;
+
+    for (i = 0; i < w; i++)
+	for (j = 0; j < w; j++) {
+            if (grid[i*w+j] == i+1)
+                return j+1;
+            if (grid[i*w+j] == j+1)
+                return i+1;
+        }
+
+    return 0;
+}
+
 static int solver_normal(struct latin_solver *solver, void *vctx)
 {
     int w = solver->o;
@@ -357,6 +374,73 @@
 		    }
 		}
 	    }
+
+    /*
+     * Fill in the row and column for the group identity, if it's not
+     * already known and if we've just found out what it is.
+     */
+    i = find_identity(solver);
+    if (i) {
+        bool done_something = false;
+        for (j = 1; j <= w; j++) {
+            if (!grid[(i-1)*w+(j-1)] || !grid[(j-1)*w+(i-1)]) {
+                done_something = true;
+            }
+        }
+        if (done_something) {
+#ifdef STANDALONE_SOLVER
+            if (solver_show_working) {
+                printf("%*s%s is the group identity\n",
+                       solver_recurse_depth*4, "", names[i-1]);
+            }
+#endif
+            for (j = 1; j <= w; j++) {
+                if (!grid[(j-1)*w+(i-1)]) {
+                    if (!cube(i-1, j-1, j)) {
+#ifdef STANDALONE_SOLVER
+                        if (solver_show_working) {
+                            printf("%*s  but %s cannot go at (%d,%d) - "
+                                   "contradiction!\n",
+                                   solver_recurse_depth*4, "",
+                                   names[j-1], i, j);
+                        }
+                        return -1;
+#endif
+                    }
+#ifdef STANDALONE_SOLVER
+                    if (solver_show_working) {
+                        printf("%*s  placing %s at (%d,%d)\n",
+                               solver_recurse_depth*4, "",
+                               names[j-1], i, j);
+                    }
+#endif
+                    latin_solver_place(solver, i-1, j-1, j);
+                }
+                if (!grid[(i-1)*w+(j-1)]) {
+                    if (!cube(j-1, i-1, j)) {
+#ifdef STANDALONE_SOLVER
+                        if (solver_show_working) {
+                            printf("%*s  but %s cannot go at (%d,%d) - "
+                                   "contradiction!\n",
+                                   solver_recurse_depth*4, "",
+                                   names[j-1], j, i);
+                        }
+                        return -1;
+#endif
+                    }
+#ifdef STANDALONE_SOLVER
+                    if (solver_show_working) {
+                        printf("%*s  placing %s at (%d,%d)\n",
+                               solver_recurse_depth*4, "",
+                               names[j-1], j, i);
+                    }
+#endif
+                    latin_solver_place(solver, j-1, i-1, j);
+                }
+            }
+            return 1;
+        }
+    }
 
     return 0;
 }