mirror of
git://git.tartarus.org/simon/puzzles.git
synced 2025-04-21 08:01:30 -07:00
Aha! It turns out, after a bit of failure-mode profiling, that when
the Mines unique grid generator fails at high mine densities it is _almost always_ for the same reason, and it also turns out that this reason is one which can be addressed. So here's an enhancement to mineperturb() which enables Mines to generate a grid at (as far as I can tell) any mine density you like, up to and including w*h-9 mines. At densities of 1 in 2 or thereabouts the grids start to look rather strange, but it can at least generate them without hanging. [originally from svn r5885]
This commit is contained in:
157
mines.c
157
mines.c
@ -10,14 +10,8 @@
|
|||||||
* That hook can talk to the game_ui and set the cheated flag,
|
* That hook can talk to the game_ui and set the cheated flag,
|
||||||
* and then make_move can avoid setting the `won' flag after that.
|
* and then make_move can avoid setting the `won' flag after that.
|
||||||
*
|
*
|
||||||
* - question marks (arrgh, preferences?)
|
* - think about configurably supporting question marks. Once,
|
||||||
*
|
* that is, we've thought about configurability in general!
|
||||||
* - sensible parameter constraints
|
|
||||||
* + 30x16: 191 mines just about works if rather slowly, 192 is
|
|
||||||
* just about doom (the latter corresponding to a density of
|
|
||||||
* exactly 1 in 2.5)
|
|
||||||
* + 9x9: 45 mines works - over 1 in 2! 50 seems a bit slow.
|
|
||||||
* + it might not be feasible to work out the exact limit
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@ -1166,13 +1160,18 @@ static int minesolve(int w, int h, int n, signed char *grid,
|
|||||||
*
|
*
|
||||||
* If we have no sets at all, we must give up.
|
* If we have no sets at all, we must give up.
|
||||||
*/
|
*/
|
||||||
if (count234(ss->sets) == 0)
|
if (count234(ss->sets) == 0) {
|
||||||
break;
|
#ifdef SOLVER_DIAGNOSTICS
|
||||||
|
printf("perturbing on entire unknown set\n");
|
||||||
|
#endif
|
||||||
|
ret = perturb(ctx, grid, 0, 0, 0);
|
||||||
|
} else {
|
||||||
s = index234(ss->sets, random_upto(rs, count234(ss->sets)));
|
s = index234(ss->sets, random_upto(rs, count234(ss->sets)));
|
||||||
#ifdef SOLVER_DIAGNOSTICS
|
#ifdef SOLVER_DIAGNOSTICS
|
||||||
printf("perturbing on set %d,%d %03x\n", s->x, s->y, s->mask);
|
printf("perturbing on set %d,%d %03x\n", s->x, s->y, s->mask);
|
||||||
#endif
|
#endif
|
||||||
ret = perturb(ctx, grid, s->x, s->y, s->mask);
|
ret = perturb(ctx, grid, s->x, s->y, s->mask);
|
||||||
|
}
|
||||||
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
assert(ret->n > 0); /* otherwise should have been NULL */
|
assert(ret->n > 0); /* otherwise should have been NULL */
|
||||||
@ -1182,6 +1181,8 @@ static int minesolve(int w, int h, int n, signed char *grid,
|
|||||||
* the returned structure tells us which. Adjust
|
* the returned structure tells us which. Adjust
|
||||||
* the mine count in any set which overlaps one of
|
* the mine count in any set which overlaps one of
|
||||||
* those squares, and put them back on the to-do
|
* those squares, and put them back on the to-do
|
||||||
|
* list. Also, if the square itself is marked as a
|
||||||
|
* known non-mine, put it back on the squares-to-do
|
||||||
* list.
|
* list.
|
||||||
*/
|
*/
|
||||||
for (i = 0; i < ret->n; i++) {
|
for (i = 0; i < ret->n; i++) {
|
||||||
@ -1191,6 +1192,11 @@ static int minesolve(int w, int h, int n, signed char *grid,
|
|||||||
ret->changes[i].x, ret->changes[i].y);
|
ret->changes[i].x, ret->changes[i].y);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (ret->changes[i].delta < 0 &&
|
||||||
|
grid[ret->changes[i].y*w+ret->changes[i].x] != -2) {
|
||||||
|
std_add(std, ret->changes[i].y*w+ret->changes[i].x);
|
||||||
|
}
|
||||||
|
|
||||||
list = ss_overlap(ss,
|
list = ss_overlap(ss,
|
||||||
ret->changes[i].x, ret->changes[i].y, 1);
|
ret->changes[i].x, ret->changes[i].y, 1);
|
||||||
|
|
||||||
@ -1212,7 +1218,7 @@ static int minesolve(int w, int h, int n, signed char *grid,
|
|||||||
/*
|
/*
|
||||||
* Dump the current known state of the grid.
|
* Dump the current known state of the grid.
|
||||||
*/
|
*/
|
||||||
printf("state after perturbation:\n", nperturbs);
|
printf("state after perturbation:\n");
|
||||||
for (y = 0; y < h; y++) {
|
for (y = 0; y < h; y++) {
|
||||||
for (x = 0; x < w; x++) {
|
for (x = 0; x < w; x++) {
|
||||||
int v = grid[y*w+x];
|
int v = grid[y*w+x];
|
||||||
@ -1284,6 +1290,7 @@ struct minectx {
|
|||||||
signed char *grid;
|
signed char *grid;
|
||||||
int w, h;
|
int w, h;
|
||||||
int sx, sy;
|
int sx, sy;
|
||||||
|
int allow_big_perturbs;
|
||||||
random_state *rs;
|
random_state *rs;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1340,15 +1347,35 @@ static int squarecmp(const void *av, const void *bv)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Normally this function is passed an (x,y,mask) set description.
|
||||||
|
* On occasions, though, there is no _localised_ set being used,
|
||||||
|
* and the set being perturbed is supposed to be the entirety of
|
||||||
|
* the unreachable area. This is signified by the special case
|
||||||
|
* mask==0: in this case, anything labelled -2 in the grid is part
|
||||||
|
* of the set.
|
||||||
|
*
|
||||||
|
* Allowing perturbation in this special case appears to make it
|
||||||
|
* guaranteeably possible to generate a workable grid for any mine
|
||||||
|
* density, but they tend to be a bit boring, with mines packed
|
||||||
|
* densely into far corners of the grid and the remainder being
|
||||||
|
* less dense than one might like. Therefore, to improve overall
|
||||||
|
* grid quality I disable this feature for the first few attempts,
|
||||||
|
* and fall back to it after no useful grid has been generated.
|
||||||
|
*/
|
||||||
static struct perturbations *mineperturb(void *vctx, signed char *grid,
|
static struct perturbations *mineperturb(void *vctx, signed char *grid,
|
||||||
int setx, int sety, int mask)
|
int setx, int sety, int mask)
|
||||||
{
|
{
|
||||||
struct minectx *ctx = (struct minectx *)vctx;
|
struct minectx *ctx = (struct minectx *)vctx;
|
||||||
struct square *sqlist;
|
struct square *sqlist;
|
||||||
int x, y, dx, dy, i, n, nfull, nempty;
|
int x, y, dx, dy, i, n, nfull, nempty;
|
||||||
struct square *tofill[9], *toempty[9], **todo;
|
struct square **tofill, **toempty, **todo;
|
||||||
int ntofill, ntoempty, ntodo, dtodo, dset;
|
int ntofill, ntoempty, ntodo, dtodo, dset;
|
||||||
struct perturbations *ret;
|
struct perturbations *ret;
|
||||||
|
int *setlist;
|
||||||
|
|
||||||
|
if (!mask && !ctx->allow_big_perturbs)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Make a list of all the squares in the grid which we can
|
* Make a list of all the squares in the grid which we can
|
||||||
@ -1379,9 +1406,10 @@ static struct perturbations *mineperturb(void *vctx, signed char *grid,
|
|||||||
* If this square is in the input set, also don't put
|
* If this square is in the input set, also don't put
|
||||||
* it on the list!
|
* it on the list!
|
||||||
*/
|
*/
|
||||||
if (x >= setx && x < setx + 3 &&
|
if ((mask == 0 && grid[y*ctx->w+x] == -2) ||
|
||||||
|
(x >= setx && x < setx + 3 &&
|
||||||
y >= sety && y < sety + 3 &&
|
y >= sety && y < sety + 3 &&
|
||||||
mask & (1 << ((y-sety)*3+(x-setx))))
|
mask & (1 << ((y-sety)*3+(x-setx)))))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
sqlist[n].x = x;
|
sqlist[n].x = x;
|
||||||
@ -1424,6 +1452,7 @@ static struct perturbations *mineperturb(void *vctx, signed char *grid,
|
|||||||
* we've been provided.
|
* we've been provided.
|
||||||
*/
|
*/
|
||||||
nfull = nempty = 0;
|
nfull = nempty = 0;
|
||||||
|
if (mask) {
|
||||||
for (dy = 0; dy < 3; dy++)
|
for (dy = 0; dy < 3; dy++)
|
||||||
for (dx = 0; dx < 3; dx++)
|
for (dx = 0; dx < 3; dx++)
|
||||||
if (mask & (1 << (dy*3+dx))) {
|
if (mask & (1 << (dy*3+dx))) {
|
||||||
@ -1434,6 +1463,16 @@ static struct perturbations *mineperturb(void *vctx, signed char *grid,
|
|||||||
else
|
else
|
||||||
nempty++;
|
nempty++;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
for (y = 0; y < ctx->h; y++)
|
||||||
|
for (x = 0; x < ctx->w; x++)
|
||||||
|
if (grid[y*ctx->w+x] == -2) {
|
||||||
|
if (ctx->grid[y*ctx->w+x])
|
||||||
|
nfull++;
|
||||||
|
else
|
||||||
|
nempty++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Now go through our sorted list until we find either `nfull'
|
* Now go through our sorted list until we find either `nfull'
|
||||||
@ -1443,6 +1482,13 @@ static struct perturbations *mineperturb(void *vctx, signed char *grid,
|
|||||||
* overall.
|
* overall.
|
||||||
*/
|
*/
|
||||||
ntofill = ntoempty = 0;
|
ntofill = ntoempty = 0;
|
||||||
|
if (mask) {
|
||||||
|
tofill = snewn(9, struct square *);
|
||||||
|
toempty = snewn(9, struct square *);
|
||||||
|
} else {
|
||||||
|
tofill = snewn(ctx->w * ctx->h, struct square *);
|
||||||
|
toempty = snewn(ctx->w * ctx->h, struct square *);
|
||||||
|
}
|
||||||
for (i = 0; i < n; i++) {
|
for (i = 0; i < n; i++) {
|
||||||
struct square *sq = &sqlist[i];
|
struct square *sq = &sqlist[i];
|
||||||
if (ctx->grid[sq->y * ctx->w + sq->x])
|
if (ctx->grid[sq->y * ctx->w + sq->x])
|
||||||
@ -1454,12 +1500,55 @@ static struct perturbations *mineperturb(void *vctx, signed char *grid,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If this didn't work at all, I think we just give up.
|
* If we haven't found enough empty squares outside the set to
|
||||||
|
* empty it into _or_ enough full squares outside it to fill it
|
||||||
|
* up with, we'll have to settle for doing only a partial job.
|
||||||
|
* In this case we choose to always _fill_ the set (because
|
||||||
|
* this case will tend to crop up when we're working with very
|
||||||
|
* high mine densities and the only way to get a solvable grid
|
||||||
|
* is going to be to pack most of the mines solidly around the
|
||||||
|
* edges). So now our job is to make a list of the empty
|
||||||
|
* squares in the set, and shuffle that list so that we fill a
|
||||||
|
* random selection of them.
|
||||||
*/
|
*/
|
||||||
if (ntofill != nfull && ntoempty != nempty) {
|
if (ntofill != nfull && ntoempty != nempty) {
|
||||||
sfree(sqlist);
|
int k;
|
||||||
return NULL;
|
|
||||||
|
assert(ntoempty != 0);
|
||||||
|
|
||||||
|
setlist = snewn(ctx->w * ctx->h, int);
|
||||||
|
i = 0;
|
||||||
|
if (mask) {
|
||||||
|
for (dy = 0; dy < 3; dy++)
|
||||||
|
for (dx = 0; dx < 3; dx++)
|
||||||
|
if (mask & (1 << (dy*3+dx))) {
|
||||||
|
assert(setx+dx <= ctx->w);
|
||||||
|
assert(sety+dy <= ctx->h);
|
||||||
|
if (!ctx->grid[(sety+dy)*ctx->w+(setx+dx)])
|
||||||
|
setlist[i++] = (sety+dy)*ctx->w+(setx+dx);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
for (y = 0; y < ctx->h; y++)
|
||||||
|
for (x = 0; x < ctx->w; x++)
|
||||||
|
if (grid[y*ctx->w+x] == -2) {
|
||||||
|
if (!ctx->grid[y*ctx->w+x])
|
||||||
|
setlist[i++] = y*ctx->w+x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(i > ntoempty);
|
||||||
|
/*
|
||||||
|
* Now pick `ntoempty' items at random from the list.
|
||||||
|
*/
|
||||||
|
for (k = 0; k < ntoempty; k++) {
|
||||||
|
int index = k + random_upto(ctx->rs, i - k);
|
||||||
|
int tmp;
|
||||||
|
|
||||||
|
tmp = setlist[k];
|
||||||
|
setlist[k] = setlist[index];
|
||||||
|
setlist[index] = tmp;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
setlist = NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Now we're pretty much there. We need to either
|
* Now we're pretty much there. We need to either
|
||||||
@ -1479,11 +1568,17 @@ static struct perturbations *mineperturb(void *vctx, signed char *grid,
|
|||||||
ntodo = ntofill;
|
ntodo = ntofill;
|
||||||
dtodo = +1;
|
dtodo = +1;
|
||||||
dset = -1;
|
dset = -1;
|
||||||
|
sfree(toempty);
|
||||||
} else {
|
} else {
|
||||||
|
/*
|
||||||
|
* (We also fall into this case if we've constructed a
|
||||||
|
* setlist.)
|
||||||
|
*/
|
||||||
todo = toempty;
|
todo = toempty;
|
||||||
ntodo = ntoempty;
|
ntodo = ntoempty;
|
||||||
dtodo = -1;
|
dtodo = -1;
|
||||||
dset = +1;
|
dset = +1;
|
||||||
|
sfree(tofill);
|
||||||
}
|
}
|
||||||
ret->n = 2 * ntodo;
|
ret->n = 2 * ntodo;
|
||||||
ret->changes = snewn(ret->n, struct perturbation);
|
ret->changes = snewn(ret->n, struct perturbation);
|
||||||
@ -1493,6 +1588,17 @@ static struct perturbations *mineperturb(void *vctx, signed char *grid,
|
|||||||
ret->changes[i].delta = dtodo;
|
ret->changes[i].delta = dtodo;
|
||||||
}
|
}
|
||||||
/* now i == ntodo */
|
/* now i == ntodo */
|
||||||
|
if (setlist) {
|
||||||
|
int j;
|
||||||
|
assert(todo == toempty);
|
||||||
|
for (j = 0; j < ntoempty; j++) {
|
||||||
|
ret->changes[i].x = setlist[j] % ctx->w;
|
||||||
|
ret->changes[i].y = setlist[j] / ctx->w;
|
||||||
|
ret->changes[i].delta = dset;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
sfree(setlist);
|
||||||
|
} else if (mask) {
|
||||||
for (dy = 0; dy < 3; dy++)
|
for (dy = 0; dy < 3; dy++)
|
||||||
for (dx = 0; dx < 3; dx++)
|
for (dx = 0; dx < 3; dx++)
|
||||||
if (mask & (1 << (dy*3+dx))) {
|
if (mask & (1 << (dy*3+dx))) {
|
||||||
@ -1504,9 +1610,23 @@ static struct perturbations *mineperturb(void *vctx, signed char *grid,
|
|||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
for (y = 0; y < ctx->h; y++)
|
||||||
|
for (x = 0; x < ctx->w; x++)
|
||||||
|
if (grid[y*ctx->w+x] == -2) {
|
||||||
|
int currval = (ctx->grid[y*ctx->w+x] ? +1 : -1);
|
||||||
|
if (dset == -currval) {
|
||||||
|
ret->changes[i].x = x;
|
||||||
|
ret->changes[i].y = y;
|
||||||
|
ret->changes[i].delta = dset;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
assert(i == ret->n);
|
assert(i == ret->n);
|
||||||
|
|
||||||
sfree(sqlist);
|
sfree(sqlist);
|
||||||
|
sfree(todo);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Having set up the precise list of changes we're going to
|
* Having set up the precise list of changes we're going to
|
||||||
@ -1593,9 +1713,11 @@ static char *minegen(int w, int h, int n, int x, int y, int unique,
|
|||||||
{
|
{
|
||||||
char *ret = snewn(w*h, char);
|
char *ret = snewn(w*h, char);
|
||||||
int success;
|
int success;
|
||||||
|
int ntries = 0;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
success = FALSE;
|
success = FALSE;
|
||||||
|
ntries++;
|
||||||
|
|
||||||
memset(ret, 0, w*h);
|
memset(ret, 0, w*h);
|
||||||
|
|
||||||
@ -1670,6 +1792,7 @@ static char *minegen(int w, int h, int n, int x, int y, int unique,
|
|||||||
ctx->sx = x;
|
ctx->sx = x;
|
||||||
ctx->sy = y;
|
ctx->sy = y;
|
||||||
ctx->rs = rs;
|
ctx->rs = rs;
|
||||||
|
ctx->allow_big_perturbs = (ntries > 100);
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
memset(solvegrid, -2, w*h);
|
memset(solvegrid, -2, w*h);
|
||||||
|
Reference in New Issue
Block a user