* or for generating the playfield for Jigsaw Sudoku.
*/
+/*
+ * Possible improvements which might cut the fail rate:
+ *
+ * - instead of picking one omino to extend in an iteration, try
+ * them all in succession (in a randomised order)
+ *
+ * - (for real rigour) instead of bfsing over ominoes, bfs over
+ * the space of possible _removed squares_. That way we aren't
+ * limited to randomly choosing a single square to remove from
+ * an omino and failing if that particular square doesn't
+ * happen to work.
+ *
+ * However, I don't currently think it's neecss~|~
+ */
+
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
* In both of the above suggested use cases, the user would
* probably want w==h==k, but that isn't a requirement.
*/
-int *divvy_rectangle(int w, int h, int k, random_state *rs)
+static int *divvy_internal(int w, int h, int k, random_state *rs)
{
int *order, *queue, *tmp, *own, *sizes, *addable, *removable, *retdsf;
int wh = w*h;
int dir;
if (curr < 0) {
- removable[yx] = 0; /* can't remove if it's not owned! */
+ removable[yx] = FALSE; /* can't remove if not owned! */
+ } else if (sizes[curr] == 1) {
+ removable[yx] = TRUE; /* can always remove a singleton */
} else {
/*
* See if this square can be removed from its
if (j == 0)
break; /* all ominoes are complete! */
j = tmp[random_upto(rs, j)];
+#ifdef DIVVY_DIAGNOSTICS
+ printf("Trying to extend %d\n", j);
+#endif
/*
* So we're trying to expand omino j. We breadth-first
tmpsq = tmp[2*j+1];
if (tmpsq >= 0) {
assert(own[tmpsq] == j);
- own[tmpsq] = -1;
+ own[tmpsq] = -3;
}
/*
for (i = 0; i < wh; i++) {
int dir;
- if (own[order[i]] >= 0)
+ if (own[order[i]] != -1)
continue; /* this square is claimed */
+
+ /*
+ * Special case: if our current omino was size 1
+ * and then had a square stolen from it, it's now
+ * size zero, which means it's valid to `expand'
+ * it into _any_ unclaimed square.
+ */
+ if (sizes[j] == 1 && tmpsq >= 0)
+ break; /* got one */
+
+ /*
+ * Failing that, we must do the full test for
+ * addability.
+ */
for (dir = 0; dir < 4; dir++)
if (addable[order[i]*4+dir] == j) {
/*
* addable to this omino when the omino is
* missing a square. To do this it's only
* necessary to re-check addremcommon.
-~|~ */
+ */
if (!addremcommon(w, h, order[i]%w, order[i]/w,
own, j))
continue;
}
if (dir == 4)
continue; /* we can't add this square to j */
+
break; /* got one! */
}
if (i < wh) {
i = order[i];
+ /*
+ * Restore the temporarily removed square _before_
+ * we start shifting ownerships about.
+ */
+ if (tmpsq >= 0)
+ own[tmpsq] = j;
+
/*
* We are done. We can add square i to omino j,
* and then backtrack along the trail in tmp
* moving squares between ominoes, ending up
* expanding our starting omino by one.
*/
+#ifdef DIVVY_DIAGNOSTICS
+ printf("(%d,%d)", i%w, i/w);
+#endif
while (1) {
own[i] = j;
+#ifdef DIVVY_DIAGNOSTICS
+ printf(" -> %d", j);
+#endif
if (tmp[2*j] == -2)
break;
i = tmp[2*j+1];
j = tmp[2*j];
+#ifdef DIVVY_DIAGNOSTICS
+ printf("; (%d,%d)", i%w, i/w);
+#endif
}
+#ifdef DIVVY_DIAGNOSTICS
+ printf("\n");
+#endif
/*
* Increment the size of the starting omino.
* FIXME: or should we loop over all ominoes before we
* give up?
*/
+#ifdef DIVVY_DIAGNOSTICS
+ printf("FAIL!\n");
+#endif
retdsf = NULL;
goto cleanup;
}
}
+#ifdef DIVVY_DIAGNOSTICS
+ {
+ int x, y;
+ printf("SUCCESS! Final grid:\n");
+ for (y = 0; y < h; y++) {
+ for (x = 0; x < w; x++)
+ printf("%3d", own[y*w+x]);
+ printf("\n");
+ }
+ }
+#endif
+
/*
* Construct the output dsf.
*/
return retdsf;
}
+#ifdef TESTMODE
+static int fail_counter = 0;
+#endif
+
+int *divvy_rectangle(int w, int h, int k, random_state *rs)
+{
+ int *ret;
+
+ do {
+ ret = divvy_internal(w, h, k, rs);
+
+#ifdef TESTMODE
+ if (!ret)
+ fail_counter++;
+#endif
+
+ } while (!ret);
+
+ return ret;
+}
+
#ifdef TESTMODE
/*
int main(int argc, char **argv)
{
int *dsf;
- int i, successes;
+ int i;
int w = 9, h = 4, k = 6, tries = 100;
random_state *rs;
if (argc > 4)
tries = atoi(argv[4]);
- successes = 0;
for (i = 0; i < tries; i++) {
- dsf = divvy_rectangle(w, h, k, rs);
- if (dsf) {
- int x, y;
+ int x, y;
- successes++;
-
- for (y = 0; y <= 2*h; y++) {
- for (x = 0; x <= 2*w; x++) {
- int miny = y/2 - 1, maxy = y/2;
- int minx = x/2 - 1, maxx = x/2;
- int classes[4], tx, ty;
- for (ty = 0; ty < 2; ty++)
- for (tx = 0; tx < 2; tx++) {
- int cx = minx+tx, cy = miny+ty;
- if (cx < 0 || cx >= w || cy < 0 || cy >= h)
- classes[ty*2+tx] = -1;
- else
- classes[ty*2+tx] = dsf_canonify(dsf, cy*w+cx);
- }
- switch (y%2 * 2 + x%2) {
- case 0: /* corner */
- /*
- * Cases for the corner:
- *
- * - if all four surrounding squares
- * belong to the same omino, we print a
- * space.
- *
- * - if the top two are the same and the
- * bottom two are the same, we print a
- * horizontal line.
- *
- * - if the left two are the same and the
- * right two are the same, we print a
- * vertical line.
- *
- * - otherwise, we print a cross.
- */
- if (classes[0] == classes[1] &&
- classes[1] == classes[2] &&
- classes[2] == classes[3])
- printf(" ");
- else if (classes[0] == classes[1] &&
- classes[2] == classes[3])
- printf("-");
- else if (classes[0] == classes[2] &&
- classes[1] == classes[3])
- printf("|");
- else
- printf("+");
- break;
- case 1: /* horiz edge */
- if (classes[1] == classes[3])
- printf(" ");
- else
- printf("--");
- break;
- case 2: /* vert edge */
- if (classes[2] == classes[3])
- printf(" ");
+ dsf = divvy_rectangle(w, h, k, rs);
+ assert(dsf);
+
+ for (y = 0; y <= 2*h; y++) {
+ for (x = 0; x <= 2*w; x++) {
+ int miny = y/2 - 1, maxy = y/2;
+ int minx = x/2 - 1, maxx = x/2;
+ int classes[4], tx, ty;
+ for (ty = 0; ty < 2; ty++)
+ for (tx = 0; tx < 2; tx++) {
+ int cx = minx+tx, cy = miny+ty;
+ if (cx < 0 || cx >= w || cy < 0 || cy >= h)
+ classes[ty*2+tx] = -1;
else
- printf("|");
- break;
- case 3: /* square centre */
- printf(" ");
- break;
+ classes[ty*2+tx] = dsf_canonify(dsf, cy*w+cx);
}
+ switch (y%2 * 2 + x%2) {
+ case 0: /* corner */
+ /*
+ * Cases for the corner:
+ *
+ * - if all four surrounding squares belong
+ * to the same omino, we print a space.
+ *
+ * - if the top two are the same and the
+ * bottom two are the same, we print a
+ * horizontal line.
+ *
+ * - if the left two are the same and the
+ * right two are the same, we print a
+ * vertical line.
+ *
+ * - otherwise, we print a cross.
+ */
+ if (classes[0] == classes[1] &&
+ classes[1] == classes[2] &&
+ classes[2] == classes[3])
+ printf(" ");
+ else if (classes[0] == classes[1] &&
+ classes[2] == classes[3])
+ printf("-");
+ else if (classes[0] == classes[2] &&
+ classes[1] == classes[3])
+ printf("|");
+ else
+ printf("+");
+ break;
+ case 1: /* horiz edge */
+ if (classes[1] == classes[3])
+ printf(" ");
+ else
+ printf("--");
+ break;
+ case 2: /* vert edge */
+ if (classes[2] == classes[3])
+ printf(" ");
+ else
+ printf("|");
+ break;
+ case 3: /* square centre */
+ printf(" ");
+ break;
}
- printf("\n");
}
printf("\n");
- sfree(dsf);
}
+ printf("\n");
+ sfree(dsf);
}
- printf("%d successes out of %d tries\n", successes, tries);
+ printf("%d retries needed for %d successes\n", fail_counter, tries);
return 0;
}