chiark / gitweb /
Fix borders on the HTML menu bar.
[sgt-puzzles.git] / mines.c
diff --git a/mines.c b/mines.c
index abd1ad8e61ccf34dc99d302920a8b817858657dc..57f778a168890a7532928614c8890e08ef253a3d 100644 (file)
--- 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,7 +2167,8 @@ 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, masked;
@@ -2272,7 +2273,7 @@ static game_state *new_game(midend *me, game_params *params, char *desc)
     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, const 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,7 +2592,7 @@ 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;
@@ -2691,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;
@@ -2703,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;
 }
@@ -2795,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);
 
@@ -2953,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) {
@@ -3008,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])
@@ -3073,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),
@@ -3082,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;
@@ -3107,7 +3147,7 @@ static float game_flash_length(game_state *oldstate, game_state *newstate,
     return 0.0F;
 }
 
-static int game_status(game_state *state)
+static int game_status(const game_state *state)
 {
     /*
      * We report the game as lost only if the player has used the
@@ -3117,18 +3157,18 @@ static int game_status(game_state *state)
     return state->won ? (state->used_solve ? -1 : +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)
 {
     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)
 {
 }
 
@@ -3139,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,