+#ifdef STANDALONE_PICTURE_GENERATOR
+ /*
+ * Postprocessing pass to prevent excessive numbers of adjacent
+ * singletons. Iterate over all edges in random shuffled order;
+ * for each edge that separates two regions, investigate
+ * whether removing that edge and merging the regions would
+ * still yield a valid and soluble puzzle. (The two regions
+ * must also be the same colour, of course.) If so, do it.
+ *
+ * This postprocessing pass is slow (due to repeated solver
+ * invocations), and seems to be unnecessary during normal
+ * unconstrained game generation. However, when generating a
+ * game under colour constraints, excessive singletons seem to
+ * turn up more often, so it's worth doing this.
+ */
+ {
+ int *posns, nposns;
+ int i, j, newdiff;
+ game_state *copy2;
+
+ nposns = params->w * (params->h+1) + params->h * (params->w+1);
+ posns = snewn(nposns, int);
+ for (i = j = 0; i < state->sx*state->sy; i++)
+ if (state->grid[i].type == s_edge)
+ posns[j++] = i;
+ assert(j == nposns);
+
+ shuffle(posns, nposns, sizeof(*posns), rs);
+
+ for (i = 0; i < nposns; i++) {
+ int x, y, x0, y0, x1, y1, cx, cy, cn, cx0, cy0, cx1, cy1, tx, ty;
+ space *s0, *s1, *ts, *d0, *d1, *dn;
+ int ok;
+
+ /* Coordinates of edge space */
+ x = posns[i] % state->sx;
+ y = posns[i] / state->sx;
+
+ /* Coordinates of square spaces on either side of edge */
+ x0 = ((x+1) & ~1) - 1; /* round down to next odd number */
+ y0 = ((y+1) & ~1) - 1;
+ x1 = 2*x-x0; /* and reflect about x to get x1 */
+ y1 = 2*y-y0;
+
+ if (!INGRID(state, x0, y0) || !INGRID(state, x1, y1))
+ continue; /* outermost edge of grid */
+ s0 = &SPACE(state, x0, y0);
+ s1 = &SPACE(state, x1, y1);
+ assert(s0->type == s_tile && s1->type == s_tile);
+
+ if (s0->dotx == s1->dotx && s0->doty == s1->doty)
+ continue; /* tiles _already_ owned by same dot */
+
+ d0 = &SPACE(state, s0->dotx, s0->doty);
+ d1 = &SPACE(state, s1->dotx, s1->doty);
+
+ if ((d0->flags ^ d1->flags) & F_DOT_BLACK)
+ continue; /* different colours: cannot merge */
+
+ /*
+ * Work out where the centre of gravity of the new
+ * region would be.
+ */
+ cx = d0->nassoc * d0->x + d1->nassoc * d1->x;
+ cy = d0->nassoc * d0->y + d1->nassoc * d1->y;
+ cn = d0->nassoc + d1->nassoc;
+ if (cx % cn || cy % cn)
+ continue; /* CoG not at integer coordinates */
+ cx /= cn;
+ cy /= cn;
+ assert(INUI(state, cx, cy));
+
+ /*
+ * Ensure that the CoG would actually be _in_ the new
+ * region, by verifying that all its surrounding tiles
+ * belong to one or other of our two dots.
+ */
+ cx0 = ((cx+1) & ~1) - 1; /* round down to next odd number */
+ cy0 = ((cy+1) & ~1) - 1;
+ cx1 = 2*cx-cx0; /* and reflect about cx to get cx1 */
+ cy1 = 2*cy-cy0;
+ ok = TRUE;
+ for (ty = cy0; ty <= cy1; ty += 2)
+ for (tx = cx0; tx <= cx1; tx += 2) {
+ ts = &SPACE(state, tx, ty);
+ assert(ts->type == s_tile);
+ if ((ts->dotx != d0->x || ts->doty != d0->y) &&
+ (ts->dotx != d1->x || ts->doty != d1->y))
+ ok = FALSE;
+ }
+ if (!ok)
+ continue;
+
+ /*
+ * Verify that for every tile in either source region,
+ * that tile's image in the new CoG is also in one of
+ * the two source regions.
+ */
+ for (ty = 1; ty < state->sy; ty += 2) {
+ for (tx = 1; tx < state->sx; tx += 2) {
+ int tx1, ty1;
+
+ ts = &SPACE(state, tx, ty);
+ assert(ts->type == s_tile);
+ if ((ts->dotx != d0->x || ts->doty != d0->y) &&
+ (ts->dotx != d1->x || ts->doty != d1->y))
+ continue; /* not part of these tiles anyway */
+ tx1 = 2*cx-tx;
+ ty1 = 2*cy-ty;
+ if (!INGRID(state, tx1, ty1)) {
+ ok = FALSE;
+ break;
+ }
+ ts = &SPACE(state, cx+cx-tx, cy+cy-ty);
+ if ((ts->dotx != d0->x || ts->doty != d0->y) &&
+ (ts->dotx != d1->x || ts->doty != d1->y)) {
+ ok = FALSE;
+ break;
+ }
+ }
+ if (!ok)
+ break;
+ }
+ if (!ok)
+ continue;
+
+ /*
+ * Now we're clear to attempt the merge. We take a copy
+ * of the game state first, so we can revert it easily
+ * if the resulting puzzle turns out to have become
+ * insoluble.
+ */
+ copy2 = dup_game(state);
+
+ remove_dot(d0);
+ remove_dot(d1);
+ dn = &SPACE(state, cx, cy);
+ add_dot(dn);
+ dn->flags |= (d0->flags & F_DOT_BLACK);
+ for (ty = 1; ty < state->sy; ty += 2) {
+ for (tx = 1; tx < state->sx; tx += 2) {
+ ts = &SPACE(state, tx, ty);
+ assert(ts->type == s_tile);
+ if ((ts->dotx != d0->x || ts->doty != d0->y) &&
+ (ts->dotx != d1->x || ts->doty != d1->y))
+ continue; /* not part of these tiles anyway */
+ add_assoc(state, ts, dn);
+ }
+ }
+
+ copy = dup_game(state);
+ clear_game(copy, 0);
+ dbg_state(copy);
+ newdiff = solver_state(copy, params->diff);
+ free_game(copy);
+ if (diff == newdiff) {
+ /* Still just as soluble. Let the merge stand. */
+ free_game(copy2);
+ } else {
+ /* Became insoluble. Revert. */
+ free_game(state);
+ state = copy2;
+ }
+ }
+ }
+#endif
+