Doc tweaks for Solo.
[sgt/puzzles] / pattern.c
index d597127..4fc1562 100644 (file)
--- a/pattern.c
+++ b/pattern.c
 #define max(x,y) ( (x)>(y) ? (x):(y) )
 #define min(x,y) ( (x)<(y) ? (x):(y) )
 
-const char *const game_name = "Pattern";
-const char *const game_winhelp_topic = "games.pattern";
-const int game_can_configure = TRUE;
-
 enum {
     COL_BACKGROUND,
     COL_EMPTY,
@@ -61,7 +57,7 @@ struct game_state {
 
 #define FLASH_TIME 0.13F
 
-game_params *default_params(void)
+static game_params *default_params(void)
 {
     game_params *ret = snew(game_params);
 
@@ -70,7 +66,7 @@ game_params *default_params(void)
     return ret;
 }
 
-int game_fetch_preset(int i, char **name, game_params **params)
+static int game_fetch_preset(int i, char **name, game_params **params)
 {
     game_params *ret;
     char str[80];
@@ -96,19 +92,19 @@ int game_fetch_preset(int i, char **name, game_params **params)
     return TRUE;
 }
 
-void free_params(game_params *params)
+static void free_params(game_params *params)
 {
     sfree(params);
 }
 
-game_params *dup_params(game_params *params)
+static game_params *dup_params(game_params *params)
 {
     game_params *ret = snew(game_params);
     *ret = *params;                   /* structure copy */
     return ret;
 }
 
-game_params *decode_params(char const *string)
+static game_params *decode_params(char const *string)
 {
     game_params *ret = default_params();
     char const *p = string;
@@ -126,7 +122,7 @@ game_params *decode_params(char const *string)
     return ret;
 }
 
-char *encode_params(game_params *params)
+static char *encode_params(game_params *params)
 {
     char ret[400];
     int len;
@@ -138,7 +134,7 @@ char *encode_params(game_params *params)
     return dupstr(ret);
 }
 
-config_item *game_configure(game_params *params)
+static config_item *game_configure(game_params *params)
 {
     config_item *ret;
     char buf[80];
@@ -165,7 +161,7 @@ config_item *game_configure(game_params *params)
     return ret;
 }
 
-game_params *custom_params(config_item *cfg)
+static game_params *custom_params(config_item *cfg)
 {
     game_params *ret = snew(game_params);
 
@@ -175,7 +171,7 @@ game_params *custom_params(config_item *cfg)
     return ret;
 }
 
-char *validate_params(game_params *params)
+static char *validate_params(game_params *params)
 {
     if (params->w <= 0 && params->h <= 0)
        return "Width and height must both be greater than zero";
@@ -285,6 +281,15 @@ static void generate(random_state *rs, int w, int h, unsigned char *retgrid)
                     for (q = -1; q <= +1; q++) {
                         if (i+p < 0 || i+p >= h || j+q < 0 || j+q >= w)
                             continue;
+                       /*
+                        * An additional special case not mentioned
+                        * above: if a grid dimension is 2xn then
+                        * we do not average across that dimension
+                        * at all. Otherwise a 2x2 grid would
+                        * contain four identical squares.
+                        */
+                       if ((h==2 && p!=0) || (w==2 && q!=0))
+                           continue;
                         n++;
                         sx += fgrid[(i+p)*w+(j+q)];
                     }
@@ -307,7 +312,7 @@ static void generate(random_state *rs, int w, int h, unsigned char *retgrid)
 
     for (i = 0; i < h; i++) {
         for (j = 0; j < w; j++) {
-            retgrid[i*w+j] = (fgrid[i*w+j] > threshold ? GRID_FULL :
+            retgrid[i*w+j] = (fgrid[i*w+j] >= threshold ? GRID_FULL :
                               GRID_EMPTY);
         }
     }
@@ -315,23 +320,23 @@ static void generate(random_state *rs, int w, int h, unsigned char *retgrid)
     sfree(fgrid);
 }
 
-int compute_rowdata(int *ret, unsigned char *start, int len, int step)
+static int compute_rowdata(int *ret, unsigned char *start, int len, int step)
 {
     int i, n;
 
     n = 0;
 
     for (i = 0; i < len; i++) {
-        if (start[i*step] == GRID_UNKNOWN)
-            return -1;
-
         if (start[i*step] == GRID_FULL) {
             int runlen = 1;
-            while (i+runlen < len && start[(i+runlen)*step])
+            while (i+runlen < len && start[(i+runlen)*step] == GRID_FULL)
                 runlen++;
             ret[n++] = runlen;
             i += runlen;
         }
+
+        if (i < len && start[i*step] == GRID_UNKNOWN)
+            return -1;
     }
 
     return n;
@@ -412,6 +417,34 @@ static unsigned char *generate_soluble(random_state *rs, int w, int h)
 
         generate(rs, w, h, grid);
 
+        /*
+         * The game is a bit too easy if any row or column is
+         * completely black or completely white. An exception is
+         * made for rows/columns that are under 3 squares,
+         * otherwise nothing will ever be successfully generated.
+         */
+        ok = TRUE;
+        if (w > 2) {
+            for (i = 0; i < h; i++) {
+                int colours = 0;
+                for (j = 0; j < w; j++)
+                    colours |= (grid[i*w+j] == GRID_FULL ? 2 : 1);
+                if (colours != 3)
+                    ok = FALSE;
+            }
+        }
+        if (h > 2) {
+            for (j = 0; j < w; j++) {
+                int colours = 0;
+                for (i = 0; i < h; i++)
+                    colours |= (grid[i*w+j] == GRID_FULL ? 2 : 1);
+                if (colours != 3)
+                    ok = FALSE;
+            }
+        }
+        if (!ok)
+            continue;
+
         memset(matrix, 0, w*h);
 
         do {
@@ -443,7 +476,7 @@ static unsigned char *generate_soluble(random_state *rs, int w, int h)
     return grid;
 }
 
-char *new_game_seed(game_params *params, random_state *rs)
+static char *new_game_seed(game_params *params, random_state *rs)
 {
     unsigned char *grid;
     int i, j, max, rowlen, *rowdata;
@@ -507,7 +540,7 @@ char *new_game_seed(game_params *params, random_state *rs)
     return seed;
 }
 
-char *validate_seed(game_params *params, char *seed)
+static char *validate_seed(game_params *params, char *seed)
 {
     int i, n, rowspace;
     char *p;
@@ -549,7 +582,7 @@ char *validate_seed(game_params *params, char *seed)
     return NULL;
 }
 
-game_state *new_game(game_params *params, char *seed)
+static game_state *new_game(game_params *params, char *seed)
 {
     int i;
     char *p;
@@ -584,7 +617,7 @@ game_state *new_game(game_params *params, char *seed)
     return state;
 }
 
-game_state *dup_game(game_state *state)
+static game_state *dup_game(game_state *state)
 {
     game_state *ret = snew(game_state);
 
@@ -607,7 +640,7 @@ game_state *dup_game(game_state *state)
     return ret;
 }
 
-void free_game(game_state *state)
+static void free_game(game_state *state)
 {
     sfree(state->rowdata);
     sfree(state->rowlen);
@@ -624,7 +657,7 @@ struct game_ui {
     int drag, release, state;
 };
 
-game_ui *new_ui(game_state *state)
+static game_ui *new_ui(game_state *state)
 {
     game_ui *ret;
 
@@ -634,12 +667,13 @@ game_ui *new_ui(game_state *state)
     return ret;
 }
 
-void free_ui(game_ui *ui)
+static void free_ui(game_ui *ui)
 {
     sfree(ui);
 }
 
-game_state *make_move(game_state *from, game_ui *ui, int x, int y, int button)
+static game_state *make_move(game_state *from, game_ui *ui,
+                            int x, int y, int button)
 {
     game_state *ret;
 
@@ -775,13 +809,13 @@ struct game_drawstate {
     unsigned char *visible;
 };
 
-void game_size(game_params *params, int *x, int *y)
+static void game_size(game_params *params, int *x, int *y)
 {
     *x = SIZE(params->w);
     *y = SIZE(params->h);
 }
 
-float *game_colours(frontend *fe, game_state *state, int *ncolours)
+static float *game_colours(frontend *fe, game_state *state, int *ncolours)
 {
     float *ret = snewn(3 * NCOLOURS, float);
 
@@ -807,7 +841,7 @@ float *game_colours(frontend *fe, game_state *state, int *ncolours)
     return ret;
 }
 
-game_drawstate *game_new_drawstate(game_state *state)
+static game_drawstate *game_new_drawstate(game_state *state)
 {
     struct game_drawstate *ds = snew(struct game_drawstate);
 
@@ -820,7 +854,7 @@ game_drawstate *game_new_drawstate(game_state *state)
     return ds;
 }
 
-void game_free_drawstate(game_drawstate *ds)
+static void game_free_drawstate(game_drawstate *ds)
 {
     sfree(ds->visible);
     sfree(ds);
@@ -848,7 +882,7 @@ static void grid_square(frontend *fe, game_drawstate *ds,
                 TILE_SIZE, TILE_SIZE);
 }
 
-void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
+static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
                  game_state *state, int dir, game_ui *ui,
                  float animtime, float flashtime)
 {
@@ -906,7 +940,7 @@ void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
          * Draw the grid outline.
          */
         draw_rect(fe, TOCOORD(ds->w, 0) - 1, TOCOORD(ds->h, 0) - 1,
-                  ds->w * TILE_SIZE + 2, ds->h * TILE_SIZE + 2,
+                  ds->w * TILE_SIZE + 3, ds->h * TILE_SIZE + 3,
                   COL_GRID);
 
         ds->started = TRUE;
@@ -957,19 +991,54 @@ void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
     }
 }
 
-float game_anim_length(game_state *oldstate, game_state *newstate, int dir)
+static float game_anim_length(game_state *oldstate,
+                             game_state *newstate, int dir)
 {
     return 0.0F;
 }
 
-float game_flash_length(game_state *oldstate, game_state *newstate, int dir)
+static float game_flash_length(game_state *oldstate,
+                              game_state *newstate, int dir)
 {
     if (!oldstate->completed && newstate->completed)
         return FLASH_TIME;
     return 0.0F;
 }
 
-int game_wants_statusbar(void)
+static int game_wants_statusbar(void)
 {
     return FALSE;
 }
+
+#ifdef COMBINED
+#define thegame pattern
+#endif
+
+const struct game thegame = {
+    "Pattern", "games.pattern", TRUE,
+    default_params,
+    game_fetch_preset,
+    decode_params,
+    encode_params,
+    free_params,
+    dup_params,
+    game_configure,
+    custom_params,
+    validate_params,
+    new_game_seed,
+    validate_seed,
+    new_game,
+    dup_game,
+    free_game,
+    new_ui,
+    free_ui,
+    make_move,
+    game_size,
+    game_colours,
+    game_new_drawstate,
+    game_free_drawstate,
+    game_redraw,
+    game_anim_length,
+    game_flash_length,
+    game_wants_statusbar,
+};