chiark / gitweb /
Make the code base clean under -Wwrite-strings.
[sgt-puzzles.git] / palisade.c
index f7b379128505d5fa0a6d5c01efa391e4d1b226f6..5227a1d56c18f2b471d4836d1080f3cd95c3d8d5 100644 (file)
@@ -119,10 +119,20 @@ static config_item *game_configure(const game_params *params)
 {
     config_item *ret = snewn(4, config_item);
 
-    CONFIG(0, "Width",       C_STRING, 0, string(20, "%d", params->w));
-    CONFIG(1, "Height",      C_STRING, 0, string(20, "%d", params->h));
-    CONFIG(2, "Region size", C_STRING, 0, string(20, "%d", params->k));
-    CONFIG(3, NULL,          C_END,    0, NULL);
+    ret[0].name = "Width";
+    ret[0].type = C_STRING;
+    ret[0].u.string.sval = string(20, "%d", params->w);
+
+    ret[1].name = "Height";
+    ret[1].type = C_STRING;
+    ret[1].u.string.sval = string(20, "%d", params->h);
+
+    ret[2].name = "Region size";
+    ret[2].type = C_STRING;
+    ret[2].u.string.sval = string(20, "%d", params->k);
+
+    ret[3].name = NULL;
+    ret[3].type = C_END;
 
     return ret;
 }
@@ -131,9 +141,9 @@ static game_params *custom_params(const config_item *cfg)
 {
     game_params *params = snew(game_params);
 
-    params->w = atoi(cfg[0].sval);
-    params->h = atoi(cfg[1].sval);
-    params->k = atoi(cfg[2].sval);
+    params->w = atoi(cfg[0].u.string.sval);
+    params->h = atoi(cfg[1].u.string.sval);
+    params->k = atoi(cfg[2].u.string.sval);
 
     return params;
 }
@@ -145,7 +155,7 @@ static game_params *custom_params(const config_item *cfg)
  * +---+   the dominos is horizontal or vertical.            +---+---+
  */
 
-static char *validate_params(const game_params *params, int full)
+static const char *validate_params(const game_params *params, int full)
 {
     int w = params->w, h = params->h, k = params->k, wh = w * h;
 
@@ -511,10 +521,20 @@ static void dfs_dsf(int i, int w, borderflag *border, int *dsf, int black)
 static int is_solved(const game_params *params, clue *clues,
                      borderflag *border)
 {
-    int wh = params->w * params->h, k = params->k, *dsf = snew_dsf(wh), i;
+    int w = params->w, h = params->h, wh = w*h, k = params->k;
+    int i, x, y;
+    int *dsf = snew_dsf(wh);
 
     assert (dsf[0] == UNVISITED); /* check: UNVISITED and dsf.c match up */
 
+    /*
+     * A game is solved if:
+     *
+     *  - the borders drawn on the grid divide it into connected
+     *    components such that every square is in a component of the
+     *    correct size
+     *  - the borders also satisfy the clue set
+     */
     for (i = 0; i < wh; ++i) {
         if (dsf[i] == UNVISITED) dfs_dsf(i, params->w, border, dsf, TRUE);
         if (dsf_size(dsf, i) != k) goto error;
@@ -522,6 +542,26 @@ static int is_solved(const game_params *params, clue *clues,
         if (clues[i] != bitcount[border[i] & BORDER_MASK]) goto error;
     }
 
+    /*
+     * ... and thirdly:
+     *
+     *  - there are no *stray* borders, in that every border is
+     *    actually part of the division between two components.
+     *    Otherwise you could cheat by finding a subdivision which did
+     *    not *exceed* any clue square's counter, and then adding a
+     *    few extra edges.
+     */
+    for (y = 0; y < h; y++) {
+        for (x = 0; x < w; x++) {
+            if (x+1 < w && (border[y*w+x] & BORDER_R) &&
+                dsf_canonify(dsf, y*w+x) == dsf_canonify(dsf, y*w+(x+1)))
+                goto error;
+            if (y+1 < h && (border[y*w+x] & BORDER_D) &&
+                dsf_canonify(dsf, y*w+x) == dsf_canonify(dsf, (y+1)*w+x))
+                goto error;
+        }
+    }
+
     sfree(dsf);
     return TRUE;
 
@@ -661,23 +701,23 @@ static char *new_game_desc(const game_params *params, random_state *rs,
     return sresize(numbers, p - numbers, clue);
 }
 
-static char *validate_desc(const game_params *params, const char *desc)
+static const char *validate_desc(const game_params *params, const char *desc)
 {
 
     int w = params->w, h = params->h, wh = w*h, squares = 0;
 
     for (/* nop */; *desc; ++desc) {
-        if (islower(*desc)) {
+        if (islower((unsigned char)*desc)) {
             squares += *desc - 'a' + 1;
-        } else if (isdigit(*desc)) {
+        } else if (isdigit((unsigned char)*desc)) {
             if (*desc > '4') {
                 static char buf[] = "Invalid (too large) number: '5'";
-                assert (isdigit(buf[lenof(buf) - 3]));
+                assert (isdigit((unsigned char)buf[lenof(buf) - 3]));
                 buf[lenof(buf) - 3] = *desc; /* ... or 6, 7, 8, 9 :-) */
                 return buf;
             }
             ++squares;
-        } else if (isprint(*desc)) {
+        } else if (isprint((unsigned char)*desc)) {
             static char buf[] = "Invalid character in data: '?'";
             buf[lenof(buf) - 3] = *desc;
             return buf;
@@ -702,8 +742,8 @@ static game_state *new_game(midend *me, const game_params *params,
 
     setmem(state->shared->clues, EMPTY, wh);
     for (i = 0; *desc; ++desc) {
-        if (isdigit(*desc)) state->shared->clues[i++] = *desc - '0';
-        else if (isalpha(*desc)) i += *desc - 'a' + 1;
+        if (isdigit((unsigned char)*desc)) state->shared->clues[i++] = *desc - '0';
+        else if (isalpha((unsigned char)*desc)) i += *desc - 'a' + 1;
     }
 
     snewa(state->borders, wh);
@@ -742,7 +782,7 @@ static void free_game(game_state *state)
 }
 
 static char *solve_game(const game_state *state, const game_state *currstate,
-                        const char *aux, char **error)
+                        const char *aux, const char **error)
 {
     int w = state->shared->params.w, h = state->shared->params.h, wh = w*h;
     borderflag *move;
@@ -956,7 +996,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
                           ui->x, ui->y, flag, x, y, newflag);
         } else {
             move_cursor(button, &ui->x, &ui->y, w, h, FALSE);
-            return "";
+            return UI_UPDATE;
         }
     }
 
@@ -1317,7 +1357,7 @@ static void game_print(drawing *dr, const game_state *state, int tilesize)
 const struct game thegame = {
     "Palisade", "games.palisade", "palisade",
     default_params,
-    game_fetch_preset,
+    game_fetch_preset, NULL,
     decode_params,
     encode_params,
     free_params,