* factorial-time enumeration at a sensible rate. Easy puzzles
* higher than that would be possible, but more latin-squarey
* than skyscrapery, as it were.
- *
- * - UI work?
- * + Allow the user to mark a clue as 'spent' in some way once
- * it's no longer interesting (typically because no
- * arrangement of the remaining possibilities _can_ violate
- * it)?
*/
#include <stdio.h>
COL_HIGHLIGHT,
COL_ERROR,
COL_PENCIL,
+ COL_DONE,
NCOLOURS
};
struct game_state {
game_params par;
struct clues *clues;
+ unsigned char *clues_done;
digit *grid;
int *pencil; /* bitmaps using bits 1<<1..1<<n */
int completed, cheated;
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 ret[80];
return dupstr(ret);
}
-static config_item *game_configure(game_params *params)
+static config_item *game_configure(const game_params *params)
{
config_item *ret;
char buf[80];
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)
{
if (params->w < 3 || params->w > 9)
return "Grid size must be between 3 and 9";
* Grid generation.
*/
-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, a = w*w;
desc = snewn(40*a, char);
p = desc;
for (i = 0; i < 4*w; i++) {
- p += sprintf(p, "%s%.0d", i?"/":"", clues[i]);
+ if (i)
+ *p++ = '/';
+ if (clues[i])
+ p += sprintf(p, "%d", clues[i]);
}
for (i = 0; i < a; i++)
if (grid[i])
* Gameplay.
*/
-static char *validate_desc(game_params *params, char *desc)
+static char *validate_desc(const game_params *params, const char *desc)
{
int w = params->w, a = w*w;
const char *p = desc;
return NULL;
}
-static game_state *new_game(midend *me, game_params *params, char *desc)
+static game_state *new_game(midend *me, const game_params *params,
+ const char *desc)
{
int w = params->w, a = w*w;
game_state *state = snew(game_state);
state->clues->clues = snewn(4*w, int);
state->clues->immutable = snewn(a, digit);
state->grid = snewn(a, digit);
+ state->clues_done = snewn(4*w, unsigned char);
state->pencil = snewn(a, int);
for (i = 0; i < a; i++) {
}
memset(state->clues->immutable, 0, a);
+ memset(state->clues_done, 0, 4*w*sizeof(unsigned char));
for (i = 0; i < 4*w; i++) {
if (i > 0) {
return state;
}
-static game_state *dup_game(game_state *state)
+static game_state *dup_game(const game_state *state)
{
int w = state->par.w, a = w*w;
game_state *ret = snew(game_state);
ret->grid = snewn(a, digit);
ret->pencil = snewn(a, int);
+ ret->clues_done = snewn(4*w, unsigned char);
memcpy(ret->grid, state->grid, a*sizeof(digit));
memcpy(ret->pencil, state->pencil, a*sizeof(int));
+ memcpy(ret->clues_done, state->clues_done, 4*w*sizeof(unsigned char));
ret->completed = state->completed;
ret->cheated = state->cheated;
{
sfree(state->grid);
sfree(state->pencil);
+ sfree(state->clues_done);
if (--state->clues->refcount <= 0) {
sfree(state->clues->immutable);
sfree(state->clues->clues);
sfree(state);
}
-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->par.w, a = w*w;
int i, ret;
return out;
}
-static int game_can_format_as_text_now(game_params *params)
+static int game_can_format_as_text_now(const game_params *params)
{
return TRUE;
}
-static char *game_text_format(game_state *state)
+static char *game_text_format(const game_state *state)
{
int w = state->par.w /* , a = w*w */;
char *ret;
int hcursor;
};
-static game_ui *new_ui(game_state *state)
+static game_ui *new_ui(const game_state *state)
{
game_ui *ui = snew(game_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)
{
int w = newstate->par.w;
/*
#define COORD(x) ((x)*TILESIZE + BORDER)
#define FROMCOORD(x) (((x)+(TILESIZE-BORDER)) / TILESIZE - 1)
+/* These always return positive values, though y offsets are actually -ve */
+#define X_3D_DISP(height, w) ((height) * TILESIZE / (8 * (w)))
+#define Y_3D_DISP(height, w) ((height) * TILESIZE / (4 * (w)))
+
#define FLASH_TIME 0.4F
#define DF_PENCIL_SHIFT 16
+#define DF_CLUE_DONE 0x10000
#define DF_ERROR 0x8000
#define DF_HIGHLIGHT 0x4000
#define DF_HIGHLIGHT_PENCIL 0x2000
struct game_drawstate {
int tilesize;
+ int three_d; /* default 3D graphics are user-disableable */
int started;
- long *tiles;
+ long *tiles; /* (w+2)*(w+2) temp space */
+ long *drawn; /* (w+2)*(w+2)*4: current drawn data */
int *errtmp;
};
-static int check_errors(game_state *state, int *errors)
+static int check_errors(const game_state *state, int *errors)
{
int w = state->par.w /*, a = w*w */;
int W = w+2, A = W*W; /* the errors array is (w+2) square */
}
for (i = 0; i < 4*w; i++) {
- int start, step, j, k, n, best;
+ int start, step, j, n, best;
STARTSTEP(start, step, i, w);
if (!clues[i])
continue;
best = n = 0;
- k = 0;
for (j = 0; j < w; j++) {
int number = grid[start+j*step];
if (!number)
return errs;
}
-static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds,
- int x, int y, int button)
+static int clue_index(const game_state *state, int x, int y)
+{
+ int w = state->par.w;
+
+ if (x == -1 || x == w)
+ return w * (x == -1 ? 2 : 3) + y;
+ else if (y == -1 || y == w)
+ return (y == -1 ? 0 : w) + x;
+
+ return -1;
+}
+
+static int is_clue(const game_state *state, int x, int y)
+{
+ int w = state->par.w;
+
+ if (((x == -1 || x == w) && y >= 0 && y < w) ||
+ ((y == -1 || y == w) && x >= 0 && x < w))
+ {
+ if (state->clues->clues[clue_index(state, x, y)] & DF_DIGIT_MASK)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static char *interpret_move(const game_state *state, game_ui *ui,
+ const game_drawstate *ds,
+ int x, int y, int button)
{
int w = state->par.w;
int tx, ty;
tx = FROMCOORD(x);
ty = FROMCOORD(y);
+ if (ds->three_d) {
+ /*
+ * In 3D mode, just locating the mouse click in the natural
+ * square grid may not be sufficient to tell which tower the
+ * user clicked on. Investigate the _tops_ of the nearby
+ * towers to see if a click on one grid square was actually
+ * a click on a tower protruding into that region from
+ * another.
+ */
+ int dx, dy;
+ for (dy = 0; dy <= 1; dy++)
+ for (dx = 0; dx >= -1; dx--) {
+ int cx = tx + dx, cy = ty + dy;
+ if (cx >= 0 && cx < w && cy >= 0 && cy < w) {
+ int height = state->grid[cy*w+cx];
+ int bx = COORD(cx), by = COORD(cy);
+ int ox = bx + X_3D_DISP(height, w);
+ int oy = by - Y_3D_DISP(height, w);
+ if (/* on top face? */
+ (x - ox >= 0 && x - ox < TILESIZE &&
+ y - oy >= 0 && y - oy < TILESIZE) ||
+ /* in triangle between top-left corners? */
+ (ox > bx && x >= bx && x <= ox && y <= by &&
+ (by-y) * (ox-bx) <= (by-oy) * (x-bx)) ||
+ /* in triangle between bottom-right corners? */
+ (ox > bx && x >= bx+TILESIZE && x <= ox+TILESIZE &&
+ y >= oy+TILESIZE &&
+ (by-y+TILESIZE)*(ox-bx) >= (by-oy)*(x-bx-TILESIZE))) {
+ tx = cx;
+ ty = cy;
+ }
+ }
+ }
+ }
+
if (tx >= 0 && tx < w && ty >= 0 && ty < w) {
if (button == LEFT_BUTTON) {
if (tx == ui->hx && ty == ui->hy &&
ui->hcursor = 0;
return ""; /* UI activity occurred */
}
+ } else if (button == LEFT_BUTTON) {
+ if (is_clue(state, tx, ty)) {
+ sprintf(buf, "%c%d,%d", 'D', tx, ty);
+ return dupstr(buf);
+ }
}
if (IS_CURSOR_MOVE(button)) {
move_cursor(button, &ui->hx, &ui->hy, w, w, 0);
return NULL;
}
-static game_state *execute_move(game_state *from, char *move)
+static game_state *execute_move(const game_state *from, const char *move)
{
int w = from->par.w, a = w*w;
- game_state *ret;
+ game_state *ret = dup_game(from);
int x, y, i, n;
if (move[0] == 'S') {
- ret = dup_game(from);
ret->completed = ret->cheated = TRUE;
for (i = 0; i < a; i++) {
- if (move[i+1] < '1' || move[i+1] > '0'+w) {
- free_game(ret);
- return NULL;
- }
+ if (move[i+1] < '1' || move[i+1] > '0'+w)
+ goto badmove;
ret->grid[i] = move[i+1] - '0';
ret->pencil[i] = 0;
}
- if (move[a+1] != '\0') {
- free_game(ret);
- return NULL;
- }
+ if (move[a+1] != '\0')
+ goto badmove;
return ret;
} else if ((move[0] == 'P' || move[0] == 'R') &&
sscanf(move+1, "%d,%d,%d", &x, &y, &n) == 3 &&
x >= 0 && x < w && y >= 0 && y < w && n >= 0 && n <= w) {
if (from->clues->immutable[y*w+x])
- return NULL;
+ goto badmove;
- ret = dup_game(from);
if (move[0] == 'P' && n > 0) {
ret->pencil[y*w+x] ^= 1L << n;
} else {
* starting point when following through a set of
* diagnostics output by the standalone solver.)
*/
- ret = dup_game(from);
for (i = 0; i < a; i++) {
if (!ret->grid[i])
ret->pencil[i] = (1L << (w+1)) - (1L << 1);
}
return ret;
- } else
- return NULL; /* couldn't parse move string */
+ } else if (move[0] == 'D' && sscanf(move+1, "%d,%d", &x, &y) == 2 &&
+ is_clue(from, x, y)) {
+ int index = clue_index(from, x, y);
+ ret->clues_done[index] = !ret->clues_done[index];
+ return ret;
+ }
+
+ badmove:
+ /* couldn't parse move string */
+ free_game(ret);
+ return NULL;
}
/* ----------------------------------------------------------------------
#define SIZE(w) ((w) * TILESIZE + 2*BORDER)
-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)
{
/* Ick: fake up `ds->tilesize' for macro expansion purposes */
struct { int tilesize; } ads, *ds = &ads;
}
static void game_set_size(drawing *dr, game_drawstate *ds,
- game_params *params, int tilesize)
+ const game_params *params, int tilesize)
{
ds->tilesize = tilesize;
}
ret[COL_PENCIL * 3 + 1] = 0.5F * ret[COL_BACKGROUND * 3 + 1];
ret[COL_PENCIL * 3 + 2] = ret[COL_BACKGROUND * 3 + 2];
+ ret[COL_DONE * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] / 1.5F;
+ ret[COL_DONE * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] / 1.5F;
+ ret[COL_DONE * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] / 1.5F;
+
*ncolours = NCOLOURS;
return ret;
}
-static const char *const minus_signs[] = { "\xE2\x88\x92", "-" };
-static const char *const times_signs[] = { "\xC3\x97", "*" };
-static const char *const divide_signs[] = { "\xC3\xB7", "/" };
-
-static game_drawstate *game_new_drawstate(drawing *dr, game_state *state)
+static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
{
int w = state->par.w /*, a = w*w */;
struct game_drawstate *ds = snew(struct game_drawstate);
int i;
ds->tilesize = 0;
+ ds->three_d = !getenv("TOWERS_2D");
ds->started = FALSE;
ds->tiles = snewn((w+2)*(w+2), long);
- for (i = 0; i < (w+2)*(w+2); i++)
- ds->tiles[i] = -1;
+ ds->drawn = snewn((w+2)*(w+2)*4, long);
+ for (i = 0; i < (w+2)*(w+2)*4; i++)
+ ds->drawn[i] = -1;
ds->errtmp = snewn((w+2)*(w+2), int);
return ds;
{
sfree(ds->errtmp);
sfree(ds->tiles);
+ sfree(ds->drawn);
sfree(ds);
}
int x, int y, long tile)
{
int w = clues->w /* , a = w*w */;
- int tx, ty, tw, th;
- int cx, cy, cw, ch;
+ int tx, ty, bg;
char str[64];
- tx = BORDER + x * TILESIZE + 1;
- ty = BORDER + y * TILESIZE + 1;
-
- cx = tx;
- cy = ty;
- cw = tw = TILESIZE-1;
- ch = th = TILESIZE-1;
-
- clip(dr, cx, cy, cw, ch);
+ tx = COORD(x);
+ ty = COORD(y);
+
+ bg = (tile & DF_HIGHLIGHT) ? COL_HIGHLIGHT : COL_BACKGROUND;
+
+ /* draw tower */
+ if (ds->three_d && (tile & DF_PLAYAREA) && (tile & DF_DIGIT_MASK)) {
+ int coords[8];
+ int xoff = X_3D_DISP(tile & DF_DIGIT_MASK, w);
+ int yoff = Y_3D_DISP(tile & DF_DIGIT_MASK, w);
+
+ /* left face of tower */
+ coords[0] = tx;
+ coords[1] = ty - 1;
+ coords[2] = tx;
+ coords[3] = ty + TILESIZE - 1;
+ coords[4] = coords[2] + xoff;
+ coords[5] = coords[3] - yoff;
+ coords[6] = coords[0] + xoff;
+ coords[7] = coords[1] - yoff;
+ draw_polygon(dr, coords, 4, bg, COL_GRID);
+
+ /* bottom face of tower */
+ coords[0] = tx + TILESIZE;
+ coords[1] = ty + TILESIZE - 1;
+ coords[2] = tx;
+ coords[3] = ty + TILESIZE - 1;
+ coords[4] = coords[2] + xoff;
+ coords[5] = coords[3] - yoff;
+ coords[6] = coords[0] + xoff;
+ coords[7] = coords[1] - yoff;
+ draw_polygon(dr, coords, 4, bg, COL_GRID);
+
+ /* now offset all subsequent drawing to the top of the tower */
+ tx += xoff;
+ ty -= yoff;
+ }
- /* background needs erasing */
- draw_rect(dr, cx, cy, cw, ch,
- (tile & DF_HIGHLIGHT) ? COL_HIGHLIGHT : COL_BACKGROUND);
+ /* erase background */
+ draw_rect(dr, tx, ty, TILESIZE, TILESIZE, bg);
/* pencil-mode highlight */
if (tile & DF_HIGHLIGHT_PENCIL) {
int coords[6];
- coords[0] = cx;
- coords[1] = cy;
- coords[2] = cx+cw/2;
- coords[3] = cy;
- coords[4] = cx;
- coords[5] = cy+ch/2;
+ coords[0] = tx;
+ coords[1] = ty;
+ coords[2] = tx+TILESIZE/2;
+ coords[3] = ty;
+ coords[4] = tx;
+ coords[5] = ty+TILESIZE/2;
draw_polygon(dr, coords, 3, COL_HIGHLIGHT, COL_HIGHLIGHT);
}
+ /* draw box outline */
+ if (tile & DF_PLAYAREA) {
+ int coords[8];
+ coords[0] = tx;
+ coords[1] = ty - 1;
+ coords[2] = tx + TILESIZE;
+ coords[3] = ty - 1;
+ coords[4] = tx + TILESIZE;
+ coords[5] = ty + TILESIZE - 1;
+ coords[6] = tx;
+ coords[7] = ty + TILESIZE - 1;
+ draw_polygon(dr, coords, 4, -1, COL_GRID);
+ }
+
/* new number needs drawing? */
if (tile & DF_DIGIT_MASK) {
+ int color;
+
str[1] = '\0';
str[0] = (tile & DF_DIGIT_MASK) + '0';
+
+ if (tile & DF_ERROR)
+ color = COL_ERROR;
+ else if (tile & DF_CLUE_DONE)
+ color = COL_DONE;
+ else if (x < 0 || y < 0 || x >= w || y >= w)
+ color = COL_GRID;
+ else if (tile & DF_IMMUTABLE)
+ color = COL_GRID;
+ else
+ color = COL_USER;
+
draw_text(dr, tx + TILESIZE/2, ty + TILESIZE/2, FONT_VARIABLE,
(tile & DF_PLAYAREA ? TILESIZE/2 : TILESIZE*2/5),
- ALIGN_VCENTRE | ALIGN_HCENTRE,
- (tile & DF_ERROR) ? COL_ERROR :
- (x < 0 || y < 0 || x >= w || y >= w) ? COL_GRID :
- (tile & DF_IMMUTABLE) ? COL_GRID : COL_USER, str);
+ ALIGN_VCENTRE | ALIGN_HCENTRE, color, str);
} else {
int i, j, npencil;
int pl, pr, pt, pb;
* Determine the bounding rectangle within which we're going
* to put the pencil marks.
*/
- /* Start with the whole square */
- pl = tx;
- pr = pl + TILESIZE;
+ /* Start with the whole square, minus space for impinging towers */
+ pl = tx + (ds->three_d ? X_3D_DISP(w,w) : 0);
+ pr = tx + TILESIZE;
pt = ty;
- pb = pt + TILESIZE;
+ pb = ty + TILESIZE - (ds->three_d ? Y_3D_DISP(w,w) : 0);
/*
* We arrange our pencil marks in a grid layout, with
/*
* Centre the resulting figure in the square.
*/
- pl = tx + (TILESIZE - fontsize * pw) / 2;
- pt = ty + (TILESIZE - fontsize * ph) / 2;
+ pl = pl + (pr - pl - fontsize * pw) / 2;
+ pt = pt + (pb - pt - fontsize * ph) / 2;
/*
* Now actually draw the pencil marks.
}
}
}
-
- unclip(dr);
-
- draw_update(dr, cx, cy, cw, ch);
}
-static void game_redraw(drawing *dr, 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->par.w /*, a = w*w */;
int i, x, y;
*/
draw_rect(dr, 0, 0, SIZE(w), SIZE(w), COL_BACKGROUND);
- /*
- * Big containing rectangle.
- */
- draw_rect(dr, COORD(0), COORD(0),
- w*TILESIZE+1, w*TILESIZE+1,
- COL_GRID);
-
draw_update(dr, 0, 0, SIZE(w), SIZE(w));
ds->started = TRUE;
check_errors(state, ds->errtmp);
/*
- * Draw the clues.
+ * Work out what data each tile should contain.
*/
+ for (i = 0; i < (w+2)*(w+2); i++)
+ ds->tiles[i] = 0; /* completely blank square */
+ /* The clue squares... */
for (i = 0; i < 4*w; i++) {
long tile = state->clues->clues[i];
- if (!tile)
- continue;
-
CLUEPOS(x, y, i, w);
if (ds->errtmp[(y+1)*(w+2)+(x+1)])
tile |= DF_ERROR;
+ else if (state->clues_done[i])
+ tile |= DF_CLUE_DONE;
- if (ds->tiles[(y+1)*(w+2)+(x+1)] != tile) {
- ds->tiles[(y+1)*(w+2)+(x+1)] = tile;
- draw_tile(dr, ds, state->clues, x, y, tile);
- }
+ ds->tiles[(y+1)*(w+2)+(x+1)] = tile;
}
-
- /*
- * Draw the main grid.
- */
+ /* ... and the main grid. */
for (y = 0; y < w; y++) {
for (x = 0; x < w; x++) {
long tile = DF_PLAYAREA;
if (ds->errtmp[(y+1)*(w+2)+(x+1)])
tile |= DF_ERROR;
- if (ds->tiles[(y+1)*(w+2)+(x+1)] != tile) {
- ds->tiles[(y+1)*(w+2)+(x+1)] = tile;
- draw_tile(dr, ds, state->clues, x, y, tile);
+ ds->tiles[(y+1)*(w+2)+(x+1)] = tile;
+ }
+ }
+
+ /*
+ * Now actually draw anything that needs to be changed.
+ */
+ for (y = 0; y < w+2; y++) {
+ for (x = 0; x < w+2; x++) {
+ long tl, tr, bl, br;
+ int i = y*(w+2)+x;
+
+ tr = ds->tiles[y*(w+2)+x];
+ tl = (x == 0 ? 0 : ds->tiles[y*(w+2)+(x-1)]);
+ br = (y == w+1 ? 0 : ds->tiles[(y+1)*(w+2)+x]);
+ bl = (x == 0 || y == w+1 ? 0 : ds->tiles[(y+1)*(w+2)+(x-1)]);
+
+ if (ds->drawn[i*4] != tl || ds->drawn[i*4+1] != tr ||
+ ds->drawn[i*4+2] != bl || ds->drawn[i*4+3] != br) {
+ clip(dr, COORD(x-1), COORD(y-1), TILESIZE, TILESIZE);
+
+ draw_tile(dr, ds, state->clues, x-1, y-1, tr);
+ if (x > 0)
+ draw_tile(dr, ds, state->clues, x-2, y-1, tl);
+ if (y <= w)
+ draw_tile(dr, ds, state->clues, x-1, y, br);
+ if (x > 0 && y <= w)
+ draw_tile(dr, ds, state->clues, x-2, y, bl);
+
+ unclip(dr);
+ draw_update(dr, COORD(x-1), COORD(y-1), TILESIZE, TILESIZE);
+
+ ds->drawn[i*4] = tl;
+ ds->drawn[i*4+1] = tr;
+ ds->drawn[i*4+2] = bl;
+ ds->drawn[i*4+3] = br;
}
}
}
}
-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->cheated && !newstate->cheated)
return 0.0F;
}
-static int game_timing_state(game_state *state, game_ui *ui)
+static int game_status(const game_state *state)
+{
+ return state->completed ? +1 : 0;
+}
+
+static int game_timing_state(const game_state *state, game_ui *ui)
{
if (state->completed)
return FALSE;
return TRUE;
}
-static void game_print_size(game_params *params, float *x, float *y)
+static void game_print_size(const game_params *params, float *x, float *y)
{
int pw, ph;
*y = ph / 100.0F;
}
-static void game_print(drawing *dr, game_state *state, int tilesize)
+static void game_print(drawing *dr, const game_state *state, int tilesize)
{
int w = state->par.w;
int ink = print_mono_colour(dr, 0);
game_redraw,
game_anim_length,
game_flash_length,
+ game_status,
TRUE, FALSE, game_print_size, game_print,
FALSE, /* wants_statusbar */
FALSE, game_timing_state,