COL_1, COL_2, COL_3, COL_4, COL_5, COL_6, COL_7, COL_8,
COL_MINE, COL_BANG, COL_CROSS, COL_FLAG, COL_FLAGBASE, COL_QUERY,
COL_HIGHLIGHT, COL_LOWLIGHT,
+ COL_WRONGNUMBER,
NCOLOURS
};
*/
int n, unique;
random_state *rs;
- midend_data *me; /* to give back the new game desc */
+ midend *me; /* to give back the new game desc */
};
struct game_state {
int w, h, n, dead, won;
- int used_solve, just_used_solve;
+ int used_solve;
struct mine_layout *layout; /* real mine positions */
signed char *grid; /* player knowledge */
/*
return ret;
}
-static char *validate_params(game_params *params)
+static char *validate_params(game_params *params, int full)
{
/*
* Lower limit on grid size: each dimension must be at least 3.
* _have_ to have a gap somewhere which you can't determine the
* position of.
*/
- if (params->w <= 2 || params->h <= 2)
+ if (full && params->unique && (params->w <= 2 || params->h <= 2))
return "Width and height must both be greater than two";
if (params->n > params->w * params->h - 9)
return "Too many mines for grid size";
/*
* Count the bits in a word. Only needs to cope with 16 bits.
*/
-static int bitcount16(int word)
+static int bitcount16(int inword)
{
+ unsigned int word = inword;
+
word = ((word & 0xAAAA) >> 1) + (word & 0x5555);
word = ((word & 0xCCCC) >> 2) + (word & 0x3333);
word = ((word & 0xF0F0) >> 4) + (word & 0x0F0F);
word = ((word & 0xFF00) >> 8) + (word & 0x00FF);
- return word;
+ return (int)word;
}
/*
}
static char *new_game_desc(game_params *params, random_state *rs,
- game_aux_info **aux, int interactive)
+ char **aux, int interactive)
{
/*
* We generate the coordinates of an initial click even if they
}
}
-static void game_free_aux_info(game_aux_info *aux)
-{
- assert(!"Shouldn't happen");
-}
-
static char *validate_desc(game_params *params, char *desc)
{
int wh = params->w * params->h;
int x, y;
if (*desc == 'r') {
+ desc++;
if (!*desc || !isdigit((unsigned char)*desc))
return "No initial mine count in game description";
while (*desc && isdigit((unsigned char)*desc))
return 0;
}
-static game_state *new_game(midend_data *me, game_params *params, char *desc)
+static game_state *new_game(midend *me, game_params *params, char *desc)
{
game_state *state = snew(game_state);
int i, wh, x, y, ret, masked;
state->h = params->h;
state->n = params->n;
state->dead = state->won = FALSE;
- state->used_solve = state->just_used_solve = FALSE;
+ state->used_solve = FALSE;
wh = state->w * state->h;
ret->dead = state->dead;
ret->won = state->won;
ret->used_solve = state->used_solve;
- ret->just_used_solve = state->just_used_solve;
ret->layout = state->layout;
ret->layout->refcount++;
ret->grid = snewn(ret->w * ret->h, signed char);
}
static char *solve_game(game_state *state, game_state *currstate,
- game_aux_info *aux, char **error)
+ char *aux, char **error)
{
if (!state->layout->mines) {
*error = "Game has not been started yet";
struct game_ui {
int hx, hy, hradius; /* for mouse-down highlights */
+ int validradius;
int flash_is_death;
- int deaths;
+ int deaths, completed;
};
static game_ui *new_ui(game_state *state)
{
game_ui *ui = snew(game_ui);
ui->hx = ui->hy = -1;
- ui->hradius = 0;
+ ui->hradius = ui->validradius = 0;
ui->deaths = 0;
+ ui->completed = FALSE;
ui->flash_is_death = FALSE; /* *shrug* */
return ui;
}
sfree(ui);
}
-char *encode_ui(game_ui *ui)
+static char *encode_ui(game_ui *ui)
{
char buf[80];
/*
- * The deaths counter needs preserving across a serialisation.
+ * The deaths counter and completion status need preserving
+ * across a serialisation.
*/
sprintf(buf, "D%d", ui->deaths);
+ if (ui->completed)
+ strcat(buf, "C");
return dupstr(buf);
}
-void decode_ui(game_ui *ui, char *encoding)
+static void decode_ui(game_ui *ui, char *encoding)
{
- sscanf(encoding, "D%d", &ui->deaths);
+ int p= 0;
+ sscanf(encoding, "D%d%n", &ui->deaths, &p);
+ if (encoding[p] == 'C')
+ ui->completed = TRUE;
}
static void game_changed_state(game_ui *ui, game_state *oldstate,
game_state *newstate)
{
+ if (newstate->won)
+ ui->completed = TRUE;
}
struct game_drawstate {
- int w, h, started, tilesize;
+ int w, h, started, tilesize, bg;
signed char *grid;
/*
* Items in this `grid' array have all the same values as in
ui->hx = cx;
ui->hy = cy;
ui->hradius = (from->grid[cy*from->w+cx] >= 0 ? 1 : 0);
+ if (button == LEFT_BUTTON)
+ ui->validradius = ui->hradius;
+ else if (button == MIDDLE_BUTTON)
+ ui->validradius = 1;
return "";
}
*/
if (button == LEFT_RELEASE &&
(from->grid[cy * from->w + cx] == -2 ||
- from->grid[cy * from->w + cx] == -3)) {
+ from->grid[cy * from->w + cx] == -3) &&
+ ui->validradius == 0) {
/* Check if you've killed yourself. */
if (from->layout->mines && from->layout->mines[cy * from->w + cx])
ui->deaths++;
* surrounding the tile is equal to its mine count, and if
* so then we open all other surrounding squares.
*/
- if (from->grid[cy * from->w + cx] > 0) {
+ if (from->grid[cy * from->w + cx] > 0 && ui->validradius == 1) {
int dy, dx, n;
/* Count mine markers. */
ret->grid[yy*ret->w+xx] = v;
}
}
- ret->used_solve = ret->just_used_solve = TRUE;
+ ret->used_solve = TRUE;
ret->won = TRUE;
return ret;
} else {
ret = dup_game(from);
- ret->just_used_solve = FALSE;
while (*move) {
if (move[0] == 'F' &&
* Drawing routines.
*/
-static void game_size(game_params *params, game_drawstate *ds,
- int *x, int *y, int expand)
+static void game_compute_size(game_params *params, int tilesize,
+ int *x, int *y)
{
- int tsx, tsy, ts;
- /*
- * 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);
+ /* Ick: fake up `ds->tilesize' for macro expansion purposes */
+ struct { int tilesize; } ads, *ds = &ads;
+ ads.tilesize = tilesize;
*x = BORDER * 2 + TILE_SIZE * params->w;
*y = BORDER * 2 + TILE_SIZE * params->h;
}
-static float *game_colours(frontend *fe, game_state *state, int *ncolours)
+static void game_set_size(drawing *dr, game_drawstate *ds,
+ game_params *params, int tilesize)
+{
+ ds->tilesize = tilesize;
+}
+
+static float *game_colours(frontend *fe, int *ncolours)
{
float *ret = snewn(3 * NCOLOURS, float);
ret[COL_LOWLIGHT * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 2.0 / 3.0;
ret[COL_LOWLIGHT * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] * 2.0 / 3.0;
+ ret[COL_WRONGNUMBER * 3 + 0] = 1.0F;
+ ret[COL_WRONGNUMBER * 3 + 1] = 0.6F;
+ ret[COL_WRONGNUMBER * 3 + 2] = 0.6F;
+
*ncolours = NCOLOURS;
return ret;
}
-static game_drawstate *game_new_drawstate(game_state *state)
+static game_drawstate *game_new_drawstate(drawing *dr, game_state *state)
{
struct game_drawstate *ds = snew(struct game_drawstate);
ds->started = FALSE;
ds->tilesize = 0; /* not decided yet */
ds->grid = snewn(ds->w * ds->h, signed char);
+ ds->bg = -1;
memset(ds->grid, -99, ds->w * ds->h);
return ds;
}
-static void game_free_drawstate(game_drawstate *ds)
+static void game_free_drawstate(drawing *dr, game_drawstate *ds)
{
sfree(ds->grid);
sfree(ds);
}
-static void draw_tile(frontend *fe, game_drawstate *ds,
+static void draw_tile(drawing *dr, game_drawstate *ds,
int x, int y, int v, int bg)
{
if (v < 0) {
/*
* Omit the highlights in this case.
*/
- draw_rect(fe, x, y, TILE_SIZE, TILE_SIZE,
+ draw_rect(dr, x, y, TILE_SIZE, TILE_SIZE,
bg == COL_BACKGROUND ? COL_BACKGROUND2 : bg);
- draw_line(fe, x, y, x + TILE_SIZE - 1, y, COL_LOWLIGHT);
- draw_line(fe, x, y, x, y + TILE_SIZE - 1, COL_LOWLIGHT);
+ draw_line(dr, x, y, x + TILE_SIZE - 1, y, COL_LOWLIGHT);
+ draw_line(dr, x, y, x, y + TILE_SIZE - 1, COL_LOWLIGHT);
} else {
/*
* Draw highlights to indicate the square is covered.
coords[3] = y;
coords[4] = x;
coords[5] = y + TILE_SIZE - 1;
- draw_polygon(fe, coords, 3, TRUE, COL_LOWLIGHT ^ hl);
- draw_polygon(fe, coords, 3, FALSE, COL_LOWLIGHT ^ hl);
+ draw_polygon(dr, coords, 3, COL_LOWLIGHT ^ hl, COL_LOWLIGHT ^ hl);
coords[0] = x;
coords[1] = y;
- draw_polygon(fe, coords, 3, TRUE, COL_HIGHLIGHT ^ hl);
- draw_polygon(fe, coords, 3, FALSE, COL_HIGHLIGHT ^ hl);
+ draw_polygon(dr, coords, 3, COL_HIGHLIGHT ^ hl,
+ COL_HIGHLIGHT ^ hl);
- draw_rect(fe, x + HIGHLIGHT_WIDTH, y + HIGHLIGHT_WIDTH,
+ draw_rect(dr, x + HIGHLIGHT_WIDTH, y + HIGHLIGHT_WIDTH,
TILE_SIZE - 2*HIGHLIGHT_WIDTH, TILE_SIZE - 2*HIGHLIGHT_WIDTH,
bg);
}
SETCOORD(3, 0.25, 0.8);
SETCOORD(4, 0.55, 0.7);
SETCOORD(5, 0.55, 0.35);
- draw_polygon(fe, coords, 6, TRUE, COL_FLAGBASE);
- draw_polygon(fe, coords, 6, FALSE, COL_FLAGBASE);
+ draw_polygon(dr, coords, 6, COL_FLAGBASE, COL_FLAGBASE);
SETCOORD(0, 0.6, 0.2);
SETCOORD(1, 0.6, 0.5);
SETCOORD(2, 0.2, 0.35);
- draw_polygon(fe, coords, 3, TRUE, COL_FLAG);
- draw_polygon(fe, coords, 3, FALSE, COL_FLAG);
+ draw_polygon(dr, coords, 3, COL_FLAG, COL_FLAG);
#undef SETCOORD
} else if (v == -3) {
/*
* Draw a question mark.
*/
- draw_text(fe, x + TILE_SIZE / 2, y + TILE_SIZE / 2,
+ draw_text(dr, x + TILE_SIZE / 2, y + TILE_SIZE / 2,
FONT_VARIABLE, TILE_SIZE * 6 / 8,
ALIGN_VCENTRE | ALIGN_HCENTRE,
COL_QUERY, "?");
* Exception is that for value 65 (mine we've just trodden
* on), we clear the square to COL_BANG.
*/
- draw_rect(fe, x, y, TILE_SIZE, TILE_SIZE,
+ if (v & 32) {
+ bg = COL_WRONGNUMBER;
+ v &= ~32;
+ }
+ draw_rect(dr, x, y, TILE_SIZE, TILE_SIZE,
(v == 65 ? COL_BANG :
bg == COL_BACKGROUND ? COL_BACKGROUND2 : bg));
- draw_line(fe, x, y, x + TILE_SIZE - 1, y, COL_LOWLIGHT);
- draw_line(fe, x, y, x, y + TILE_SIZE - 1, COL_LOWLIGHT);
+ draw_line(dr, x, y, x + TILE_SIZE - 1, y, COL_LOWLIGHT);
+ draw_line(dr, x, y, x, y + TILE_SIZE - 1, COL_LOWLIGHT);
if (v > 0 && v <= 8) {
/*
char str[2];
str[0] = v + '0';
str[1] = '\0';
- draw_text(fe, x + TILE_SIZE / 2, y + TILE_SIZE / 2,
+ draw_text(dr, x + TILE_SIZE / 2, y + TILE_SIZE / 2,
FONT_VARIABLE, TILE_SIZE * 7 / 8,
ALIGN_VCENTRE | ALIGN_HCENTRE,
(COL_1 - 1) + v, str);
* FIXME: this could be done better!
*/
#if 0
- draw_text(fe, x + TILE_SIZE / 2, y + TILE_SIZE / 2,
+ draw_text(dr, x + TILE_SIZE / 2, y + TILE_SIZE / 2,
FONT_VARIABLE, TILE_SIZE * 7 / 8,
ALIGN_VCENTRE | ALIGN_HCENTRE,
COL_MINE, "*");
xdy = -tdy;
}
- draw_polygon(fe, coords, 5*4, TRUE, COL_MINE);
- draw_polygon(fe, coords, 5*4, FALSE, COL_MINE);
+ draw_polygon(dr, coords, 5*4, COL_MINE, COL_MINE);
- draw_rect(fe, cx-r/3, cy-r/3, r/3, r/4, COL_HIGHLIGHT);
+ draw_rect(dr, cx-r/3, cy-r/3, r/3, r/4, COL_HIGHLIGHT);
}
#endif
*/
int dx;
for (dx = -1; dx <= +1; dx++) {
- draw_line(fe, x + 3 + dx, y + 2,
+ draw_line(dr, x + 3 + dx, y + 2,
x + TILE_SIZE - 3 + dx,
y + TILE_SIZE - 2, COL_CROSS);
- draw_line(fe, x + TILE_SIZE - 3 + dx, y + 2,
+ draw_line(dr, x + TILE_SIZE - 3 + dx, y + 2,
x + 3 + dx, y + TILE_SIZE - 2,
COL_CROSS);
}
}
}
- draw_update(fe, x, y, TILE_SIZE, TILE_SIZE);
+ draw_update(dr, x, y, TILE_SIZE, TILE_SIZE);
}
-static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
+static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
game_state *state, int dir, game_ui *ui,
float animtime, float flashtime)
{
if (!ds->started) {
int coords[10];
- draw_rect(fe, 0, 0,
+ draw_rect(dr, 0, 0,
TILE_SIZE * state->w + 2 * BORDER,
TILE_SIZE * state->h + 2 * BORDER, COL_BACKGROUND);
- draw_update(fe, 0, 0,
+ draw_update(dr, 0, 0,
TILE_SIZE * state->w + 2 * BORDER,
TILE_SIZE * state->h + 2 * BORDER);
coords[9] = COORD(state->h) + OUTER_HIGHLIGHT_WIDTH - 1;
coords[6] = coords[8] + TILE_SIZE;
coords[7] = coords[9] - TILE_SIZE;
- draw_polygon(fe, coords, 5, TRUE, COL_HIGHLIGHT);
- draw_polygon(fe, coords, 5, FALSE, COL_HIGHLIGHT);
+ draw_polygon(dr, coords, 5, COL_HIGHLIGHT, COL_HIGHLIGHT);
coords[1] = COORD(0) - OUTER_HIGHLIGHT_WIDTH;
coords[0] = COORD(0) - OUTER_HIGHLIGHT_WIDTH;
- draw_polygon(fe, coords, 5, TRUE, COL_LOWLIGHT);
- draw_polygon(fe, coords, 5, FALSE, COL_LOWLIGHT);
+ draw_polygon(dr, coords, 5, COL_LOWLIGHT, COL_LOWLIGHT);
ds->started = TRUE;
}
if (state->layout->mines && state->layout->mines[y*ds->w+x])
mines++;
+ if (v >= 0 && v <= 8) {
+ /*
+ * Count up the flags around this tile, and if
+ * there are too _many_, highlight the tile.
+ */
+ int dx, dy, flags = 0;
+
+ for (dy = -1; dy <= +1; dy++)
+ for (dx = -1; dx <= +1; dx++) {
+ int nx = x+dx, ny = y+dy;
+ if (nx >= 0 && nx < ds->w &&
+ ny >= 0 && ny < ds->h &&
+ state->grid[ny*ds->w+nx] == -1)
+ flags++;
+ }
+
+ if (flags > v)
+ v |= 32;
+ }
+
if ((v == -2 || v == -3) &&
(abs(x-ui->hx) <= ui->hradius && abs(y-ui->hy) <= ui->hradius))
v -= 20;
- if (ds->grid[y*ds->w+x] != v || bg != COL_BACKGROUND) {
- draw_tile(fe, ds, COORD(x), COORD(y), v, bg);
- ds->grid[y*ds->w+x] = (bg == COL_BACKGROUND ? v : -10);
+ if (ds->grid[y*ds->w+x] != v || bg != ds->bg) {
+ draw_tile(dr, ds, COORD(x), COORD(y), v, bg);
+ ds->grid[y*ds->w+x] = v;
}
}
+ ds->bg = bg;
if (!state->layout->mines)
mines = state->layout->n;
if (ui->deaths)
sprintf(statusbar + strlen(statusbar),
" Deaths: %d", ui->deaths);
- status_bar(fe, statusbar);
+ status_bar(dr, statusbar);
}
}
return 0.0F;
}
-static int game_wants_statusbar(void)
+static int game_timing_state(game_state *state, game_ui *ui)
{
+ if (state->dead || state->won || ui->completed || !state->layout->mines)
+ return FALSE;
return TRUE;
}
-static int game_timing_state(game_state *state)
+static void game_print_size(game_params *params, float *x, float *y)
+{
+}
+
+static void game_print(drawing *dr, game_state *state, int tilesize)
{
- if (state->dead || state->won || !state->layout->mines)
- return FALSE;
- return TRUE;
}
#ifdef COMBINED
#endif
const struct game thegame = {
- "Mines", "games.mines",
+ "Mines", "games.mines", "mines",
default_params,
game_fetch_preset,
decode_params,
TRUE, game_configure, custom_params,
validate_params,
new_game_desc,
- game_free_aux_info,
validate_desc,
new_game,
dup_game,
game_changed_state,
interpret_move,
execute_move,
- game_size,
+ PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
game_colours,
game_new_drawstate,
game_free_drawstate,
game_redraw,
game_anim_length,
game_flash_length,
- game_wants_statusbar,
+ FALSE, FALSE, game_print_size, game_print,
+ TRUE, /* wants_statusbar */
TRUE, game_timing_state,
BUTTON_BEATS(LEFT_BUTTON, RIGHT_BUTTON),
};
* 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]);
+ fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p);
return 1;
} else {
id = p;