X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?a=blobdiff_plain;f=mines.c;h=57f778a168890a7532928614c8890e08ef253a3d;hb=db313b3948d27244dd7c34c2609c66d6204d8931;hp=f6f6a855e38548f2ac48bb18b37d7ca36e305e93;hpb=dfb8fa2e928e86f647373814997ee5123740f427;p=sgt-puzzles.git diff --git a/mines.c b/mines.c index f6f6a85..57f778a 100644 --- a/mines.c +++ b/mines.c @@ -136,7 +136,7 @@ static void free_params(game_params *params) 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 */ @@ -173,7 +173,7 @@ static void decode_params(game_params *params, char const *string) } } -static char *encode_params(game_params *params, int full) +static char *encode_params(const game_params *params, int full) { char ret[400]; int len; @@ -193,7 +193,7 @@ static char *encode_params(game_params *params, int full) 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]; @@ -231,7 +231,7 @@ static config_item *game_configure(game_params *params) 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); @@ -245,7 +245,7 @@ static game_params *custom_params(config_item *cfg) return ret; } -static char *validate_params(game_params *params, int full) +static char *validate_params(const game_params *params, int full) { /* * Lower limit on grid size: each dimension must be at least 3. @@ -1955,7 +1955,7 @@ static char *new_mine_layout(int w, int h, int n, int x, int y, int unique, return grid; } -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) { /* @@ -1994,7 +1994,7 @@ static char *new_game_desc(game_params *params, random_state *rs, } } -static char *validate_desc(game_params *params, char *desc) +static char *validate_desc(const game_params *params, const char *desc) { int wh = params->w * params->h; int x, y; @@ -2167,10 +2167,11 @@ static int open_square(game_state *state, int x, int y) return 0; } -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) { game_state *state = snew(game_state); - int i, wh, x, y, ret, masked; + int i, wh, x, y, masked; unsigned char *bmp; state->w = params->w; @@ -2265,14 +2266,14 @@ static game_state *new_game(midend *me, game_params *params, char *desc) } if (x >= 0 && y >= 0) - ret = open_square(state, x, y); + open_square(state, x, y); sfree(bmp); } return state; } -static game_state *dup_game(game_state *state) +static game_state *dup_game(const game_state *state) { game_state *ret = snew(game_state); @@ -2302,8 +2303,8 @@ static void free_game(game_state *state) 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) { if (!state->layout->mines) { *error = "Game has not been started yet"; @@ -2313,12 +2314,12 @@ static char *solve_game(game_state *state, game_state *currstate, return dupstr("S"); } -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) { char *ret; int x, y; @@ -2354,7 +2355,7 @@ struct game_ui { int cur_x, cur_y, cur_visible; }; -static game_ui *new_ui(game_state *state) +static game_ui *new_ui(const game_state *state) { game_ui *ui = snew(game_ui); ui->hx = ui->hy = -1; @@ -2371,7 +2372,7 @@ static void free_ui(game_ui *ui) sfree(ui); } -static char *encode_ui(game_ui *ui) +static char *encode_ui(const game_ui *ui) { char buf[80]; /* @@ -2384,7 +2385,7 @@ static char *encode_ui(game_ui *ui) return dupstr(buf); } -static void decode_ui(game_ui *ui, char *encoding) +static void decode_ui(game_ui *ui, const char *encoding) { int p= 0; sscanf(encoding, "D%d%n", &ui->deaths, &p); @@ -2392,8 +2393,8 @@ static void decode_ui(game_ui *ui, char *encoding) ui->completed = TRUE; } -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) { if (newstate->won) ui->completed = TRUE; @@ -2415,8 +2416,9 @@ struct game_drawstate { int cur_x, cur_y; /* -1, -1 for no cursor displayed. */ }; -static char *interpret_move(game_state *from, game_ui *ui, game_drawstate *ds, - int x, int y, int button) +static char *interpret_move(const game_state *from, game_ui *ui, + const game_drawstate *ds, + int x, int y, int button) { int cx, cy; char buf[256]; @@ -2590,41 +2592,64 @@ uncover: } } -static game_state *execute_move(game_state *from, char *move) +static game_state *execute_move(const game_state *from, const char *move) { int cy, cx; game_state *ret; if (!strcmp(move, "S")) { - /* - * Simply expose the entire grid as if it were a completed - * solution. - */ int yy, xx; ret = dup_game(from); - for (yy = 0; yy < ret->h; yy++) - for (xx = 0; xx < ret->w; xx++) { - - if (ret->layout->mines[yy*ret->w+xx]) { - ret->grid[yy*ret->w+xx] = -1; - } else { - int dx, dy, v; - - v = 0; - - for (dx = -1; dx <= +1; dx++) - for (dy = -1; dy <= +1; dy++) - if (xx+dx >= 0 && xx+dx < ret->w && - yy+dy >= 0 && yy+dy < ret->h && - ret->layout->mines[(yy+dy)*ret->w+(xx+dx)]) - v++; - - ret->grid[yy*ret->w+xx] = v; - } - } - ret->used_solve = TRUE; - ret->won = TRUE; + if (!ret->dead) { + /* + * If the player is still alive at the moment of pressing + * Solve, expose the entire grid as if it were a completed + * solution. + */ + for (yy = 0; yy < ret->h; yy++) + for (xx = 0; xx < ret->w; xx++) { + + if (ret->layout->mines[yy*ret->w+xx]) { + ret->grid[yy*ret->w+xx] = -1; + } else { + int dx, dy, v; + + v = 0; + + for (dx = -1; dx <= +1; dx++) + for (dy = -1; dy <= +1; dy++) + if (xx+dx >= 0 && xx+dx < ret->w && + yy+dy >= 0 && yy+dy < ret->h && + ret->layout->mines[(yy+dy)*ret->w+(xx+dx)]) + v++; + + ret->grid[yy*ret->w+xx] = v; + } + } + } else { + /* + * If the player pressed Solve _after dying_, show a full + * corrections grid in the style of standard Minesweeper. + * Players who don't like Mines's behaviour on death of + * only showing the mine that killed you (so that in case + * of a typo you can undo and carry on without the rest of + * the grid being spoiled) can use this to get the display + * that ordinary Minesweeper would have given them. + */ + for (yy = 0; yy < ret->h; yy++) + for (xx = 0; xx < ret->w; xx++) { + int pos = yy*ret->w+xx; + if ((ret->grid[pos] == -2 || ret->grid[pos] == -3) && + ret->layout->mines[pos]) { + ret->grid[pos] = 64; + } else if (ret->grid[pos] == -1 && + !ret->layout->mines[pos]) { + ret->grid[pos] = 66; + } + } + } + ret->used_solve = TRUE; return ret; } else { @@ -2668,8 +2693,8 @@ static game_state *execute_move(game_state *from, char *move) * 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) { /* Ick: fake up `ds->tilesize' for macro expansion purposes */ struct { int tilesize; } ads, *ds = &ads; @@ -2680,7 +2705,7 @@ static void game_compute_size(game_params *params, int tilesize, } static void game_set_size(drawing *dr, game_drawstate *ds, - game_params *params, int tilesize) + const game_params *params, int tilesize) { ds->tilesize = tilesize; } @@ -2772,7 +2797,7 @@ static float *game_colours(frontend *fe, int *ncolours) return ret; } -static game_drawstate *game_new_drawstate(drawing *dr, game_state *state) +static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) { struct game_drawstate *ds = snew(struct game_drawstate); @@ -2930,12 +2955,13 @@ static void draw_tile(drawing *dr, game_drawstate *ds, draw_update(dr, x, y, TILE_SIZE, TILE_SIZE); } -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 x, y; - int mines, markers, bg; + int mines, markers, closed, bg; int cx = -1, cy = -1, cmoved; if (flashtime) { @@ -2985,13 +3011,15 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate, /* * Now draw the tiles. Also in this loop, count up the number - * of mines and mine markers. + * of mines, mine markers, and closed squares. */ - mines = markers = 0; + mines = markers = closed = 0; for (y = 0; y < ds->h; y++) for (x = 0; x < ds->w; x++) { int v = state->grid[y*ds->w+x], cc = 0; + if (v < 0) + closed++; if (v == -1) markers++; if (state->layout->mines && state->layout->mines[y*ds->w+x]) @@ -3050,7 +3078,42 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate, else sprintf(statusbar, "COMPLETED!"); } else { + int safe_closed = closed - mines; sprintf(statusbar, "Marked: %d / %d", markers, mines); + if (safe_closed > 0 && safe_closed <= 9) { + /* + * In the situation where there's a very small number + * of _non_-mine squares left unopened, it's helpful + * to mention that number in the status line, to save + * the player from having to count it up + * painstakingly. This is particularly important if + * the player has turned up the mine density to the + * point where game generation resorts to its weird + * pathological fallback of a very dense mine area + * with a clearing in the middle, because that often + * leads to a deduction you can only make by knowing + * that there is (say) exactly one non-mine square to + * find, and it's a real pain to have to count up two + * large numbers of squares and subtract them to get + * that value of 1. + * + * The threshold value of 8 for displaying this + * information is because that's the largest number of + * non-mine squares that might conceivably fit around + * a single central square, and the most likely way to + * _use_ this information is to observe that if all + * the remaining safe squares are adjacent to _this_ + * square then everything else can be immediately + * flagged as a mine. + */ + if (safe_closed == 1) { + sprintf(statusbar + strlen(statusbar), + " (1 safe square remains)"); + } else { + sprintf(statusbar + strlen(statusbar), + " (%d safe squares remain)", safe_closed); + } + } } if (ui->deaths) sprintf(statusbar + strlen(statusbar), @@ -3059,14 +3122,14 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate, } } -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->used_solve || newstate->used_solve) return 0.0F; @@ -3084,18 +3147,28 @@ static float game_flash_length(game_state *oldstate, game_state *newstate, return 0.0F; } -static int game_timing_state(game_state *state, game_ui *ui) +static int game_status(const game_state *state) +{ + /* + * We report the game as lost only if the player has used the + * Solve function to reveal all the mines. Otherwise, we assume + * they'll undo and continue play. + */ + return state->won ? (state->used_solve ? -1 : +1) : 0; +} + +static int game_timing_state(const game_state *state, game_ui *ui) { if (state->dead || state->won || ui->completed || !state->layout->mines) 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) { } -static void game_print(drawing *dr, game_state *state, int tilesize) +static void game_print(drawing *dr, const game_state *state, int tilesize) { } @@ -3106,7 +3179,7 @@ static void game_print(drawing *dr, game_state *state, int tilesize) const struct game thegame = { "Mines", "games.mines", "mines", default_params, - game_fetch_preset, + game_fetch_preset, NULL, decode_params, encode_params, free_params, @@ -3134,6 +3207,7 @@ const struct game thegame = { game_redraw, game_anim_length, game_flash_length, + game_status, FALSE, FALSE, game_print_size, game_print, TRUE, /* wants_statusbar */ TRUE, game_timing_state,