Stop putting dsfs in existing scratch int arrays.

I'm going to work towards turning 'dsf' into an opaque type, so that I
can improve its implementation without breaking clients. The first
step is to deal manually with puzzles that currently reuse a single
array of ints for multiple purposes, some of which are dsf and some
are not.

In divvy_rectangle_attempt, 'tmp' was used as an int array and later a
dsf, and I've made a new 'tmpdsf' to be the latter.

In Dominosa, solver->pc_scratch2 was sometimes a dsf, and now there's
a new solver->dsf_scratch.

In Map, parse_edge_list() needed a dsf internally and then never
exported it; it expected to be passed an array of 2*w*h ints and used
the second half as a dsf. Now it expects only w*h ints, and allocates
its own dsf internally, freeing it again before returning.

And in Tents, find_errors() was allocating a single block of 2*w*h
ints and using the second half of it as a dsf, apparently just to save
one malloc. Now we malloc and free the dsf separately.
This commit is contained in:
Simon Tatham
2023-04-20 13:13:47 +01:00
parent 6c02b72d76
commit 16f997d34c
4 changed files with 44 additions and 26 deletions

12
divvy.c
View File

@ -262,7 +262,7 @@ static bool addremcommon(int w, int h, int x, int y, int *own, int val)
*/ */
int *divvy_rectangle_attempt(int w, int h, int k, random_state *rs) int *divvy_rectangle_attempt(int w, int h, int k, random_state *rs)
{ {
int *order, *queue, *tmp, *own, *sizes, *addable, *retdsf; int *order, *queue, *tmp, *own, *sizes, *addable, *retdsf, *tmpdsf;
bool *removable; bool *removable;
int wh = w*h; int wh = w*h;
int i, j, n, x, y, qhead, qtail; int i, j, n, x, y, qhead, qtail;
@ -277,6 +277,7 @@ int *divvy_rectangle_attempt(int w, int h, int k, random_state *rs)
queue = snewn(n, int); queue = snewn(n, int);
addable = snewn(wh*4, int); addable = snewn(wh*4, int);
removable = snewn(wh, bool); removable = snewn(wh, bool);
retdsf = tmpdsf = NULL;
/* /*
* Permute the grid squares into a random order, which will be * Permute the grid squares into a random order, which will be
@ -619,18 +620,18 @@ int *divvy_rectangle_attempt(int w, int h, int k, random_state *rs)
* the ominoes really are k-ominoes and we haven't * the ominoes really are k-ominoes and we haven't
* accidentally split one into two disconnected pieces. * accidentally split one into two disconnected pieces.
*/ */
dsf_init(tmp, wh); tmpdsf = snew_dsf(wh);
for (y = 0; y < h; y++) for (y = 0; y < h; y++)
for (x = 0; x+1 < w; x++) for (x = 0; x+1 < w; x++)
if (own[y*w+x] == own[y*w+(x+1)]) if (own[y*w+x] == own[y*w+(x+1)])
dsf_merge(tmp, y*w+x, y*w+(x+1)); dsf_merge(tmpdsf, y*w+x, y*w+(x+1));
for (x = 0; x < w; x++) for (x = 0; x < w; x++)
for (y = 0; y+1 < h; y++) for (y = 0; y+1 < h; y++)
if (own[y*w+x] == own[(y+1)*w+x]) if (own[y*w+x] == own[(y+1)*w+x])
dsf_merge(tmp, y*w+x, (y+1)*w+x); dsf_merge(tmpdsf, y*w+x, (y+1)*w+x);
for (i = 0; i < wh; i++) { for (i = 0; i < wh; i++) {
j = dsf_canonify(retdsf, i); j = dsf_canonify(retdsf, i);
assert(dsf_canonify(tmp, j) == dsf_canonify(tmp, i)); assert(dsf_canonify(tmpdsf, j) == dsf_canonify(tmpdsf, i));
} }
cleanup: cleanup:
@ -640,6 +641,7 @@ int *divvy_rectangle_attempt(int w, int h, int k, random_state *rs)
*/ */
sfree(order); sfree(order);
sfree(tmp); sfree(tmp);
sfree(tmpdsf);
sfree(own); sfree(own);
sfree(sizes); sfree(sizes);
sfree(queue); sfree(queue);

View File

@ -350,7 +350,7 @@ struct solver_scratch {
struct solver_square **squares_by_number; struct solver_square **squares_by_number;
struct findloopstate *fls; struct findloopstate *fls;
bool squares_by_number_initialised; bool squares_by_number_initialised;
int *wh_scratch, *pc_scratch, *pc_scratch2, *dc_scratch; int *wh_scratch, *pc_scratch, *pc_scratch2, *dc_scratch, *dsf_scratch;
}; };
static struct solver_scratch *solver_make_scratch(int n) static struct solver_scratch *solver_make_scratch(int n)
@ -482,6 +482,7 @@ static struct solver_scratch *solver_make_scratch(int n)
sc->wh_scratch = NULL; sc->wh_scratch = NULL;
sc->pc_scratch = sc->pc_scratch2 = NULL; sc->pc_scratch = sc->pc_scratch2 = NULL;
sc->dc_scratch = NULL; sc->dc_scratch = NULL;
sc->dsf_scratch = NULL;
return sc; return sc;
} }
@ -509,6 +510,7 @@ static void solver_free_scratch(struct solver_scratch *sc)
sfree(sc->pc_scratch); sfree(sc->pc_scratch);
sfree(sc->pc_scratch2); sfree(sc->pc_scratch2);
sfree(sc->dc_scratch); sfree(sc->dc_scratch);
sfree(sc->dsf_scratch);
sfree(sc); sfree(sc);
} }
@ -1425,19 +1427,21 @@ static bool deduce_forcing_chain(struct solver_scratch *sc)
sc->pc_scratch2 = snewn(sc->pc, int); sc->pc_scratch2 = snewn(sc->pc, int);
if (!sc->dc_scratch) if (!sc->dc_scratch)
sc->dc_scratch = snewn(sc->dc, int); sc->dc_scratch = snewn(sc->dc, int);
if (!sc->dsf_scratch)
sc->dsf_scratch = snew_dsf(sc->pc);
/* /*
* Start by identifying chains of placements which must all occur * Start by identifying chains of placements which must all occur
* together if any of them occurs. We do this by making * together if any of them occurs. We do this by making
* pc_scratch2 an edsf binding the placements into an equivalence * dsf_scratch an edsf binding the placements into an equivalence
* class for each entire forcing chain, with the two possible sets * class for each entire forcing chain, with the two possible sets
* of dominoes for the chain listed as inverses. * of dominoes for the chain listed as inverses.
*/ */
dsf_init(sc->pc_scratch2, sc->pc); dsf_init(sc->dsf_scratch, sc->pc);
for (si = 0; si < sc->wh; si++) { for (si = 0; si < sc->wh; si++) {
struct solver_square *sq = &sc->squares[si]; struct solver_square *sq = &sc->squares[si];
if (sq->nplacements == 2) if (sq->nplacements == 2)
edsf_merge(sc->pc_scratch2, edsf_merge(sc->dsf_scratch,
sq->placements[0]->index, sq->placements[0]->index,
sq->placements[1]->index, true); sq->placements[1]->index, true);
} }
@ -1452,7 +1456,7 @@ static bool deduce_forcing_chain(struct solver_scratch *sc)
*/ */
for (pi = 0; pi < sc->pc; pi++) { for (pi = 0; pi < sc->pc; pi++) {
bool inv; bool inv;
int c = edsf_canonify(sc->pc_scratch2, pi, &inv); int c = edsf_canonify(sc->dsf_scratch, pi, &inv);
sc->pc_scratch[pi] = c * 2 + (inv ? 1 : 0); sc->pc_scratch[pi] = c * 2 + (inv ? 1 : 0);
} }

39
map.c
View File

@ -1724,8 +1724,8 @@ static const char *parse_edge_list(const game_params *params,
int i, k, pos; int i, k, pos;
bool state; bool state;
const char *p = *desc; const char *p = *desc;
const char *err = NULL;
dsf_init(map+wh, wh); int *dsf = snew_dsf(wh);
pos = -1; pos = -1;
state = false; state = false;
@ -1736,8 +1736,10 @@ static const char *parse_edge_list(const game_params *params,
* pairs of squares whenever the edge list shows a non-edge). * pairs of squares whenever the edge list shows a non-edge).
*/ */
while (*p && *p != ',') { while (*p && *p != ',') {
if (*p < 'a' || *p > 'z') if (*p < 'a' || *p > 'z') {
return "Unexpected character in edge list"; err = "Unexpected character in edge list";
goto out;
}
if (*p == 'z') if (*p == 'z')
k = 25; k = 25;
else else
@ -1760,10 +1762,12 @@ static const char *parse_edge_list(const game_params *params,
y = (pos - w*(h-1)) % h; y = (pos - w*(h-1)) % h;
dx = 1; dx = 1;
dy = 0; dy = 0;
} else } else {
return "Too much data in edge list"; err = "Too much data in edge list";
goto out;
}
if (!state) if (!state)
dsf_merge(map+wh, y*w+x, (y+dy)*w+(x+dx)); dsf_merge(dsf, y*w+x, (y+dy)*w+(x+dx));
pos++; pos++;
} }
@ -1772,8 +1776,10 @@ static const char *parse_edge_list(const game_params *params,
p++; p++;
} }
assert(pos <= 2*wh-w-h); assert(pos <= 2*wh-w-h);
if (pos < 2*wh-w-h) if (pos < 2*wh-w-h) {
return "Too little data in edge list"; err = "Too little data in edge list";
goto out;
}
/* /*
* Now go through again and allocate region numbers. * Now go through again and allocate region numbers.
@ -1782,17 +1788,22 @@ static const char *parse_edge_list(const game_params *params,
for (i = 0; i < wh; i++) for (i = 0; i < wh; i++)
map[i] = -1; map[i] = -1;
for (i = 0; i < wh; i++) { for (i = 0; i < wh; i++) {
k = dsf_canonify(map+wh, i); k = dsf_canonify(dsf, i);
if (map[k] < 0) if (map[k] < 0)
map[k] = pos++; map[k] = pos++;
map[i] = map[k]; map[i] = map[k];
} }
if (pos != n) if (pos != n) {
return "Edge list defines the wrong number of regions"; err = "Edge list defines the wrong number of regions";
goto out;
}
*desc = p; *desc = p;
err = NULL; /* no error */
return NULL; out:
sfree(dsf);
return err;
} }
static const char *validate_desc(const game_params *params, const char *desc) static const char *validate_desc(const game_params *params, const char *desc)
@ -1802,7 +1813,7 @@ static const char *validate_desc(const game_params *params, const char *desc)
int *map; int *map;
const char *ret; const char *ret;
map = snewn(2*wh, int); map = snewn(wh, int);
ret = parse_edge_list(params, &desc, map); ret = parse_edge_list(params, &desc, map);
sfree(map); sfree(map);
if (ret) if (ret)

View File

@ -2006,7 +2006,7 @@ static int *find_errors(const game_state *state, char *grid)
{ {
int w = state->p.w, h = state->p.h; int w = state->p.w, h = state->p.h;
int *ret = snewn(w*h + w + h, int); int *ret = snewn(w*h + w + h, int);
int *tmp = snewn(w*h*2, int), *dsf = tmp + w*h; int *tmp = snewn(w*h, int), *dsf;
int x, y; int x, y;
/* /*
@ -2223,7 +2223,7 @@ static int *find_errors(const game_state *state, char *grid)
* all the tents in any component which has a smaller tree * all the tents in any component which has a smaller tree
* count. * count.
*/ */
dsf_init(dsf, w*h); dsf = snew_dsf(w*h);
/* Construct the equivalence classes. */ /* Construct the equivalence classes. */
for (y = 0; y < h; y++) { for (y = 0; y < h; y++) {
for (x = 0; x < w-1; x++) { for (x = 0; x < w-1; x++) {
@ -2302,6 +2302,7 @@ static int *find_errors(const game_state *state, char *grid)
#undef TENT #undef TENT
sfree(tmp); sfree(tmp);
sfree(dsf);
return ret; return ret;
} }