+ /*
+ * Intersectional analysis, rows vs blocks.
+ */
+ for (y = 0; y < cr; y++)
+ for (b = 0; b < cr; b++)
+ for (n = 1; n <= cr; n++) {
+ if (usage->row[y*cr+n-1] ||
+ usage->blk[b*cr+n-1])
+ continue;
+ for (i = 0; i < cr; i++) {
+ scratch->indexlist[i] = cubepos(i, y, n);
+ scratch->indexlist2[i] = cubepos2(usage->blocks->blocks[b][i], n);
+ }
+ /*
+ * solver_intersect() never returns -1.
+ */
+ if (solver_intersect(usage, scratch->indexlist,
+ scratch->indexlist2
+#ifdef STANDALONE_SOLVER
+ , "intersectional analysis,"
+ " %d in row %d vs block %s",
+ n, 1+y, usage->blocks->blocknames[b]
+#endif
+ ) ||
+ solver_intersect(usage, scratch->indexlist2,
+ scratch->indexlist
+#ifdef STANDALONE_SOLVER
+ , "intersectional analysis,"
+ " %d in block %s vs row %d",
+ n, usage->blocks->blocknames[b], 1+y
+#endif
+ )) {
+ diff = max(diff, DIFF_INTERSECT);
+ goto cont;
+ }
+ }
+
+ /*
+ * Intersectional analysis, columns vs blocks.
+ */
+ for (x = 0; x < cr; x++)
+ for (b = 0; b < cr; b++)
+ for (n = 1; n <= cr; n++) {
+ if (usage->col[x*cr+n-1] ||
+ usage->blk[b*cr+n-1])
+ continue;
+ for (i = 0; i < cr; i++) {
+ scratch->indexlist[i] = cubepos(x, i, n);
+ scratch->indexlist2[i] = cubepos2(usage->blocks->blocks[b][i], n);
+ }
+ if (solver_intersect(usage, scratch->indexlist,
+ scratch->indexlist2
+#ifdef STANDALONE_SOLVER
+ , "intersectional analysis,"
+ " %d in column %d vs block %s",
+ n, 1+x, usage->blocks->blocknames[b]
+#endif
+ ) ||
+ solver_intersect(usage, scratch->indexlist2,
+ scratch->indexlist
+#ifdef STANDALONE_SOLVER
+ , "intersectional analysis,"
+ " %d in block %s vs column %d",
+ n, usage->blocks->blocknames[b], 1+x
+#endif
+ )) {
+ diff = max(diff, DIFF_INTERSECT);
+ goto cont;
+ }
+ }
+
+ if (usage->diag) {
+ /*
+ * Intersectional analysis, \-diagonal vs blocks.
+ */
+ for (b = 0; b < cr; b++)
+ for (n = 1; n <= cr; n++) {
+ if (usage->diag[n-1] ||
+ usage->blk[b*cr+n-1])
+ continue;
+ for (i = 0; i < cr; i++) {
+ scratch->indexlist[i] = cubepos2(diag0(i), n);
+ scratch->indexlist2[i] = cubepos2(usage->blocks->blocks[b][i], n);
+ }
+ if (solver_intersect(usage, scratch->indexlist,
+ scratch->indexlist2
+#ifdef STANDALONE_SOLVER
+ , "intersectional analysis,"
+ " %d in \\-diagonal vs block %s",
+ n, usage->blocks->blocknames[b]
+#endif
+ ) ||
+ solver_intersect(usage, scratch->indexlist2,
+ scratch->indexlist
+#ifdef STANDALONE_SOLVER
+ , "intersectional analysis,"
+ " %d in block %s vs \\-diagonal",
+ n, usage->blocks->blocknames[b]
+#endif
+ )) {
+ diff = max(diff, DIFF_INTERSECT);
+ goto cont;
+ }
+ }
+
+ /*
+ * Intersectional analysis, /-diagonal vs blocks.
+ */
+ for (b = 0; b < cr; b++)
+ for (n = 1; n <= cr; n++) {
+ if (usage->diag[cr+n-1] ||
+ usage->blk[b*cr+n-1])
+ continue;
+ for (i = 0; i < cr; i++) {
+ scratch->indexlist[i] = cubepos2(diag1(i), n);
+ scratch->indexlist2[i] = cubepos2(usage->blocks->blocks[b][i], n);
+ }
+ if (solver_intersect(usage, scratch->indexlist,
+ scratch->indexlist2
+#ifdef STANDALONE_SOLVER
+ , "intersectional analysis,"
+ " %d in /-diagonal vs block %s",
+ n, usage->blocks->blocknames[b]
+#endif
+ ) ||
+ solver_intersect(usage, scratch->indexlist2,
+ scratch->indexlist
+#ifdef STANDALONE_SOLVER
+ , "intersectional analysis,"
+ " %d in block %s vs /-diagonal",
+ n, usage->blocks->blocknames[b]
+#endif
+ )) {
+ diff = max(diff, DIFF_INTERSECT);
+ goto cont;
+ }
+ }
+ }
+
+ if (dlev->maxdiff <= DIFF_INTERSECT)
+ break;
+
+ /*
+ * Blockwise set elimination.
+ */
+ for (b = 0; b < cr; b++) {
+ for (i = 0; i < cr; i++)
+ for (n = 1; n <= cr; n++)
+ scratch->indexlist[i*cr+n-1] = cubepos2(usage->blocks->blocks[b][i], n);
+ ret = solver_set(usage, scratch, scratch->indexlist
+#ifdef STANDALONE_SOLVER
+ , "set elimination, block %s",
+ usage->blocks->blocknames[b]
+#endif
+ );
+ if (ret < 0) {
+ diff = DIFF_IMPOSSIBLE;
+ goto got_result;
+ } else if (ret > 0) {
+ diff = max(diff, DIFF_SET);
+ goto cont;
+ }
+ }
+
+ /*
+ * Row-wise set elimination.
+ */
+ for (y = 0; y < cr; y++) {
+ for (x = 0; x < cr; x++)
+ for (n = 1; n <= cr; n++)
+ scratch->indexlist[x*cr+n-1] = cubepos(x, y, n);
+ ret = solver_set(usage, scratch, scratch->indexlist
+#ifdef STANDALONE_SOLVER
+ , "set elimination, row %d", 1+y
+#endif
+ );
+ if (ret < 0) {
+ diff = DIFF_IMPOSSIBLE;
+ goto got_result;
+ } else if (ret > 0) {
+ diff = max(diff, DIFF_SET);
+ goto cont;
+ }
+ }
+
+ /*
+ * Column-wise set elimination.
+ */
+ for (x = 0; x < cr; x++) {
+ for (y = 0; y < cr; y++)
+ for (n = 1; n <= cr; n++)
+ scratch->indexlist[y*cr+n-1] = cubepos(x, y, n);
+ ret = solver_set(usage, scratch, scratch->indexlist
+#ifdef STANDALONE_SOLVER
+ , "set elimination, column %d", 1+x
+#endif
+ );
+ if (ret < 0) {
+ diff = DIFF_IMPOSSIBLE;
+ goto got_result;
+ } else if (ret > 0) {
+ diff = max(diff, DIFF_SET);
+ goto cont;
+ }
+ }
+
+ if (usage->diag) {
+ /*
+ * \-diagonal set elimination.
+ */
+ for (i = 0; i < cr; i++)
+ for (n = 1; n <= cr; n++)
+ scratch->indexlist[i*cr+n-1] = cubepos2(diag0(i), n);
+ ret = solver_set(usage, scratch, scratch->indexlist
+#ifdef STANDALONE_SOLVER
+ , "set elimination, \\-diagonal"
+#endif
+ );
+ if (ret < 0) {
+ diff = DIFF_IMPOSSIBLE;
+ goto got_result;
+ } else if (ret > 0) {
+ diff = max(diff, DIFF_SET);
+ goto cont;
+ }
+
+ /*
+ * /-diagonal set elimination.
+ */
+ for (i = 0; i < cr; i++)
+ for (n = 1; n <= cr; n++)
+ scratch->indexlist[i*cr+n-1] = cubepos2(diag1(i), n);
+ ret = solver_set(usage, scratch, scratch->indexlist
+#ifdef STANDALONE_SOLVER
+ , "set elimination, /-diagonal"
+#endif
+ );
+ if (ret < 0) {
+ diff = DIFF_IMPOSSIBLE;
+ goto got_result;
+ } else if (ret > 0) {
+ diff = max(diff, DIFF_SET);
+ goto cont;
+ }
+ }
+
+ if (dlev->maxdiff <= DIFF_SET)
+ break;
+
+ /*
+ * Row-vs-column set elimination on a single number.
+ */
+ for (n = 1; n <= cr; n++) {
+ for (y = 0; y < cr; y++)
+ for (x = 0; x < cr; x++)
+ scratch->indexlist[y*cr+x] = cubepos(x, y, n);
+ ret = solver_set(usage, scratch, scratch->indexlist
+#ifdef STANDALONE_SOLVER
+ , "positional set elimination, number %d", n
+#endif
+ );
+ if (ret < 0) {
+ diff = DIFF_IMPOSSIBLE;
+ goto got_result;
+ } else if (ret > 0) {
+ diff = max(diff, DIFF_EXTREME);
+ goto cont;
+ }
+ }
+
+ /*
+ * Forcing chains.
+ */
+ if (solver_forcing(usage, scratch)) {
+ diff = max(diff, DIFF_EXTREME);
+ goto cont;
+ }
+
+ /*
+ * If we reach here, we have made no deductions in this
+ * iteration, so the algorithm terminates.
+ */
+ break;
+ }
+
+ /*
+ * Last chance: if we haven't fully solved the puzzle yet, try
+ * recursing based on guesses for a particular square. We pick
+ * one of the most constrained empty squares we can find, which
+ * has the effect of pruning the search tree as much as
+ * possible.
+ */
+ if (dlev->maxdiff >= DIFF_RECURSIVE) {
+ int best, bestcount;
+
+ best = -1;
+ bestcount = cr+1;
+
+ for (y = 0; y < cr; y++)
+ for (x = 0; x < cr; x++)
+ if (!grid[y*cr+x]) {
+ int count;
+
+ /*
+ * An unfilled square. Count the number of
+ * possible digits in it.
+ */
+ count = 0;
+ for (n = 1; n <= cr; n++)
+ if (cube(x,y,n))
+ count++;
+
+ /*
+ * We should have found any impossibilities
+ * already, so this can safely be an assert.
+ */
+ assert(count > 1);
+
+ if (count < bestcount) {
+ bestcount = count;
+ best = y*cr+x;
+ }
+ }
+
+ if (best != -1) {
+ int i, j;
+ digit *list, *ingrid, *outgrid;
+
+ diff = DIFF_IMPOSSIBLE; /* no solution found yet */
+
+ /*
+ * Attempt recursion.
+ */
+ y = best / cr;
+ x = best % cr;
+
+ list = snewn(cr, digit);
+ ingrid = snewn(cr * cr, digit);
+ outgrid = snewn(cr * cr, digit);
+ memcpy(ingrid, grid, cr * cr);
+
+ /* Make a list of the possible digits. */
+ for (j = 0, n = 1; n <= cr; n++)
+ if (cube(x,y,n))
+ list[j++] = n;
+
+#ifdef STANDALONE_SOLVER
+ if (solver_show_working) {
+ char *sep = "";
+ printf("%*srecursing on (%d,%d) [",
+ solver_recurse_depth*4, "", x + 1, y + 1);
+ for (i = 0; i < j; i++) {
+ printf("%s%d", sep, list[i]);
+ sep = " or ";
+ }
+ printf("]\n");
+ }
+#endif
+
+ /*
+ * And step along the list, recursing back into the
+ * main solver at every stage.
+ */
+ for (i = 0; i < j; i++) {
+ memcpy(outgrid, ingrid, cr * cr);
+ outgrid[y*cr+x] = list[i];
+
+#ifdef STANDALONE_SOLVER
+ if (solver_show_working)
+ printf("%*sguessing %d at (%d,%d)\n",
+ solver_recurse_depth*4, "", list[i], x + 1, y + 1);
+ solver_recurse_depth++;
+#endif
+
+ solver(cr, blocks, kblocks, xtype, outgrid, kgrid, dlev);
+
+#ifdef STANDALONE_SOLVER
+ solver_recurse_depth--;
+ if (solver_show_working) {
+ printf("%*sretracting %d at (%d,%d)\n",
+ solver_recurse_depth*4, "", list[i], x + 1, y + 1);
+ }
+#endif
+
+ /*
+ * If we have our first solution, copy it into the
+ * grid we will return.
+ */
+ if (diff == DIFF_IMPOSSIBLE && dlev->diff != DIFF_IMPOSSIBLE)
+ memcpy(grid, outgrid, cr*cr);
+
+ if (dlev->diff == DIFF_AMBIGUOUS)
+ diff = DIFF_AMBIGUOUS;
+ else if (dlev->diff == DIFF_IMPOSSIBLE)
+ /* do not change our return value */;
+ else {
+ /* the recursion turned up exactly one solution */
+ if (diff == DIFF_IMPOSSIBLE)
+ diff = DIFF_RECURSIVE;
+ else
+ diff = DIFF_AMBIGUOUS;
+ }
+
+ /*
+ * As soon as we've found more than one solution,
+ * give up immediately.
+ */
+ if (diff == DIFF_AMBIGUOUS)
+ break;
+ }
+
+ sfree(outgrid);
+ sfree(ingrid);
+ sfree(list);
+ }
+
+ } else {
+ /*
+ * We're forbidden to use recursion, so we just see whether
+ * our grid is fully solved, and return DIFF_IMPOSSIBLE
+ * otherwise.
+ */
+ for (y = 0; y < cr; y++)
+ for (x = 0; x < cr; x++)
+ if (!grid[y*cr+x])
+ diff = DIFF_IMPOSSIBLE;
+ }
+
+ got_result:
+ dlev->diff = diff;
+ dlev->kdiff = kdiff;
+
+#ifdef STANDALONE_SOLVER
+ if (solver_show_working)
+ printf("%*s%s found\n",
+ solver_recurse_depth*4, "",
+ diff == DIFF_IMPOSSIBLE ? "no solution" :
+ diff == DIFF_AMBIGUOUS ? "multiple solutions" :
+ "one solution");
+#endif
+
+ sfree(usage->sq2region);
+ sfree(usage->regions);
+ sfree(usage->cube);
+ sfree(usage->row);
+ sfree(usage->col);