mirror of
git://git.tartarus.org/simon/puzzles.git
synced 2025-04-21 08:01:30 -07:00
Bridges: use the new findloop for loop detection.
Bridges only needs a loop detector for its non-default 'don't allow loops' mode. But the one it had was using the graph-pruning strategy, which means it had the dumb-bell bug - two loops joined by a path would highlight the path as well as the loops. Switching to the new findloop system fixes that bug. A side effect is that I've been able to remove the 'scratch' array from the game_state, which was only used by the old loop finder, so that should save memory.
This commit is contained in:
@ -1,6 +1,6 @@
|
|||||||
# -*- makefile -*-
|
# -*- makefile -*-
|
||||||
|
|
||||||
BRIDGES_EXTRA = dsf
|
BRIDGES_EXTRA = dsf findloop
|
||||||
|
|
||||||
bridges : [X] GTK COMMON bridges BRIDGES_EXTRA bridges-icon|no-icon
|
bridges : [X] GTK COMMON bridges BRIDGES_EXTRA bridges-icon|no-icon
|
||||||
|
|
||||||
|
109
bridges.c
109
bridges.c
@ -156,7 +156,7 @@ struct island {
|
|||||||
|
|
||||||
struct game_state {
|
struct game_state {
|
||||||
int w, h, completed, solved, allowloops, maxb;
|
int w, h, completed, solved, allowloops, maxb;
|
||||||
grid_type *grid, *scratch;
|
grid_type *grid;
|
||||||
struct island *islands;
|
struct island *islands;
|
||||||
int n_islands, n_islands_alloc;
|
int n_islands, n_islands_alloc;
|
||||||
game_params params; /* used by the aux solver. */
|
game_params params; /* used by the aux solver. */
|
||||||
@ -175,7 +175,6 @@ struct game_state {
|
|||||||
#define INDEX(s,g,x,y) ((s)->g[(y)*((s)->w) + (x)])
|
#define INDEX(s,g,x,y) ((s)->g[(y)*((s)->w) + (x)])
|
||||||
#define IDX(s,g,i) ((s)->g[(i)])
|
#define IDX(s,g,i) ((s)->g[(i)])
|
||||||
#define GRID(s,x,y) INDEX(s,grid,x,y)
|
#define GRID(s,x,y) INDEX(s,grid,x,y)
|
||||||
#define SCRATCH(s,x,y) INDEX(s,scratch,x,y)
|
|
||||||
#define POSSIBLES(s,dx,x,y) ((dx) ? (INDEX(s,possh,x,y)) : (INDEX(s,possv,x,y)))
|
#define POSSIBLES(s,dx,x,y) ((dx) ? (INDEX(s,possh,x,y)) : (INDEX(s,possv,x,y)))
|
||||||
#define MAXIMUM(s,dx,x,y) ((dx) ? (INDEX(s,maxh,x,y)) : (INDEX(s,maxv,x,y)))
|
#define MAXIMUM(s,dx,x,y) ((dx) ? (INDEX(s,maxh,x,y)) : (INDEX(s,maxv,x,y)))
|
||||||
|
|
||||||
@ -1051,23 +1050,30 @@ static void map_find_orthogonal(game_state *state)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int grid_degree(game_state *state, int x, int y, int *nx_r, int *ny_r)
|
struct bridges_neighbour_ctx {
|
||||||
|
game_state *state;
|
||||||
|
int i, n, neighbours[4];
|
||||||
|
};
|
||||||
|
static int bridges_neighbour(int vertex, void *vctx)
|
||||||
{
|
{
|
||||||
grid_type grid = SCRATCH(state, x, y), gline = grid & G_LINE;
|
struct bridges_neighbour_ctx *ctx = (struct bridges_neighbour_ctx *)vctx;
|
||||||
|
if (vertex >= 0) {
|
||||||
|
game_state *state = ctx->state;
|
||||||
|
int w = state->w, x = vertex % w, y = vertex / w;
|
||||||
|
grid_type grid = GRID(state, x, y), gline = grid & G_LINE;
|
||||||
struct island *is;
|
struct island *is;
|
||||||
int x1, y1, x2, y2, c = 0, i, nx, ny;
|
int x1, y1, x2, y2, i;
|
||||||
|
|
||||||
|
ctx->i = ctx->n = 0;
|
||||||
|
|
||||||
nx = ny = -1; /* placate optimiser */
|
|
||||||
is = INDEX(state, gridi, x, y);
|
is = INDEX(state, gridi, x, y);
|
||||||
if (is) {
|
if (is) {
|
||||||
for (i = 0; i < is->adj.npoints; i++) {
|
for (i = 0; i < is->adj.npoints; i++) {
|
||||||
gline = is->adj.points[i].dx ? G_LINEH : G_LINEV;
|
gline = is->adj.points[i].dx ? G_LINEH : G_LINEV;
|
||||||
if (SCRATCH(state,
|
if (GRID(state, is->adj.points[i].x,
|
||||||
is->adj.points[i].x,
|
|
||||||
is->adj.points[i].y) & gline) {
|
is->adj.points[i].y) & gline) {
|
||||||
nx = is->adj.points[i].x;
|
ctx->neighbours[ctx->n++] =
|
||||||
ny = is->adj.points[i].y;
|
(is->adj.points[i].y * w + is->adj.points[i].x);
|
||||||
c++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (gline) {
|
} else if (gline) {
|
||||||
@ -1078,68 +1084,50 @@ static int grid_degree(game_state *state, int x, int y, int *nx_r, int *ny_r)
|
|||||||
x1 = x-1; x2 = x+1;
|
x1 = x-1; x2 = x+1;
|
||||||
y1 = y2 = y;
|
y1 = y2 = y;
|
||||||
}
|
}
|
||||||
/* Non-island squares with edges in should never be pointing off the
|
/* Non-island squares with edges in should never be
|
||||||
* edge of the grid. */
|
* pointing off the edge of the grid. */
|
||||||
assert(INGRID(state, x1, y1));
|
assert(INGRID(state, x1, y1));
|
||||||
assert(INGRID(state, x2, y2));
|
assert(INGRID(state, x2, y2));
|
||||||
if (SCRATCH(state, x1, y1) & (gline | G_ISLAND)) {
|
if (GRID(state, x1, y1) & (gline | G_ISLAND))
|
||||||
nx = x1; ny = y1; c++;
|
ctx->neighbours[ctx->n++] = y1 * w + x1;
|
||||||
}
|
if (GRID(state, x2, y2) & (gline | G_ISLAND))
|
||||||
if (SCRATCH(state, x2, y2) & (gline | G_ISLAND)) {
|
ctx->neighbours[ctx->n++] = y2 * w + x2;
|
||||||
nx = x2; ny = y2; c++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (c == 1) {
|
|
||||||
assert(nx != -1 && ny != -1); /* paranoia */
|
if (ctx->i < ctx->n)
|
||||||
*nx_r = nx; *ny_r = ny;
|
return ctx->neighbours[ctx->i++];
|
||||||
}
|
else
|
||||||
return c;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int map_hasloops(game_state *state, int mark)
|
static int map_hasloops(game_state *state, int mark)
|
||||||
{
|
{
|
||||||
int x, y, ox, oy, nx = 0, ny = 0, loop = 0;
|
int x, y;
|
||||||
|
struct findloopstate *fls;
|
||||||
|
struct bridges_neighbour_ctx ctx;
|
||||||
|
int ret;
|
||||||
|
|
||||||
memcpy(state->scratch, state->grid, GRIDSZ(state));
|
fls = findloop_new_state(state->w * state->h);
|
||||||
|
ctx.state = state;
|
||||||
|
ret = findloop_run(fls, state->w * state->h, bridges_neighbour, &ctx);
|
||||||
|
|
||||||
/* This algorithm is actually broken; if there are two loops connected
|
|
||||||
* by bridges this will also highlight bridges. The correct algorithm
|
|
||||||
* uses a dsf and a two-pass edge-detection algorithm (see check_correct
|
|
||||||
* in slant.c); this is BALGE for now, especially since disallow-loops
|
|
||||||
* is not the default for this puzzle. If we want to fix this later then
|
|
||||||
* copy the alg in slant.c to the empty statement in map_group. */
|
|
||||||
|
|
||||||
/* Remove all 1-degree edges. */
|
|
||||||
for (y = 0; y < state->h; y++) {
|
|
||||||
for (x = 0; x < state->w; x++) {
|
|
||||||
ox = x; oy = y;
|
|
||||||
while (grid_degree(state, ox, oy, &nx, &ny) == 1) {
|
|
||||||
/*debug(("hasloops: removing 1-degree at (%d,%d).\n", ox, oy));*/
|
|
||||||
SCRATCH(state, ox, oy) &= ~(G_LINE|G_ISLAND);
|
|
||||||
ox = nx; oy = ny;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Mark any remaining edges as G_WARN, if required. */
|
|
||||||
for (x = 0; x < state->w; x++) {
|
|
||||||
for (y = 0; y < state->h; y++) {
|
|
||||||
if (GRID(state,x,y) & G_ISLAND) continue;
|
|
||||||
|
|
||||||
if (SCRATCH(state, x, y) & G_LINE) {
|
|
||||||
if (mark) {
|
if (mark) {
|
||||||
/*debug(("hasloops: marking loop square at (%d,%d).\n",
|
for (y = 0; y < state->h; y++) {
|
||||||
x, y));*/
|
for (x = 0; x < state->w; x++) {
|
||||||
|
int u, v;
|
||||||
|
|
||||||
|
u = y * state->w + x;
|
||||||
|
for (v = bridges_neighbour(u, &ctx); v >= 0;
|
||||||
|
v = bridges_neighbour(-1, &ctx))
|
||||||
|
if (findloop_is_loop_edge(fls, u, v))
|
||||||
GRID(state,x,y) |= G_WARN;
|
GRID(state,x,y) |= G_WARN;
|
||||||
loop = 1;
|
|
||||||
} else
|
|
||||||
return 1; /* short-cut as soon as we find one */
|
|
||||||
} else {
|
|
||||||
if (mark)
|
|
||||||
GRID(state,x,y) &= ~G_WARN;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return loop;
|
|
||||||
|
findloop_free_state(fls);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void map_group(game_state *state)
|
static void map_group(game_state *state)
|
||||||
@ -1743,8 +1731,6 @@ static game_state *new_state(const game_params *params)
|
|||||||
|
|
||||||
ret->grid = snewn(wh, grid_type);
|
ret->grid = snewn(wh, grid_type);
|
||||||
memset(ret->grid, 0, GRIDSZ(ret));
|
memset(ret->grid, 0, GRIDSZ(ret));
|
||||||
ret->scratch = snewn(wh, grid_type);
|
|
||||||
memset(ret->scratch, 0, GRIDSZ(ret));
|
|
||||||
|
|
||||||
ret->wha = snewn(wh*N_WH_ARRAYS, char);
|
ret->wha = snewn(wh*N_WH_ARRAYS, char);
|
||||||
memset(ret->wha, 0, wh*N_WH_ARRAYS*sizeof(char));
|
memset(ret->wha, 0, wh*N_WH_ARRAYS*sizeof(char));
|
||||||
@ -1789,8 +1775,6 @@ static game_state *dup_game(const game_state *state)
|
|||||||
|
|
||||||
ret->grid = snewn(wh, grid_type);
|
ret->grid = snewn(wh, grid_type);
|
||||||
memcpy(ret->grid, state->grid, GRIDSZ(ret));
|
memcpy(ret->grid, state->grid, GRIDSZ(ret));
|
||||||
ret->scratch = snewn(wh, grid_type);
|
|
||||||
memcpy(ret->scratch, state->scratch, GRIDSZ(ret));
|
|
||||||
|
|
||||||
ret->wha = snewn(wh*N_WH_ARRAYS, char);
|
ret->wha = snewn(wh*N_WH_ARRAYS, char);
|
||||||
memcpy(ret->wha, state->wha, wh*N_WH_ARRAYS*sizeof(char));
|
memcpy(ret->wha, state->wha, wh*N_WH_ARRAYS*sizeof(char));
|
||||||
@ -1830,7 +1814,6 @@ static void free_game(game_state *state)
|
|||||||
|
|
||||||
sfree(state->wha);
|
sfree(state->wha);
|
||||||
|
|
||||||
sfree(state->scratch);
|
|
||||||
sfree(state->grid);
|
sfree(state->grid);
|
||||||
sfree(state);
|
sfree(state);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user