#include <stdio.h>
#include <stdlib.h>
+#include <stdarg.h>
#include <string.h>
#include <assert.h>
#include <ctype.h>
COL_INK,
COL_SLANT1,
COL_SLANT2,
+ COL_ERROR,
+ COL_CURSOR,
+ COL_FILLEDSQUARE,
NCOLOURS
};
typedef struct game_clues {
int w, h;
signed char *clues;
- int *dsf; /* scratch space for completion check */
+ int *tmpdsf;
int refcount;
} game_clues;
+#define ERR_VERTEX 1
+#define ERR_SQUARE 2
+
struct game_state {
struct game_params p;
game_clues *clues;
signed char *soln;
+ unsigned char *errors;
int completed;
int used_solve; /* used to suppress completion flash */
};
sfree(params);
}
-static game_params *dup_params(game_params *params)
+static game_params *dup_params(const game_params *params)
{
game_params *ret = snew(game_params);
*ret = *params; /* structure copy */
}
}
-static char *encode_params(game_params *params, int full)
+static char *encode_params(const game_params *params, int full)
{
char data[256];
return dupstr(data);
}
-static config_item *game_configure(game_params *params)
+static config_item *game_configure(const game_params *params)
{
config_item *ret;
char buf[80];
- ret = snewn(2, config_item);
+ ret = snewn(4, config_item);
ret[0].name = "Width";
ret[0].type = C_STRING;
return ret;
}
-static game_params *custom_params(config_item *cfg)
+static game_params *custom_params(const config_item *cfg)
{
game_params *ret = snew(game_params);
return ret;
}
-static char *validate_params(game_params *params, int full)
+static char *validate_params(const game_params *params, int full)
{
/*
* (At least at the time of writing this comment) The grid
*/
signed char *slashval;
+ /*
+ * Stores possible v-shapes. This array is w by h in size, but
+ * not every bit of every entry is meaningful. The bits mean:
+ *
+ * - bit 0 for a square means that that square and the one to
+ * its right might form a v-shape between them
+ * - bit 1 for a square means that that square and the one to
+ * its right might form a ^-shape between them
+ * - bit 2 for a square means that that square and the one
+ * below it might form a >-shape between them
+ * - bit 3 for a square means that that square and the one
+ * below it might form a <-shape between them
+ *
+ * Any starting 1 or 3 clue rules out four bits in this array
+ * immediately; a 2 clue propagates any ruled-out bit past it
+ * (if the two squares on one side of a 2 cannot be a v-shape,
+ * then neither can the two on the other side be the same
+ * v-shape); we can rule out further bits during play using
+ * partially filled 2 clues; whenever a pair of squares is
+ * known not to be _either_ kind of v-shape, we can mark them
+ * as equivalent.
+ */
+ unsigned char *vbitmap;
+
/*
* Useful to have this information automatically passed to
* solver subroutines. (This pointer is not dynamically
ret->border = snewn(W*H, unsigned char);
ret->equiv = snewn(w*h, int);
ret->slashval = snewn(w*h, signed char);
+ ret->vbitmap = snewn(w*h, unsigned char);
return ret;
}
static void free_scratch(struct solver_scratch *sc)
{
+ sfree(sc->vbitmap);
sfree(sc->slashval);
sfree(sc->equiv);
sfree(sc->border);
}
}
+static int vbitmap_clear(int w, int h, struct solver_scratch *sc,
+ int x, int y, int vbits, char *reason, ...)
+{
+ int done_something = FALSE;
+ int vbit;
+
+ for (vbit = 1; vbit <= 8; vbit <<= 1)
+ if (vbits & sc->vbitmap[y*w+x] & vbit) {
+ done_something = TRUE;
+#ifdef SOLVER_DIAGNOSTICS
+ if (verbose) {
+ va_list ap;
+
+ printf("ruling out %c shape at (%d,%d)-(%d,%d) (",
+ "!v^!>!!!<"[vbit], x, y,
+ x+((vbit&0x3)!=0), y+((vbit&0xC)!=0));
+
+ va_start(ap, reason);
+ vprintf(reason, ap);
+ va_end(ap);
+
+ printf(")\n");
+ }
+#endif
+ sc->vbitmap[y*w+x] &= ~vbit;
+ }
+
+ return done_something;
+}
+
/*
* Solver. Returns 0 for impossibility, 1 for success, 2 for
* ambiguity or failure to converge.
* Establish a disjoint set forest for tracking connectedness
* between grid points.
*/
- for (i = 0; i < W*H; i++)
- sc->connected[i] = i; /* initially all distinct */
+ dsf_init(sc->connected, W*H);
/*
* Establish a disjoint set forest for tracking which squares
* are known to slant in the same direction.
*/
- for (i = 0; i < w*h; i++)
- sc->equiv[i] = i; /* initially all distinct */
+ dsf_init(sc->equiv, w*h);
/*
* Clear the slashval array.
memset(sc->slashval, 0, w*h);
/*
- * Initialise the `exits' and `border' arrays. Theses is used
+ * Set up the vbitmap array. Initially all types of v are possible.
+ */
+ memset(sc->vbitmap, 0xF, w*h);
+
+ /*
+ * Initialise the `exits' and `border' arrays. These are used
* to do second-order loop avoidance: the dual of the no loops
* constraint is that every point must be somehow connected to
* the border of the grid (otherwise there would be a solid
sc->exits[y*W+x] = clues[y*W+x];
}
- /*
- * Make a one-off preliminary pass over the grid looking for
- * starting-point arrangements. The ones we need to spot are:
- *
- * - two adjacent 1s in the centre of the grid imply that each
- * one's single line points towards the other. (If either 1
- * were connected on the far side, the two squares shared
- * between the 1s would both link to the other 1 as a
- * consequence of neither linking to the first.) Thus, we
- * can fill in the four squares around them.
- *
- * - dually, two adjacent 3s imply that each one's _non_-line
- * points towards the other.
- *
- * - if the pair of 1s and 3s is not _adjacent_ but is
- * separated by one or more 2s, the reasoning still applies.
- *
- * This is more advanced than just spotting obvious starting
- * squares such as central 4s and edge 2s, so we disable it on
- * DIFF_EASY.
- *
- * (I don't like this loop; it feels grubby to me. My
- * mathematical intuition feels there ought to be some more
- * general deductive form which contains this loop as a special
- * case, but I can't bring it to mind right now.)
- */
- if (difficulty > DIFF_EASY) {
- for (y = 1; y+1 < H; y++)
- for (x = 1; x+1 < W; x++) {
- int v = clues[y*W+x], s, x2, y2, dx, dy;
- if (v != 1 && v != 3)
- continue;
- /* Slash value of the square up and left of (x,y). */
- s = (v == 1 ? +1 : -1);
-
- /* Look in each direction once. */
- for (dy = 0; dy < 2; dy++) {
- dx = 1 - dy;
- x2 = x+dx;
- y2 = y+dy;
- if (x2+1 >= W || y2+1 >= H)
- continue; /* too close to the border */
- while (x2+dx+1 < W && y2+dy+1 < H && clues[y2*W+x2] == 2)
- x2 += dx, y2 += dy;
- if (clues[y2*W+x2] == v) {
-#ifdef SOLVER_DIAGNOSTICS
- if (verbose)
- printf("found adjacent %ds at %d,%d and %d,%d\n",
- v, x, y, x2, y2);
-#endif
- fill_square(w, h, x-1, y-1, s, soln,
- sc->connected, sc);
- fill_square(w, h, x-1+dy, y-1+dx, -s, soln,
- sc->connected, sc);
- fill_square(w, h, x2, y2, s, soln,
- sc->connected, sc);
- fill_square(w, h, x2-dy, y2-dx, -s, soln,
- sc->connected, sc);
- }
- }
- }
- }
-
/*
* Repeatedly try to deduce something until we can't.
*/
}
}
+ if (done_something)
+ continue;
+
+ /*
+ * Now see what we can do with the vbitmap array. All
+ * vbitmap deductions are disabled at Easy level.
+ */
+ if (difficulty <= DIFF_EASY)
+ continue;
+
+ for (y = 0; y < h; y++)
+ for (x = 0; x < w; x++) {
+ int s, c;
+
+ /*
+ * Any line already placed in a square must rule
+ * out any type of v which contradicts it.
+ */
+ if ((s = soln[y*w+x]) != 0) {
+ if (x > 0)
+ done_something |=
+ vbitmap_clear(w, h, sc, x-1, y, (s < 0 ? 0x1 : 0x2),
+ "contradicts known edge at (%d,%d)",x,y);
+ if (x+1 < w)
+ done_something |=
+ vbitmap_clear(w, h, sc, x, y, (s < 0 ? 0x2 : 0x1),
+ "contradicts known edge at (%d,%d)",x,y);
+ if (y > 0)
+ done_something |=
+ vbitmap_clear(w, h, sc, x, y-1, (s < 0 ? 0x4 : 0x8),
+ "contradicts known edge at (%d,%d)",x,y);
+ if (y+1 < h)
+ done_something |=
+ vbitmap_clear(w, h, sc, x, y, (s < 0 ? 0x8 : 0x4),
+ "contradicts known edge at (%d,%d)",x,y);
+ }
+
+ /*
+ * If both types of v are ruled out for a pair of
+ * adjacent squares, mark them as equivalent.
+ */
+ if (x+1 < w && !(sc->vbitmap[y*w+x] & 0x3)) {
+ int n1 = y*w+x, n2 = y*w+(x+1);
+ if (dsf_canonify(sc->equiv, n1) !=
+ dsf_canonify(sc->equiv, n2)) {
+ dsf_merge(sc->equiv, n1, n2);
+ done_something = TRUE;
+#ifdef SOLVER_DIAGNOSTICS
+ if (verbose)
+ printf("(%d,%d) and (%d,%d) must be equivalent"
+ " because both v-shapes are ruled out\n",
+ x, y, x+1, y);
+#endif
+ }
+ }
+ if (y+1 < h && !(sc->vbitmap[y*w+x] & 0xC)) {
+ int n1 = y*w+x, n2 = (y+1)*w+x;
+ if (dsf_canonify(sc->equiv, n1) !=
+ dsf_canonify(sc->equiv, n2)) {
+ dsf_merge(sc->equiv, n1, n2);
+ done_something = TRUE;
+#ifdef SOLVER_DIAGNOSTICS
+ if (verbose)
+ printf("(%d,%d) and (%d,%d) must be equivalent"
+ " because both v-shapes are ruled out\n",
+ x, y, x, y+1);
+#endif
+ }
+ }
+
+ /*
+ * The remaining work in this loop only works
+ * around non-edge clue points.
+ */
+ if (y == 0 || x == 0)
+ continue;
+ if ((c = clues[y*W+x]) < 0)
+ continue;
+
+ /*
+ * x,y marks a clue point not on the grid edge. See
+ * if this clue point allows us to rule out any v
+ * shapes.
+ */
+
+ if (c == 1) {
+ /*
+ * A 1 clue can never have any v shape pointing
+ * at it.
+ */
+ done_something |=
+ vbitmap_clear(w, h, sc, x-1, y-1, 0x5,
+ "points at 1 clue at (%d,%d)", x, y);
+ done_something |=
+ vbitmap_clear(w, h, sc, x-1, y, 0x2,
+ "points at 1 clue at (%d,%d)", x, y);
+ done_something |=
+ vbitmap_clear(w, h, sc, x, y-1, 0x8,
+ "points at 1 clue at (%d,%d)", x, y);
+ } else if (c == 3) {
+ /*
+ * A 3 clue can never have any v shape pointing
+ * away from it.
+ */
+ done_something |=
+ vbitmap_clear(w, h, sc, x-1, y-1, 0xA,
+ "points away from 3 clue at (%d,%d)", x, y);
+ done_something |=
+ vbitmap_clear(w, h, sc, x-1, y, 0x1,
+ "points away from 3 clue at (%d,%d)", x, y);
+ done_something |=
+ vbitmap_clear(w, h, sc, x, y-1, 0x4,
+ "points away from 3 clue at (%d,%d)", x, y);
+ } else if (c == 2) {
+ /*
+ * If a 2 clue has any kind of v ruled out on
+ * one side of it, the same v is ruled out on
+ * the other side.
+ */
+ done_something |=
+ vbitmap_clear(w, h, sc, x-1, y-1,
+ (sc->vbitmap[(y )*w+(x-1)] & 0x3) ^ 0x3,
+ "propagated by 2 clue at (%d,%d)", x, y);
+ done_something |=
+ vbitmap_clear(w, h, sc, x-1, y-1,
+ (sc->vbitmap[(y-1)*w+(x )] & 0xC) ^ 0xC,
+ "propagated by 2 clue at (%d,%d)", x, y);
+ done_something |=
+ vbitmap_clear(w, h, sc, x-1, y,
+ (sc->vbitmap[(y-1)*w+(x-1)] & 0x3) ^ 0x3,
+ "propagated by 2 clue at (%d,%d)", x, y);
+ done_something |=
+ vbitmap_clear(w, h, sc, x, y-1,
+ (sc->vbitmap[(y-1)*w+(x-1)] & 0xC) ^ 0xC,
+ "propagated by 2 clue at (%d,%d)", x, y);
+ }
+
+#undef CLEARBITS
+
+ }
+
} while (done_something);
/*
* Establish a disjoint set forest for tracking connectedness
* between grid points.
*/
- connected = snewn(W*H, int);
- for (i = 0; i < W*H; i++)
- connected[i] = i; /* initially all distinct */
+ connected = snew_dsf(W*H);
/*
* Prepare a list of the squares in the grid, and fill them in
sfree(connected);
}
-static char *new_game_desc(game_params *params, random_state *rs,
+static char *new_game_desc(const game_params *params, random_state *rs,
char **aux, int interactive)
{
int w = params->w, h = params->h, W = w+1, H = h+1;
return desc;
}
-static char *validate_desc(game_params *params, char *desc)
+static char *validate_desc(const game_params *params, const char *desc)
{
int w = params->w, h = params->h, W = w+1, H = h+1;
int area = W*H;
return NULL;
}
-static game_state *new_game(midend_data *me, game_params *params, char *desc)
+static game_state *new_game(midend *me, const game_params *params,
+ const char *desc)
{
int w = params->w, h = params->h, W = w+1, H = h+1;
game_state *state = snew(game_state);
state->soln = snewn(w*h, signed char);
memset(state->soln, 0, w*h);
state->completed = state->used_solve = FALSE;
+ state->errors = snewn(W*H, unsigned char);
+ memset(state->errors, 0, W*H);
state->clues = snew(game_clues);
state->clues->w = w;
state->clues->h = h;
state->clues->clues = snewn(W*H, signed char);
state->clues->refcount = 1;
- state->clues->dsf = snewn(W*H, int);
+ state->clues->tmpdsf = snewn(W*H*2+W+H, int);
memset(state->clues->clues, -1, W*H);
while (*desc) {
int n = *desc++;
return state;
}
-static game_state *dup_game(game_state *state)
+static game_state *dup_game(const game_state *state)
{
- int w = state->p.w, h = state->p.h;
+ int w = state->p.w, h = state->p.h, W = w+1, H = h+1;
game_state *ret = snew(game_state);
ret->p = state->p;
ret->soln = snewn(w*h, signed char);
memcpy(ret->soln, state->soln, w*h);
+ ret->errors = snewn(W*H, unsigned char);
+ memcpy(ret->errors, state->errors, W*H);
+
return ret;
}
static void free_game(game_state *state)
{
+ sfree(state->errors);
sfree(state->soln);
assert(state->clues);
if (--state->clues->refcount <= 0) {
sfree(state->clues->clues);
- sfree(state->clues->dsf);
+ sfree(state->clues->tmpdsf);
sfree(state->clues);
}
sfree(state);
}
+/*
+ * Utility function to return the current degree of a vertex. If
+ * `anti' is set, it returns the number of filled-in edges
+ * surrounding the point which _don't_ connect to it; thus 4 minus
+ * its anti-degree is the maximum degree it could have if all the
+ * empty spaces around it were filled in.
+ *
+ * (Yes, _4_ minus its anti-degree even if it's a border vertex.)
+ *
+ * If ret > 0, *sx and *sy are set to the coordinates of one of the
+ * squares that contributed to it.
+ */
+static int vertex_degree(int w, int h, signed char *soln, int x, int y,
+ int anti, int *sx, int *sy)
+{
+ int ret = 0;
+
+ assert(x >= 0 && x <= w && y >= 0 && y <= h);
+ if (x > 0 && y > 0 && soln[(y-1)*w+(x-1)] - anti < 0) {
+ if (sx) *sx = x-1;
+ if (sy) *sy = y-1;
+ ret++;
+ }
+ if (x > 0 && y < h && soln[y*w+(x-1)] + anti > 0) {
+ if (sx) *sx = x-1;
+ if (sy) *sy = y;
+ ret++;
+ }
+ if (x < w && y > 0 && soln[(y-1)*w+x] + anti > 0) {
+ if (sx) *sx = x;
+ if (sy) *sy = y-1;
+ ret++;
+ }
+ if (x < w && y < h && soln[y*w+x] - anti < 0) {
+ if (sx) *sx = x;
+ if (sy) *sy = y;
+ ret++;
+ }
+
+ return anti ? 4 - ret : ret;
+}
+
+struct slant_neighbour_ctx {
+ const game_state *state;
+ int i, n, neighbours[4];
+};
+static int slant_neighbour(int vertex, void *vctx)
+{
+ struct slant_neighbour_ctx *ctx = (struct slant_neighbour_ctx *)vctx;
+
+ if (vertex >= 0) {
+ int w = ctx->state->p.w, h = ctx->state->p.h, W = w+1;
+ int x = vertex % W, y = vertex / W;
+ ctx->n = ctx->i = 0;
+ if (x < w && y < h && ctx->state->soln[y*w+x] < 0)
+ ctx->neighbours[ctx->n++] = (y+1)*W+(x+1);
+ if (x > 0 && y > 0 && ctx->state->soln[(y-1)*w+(x-1)] < 0)
+ ctx->neighbours[ctx->n++] = (y-1)*W+(x-1);
+ if (x > 0 && y < h && ctx->state->soln[y*w+(x-1)] > 0)
+ ctx->neighbours[ctx->n++] = (y+1)*W+(x-1);
+ if (x < w && y > 0 && ctx->state->soln[(y-1)*w+x] > 0)
+ ctx->neighbours[ctx->n++] = (y-1)*W+(x+1);
+ }
+
+ if (ctx->i < ctx->n)
+ return ctx->neighbours[ctx->i++];
+ else
+ return -1;
+}
+
static int check_completion(game_state *state)
{
int w = state->p.w, h = state->p.h, W = w+1, H = h+1;
- int i, x, y;
+ int x, y, err = FALSE;
- /*
- * Establish a disjoint set forest for tracking connectedness
- * between grid points. Use the dsf scratch space in the shared
- * clues structure, to avoid mallocing too often.
- */
- for (i = 0; i < W*H; i++)
- state->clues->dsf[i] = i; /* initially all distinct */
+ memset(state->errors, 0, W*H);
/*
- * Now go through the grid checking connectedness. While we're
- * here, also check that everything is filled in.
+ * Detect and error-highlight loops in the grid.
*/
- for (y = 0; y < h; y++)
- for (x = 0; x < w; x++) {
- int i1, i2;
-
- if (state->soln[y*w+x] == 0)
- return FALSE;
- if (state->soln[y*w+x] < 0) {
- i1 = y*W+x;
- i2 = (y+1)*W+(x+1);
- } else {
- i1 = (y+1)*W+x;
- i2 = y*W+(x+1);
+ {
+ struct findloopstate *fls = findloop_new_state(W*H);
+ struct slant_neighbour_ctx ctx;
+ ctx.state = state;
+
+ if (findloop_run(fls, W*H, slant_neighbour, &ctx))
+ err = TRUE;
+ for (y = 0; y < h; y++) {
+ for (x = 0; x < w; x++) {
+ int u, v;
+ if (state->soln[y*w+x] == 0) {
+ continue;
+ } else if (state->soln[y*w+x] > 0) {
+ u = y*W+(x+1);
+ v = (y+1)*W+x;
+ } else {
+ u = (y+1)*W+(x+1);
+ v = y*W+x;
+ }
+ if (findloop_is_loop_edge(fls, u, v))
+ state->errors[y*W+x] |= ERR_SQUARE;
}
+ }
- /*
- * Our edge connects i1 with i2. If they're already
- * connected, return failure. Otherwise, link them.
- */
- if (dsf_canonify(state->clues->dsf, i1) ==
- dsf_canonify(state->clues->dsf, i2))
- return FALSE;
- else
- dsf_merge(state->clues->dsf, i1, i2);
- }
+ findloop_free_state(fls);
+ }
/*
- * The grid is _a_ valid grid; let's see if it matches the
- * clues.
+ * Now go through and check the degree of each clue vertex, and
+ * mark it with ERR_VERTEX if it cannot be fulfilled.
*/
for (y = 0; y < H; y++)
- for (x = 0; x < W; x++) {
- int v, c;
+ for (x = 0; x < W; x++) {
+ int c;
if ((c = state->clues->clues[y*W+x]) < 0)
continue;
- v = 0;
+ /*
+ * Check to see if there are too many connections to
+ * this vertex _or_ too many non-connections. Either is
+ * grounds for marking the vertex as erroneous.
+ */
+ if (vertex_degree(w, h, state->soln, x, y,
+ FALSE, NULL, NULL) > c ||
+ vertex_degree(w, h, state->soln, x, y,
+ TRUE, NULL, NULL) > 4-c) {
+ state->errors[y*W+x] |= ERR_VERTEX;
+ err = TRUE;
+ }
+ }
+
+ /*
+ * Now our actual victory condition is that (a) none of the
+ * above code marked anything as erroneous, and (b) every
+ * square has an edge in it.
+ */
- if (x > 0 && y > 0 && state->soln[(y-1)*w+(x-1)] == -1) v++;
- if (x > 0 && y < h && state->soln[y*w+(x-1)] == +1) v++;
- if (x < w && y > 0 && state->soln[(y-1)*w+x] == +1) v++;
- if (x < w && y < h && state->soln[y*w+x] == -1) v++;
+ if (err)
+ return FALSE;
- if (c != v)
+ for (y = 0; y < h; y++)
+ for (x = 0; x < w; x++)
+ if (state->soln[y*w+x] == 0)
return FALSE;
- }
return TRUE;
}
-static char *solve_game(game_state *state, game_state *currstate,
- char *aux, char **error)
+static char *solve_game(const game_state *state, const game_state *currstate,
+ const char *aux, char **error)
{
int w = state->p.w, h = state->p.h;
signed char *soln;
return move;
}
-static char *game_text_format(game_state *state)
+static int game_can_format_as_text_now(const game_params *params)
+{
+ return TRUE;
+}
+
+static char *game_text_format(const game_state *state)
{
int w = state->p.w, h = state->p.h, W = w+1, H = h+1;
int x, y, len;
return ret;
}
-static game_ui *new_ui(game_state *state)
+struct game_ui {
+ int cur_x, cur_y, cur_visible;
+};
+
+static game_ui *new_ui(const game_state *state)
{
- return NULL;
+ game_ui *ui = snew(game_ui);
+ ui->cur_x = ui->cur_y = ui->cur_visible = 0;
+ return ui;
}
static void free_ui(game_ui *ui)
{
+ sfree(ui);
}
-static char *encode_ui(game_ui *ui)
+static char *encode_ui(const game_ui *ui)
{
return NULL;
}
-static void decode_ui(game_ui *ui, char *encoding)
+static void decode_ui(game_ui *ui, const char *encoding)
{
}
-static void game_changed_state(game_ui *ui, game_state *oldstate,
- game_state *newstate)
+static void game_changed_state(game_ui *ui, const game_state *oldstate,
+ const game_state *newstate)
{
}
/*
* Bit fields in the `grid' and `todraw' elements of the drawstate.
*/
-#define BACKSLASH 0x0001
-#define FORWSLASH 0x0002
-#define L_T 0x0004
-#define L_B 0x0008
-#define T_L 0x0010
-#define T_R 0x0020
-#define R_T 0x0040
-#define R_B 0x0080
-#define B_L 0x0100
-#define B_R 0x0200
-#define C_TL 0x0400
-#define C_TR 0x0800
-#define C_BL 0x1000
-#define C_BR 0x2000
-#define FLASH 0x4000
+#define BACKSLASH 0x00000001L
+#define FORWSLASH 0x00000002L
+#define L_T 0x00000004L
+#define ERR_L_T 0x00000008L
+#define L_B 0x00000010L
+#define ERR_L_B 0x00000020L
+#define T_L 0x00000040L
+#define ERR_T_L 0x00000080L
+#define T_R 0x00000100L
+#define ERR_T_R 0x00000200L
+#define C_TL 0x00000400L
+#define ERR_C_TL 0x00000800L
+#define FLASH 0x00001000L
+#define ERRSLASH 0x00002000L
+#define ERR_TL 0x00004000L
+#define ERR_TR 0x00008000L
+#define ERR_BL 0x00010000L
+#define ERR_BR 0x00020000L
+#define CURSOR 0x00040000L
struct game_drawstate {
int tilesize;
int started;
- int *grid;
- int *todraw;
+ long *grid;
+ long *todraw;
};
-static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds,
- int x, int y, int button)
+static char *interpret_move(const game_state *state, game_ui *ui,
+ const game_drawstate *ds,
+ int x, int y, int button)
{
int w = state->p.w, h = state->p.h;
+ int v;
+ char buf[80];
+ enum { CLOCKWISE, ANTICLOCKWISE, NONE } action = NONE;
if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
- int v;
- char buf[80];
+ /*
+ * This is an utterly awful hack which I should really sort out
+ * by means of a proper configuration mechanism. One Slant
+ * player has observed that they prefer the mouse buttons to
+ * function exactly the opposite way round, so here's a
+ * mechanism for environment-based configuration. I cache the
+ * result in a global variable - yuck! - to avoid repeated
+ * lookups.
+ */
+ {
+ static int swap_buttons = -1;
+ if (swap_buttons < 0) {
+ char *env = getenv("SLANT_SWAP_BUTTONS");
+ swap_buttons = (env && (env[0] == 'y' || env[0] == 'Y'));
+ }
+ if (swap_buttons) {
+ if (button == LEFT_BUTTON)
+ button = RIGHT_BUTTON;
+ else
+ button = LEFT_BUTTON;
+ }
+ }
+ action = (button == LEFT_BUTTON) ? CLOCKWISE : ANTICLOCKWISE;
x = FROMCOORD(x);
y = FROMCOORD(y);
if (x < 0 || y < 0 || x >= w || y >= h)
return NULL;
+ ui->cur_visible = 0;
+ } else if (IS_CURSOR_SELECT(button)) {
+ if (!ui->cur_visible) {
+ ui->cur_visible = 1;
+ return "";
+ }
+ x = ui->cur_x;
+ y = ui->cur_y;
+
+ action = (button == CURSOR_SELECT2) ? ANTICLOCKWISE : CLOCKWISE;
+ } else if (IS_CURSOR_MOVE(button)) {
+ move_cursor(button, &ui->cur_x, &ui->cur_y, w, h, 0);
+ ui->cur_visible = 1;
+ return "";
+ } else if (button == '\\' || button == '\b' || button == '/') {
+ int x = ui->cur_x, y = ui->cur_y;
+ if (button == ("\\" "\b" "/")[state->soln[y*w + x] + 1]) return NULL;
+ sprintf(buf, "%c%d,%d", button == '\b' ? 'C' : button, x, y);
+ return dupstr(buf);
+ }
- if (button == LEFT_BUTTON) {
+ if (action != NONE) {
+ if (action == CLOCKWISE) {
/*
* Left-clicking cycles blank -> \ -> / -> blank.
*/
return NULL;
}
-static game_state *execute_move(game_state *state, char *move)
+static game_state *execute_move(const game_state *state, const char *move)
{
int w = state->p.w, h = state->p.h;
char c;
}
}
- if (!ret->completed)
- ret->completed = check_completion(ret);
+ /*
+ * We never clear the `completed' flag, but we must always
+ * re-run the completion check because it also highlights
+ * errors in the grid.
+ */
+ ret->completed = check_completion(ret) || ret->completed;
return ret;
}
* Drawing routines.
*/
-static void game_compute_size(game_params *params, int tilesize,
- int *x, int *y)
+static void game_compute_size(const game_params *params, int tilesize,
+ int *x, int *y)
{
/* fool the macros */
- struct dummy { int tilesize; } dummy = { tilesize }, *ds = &dummy;
+ struct dummy { int tilesize; } dummy, *ds = &dummy;
+ dummy.tilesize = tilesize;
*x = 2 * BORDER + params->w * TILESIZE + 1;
*y = 2 * BORDER + params->h * TILESIZE + 1;
}
-static void game_set_size(game_drawstate *ds, game_params *params,
- int tilesize)
+static void game_set_size(drawing *dr, game_drawstate *ds,
+ const game_params *params, int tilesize)
{
ds->tilesize = tilesize;
}
-static float *game_colours(frontend *fe, game_state *state, int *ncolours)
+static float *game_colours(frontend *fe, int *ncolours)
{
float *ret = snewn(3 * NCOLOURS, float);
- frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
+ /* CURSOR colour is a background highlight. */
+ game_mkhighlight(fe, ret, COL_BACKGROUND, COL_CURSOR, COL_FILLEDSQUARE);
ret[COL_GRID * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 0.7F;
ret[COL_GRID * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 0.7F;
ret[COL_SLANT2 * 3 + 1] = 0.0F;
ret[COL_SLANT2 * 3 + 2] = 0.0F;
+ ret[COL_ERROR * 3 + 0] = 1.0F;
+ ret[COL_ERROR * 3 + 1] = 0.0F;
+ ret[COL_ERROR * 3 + 2] = 0.0F;
+
*ncolours = NCOLOURS;
return ret;
}
-static game_drawstate *game_new_drawstate(game_state *state)
+static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
{
int w = state->p.w, h = state->p.h;
int i;
ds->tilesize = 0;
ds->started = FALSE;
- ds->grid = snewn(w*h, int);
- ds->todraw = snewn(w*h, int);
- for (i = 0; i < w*h; i++)
+ ds->grid = snewn((w+2)*(h+2), long);
+ ds->todraw = snewn((w+2)*(h+2), long);
+ for (i = 0; i < (w+2)*(h+2); i++)
ds->grid[i] = ds->todraw[i] = -1;
return ds;
}
-static void game_free_drawstate(game_drawstate *ds)
+static void game_free_drawstate(drawing *dr, game_drawstate *ds)
{
sfree(ds->todraw);
sfree(ds->grid);
sfree(ds);
}
-static void draw_clue(frontend *fe, game_drawstate *ds,
- int x, int y, int v)
+static void draw_clue(drawing *dr, game_drawstate *ds,
+ int x, int y, long v, long err, int bg, int colour)
{
char p[2];
- int col = ((x ^ y) & 1) ? COL_SLANT1 : COL_SLANT2;
+ int ccol = colour >= 0 ? colour : ((x ^ y) & 1) ? COL_SLANT1 : COL_SLANT2;
+ int tcol = colour >= 0 ? colour : err ? COL_ERROR : COL_INK;
if (v < 0)
return;
- p[0] = v + '0';
+ p[0] = (char)v + '0';
p[1] = '\0';
- draw_circle(fe, COORD(x), COORD(y), CLUE_RADIUS, COL_BACKGROUND, col);
- draw_text(fe, COORD(x), COORD(y), FONT_VARIABLE,
- CLUE_TEXTSIZE, ALIGN_VCENTRE|ALIGN_HCENTRE,
- COL_INK, p);
+ draw_circle(dr, COORD(x), COORD(y), CLUE_RADIUS,
+ bg >= 0 ? bg : COL_BACKGROUND, ccol);
+ draw_text(dr, COORD(x), COORD(y), FONT_VARIABLE,
+ CLUE_TEXTSIZE, ALIGN_VCENTRE|ALIGN_HCENTRE, tcol, p);
}
-static void draw_tile(frontend *fe, game_drawstate *ds, game_clues *clues,
- int x, int y, int v)
+static void draw_tile(drawing *dr, game_drawstate *ds, game_clues *clues,
+ int x, int y, long v)
{
- int w = clues->w /*, h = clues->h*/, W = w+1 /*, H = h+1 */;
- int xx, yy;
+ int w = clues->w, h = clues->h, W = w+1 /*, H = h+1 */;
int chesscolour = (x ^ y) & 1;
int fscol = chesscolour ? COL_SLANT2 : COL_SLANT1;
int bscol = chesscolour ? COL_SLANT1 : COL_SLANT2;
- clip(fe, COORD(x), COORD(y), TILESIZE+1, TILESIZE+1);
+ clip(dr, COORD(x), COORD(y), TILESIZE, TILESIZE);
- draw_rect(fe, COORD(x), COORD(y), TILESIZE, TILESIZE,
- (v & FLASH) ? COL_GRID : COL_BACKGROUND);
+ draw_rect(dr, COORD(x), COORD(y), TILESIZE, TILESIZE,
+ (v & FLASH) ? COL_GRID :
+ (v & CURSOR) ? COL_CURSOR :
+ (v & (BACKSLASH | FORWSLASH)) ? COL_FILLEDSQUARE :
+ COL_BACKGROUND);
/*
* Draw the grid lines.
*/
- draw_line(fe, COORD(x), COORD(y), COORD(x+1), COORD(y), COL_GRID);
- draw_line(fe, COORD(x), COORD(y+1), COORD(x+1), COORD(y+1), COL_GRID);
- draw_line(fe, COORD(x), COORD(y), COORD(x), COORD(y+1), COL_GRID);
- draw_line(fe, COORD(x+1), COORD(y), COORD(x+1), COORD(y+1), COL_GRID);
+ if (x >= 0 && x < w && y >= 0)
+ draw_rect(dr, COORD(x), COORD(y), TILESIZE+1, 1, COL_GRID);
+ if (x >= 0 && x < w && y < h)
+ draw_rect(dr, COORD(x), COORD(y+1), TILESIZE+1, 1, COL_GRID);
+ if (y >= 0 && y < h && x >= 0)
+ draw_rect(dr, COORD(x), COORD(y), 1, TILESIZE+1, COL_GRID);
+ if (y >= 0 && y < h && x < w)
+ draw_rect(dr, COORD(x+1), COORD(y), 1, TILESIZE+1, COL_GRID);
+ if (x == -1 && y == -1)
+ draw_rect(dr, COORD(x+1), COORD(y+1), 1, 1, COL_GRID);
+ if (x == -1 && y == h)
+ draw_rect(dr, COORD(x+1), COORD(y), 1, 1, COL_GRID);
+ if (x == w && y == -1)
+ draw_rect(dr, COORD(x), COORD(y+1), 1, 1, COL_GRID);
+ if (x == w && y == h)
+ draw_rect(dr, COORD(x), COORD(y), 1, 1, COL_GRID);
/*
* Draw the slash.
*/
if (v & BACKSLASH) {
- draw_line(fe, COORD(x), COORD(y), COORD(x+1), COORD(y+1), bscol);
- draw_line(fe, COORD(x)+1, COORD(y), COORD(x+1), COORD(y+1)-1,
- bscol);
- draw_line(fe, COORD(x), COORD(y)+1, COORD(x+1)-1, COORD(y+1),
- bscol);
+ int scol = (v & ERRSLASH) ? COL_ERROR : bscol;
+ draw_line(dr, COORD(x), COORD(y), COORD(x+1), COORD(y+1), scol);
+ draw_line(dr, COORD(x)+1, COORD(y), COORD(x+1), COORD(y+1)-1,
+ scol);
+ draw_line(dr, COORD(x), COORD(y)+1, COORD(x+1)-1, COORD(y+1),
+ scol);
} else if (v & FORWSLASH) {
- draw_line(fe, COORD(x+1), COORD(y), COORD(x), COORD(y+1), fscol);
- draw_line(fe, COORD(x+1)-1, COORD(y), COORD(x), COORD(y+1)-1,
- fscol);
- draw_line(fe, COORD(x+1), COORD(y)+1, COORD(x)+1, COORD(y+1),
- fscol);
+ int scol = (v & ERRSLASH) ? COL_ERROR : fscol;
+ draw_line(dr, COORD(x+1), COORD(y), COORD(x), COORD(y+1), scol);
+ draw_line(dr, COORD(x+1)-1, COORD(y), COORD(x), COORD(y+1)-1,
+ scol);
+ draw_line(dr, COORD(x+1), COORD(y)+1, COORD(x)+1, COORD(y+1),
+ scol);
}
/*
* Draw dots on the grid corners that appear if a slash is in a
* neighbouring cell.
*/
- if (v & L_T)
- draw_rect(fe, COORD(x), COORD(y)+1, 1, 1, bscol);
- if (v & L_B)
- draw_rect(fe, COORD(x), COORD(y+1)-1, 1, 1, fscol);
- if (v & R_T)
- draw_rect(fe, COORD(x+1), COORD(y)+1, 1, 1, fscol);
- if (v & R_B)
- draw_rect(fe, COORD(x+1), COORD(y+1)-1, 1, 1, bscol);
- if (v & T_L)
- draw_rect(fe, COORD(x)+1, COORD(y), 1, 1, bscol);
- if (v & T_R)
- draw_rect(fe, COORD(x+1)-1, COORD(y), 1, 1, fscol);
- if (v & B_L)
- draw_rect(fe, COORD(x)+1, COORD(y+1), 1, 1, fscol);
- if (v & B_R)
- draw_rect(fe, COORD(x+1)-1, COORD(y+1), 1, 1, bscol);
- if (v & C_TL)
- draw_rect(fe, COORD(x), COORD(y), 1, 1, bscol);
- if (v & C_TR)
- draw_rect(fe, COORD(x+1), COORD(y), 1, 1, fscol);
- if (v & C_BL)
- draw_rect(fe, COORD(x), COORD(y+1), 1, 1, fscol);
- if (v & C_BR)
- draw_rect(fe, COORD(x+1), COORD(y+1), 1, 1, bscol);
+ if (v & (L_T | BACKSLASH))
+ draw_rect(dr, COORD(x), COORD(y)+1, 1, 1,
+ (v & ERR_L_T ? COL_ERROR : bscol));
+ if (v & (L_B | FORWSLASH))
+ draw_rect(dr, COORD(x), COORD(y+1)-1, 1, 1,
+ (v & ERR_L_B ? COL_ERROR : fscol));
+ if (v & (T_L | BACKSLASH))
+ draw_rect(dr, COORD(x)+1, COORD(y), 1, 1,
+ (v & ERR_T_L ? COL_ERROR : bscol));
+ if (v & (T_R | FORWSLASH))
+ draw_rect(dr, COORD(x+1)-1, COORD(y), 1, 1,
+ (v & ERR_T_R ? COL_ERROR : fscol));
+ if (v & (C_TL | BACKSLASH))
+ draw_rect(dr, COORD(x), COORD(y), 1, 1,
+ (v & ERR_C_TL ? COL_ERROR : bscol));
/*
* And finally the clues at the corners.
*/
- for (xx = x; xx <= x+1; xx++)
- for (yy = y; yy <= y+1; yy++)
- draw_clue(fe, ds, xx, yy, clues->clues[yy*W+xx]);
-
- unclip(fe);
- draw_update(fe, COORD(x), COORD(y), TILESIZE+1, TILESIZE+1);
+ if (x >= 0 && y >= 0)
+ draw_clue(dr, ds, x, y, clues->clues[y*W+x], v & ERR_TL, -1, -1);
+ if (x < w && y >= 0)
+ draw_clue(dr, ds, x+1, y, clues->clues[y*W+(x+1)], v & ERR_TR, -1, -1);
+ if (x >= 0 && y < h)
+ draw_clue(dr, ds, x, y+1, clues->clues[(y+1)*W+x], v & ERR_BL, -1, -1);
+ if (x < w && y < h)
+ draw_clue(dr, ds, x+1, y+1, clues->clues[(y+1)*W+(x+1)], v & ERR_BR,
+ -1, -1);
+
+ unclip(dr);
+ draw_update(dr, COORD(x), COORD(y), TILESIZE, TILESIZE);
}
-static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
- game_state *state, int dir, game_ui *ui,
- float animtime, float flashtime)
+static void game_redraw(drawing *dr, game_drawstate *ds,
+ const game_state *oldstate, const game_state *state,
+ int dir, const game_ui *ui,
+ float animtime, float flashtime)
{
int w = state->p.w, h = state->p.h, W = w+1, H = h+1;
int x, y;
if (!ds->started) {
int ww, wh;
game_compute_size(&state->p, TILESIZE, &ww, &wh);
- draw_rect(fe, 0, 0, ww, wh, COL_BACKGROUND);
- draw_update(fe, 0, 0, ww, wh);
-
- /*
- * Draw any clues on the very edges (since normal tile
- * redraw won't draw the bits outside the grid boundary).
- */
- for (y = 0; y < H; y++) {
- draw_clue(fe, ds, 0, y, state->clues->clues[y*W+0]);
- draw_clue(fe, ds, w, y, state->clues->clues[y*W+w]);
- }
- for (x = 0; x < W; x++) {
- draw_clue(fe, ds, x, 0, state->clues->clues[0*W+x]);
- draw_clue(fe, ds, x, h, state->clues->clues[h*W+x]);
- }
-
+ draw_rect(dr, 0, 0, ww, wh, COL_BACKGROUND);
+ draw_update(dr, 0, 0, ww, wh);
ds->started = TRUE;
}
* We need to do this because a slash in one square affects the
* drawing of the next one along.
*/
- for (y = 0; y < h; y++)
- for (x = 0; x < w; x++)
- ds->todraw[y*w+x] = flashing ? FLASH : 0;
+ for (y = -1; y <= h; y++)
+ for (x = -1; x <= w; x++) {
+ if (x >= 0 && x < w && y >= 0 && y < h)
+ ds->todraw[(y+1)*(w+2)+(x+1)] = flashing ? FLASH : 0;
+ else
+ ds->todraw[(y+1)*(w+2)+(x+1)] = 0;
+ }
for (y = 0; y < h; y++) {
for (x = 0; x < w; x++) {
+ int err = state->errors[y*W+x] & ERR_SQUARE;
+
if (state->soln[y*w+x] < 0) {
- ds->todraw[y*w+x] |= BACKSLASH;
- if (x > 0)
- ds->todraw[y*w+(x-1)] |= R_T | C_TR;
- if (x+1 < w)
- ds->todraw[y*w+(x+1)] |= L_B | C_BL;
- if (y > 0)
- ds->todraw[(y-1)*w+x] |= B_L | C_BL;
- if (y+1 < h)
- ds->todraw[(y+1)*w+x] |= T_R | C_TR;
- if (x > 0 && y > 0)
- ds->todraw[(y-1)*w+(x-1)] |= C_BR;
- if (x+1 < w && y+1 < h)
- ds->todraw[(y+1)*w+(x+1)] |= C_TL;
+ ds->todraw[(y+1)*(w+2)+(x+1)] |= BACKSLASH;
+ ds->todraw[(y+2)*(w+2)+(x+1)] |= T_R;
+ ds->todraw[(y+1)*(w+2)+(x+2)] |= L_B;
+ ds->todraw[(y+2)*(w+2)+(x+2)] |= C_TL;
+ if (err) {
+ ds->todraw[(y+1)*(w+2)+(x+1)] |= ERRSLASH |
+ ERR_T_L | ERR_L_T | ERR_C_TL;
+ ds->todraw[(y+2)*(w+2)+(x+1)] |= ERR_T_R;
+ ds->todraw[(y+1)*(w+2)+(x+2)] |= ERR_L_B;
+ ds->todraw[(y+2)*(w+2)+(x+2)] |= ERR_C_TL;
+ }
} else if (state->soln[y*w+x] > 0) {
- ds->todraw[y*w+x] |= FORWSLASH;
- if (x > 0)
- ds->todraw[y*w+(x-1)] |= R_B | C_BR;
- if (x+1 < w)
- ds->todraw[y*w+(x+1)] |= L_T | C_TL;
- if (y > 0)
- ds->todraw[(y-1)*w+x] |= B_R | C_BR;
- if (y+1 < h)
- ds->todraw[(y+1)*w+x] |= T_L | C_TL;
- if (x > 0 && y+1 < h)
- ds->todraw[(y+1)*w+(x-1)] |= C_TR;
- if (x+1 < w && y > 0)
- ds->todraw[(y-1)*w+(x+1)] |= C_BL;
+ ds->todraw[(y+1)*(w+2)+(x+1)] |= FORWSLASH;
+ ds->todraw[(y+1)*(w+2)+(x+2)] |= L_T | C_TL;
+ ds->todraw[(y+2)*(w+2)+(x+1)] |= T_L | C_TL;
+ if (err) {
+ ds->todraw[(y+1)*(w+2)+(x+1)] |= ERRSLASH |
+ ERR_L_B | ERR_T_R;
+ ds->todraw[(y+1)*(w+2)+(x+2)] |= ERR_L_T | ERR_C_TL;
+ ds->todraw[(y+2)*(w+2)+(x+1)] |= ERR_T_L | ERR_C_TL;
+ }
}
+ if (ui->cur_visible && ui->cur_x == x && ui->cur_y == y)
+ ds->todraw[(y+1)*(w+2)+(x+1)] |= CURSOR;
}
}
+ for (y = 0; y < H; y++)
+ for (x = 0; x < W; x++)
+ if (state->errors[y*W+x] & ERR_VERTEX) {
+ ds->todraw[y*(w+2)+x] |= ERR_BR;
+ ds->todraw[y*(w+2)+(x+1)] |= ERR_BL;
+ ds->todraw[(y+1)*(w+2)+x] |= ERR_TR;
+ ds->todraw[(y+1)*(w+2)+(x+1)] |= ERR_TL;
+ }
+
/*
* Now go through and draw the grid squares.
*/
- for (y = 0; y < h; y++) {
- for (x = 0; x < w; x++) {
- if (ds->todraw[y*w+x] != ds->grid[y*w+x]) {
- draw_tile(fe, ds, state->clues, x, y, ds->todraw[y*w+x]);
- ds->grid[y*w+x] = ds->todraw[y*w+x];
+ for (y = -1; y <= h; y++) {
+ for (x = -1; x <= w; x++) {
+ if (ds->todraw[(y+1)*(w+2)+(x+1)] != ds->grid[(y+1)*(w+2)+(x+1)]) {
+ draw_tile(dr, ds, state->clues, x, y,
+ ds->todraw[(y+1)*(w+2)+(x+1)]);
+ ds->grid[(y+1)*(w+2)+(x+1)] = ds->todraw[(y+1)*(w+2)+(x+1)];
}
}
}
}
-static float game_anim_length(game_state *oldstate, game_state *newstate,
- int dir, game_ui *ui)
+static float game_anim_length(const game_state *oldstate,
+ const game_state *newstate, int dir, game_ui *ui)
{
return 0.0F;
}
-static float game_flash_length(game_state *oldstate, game_state *newstate,
- int dir, game_ui *ui)
+static float game_flash_length(const game_state *oldstate,
+ const game_state *newstate, int dir, game_ui *ui)
{
if (!oldstate->completed && newstate->completed &&
!oldstate->used_solve && !newstate->used_solve)
return 0.0F;
}
-static int game_wants_statusbar(void)
+static int game_status(const game_state *state)
{
- return FALSE;
+ return state->completed ? +1 : 0;
}
-static int game_timing_state(game_state *state, game_ui *ui)
+static int game_timing_state(const game_state *state, game_ui *ui)
{
return TRUE;
}
+static void game_print_size(const game_params *params, float *x, float *y)
+{
+ int pw, ph;
+
+ /*
+ * I'll use 6mm squares by default.
+ */
+ game_compute_size(params, 600, &pw, &ph);
+ *x = pw / 100.0F;
+ *y = ph / 100.0F;
+}
+
+static void game_print(drawing *dr, const game_state *state, int tilesize)
+{
+ int w = state->p.w, h = state->p.h, W = w+1;
+ int ink = print_mono_colour(dr, 0);
+ int paper = print_mono_colour(dr, 1);
+ int x, y;
+
+ /* Ick: fake up `ds->tilesize' for macro expansion purposes */
+ game_drawstate ads, *ds = &ads;
+ game_set_size(dr, ds, NULL, tilesize);
+
+ /*
+ * Border.
+ */
+ print_line_width(dr, TILESIZE / 16);
+ draw_rect_outline(dr, COORD(0), COORD(0), w*TILESIZE, h*TILESIZE, ink);
+
+ /*
+ * Grid.
+ */
+ print_line_width(dr, TILESIZE / 24);
+ for (x = 1; x < w; x++)
+ draw_line(dr, COORD(x), COORD(0), COORD(x), COORD(h), ink);
+ for (y = 1; y < h; y++)
+ draw_line(dr, COORD(0), COORD(y), COORD(w), COORD(y), ink);
+
+ /*
+ * Solution.
+ */
+ print_line_width(dr, TILESIZE / 12);
+ for (y = 0; y < h; y++)
+ for (x = 0; x < w; x++)
+ if (state->soln[y*w+x]) {
+ int ly, ry;
+ /*
+ * To prevent nasty line-ending artefacts at
+ * corners, I'll do something slightly cunning
+ * here.
+ */
+ clip(dr, COORD(x), COORD(y), TILESIZE, TILESIZE);
+ if (state->soln[y*w+x] < 0)
+ ly = y-1, ry = y+2;
+ else
+ ry = y-1, ly = y+2;
+ draw_line(dr, COORD(x-1), COORD(ly), COORD(x+2), COORD(ry),
+ ink);
+ unclip(dr);
+ }
+
+ /*
+ * Clues.
+ */
+ print_line_width(dr, TILESIZE / 24);
+ for (y = 0; y <= h; y++)
+ for (x = 0; x <= w; x++)
+ draw_clue(dr, ds, x, y, state->clues->clues[y*W+x],
+ FALSE, paper, ink);
+}
+
#ifdef COMBINED
#define thegame slant
#endif
const struct game thegame = {
- "Slant", "games.slant",
+ "Slant", "games.slant", "slant",
default_params,
- game_fetch_preset,
+ game_fetch_preset, NULL,
decode_params,
encode_params,
free_params,
dup_game,
free_game,
TRUE, solve_game,
- TRUE, game_text_format,
+ TRUE, game_can_format_as_text_now, game_text_format,
new_ui,
free_ui,
encode_ui,
game_redraw,
game_anim_length,
game_flash_length,
- game_wants_statusbar,
+ game_status,
+ TRUE, FALSE, game_print_size, game_print,
+ FALSE, /* wants_statusbar */
FALSE, game_timing_state,
- 0, /* mouse_priorities */
+ 0, /* flags */
};
#ifdef STANDALONE_SOLVER
#include <stdarg.h>
-/*
- * gcc -DSTANDALONE_SOLVER -o slantsolver slant.c malloc.c
- */
-
-void frontend_default_colour(frontend *fe, float *output) {}
-void draw_text(frontend *fe, int x, int y, int fonttype, int fontsize,
- int align, int colour, char *text) {}
-void draw_rect(frontend *fe, int x, int y, int w, int h, int colour) {}
-void draw_line(frontend *fe, int x1, int y1, int x2, int y2, int colour) {}
-void draw_polygon(frontend *fe, int *coords, int npoints,
- int fillcolour, int outlinecolour) {}
-void draw_circle(frontend *fe, int cx, int cy, int radius,
- int fillcolour, int outlinecolour) {}
-void clip(frontend *fe, int x, int y, int w, int h) {}
-void unclip(frontend *fe) {}
-void start_draw(frontend *fe) {}
-void draw_update(frontend *fe, int x, int y, int w, int h) {}
-void end_draw(frontend *fe) {}
-unsigned long random_bits(random_state *state, int bits)
-{ assert(!"Shouldn't get randomness"); return 0; }
-unsigned long random_upto(random_state *state, unsigned long limit)
-{ assert(!"Shouldn't get randomness"); return 0; }
-void shuffle(void *array, int nelts, int eltsize, random_state *rs)
-{ assert(!"Shouldn't get randomness"); }
-
-void fatal(char *fmt, ...)
-{
- va_list ap;
-
- fprintf(stderr, "fatal error: ");
-
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
-
- fprintf(stderr, "\n");
- exit(1);
-}
-
int main(int argc, char **argv)
{
game_params *p;
game_state *s;
char *id = NULL, *desc, *err;
int grade = FALSE;
- int ret;
+ int ret, diff, really_verbose = FALSE;
struct solver_scratch *sc;
while (--argc > 0) {
char *p = *++argv;
if (!strcmp(p, "-v")) {
- verbose = TRUE;
+ really_verbose = TRUE;
} else if (!strcmp(p, "-g")) {
grade = TRUE;
} else if (*p == '-') {
sc = new_scratch(p->w, p->h);
- if (grade) {
+ /*
+ * When solving an Easy puzzle, we don't want to bother the
+ * user with Hard-level deductions. For this reason, we grade
+ * the puzzle internally before doing anything else.
+ */
+ ret = -1; /* placate optimiser */
+ for (diff = 0; diff < DIFFCOUNT; diff++) {
ret = slant_solve(p->w, p->h, s->clues->clues,
- s->soln, sc, DIFF_EASY);
- if (ret == 0)
- printf("Difficulty rating: impossible (no solution exists)\n");
- else if (ret == 1)
- printf("Difficulty rating: Easy\n");
- else {
- ret = slant_solve(p->w, p->h, s->clues->clues,
- s->soln, sc, DIFF_HARD);
+ s->soln, sc, diff);
+ if (ret < 2)
+ break;
+ }
+
+ if (diff == DIFFCOUNT) {
+ if (grade)
+ printf("Difficulty rating: harder than Hard, or ambiguous\n");
+ else
+ printf("Unable to find a unique solution\n");
+ } else {
+ if (grade) {
if (ret == 0)
printf("Difficulty rating: impossible (no solution exists)\n");
else if (ret == 1)
- printf("Difficulty rating: Hard\n");
+ printf("Difficulty rating: %s\n", slant_diffnames[diff]);
+ } else {
+ verbose = really_verbose;
+ ret = slant_solve(p->w, p->h, s->clues->clues,
+ s->soln, sc, diff);
+ if (ret == 0)
+ printf("Puzzle is inconsistent\n");
else
- printf("Difficulty rating: harder than Hard, or ambiguous\n");
+ fputs(game_text_format(s), stdout);
}
- } else {
- ret = slant_solve(p->w, p->h, s->clues->clues,
- s->soln, sc, DIFF_HARD);
- if (ret == 0)
- printf("Puzzle is inconsistent\n");
- else if (ret > 1)
- printf("Unable to find a unique solution\n");
- else
- printf("%s\n", game_text_format(s));
}
return 0;
}
#endif
+
+/* vim: set shiftwidth=4 tabstop=8: */