ref: f061101210352b9783085ba37e1c58f1fac89862
parent: d1219cac3c2e0adf58a477e442a8656bcb55ed0f
author: Simon Tatham <anakin@pobox.com>
date: Fri Dec 11 13:09:41 EST 2015
Pattern: add a system of immutable pre-filled grid squares. The game previously only supported numeric clues round the edge; but if for some reason you really want a puzzle with a specific solution bitmap and that bitmap doesn't happen to be uniquely soluble from only its row and column counts, then this gives you a fallback approach of pre-filling a few grid squares to resolve the ambiguities. (This also applies if the puzzle is uniquely soluble *in principle* but not by Pattern's limited solver - for example, Pattern has never been able to solve 4x4:2/1/2/1/1.1/2/1/1 and still can't, but now it can solve 4x4:2/1/2/1/1.1/2/1/1,Ap which has the hard part done for it.) Immutable squares are protected from modification during play, and used as initial information by the solver.
--- a/pattern.c
+++ b/pattern.c
@@ -50,6 +50,7 @@
int w, h;
int rowsize;
int *rowdata, *rowlen;
+ unsigned char *immutable;
int refcount;
} game_state_common;
@@ -499,9 +500,15 @@
max = max(w, h);
memset(matrix, 0, w*h);
+ if (state) {
+ for (i=0; i<w*h; i++) {
+ if (state->common->immutable[i])
+ matrix[i] = state->grid[i];
+ }
+ }
/* For each column, compute how many squares can be deduced
- * from just the row-data.
+ * from just the row-data and initial clues.
* Later, changed_* will hold how many squares were changed
* in every row/column in the previous iteration
* Changed_* is used to choose the next rows / cols to re-examine
@@ -524,6 +531,9 @@
if (rowdata[j] > freespace)
changed_h[i] += rowdata[j] - freespace;
}
+ for (j = 0; j < w; j++)
+ if (matrix[i*w+j])
+ changed_h[i]++;
}
for (i=0,max_h=0; i<h; i++)
if (changed_h[i] > max_h)
@@ -546,6 +556,9 @@
if (rowdata[j] > freespace)
changed_w[i] += rowdata[j] - freespace;
}
+ for (j = 0; j < h; j++)
+ if (matrix[j*w+i])
+ changed_w[i]++;
}
for (i=0,max_w=0; i<w; i++)
if (changed_w[i] > max_w)
@@ -805,7 +818,7 @@
if (desc[-1] == '/') {
if (i+1 == params->w + params->h)
return "too many row/column specifications";
- } else if (desc[-1] == '\0') {
+ } else if (desc[-1] == '\0' || desc[-1] == ',') {
if (i+1 < params->w + params->h)
return "too few row/column specifications";
} else
@@ -812,6 +825,34 @@
return "unrecognised character in game specification";
}
+ if (desc[-1] == ',') {
+ /*
+ * Optional extra piece of game description which fills in
+ * some grid squares as extra clues.
+ */
+ i = 0;
+ while (i < params->w * params->h) {
+ int c = (unsigned char)*desc++;
+ if ((c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z')) {
+ int len = tolower(c) - 'a';
+ i += len;
+ if (len < 25 && i < params->w*params->h)
+ i++;
+ if (i > params->w * params->h) {
+ return "too much data in clue-squares section";
+ }
+ } else if (!c) {
+ return "too little data in clue-squares section";
+ } else {
+ return "unrecognised character in clue-squares section";
+ }
+ }
+ if (*desc) {
+ return "too much data in clue-squares section";
+ }
+ }
+
return NULL;
}
@@ -831,6 +872,10 @@
state->grid = snewn(state->common->w * state->common->h, unsigned char);
memset(state->grid, GRID_UNKNOWN, state->common->w * state->common->h);
+ state->common->immutable = snewn(state->common->w * state->common->h,
+ unsigned char);
+ memset(state->common->immutable, 0, state->common->w * state->common->h);
+
state->common->rowsize = max(state->common->w, state->common->h);
state->common->rowdata = snewn(state->common->rowsize * (state->common->w + state->common->h), int);
state->common->rowlen = snewn(state->common->w + state->common->h, int);
@@ -851,6 +896,24 @@
}
}
+ if (desc[-1] == ',') {
+ /*
+ * Optional extra piece of game description which fills in
+ * some grid squares as extra clues.
+ */
+ i = 0;
+ while (i < params->w * params->h) {
+ int c = (unsigned char)*desc++;
+ int full = isupper(c), len = tolower(c) - 'a';
+ i += len;
+ if (len < 25 && i < params->w*params->h) {
+ state->grid[i] = full ? GRID_FULL : GRID_EMPTY;
+ state->common->immutable[i] = TRUE;
+ i++;
+ }
+ }
+ }
+
return state;
}
@@ -875,6 +938,7 @@
if (--state->common->refcount == 0) {
sfree(state->common->rowdata);
sfree(state->common->rowlen);
+ sfree(state->common->immutable);
sfree(state->common);
}
sfree(state->grid);
@@ -1161,7 +1225,8 @@
for (yy = y1; yy <= y2; yy++)
for (xx = x1; xx <= x2; xx++)
- if (state->grid[yy * state->common->w + xx] != ui->state)
+ if (!state->common->immutable[yy * state->common->w + xx] &&
+ state->grid[yy * state->common->w + xx] != ui->state)
move_needed = TRUE;
ui->dragging = FALSE;
@@ -1253,7 +1318,8 @@
ret = dup_game(from);
for (yy = y1; yy < y2; yy++)
for (xx = x1; xx < x2; xx++)
- ret->grid[yy * ret->common->w + xx] = val;
+ if (!ret->common->immutable[yy * ret->common->w + xx])
+ ret->grid[yy * ret->common->w + xx] = val;
/*
* An actual change, so check to see if we've completed the
@@ -1674,7 +1740,8 @@
* Work out what state this square should be drawn in,
* taking any current drag operation into account.
*/
- if (ui->dragging && x1 <= j && j <= x2 && y1 <= i && i <= y2)
+ if (ui->dragging && x1 <= j && j <= x2 && y1 <= i && i <= y2 &&
+ !state->common->immutable[i * state->common->w + j])
val = ui->state;
else
val = state->grid[i * state->common->w + j];