mirror of
git://git.tartarus.org/simon/puzzles.git
synced 2025-04-21 16:05:44 -07:00
Galaxies: skew grid generation in favour of wiggliness.
Ben complained yesterday that Galaxies had a nasty habit of generating games whose solution was a boring set of rectangles. Now that even at Normal mode the solver is better at coping with wiggly tentacled regions, it seems like a good moment to fix that. This change arranges that when we initially generate a filled grid, we try ten times, and pick the wiggliest of the grids we found. This doesn't make any boring rectangle-filled grid _impossible_ - players will still have to stay on their toes, and can't rely 100% on at least a certain number of wiggles existing - but it makes the interesting grids a lot more likely to come up. This skew happens before checking solubility. So it doesn't increase grid generation time by a factor of ten (as it would if we generated ten _soluble_ grids and picked the wiggliest). It's still a speed drop, of course, but a more modest one than that.
This commit is contained in:
83
galaxies.c
83
galaxies.c
@ -1330,6 +1330,8 @@ static void generate_pass(game_state *state, random_state *rs, int *scratch,
|
|||||||
{
|
{
|
||||||
int sz = state->sx*state->sy, nspc, i, ret;
|
int sz = state->sx*state->sy, nspc, i, ret;
|
||||||
|
|
||||||
|
/* Random list of squares to try and process, one-by-one. */
|
||||||
|
for (i = 0; i < sz; i++) scratch[i] = i;
|
||||||
shuffle(scratch, sz, sizeof(int), rs);
|
shuffle(scratch, sz, sizeof(int), rs);
|
||||||
|
|
||||||
/* This bug took me a, er, little while to track down. On PalmOS,
|
/* This bug took me a, er, little while to track down. On PalmOS,
|
||||||
@ -1385,29 +1387,94 @@ static void generate_pass(game_state *state, random_state *rs, int *scratch,
|
|||||||
dbg_state(state);
|
dbg_state(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We try several times to generate a grid at all, before even feeding
|
||||||
|
* it to the solver. Then we pick whichever of the resulting grids was
|
||||||
|
* the most 'wiggly', as measured by the number of inward corners in
|
||||||
|
* the shape of any region.
|
||||||
|
*
|
||||||
|
* Rationale: wiggly shapes are what make this puzzle fun, and it's
|
||||||
|
* disappointing to be served a game whose entire solution is a
|
||||||
|
* collection of rectangles. But we also don't want to introduce a
|
||||||
|
* _hard requirement_ of wiggliness, because a player who knew that
|
||||||
|
* was there would be able to use it as an extra clue. This way, we
|
||||||
|
* just probabilistically skew in favour of wiggliness.
|
||||||
|
*/
|
||||||
|
#define GENERATE_TRIES 10
|
||||||
|
|
||||||
|
static bool is_wiggle(const game_state *state, int x, int y, int dx, int dy)
|
||||||
|
{
|
||||||
|
int x1 = x+2*dx, y1 = y+2*dy;
|
||||||
|
int x2 = x-2*dy, y2 = y+2*dx;
|
||||||
|
space *t, *t1, *t2;
|
||||||
|
|
||||||
|
if (!INGRID(state, x1, y1) || !INGRID(state, x2, y2))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
t = &SPACE(state, x, y);
|
||||||
|
t1 = &SPACE(state, x1, y1);
|
||||||
|
t2 = &SPACE(state, x2, y2);
|
||||||
|
return ((t1->dotx == t2->dotx && t1->doty == t2->doty) &&
|
||||||
|
!(t1->dotx == t->dotx && t1->doty == t->doty));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int measure_wiggliness(const game_state *state, int *scratch)
|
||||||
|
{
|
||||||
|
int sz = state->sx*state->sy;
|
||||||
|
int x, y, nwiggles = 0;
|
||||||
|
memset(scratch, 0, sz);
|
||||||
|
|
||||||
|
for (y = 1; y < state->sy; y += 2) {
|
||||||
|
for (x = 1; x < state->sx; x += 2) {
|
||||||
|
if (y+2 < state->sy) {
|
||||||
|
nwiggles += is_wiggle(state, x, y, 0, +1);
|
||||||
|
nwiggles += is_wiggle(state, x, y, 0, -1);
|
||||||
|
nwiggles += is_wiggle(state, x, y, +1, 0);
|
||||||
|
nwiggles += is_wiggle(state, x, y, -1, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nwiggles;
|
||||||
|
}
|
||||||
|
|
||||||
static char *new_game_desc(const game_params *params, random_state *rs,
|
static char *new_game_desc(const game_params *params, random_state *rs,
|
||||||
char **aux, bool interactive)
|
char **aux, bool interactive)
|
||||||
{
|
{
|
||||||
game_state *state = blank_game(params->w, params->h), *copy;
|
game_state *state = blank_game(params->w, params->h), *copy;
|
||||||
char *desc;
|
char *desc;
|
||||||
int *scratch, sz = state->sx*state->sy, i;
|
int *scratch, sz = state->sx*state->sy, i;
|
||||||
int diff;
|
int diff, best_wiggliness;
|
||||||
bool cc;
|
bool cc;
|
||||||
|
|
||||||
/* Random list of squares to try and process, one-by-one. */
|
|
||||||
scratch = snewn(sz, int);
|
scratch = snewn(sz, int);
|
||||||
for (i = 0; i < sz; i++) scratch[i] = i;
|
|
||||||
|
|
||||||
generate:
|
generate:
|
||||||
|
best_wiggliness = -1;
|
||||||
|
copy = NULL;
|
||||||
|
for (i = 0; i < GENERATE_TRIES; i++) {
|
||||||
|
int this_wiggliness;
|
||||||
|
|
||||||
|
do {
|
||||||
clear_game(state, true);
|
clear_game(state, true);
|
||||||
|
|
||||||
/* generate_pass(state, rs, scratch, 10, GP_DOTS); */
|
|
||||||
/* generate_pass(state, rs, scratch, 100, 0); */
|
|
||||||
generate_pass(state, rs, scratch, 100, GP_DOTS);
|
generate_pass(state, rs, scratch, 100, GP_DOTS);
|
||||||
|
|
||||||
game_update_dots(state);
|
game_update_dots(state);
|
||||||
|
} while (state->ndots == 1);
|
||||||
|
|
||||||
if (state->ndots == 1) goto generate;
|
this_wiggliness = measure_wiggliness(state, scratch);
|
||||||
|
debug(("Grid gen #%d: wiggliness=%d", i, this_wiggliness));
|
||||||
|
if (this_wiggliness > best_wiggliness) {
|
||||||
|
best_wiggliness = this_wiggliness;
|
||||||
|
if (copy)
|
||||||
|
free_game(copy);
|
||||||
|
copy = dup_game(state);
|
||||||
|
debug((" new best"));
|
||||||
|
}
|
||||||
|
debug(("\n"));
|
||||||
|
}
|
||||||
|
assert(copy);
|
||||||
|
free_game(state);
|
||||||
|
state = copy;
|
||||||
|
|
||||||
#ifdef DEBUGGING
|
#ifdef DEBUGGING
|
||||||
{
|
{
|
||||||
|
Reference in New Issue
Block a user