mirror of
git://git.tartarus.org/simon/puzzles.git
synced 2025-04-21 08:01:30 -07:00
Allow a 1-omino to be completely destroyed and recreated in an
arbitrary unclaimed square. This cures the most common cause of generation failures (covering a large area in dominoes was the most difficult case, and would fail even if the large area was 1xn!); the failure rate is now sufficiently low under all circumstances I've found that I'm willing to just loop until I get a success. [originally from svn r7693]
This commit is contained in:
@ -7,6 +7,21 @@
|
||||
* 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>
|
||||
@ -83,7 +98,7 @@ static int addremcommon(int w, int h, int x, int y, int *own, int val)
|
||||
* 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;
|
||||
@ -164,7 +179,9 @@ int *divvy_rectangle(int w, int h, int k, random_state *rs)
|
||||
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
|
||||
@ -243,7 +260,7 @@ int *divvy_rectangle(int w, int h, int k, random_state *rs)
|
||||
tmpsq = tmp[2*j+1];
|
||||
if (tmpsq >= 0) {
|
||||
assert(own[tmpsq] == j);
|
||||
own[tmpsq] = -1;
|
||||
own[tmpsq] = -3;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -254,8 +271,22 @@ int *divvy_rectangle(int w, int h, int k, random_state *rs)
|
||||
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) {
|
||||
/*
|
||||
@ -274,11 +305,19 @@ int *divvy_rectangle(int w, int h, int k, random_state *rs)
|
||||
}
|
||||
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
|
||||
@ -450,6 +489,27 @@ int *divvy_rectangle(int w, int h, int k, random_state *rs)
|
||||
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
|
||||
|
||||
/*
|
||||
@ -463,7 +523,7 @@ int *divvy_rectangle(int w, int h, int k, random_state *rs)
|
||||
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;
|
||||
|
||||
@ -478,13 +538,11 @@ int main(int argc, char **argv)
|
||||
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;
|
||||
|
||||
successes++;
|
||||
dsf = divvy_rectangle(w, h, k, rs);
|
||||
assert(dsf);
|
||||
|
||||
for (y = 0; y <= 2*h; y++) {
|
||||
for (x = 0; x <= 2*w; x++) {
|
||||
@ -504,9 +562,8 @@ int main(int argc, char **argv)
|
||||
/*
|
||||
* Cases for the corner:
|
||||
*
|
||||
* - if all four surrounding squares
|
||||
* belong to the same omino, we print a
|
||||
* space.
|
||||
* - 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
|
||||
@ -553,9 +610,8 @@ int main(int argc, char **argv)
|
||||
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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user