NCOLOURS
};
-#define TILE_SIZE 20
+#define PREFERRED_TILE_SIZE 20
+#define TILE_SIZE (ds->tilesize)
#define BORDER (TILE_SIZE * 3 / 2)
-#define HIGHLIGHT_WIDTH 2
-#define OUTER_HIGHLIGHT_WIDTH 3
+#define HIGHLIGHT_WIDTH (TILE_SIZE / 10)
+#define OUTER_HIGHLIGHT_WIDTH (BORDER / 10)
#define COORD(x) ( (x) * TILE_SIZE + BORDER )
#define FROMCOORD(x) ( ((x) - BORDER + TILE_SIZE) / TILE_SIZE - 1 )
return ret;
}
+static const struct game_params mines_presets[] = {
+ {9, 9, 10, TRUE},
+ {9, 9, 35, TRUE},
+ {16, 16, 40, TRUE},
+ {16, 16, 99, TRUE},
+ {30, 16, 99, TRUE},
+ {30, 16, 170, TRUE},
+};
+
static int game_fetch_preset(int i, char **name, game_params **params)
{
game_params *ret;
char str[80];
- static const struct { int w, h, n; } values[] = {
- {9, 9, 10},
- {16, 16, 40},
- {30, 16, 99},
- };
- if (i < 0 || i >= lenof(values))
+ if (i < 0 || i >= lenof(mines_presets))
return FALSE;
ret = snew(game_params);
- ret->w = values[i].w;
- ret->h = values[i].h;
- ret->n = values[i].n;
- ret->unique = TRUE;
+ *ret = mines_presets[i];
sprintf(str, "%dx%d, %d mines", ret->w, ret->h, ret->n);
std->next[i] = -1;
}
+typedef int (*open_cb)(void *, int, int);
+
static void known_squares(int w, int h, struct squaretodo *std,
- signed char *grid,
- int (*open)(void *ctx, int x, int y), void *openctx,
+ signed char *grid,
+ open_cb open, void *openctx,
int x, int y, int mask, int mine)
{
int xx, yy, bit;
* steps were required; the exact return value is the number of
* perturb calls.
*/
+
+typedef struct perturbations *(*perturb_cb) (void *, signed char *, int, int, int);
+
static int minesolve(int w, int h, int n, signed char *grid,
- int (*open)(void *ctx, int x, int y),
- struct perturbations *(*perturb)(void *ctx,
- signed char *grid,
- int x, int y, int mask),
+ open_cb open,
+ perturb_cb perturb,
void *ctx, random_state *rs)
{
struct setstore *ss = ss_new();
*/
struct minectx {
- signed char *grid;
+ char *grid;
int w, h;
int sx, sy;
int allow_big_perturbs;
* We bypass this bit if we're not after a unique grid.
*/
if (unique) {
- signed char *solvegrid = snewn(w*h, char);
+ signed char *solvegrid = snewn(w*h, signed char);
struct minectx actx, *ctx = &actx;
int solveret, prevret = -2;
}
}
+static char *describe_layout(char *grid, int area, int x, int y,
+ int obfuscate)
+{
+ char *ret, *p;
+ unsigned char *bmp;
+ int i;
+
+ /*
+ * Set up the mine bitmap and obfuscate it.
+ */
+ bmp = snewn((area + 7) / 8, unsigned char);
+ memset(bmp, 0, (area + 7) / 8);
+ for (i = 0; i < area; i++) {
+ if (grid[i])
+ bmp[i / 8] |= 0x80 >> (i % 8);
+ }
+ if (obfuscate)
+ obfuscate_bitmap(bmp, area, FALSE);
+
+ /*
+ * Now encode the resulting bitmap in hex. We can work to
+ * nibble rather than byte granularity, since the obfuscation
+ * function guarantees to return a bit string of the same
+ * length as its input.
+ */
+ ret = snewn((area+3)/4 + 100, char);
+ p = ret + sprintf(ret, "%d,%d,%s", x, y,
+ obfuscate ? "m" : ""); /* 'm' == masked */
+ for (i = 0; i < (area+3)/4; i++) {
+ int v = bmp[i/2];
+ if (i % 2 == 0)
+ v >>= 4;
+ *p++ = "0123456789abcdef"[v & 0xF];
+ }
+ *p = '\0';
+
+ sfree(bmp);
+
+ return ret;
+}
+
static char *new_mine_layout(int w, int h, int n, int x, int y, int unique,
random_state *rs, char **game_desc)
{
- signed char *grid, *ret, *p;
- unsigned char *bmp;
- int i, area;
+ char *grid;
#ifdef TEST_OBFUSCATION
static int tested_obfuscation = FALSE;
grid = minegen(w, h, n, x, y, unique, rs);
- if (game_desc) {
- /*
- * Set up the mine bitmap and obfuscate it.
- */
- area = w * h;
- bmp = snewn((area + 7) / 8, unsigned char);
- memset(bmp, 0, (area + 7) / 8);
- for (i = 0; i < area; i++) {
- if (grid[i])
- bmp[i / 8] |= 0x80 >> (i % 8);
- }
- obfuscate_bitmap(bmp, area, FALSE);
-
- /*
- * Now encode the resulting bitmap in hex. We can work to
- * nibble rather than byte granularity, since the obfuscation
- * function guarantees to return a bit string of the same
- * length as its input.
- */
- ret = snewn((area+3)/4 + 100, char);
- p = ret + sprintf(ret, "%d,%d,m", x, y); /* 'm' == masked */
- for (i = 0; i < (area+3)/4; i++) {
- int v = bmp[i/2];
- if (i % 2 == 0)
- v >>= 4;
- *p++ = "0123456789abcdef"[v & 0xF];
- }
- *p = '\0';
-
- sfree(bmp);
-
- *game_desc = ret;
- }
+ if (game_desc)
+ *game_desc = describe_layout(grid, w * h, x, y, TRUE);
return grid;
}
static char *new_game_desc(game_params *params, random_state *rs,
game_aux_info **aux, int interactive)
{
+ /*
+ * We generate the coordinates of an initial click even if they
+ * aren't actually used. This has the effect of harmonising the
+ * random number usage between interactive and batch use: if
+ * you use `mines --generate' with an explicit random seed, you
+ * should get exactly the same results as if you type the same
+ * random seed into the interactive game and click in the same
+ * initial location. (Of course you won't get the same grid if
+ * you click in a _different_ initial location, but there's
+ * nothing to be done about that.)
+ */
+ int x = random_upto(rs, params->w);
+ int y = random_upto(rs, params->h);
+
if (!interactive) {
/*
* For batch-generated grids, pre-open one square.
*/
- int x = random_upto(rs, params->w);
- int y = random_upto(rs, params->h);
- signed char *grid;
+ char *grid;
char *desc;
grid = new_mine_layout(params->w, params->h, params->n,
rsdesc = random_state_encode(rs);
desc = snewn(strlen(rsdesc) + 100, char);
- sprintf(desc, "r%d,%c,%s", params->n, params->unique ? 'u' : 'a', rsdesc);
+ sprintf(desc, "r%d,%c,%s", params->n, (char)(params->unique ? 'u' : 'a'), rsdesc);
sfree(rsdesc);
return desc;
}
wh = state->w * state->h;
state->layout = snew(struct mine_layout);
+ memset(state->layout, 0, sizeof(struct mine_layout));
state->layout->refcount = 1;
- state->grid = snewn(wh, char);
+ state->grid = snewn(wh, signed char);
memset(state->grid, -2, wh);
if (*desc == 'r') {
}
ret = open_square(state, x, y);
+ sfree(bmp);
}
return state;
ret->just_used_solve = state->just_used_solve;
ret->layout = state->layout;
ret->layout->refcount++;
- ret->grid = snewn(ret->w * ret->h, char);
+ ret->grid = snewn(ret->w * ret->h, signed char);
memcpy(ret->grid, state->grid, ret->w * ret->h);
return ret;
sfree(state);
}
-static game_state *solve_game(game_state *state, game_aux_info *aux,
- char **error)
+static game_state *solve_game(game_state *state, game_state *currstate,
+ game_aux_info *aux, char **error)
{
/*
* Simply expose the entire grid as if it were a completed
sfree(ui);
}
+static void game_changed_state(game_ui *ui, game_state *oldstate,
+ game_state *newstate)
+{
+}
+
+struct game_drawstate {
+ int w, h, started, tilesize;
+ signed char *grid;
+ /*
+ * Items in this `grid' array have all the same values as in
+ * the game_state grid, and in addition:
+ *
+ * - -10 means the tile was drawn `specially' as a result of a
+ * flash, so it will always need redrawing.
+ *
+ * - -22 and -23 mean the tile is highlighted for a possible
+ * click.
+ */
+};
+
static game_state *make_move(game_state *from, game_ui *ui, game_drawstate *ds,
int x, int y, int button)
{
cx = FROMCOORD(x);
cy = FROMCOORD(y);
- if (cx < 0 || cx >= from->w || cy < 0 || cy >= from->h)
- return NULL;
if (button == LEFT_BUTTON || button == LEFT_DRAG ||
button == MIDDLE_BUTTON || button == MIDDLE_DRAG) {
+ if (cx < 0 || cx >= from->w || cy < 0 || cy >= from->h)
+ return NULL;
+
/*
* Mouse-downs and mouse-drags just cause highlighting
* updates.
}
if (button == RIGHT_BUTTON) {
+ if (cx < 0 || cx >= from->w || cy < 0 || cy >= from->h)
+ return NULL;
+
/*
* Right-clicking only works on a covered square, and it
* toggles between -1 (marked as mine) and -2 (not marked
* At this stage we must never return NULL: we have adjusted
* the ui, so at worst we return `from'.
*/
+ if (cx < 0 || cx >= from->w || cy < 0 || cy >= from->h)
+ return from;
/*
* Left-clicking on a covered square opens a tile. Not
* Drawing routines.
*/
-struct game_drawstate {
- int w, h, started;
- signed char *grid;
+static void game_size(game_params *params, game_drawstate *ds,
+ int *x, int *y, int expand)
+{
+ int tsx, tsy, ts;
/*
- * Items in this `grid' array have all the same values as in
- * the game_state grid, and in addition:
- *
- * - -10 means the tile was drawn `specially' as a result of a
- * flash, so it will always need redrawing.
- *
- * - -22 and -23 mean the tile is highlighted for a possible
- * click.
+ * Each window dimension equals the tile size times 3 more than
+ * the grid dimension (the border is 3/2 the width of the
+ * tiles).
*/
-};
+ tsx = *x / (params->w + 3);
+ tsy = *y / (params->h + 3);
+ ts = min(tsx, tsy);
+ if (expand)
+ ds->tilesize = ts;
+ else
+ ds->tilesize = min(ts, PREFERRED_TILE_SIZE);
-static void game_size(game_params *params, int *x, int *y)
-{
*x = BORDER * 2 + TILE_SIZE * params->w;
*y = BORDER * 2 + TILE_SIZE * params->h;
}
ds->w = state->w;
ds->h = state->h;
ds->started = FALSE;
- ds->grid = snewn(ds->w * ds->h, char);
+ ds->tilesize = 0; /* not decided yet */
+ ds->grid = snewn(ds->w * ds->h, signed char);
memset(ds->grid, -99, ds->w * ds->h);
sfree(ds);
}
-static void draw_tile(frontend *fe, int x, int y, int v, int bg)
+static void draw_tile(frontend *fe, game_drawstate *ds,
+ int x, int y, int v, int bg)
{
if (v < 0) {
int coords[12];
v -= 20;
if (ds->grid[y*ds->w+x] != v || bg != COL_BACKGROUND) {
- draw_tile(fe, COORD(x), COORD(y), v, bg);
+ draw_tile(fe, ds, COORD(x), COORD(y), v, bg);
ds->grid[y*ds->w+x] = (bg == COL_BACKGROUND ? v : -10);
}
}
TRUE, game_text_format,
new_ui,
free_ui,
+ game_changed_state,
make_move,
game_size,
game_colours,
TRUE, game_timing_state,
BUTTON_BEATS(LEFT_BUTTON, RIGHT_BUTTON),
};
+
+#ifdef STANDALONE_OBFUSCATOR
+
+/*
+ * Vaguely useful stand-alone program which translates between
+ * obfuscated and clear Mines game descriptions. Pass in a game
+ * description on the command line, and if it's clear it will be
+ * obfuscated and vice versa. The output text should also be a
+ * valid game ID describing the same game. Like this:
+ *
+ * $ ./mineobfusc 9x9:4,4,mb071b49fbd1cb6a0d5868
+ * 9x9:4,4,004000007c00010022080
+ * $ ./mineobfusc 9x9:4,4,004000007c00010022080
+ * 9x9:4,4,mb071b49fbd1cb6a0d5868
+ *
+ * gcc -DSTANDALONE_OBFUSCATOR -o mineobfusc mines.c malloc.c random.c tree234.c
+ */
+
+#include <stdarg.h>
+
+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 fill, int colour) {}
+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) {}
+void midend_supersede_game_desc(midend_data *me, char *desc) {}
+void status_bar(frontend *fe, char *text) {}
+
+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;
+ int recurse = TRUE;
+ char *id = NULL, *desc, *err;
+ int y, x;
+ int grade = FALSE;
+
+ while (--argc > 0) {
+ char *p = *++argv;
+ if (*p == '-') {
+ fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0]);
+ return 1;
+ } else {
+ id = p;
+ }
+ }
+
+ if (!id) {
+ fprintf(stderr, "usage: %s <game_id>\n", argv[0]);
+ return 1;
+ }
+
+ desc = strchr(id, ':');
+ if (!desc) {
+ fprintf(stderr, "%s: game id expects a colon in it\n", argv[0]);
+ return 1;
+ }
+ *desc++ = '\0';
+
+ p = default_params();
+ decode_params(p, id);
+ err = validate_desc(p, desc);
+ if (err) {
+ fprintf(stderr, "%s: %s\n", argv[0], err);
+ return 1;
+ }
+ s = new_game(NULL, p, desc);
+
+ x = atoi(desc);
+ while (*desc && *desc != ',') desc++;
+ if (*desc) desc++;
+ y = atoi(desc);
+ while (*desc && *desc != ',') desc++;
+ if (*desc) desc++;
+
+ printf("%s:%s\n", id, describe_layout(s->layout->mines,
+ p->w * p->h,
+ x, y,
+ (*desc != 'm')));
+
+ return 0;
+}
+
+#endif