Dariusz Olszewski's changes to support compiling for PocketPC. This
[sgt/puzzles] / tents.c
diff --git a/tents.c b/tents.c
index 113fa17..de20300 100644 (file)
--- a/tents.c
+++ b/tents.c
@@ -415,8 +415,12 @@ static game_params *custom_params(config_item *cfg)
 
 static char *validate_params(game_params *params, int full)
 {
-    if (params->w < 2 || params->h < 2)
-       return "Width and height must both be at least two";
+    /*
+     * Generating anything under 4x4 runs into trouble of one kind
+     * or another.
+     */
+    if (params->w < 4 || params->h < 4)
+       return "Width and height must both be at least four";
     return NULL;
 }
 
@@ -875,7 +879,7 @@ static int tents_solve(int w, int h, const char *grid, int *numbers,
                            printf("%s %d forces %s at %d,%d\n",
                                   step==1 ? "row" : "column",
                                   step==1 ? start/w : start,
-                                  mrow[j] == TENT ? "tent" : "non-tent",
+                                  mthis[j] == TENT ? "tent" : "non-tent",
                                   pos % w, pos / w);
 #endif
                        soln[pos] = mthis[j];
@@ -916,7 +920,7 @@ static char *new_game_desc(game_params *params, random_state *rs,
     char *puzzle = snewn(w*h, char);
     int *numbers = snewn(w+h, int);
     char *soln = snewn(w*h, char);
-    int *temp = snewn(2*w*h, int), *itemp = temp + w*h;
+    int *temp = snewn(2*w*h, int);
     int maxedges = ntrees*4 + w*h;
     int *edges = snewn(2*maxedges, int);
     int *capacity = snewn(maxedges, int);
@@ -962,9 +966,9 @@ static char *new_game_desc(game_params *params, random_state *rs,
      * The maxflow algorithm is not randomised, so employed naively
      * it would give rise to grids with clear structure and
      * directional bias. Hence, I assign the network nodes as seen
-     * by maxflow to be a _random_ permutation the squares of the
-     * grid, so that any bias shown by maxflow towards low-numbered
-     * nodes is turned into a random bias.
+     * by maxflow to be a _random_ permutation of the squares of
+     * the grid, so that any bias shown by maxflow towards
+     * low-numbered nodes is turned into a random bias.
      * 
      * This generation strategy can fail at many points, including
      * as early as tent placement (if you get a bad random order in
@@ -979,16 +983,16 @@ static char *new_game_desc(game_params *params, random_state *rs,
      * trouble.
      */
 
+    if (params->diff > DIFF_EASY && params->w <= 4 && params->h <= 4)
+       params->diff = DIFF_EASY;      /* downgrade to prevent tight loop */
+
     while (1) {
        /*
-        * Arrange the grid squares into a random order, and invert
-        * that order so we can find a square's index as well.
+        * Arrange the grid squares into a random order.
         */
        for (i = 0; i < w*h; i++)
            temp[i] = i;
        shuffle(temp, w*h, sizeof(*temp), rs);
-       for (i = 0; i < w*h; i++)
-           itemp[temp[i]] = i;
 
        /*
         * The first `ntrees' entries in temp which we can get
@@ -1400,13 +1404,26 @@ static char *game_text_format(game_state *state)
     return ret;
 }
 
+struct game_ui {
+    int dsx, dsy;                      /* coords of drag start */
+    int dex, dey;                      /* coords of drag end */
+    int drag_button;                   /* -1 for none, or a button code */
+    int drag_ok;                       /* dragged off the window, to cancel */
+};
+
 static game_ui *new_ui(game_state *state)
 {
-    return NULL;
+    game_ui *ui = snew(game_ui);
+    ui->dsx = ui->dsy = -1;
+    ui->dex = ui->dey = -1;
+    ui->drag_button = -1;
+    ui->drag_ok = FALSE;
+    return ui;
 }
 
 static void free_ui(game_ui *ui)
 {
+    sfree(ui);
 }
 
 static char *encode_ui(game_ui *ui)
@@ -1439,32 +1456,151 @@ struct game_drawstate {
 
 #define FLASH_TIME 0.30F
 
+static int drag_xform(game_ui *ui, int x, int y, int v)
+{
+    int xmin, ymin, xmax, ymax;
+
+    xmin = min(ui->dsx, ui->dex);
+    xmax = max(ui->dsx, ui->dex);
+    ymin = min(ui->dsy, ui->dey);
+    ymax = max(ui->dsy, ui->dey);
+
+    /*
+     * Left-dragging has no effect, so we treat a left-drag as a
+     * single click on dsx,dsy.
+     */
+    if (ui->drag_button == LEFT_BUTTON) {
+        xmin = xmax = ui->dsx;
+        ymin = ymax = ui->dsy;
+    }
+
+    if (x < xmin || x > xmax || y < ymin || y > ymax)
+        return v;                      /* no change outside drag area */
+
+    if (v == TREE)
+        return v;                      /* trees are inviolate always */
+
+    if (xmin == xmax && ymin == ymax) {
+        /*
+         * Results of a simple click. Left button sets blanks to
+         * tents; right button sets blanks to non-tents; either
+         * button clears a non-blank square.
+         */
+        if (ui->drag_button == LEFT_BUTTON)
+            v = (v == BLANK ? TENT : BLANK);
+        else
+            v = (v == BLANK ? NONTENT : BLANK);
+    } else {
+        /*
+         * Results of a drag. Left-dragging has no effect.
+         * Right-dragging sets all blank squares to non-tents and
+         * has no effect on anything else.
+         */
+        if (ui->drag_button == RIGHT_BUTTON)
+            v = (v == BLANK ? NONTENT : v);
+        else
+            /* do nothing */;
+    }
+
+    return v;
+}
+
 static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds,
                            int x, int y, int button)
 {
     int w = state->p.w, h = state->p.h;
 
     if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
-        int v;
-        char buf[80];
-
         x = FROMCOORD(x);
         y = FROMCOORD(y);
         if (x < 0 || y < 0 || x >= w || y >= h)
             return NULL;
 
-        if (state->grid[y*w+x] == TREE)
-            return NULL;
+        ui->drag_button = button;
+        ui->dsx = ui->dex = x;
+        ui->dsy = ui->dey = y;
+        ui->drag_ok = TRUE;
+        return "";             /* ui updated */
+    }
 
-        if (button == LEFT_BUTTON) {
-            v = (state->grid[y*w+x] == BLANK ? TENT : BLANK);
+    if ((IS_MOUSE_DRAG(button) || IS_MOUSE_RELEASE(button)) &&
+        ui->drag_button > 0) {
+        int xmin, ymin, xmax, ymax;
+        char *buf, *sep, tmpbuf[80];
+        int buflen, bufsize, tmplen;
+
+        x = FROMCOORD(x);
+        y = FROMCOORD(y);
+        if (x < 0 || y < 0 || x >= w || y >= h) {
+            ui->drag_ok = FALSE;
         } else {
-            v = (state->grid[y*w+x] == BLANK ? NONTENT : BLANK);
+            /*
+             * Drags are limited to one row or column. Hence, we
+             * work out which coordinate is closer to the drag
+             * start, and move it _to_ the drag start.
+             */
+            if (abs(x - ui->dsx) < abs(y - ui->dsy))
+                x = ui->dsx;
+            else
+                y = ui->dsy;
+
+            ui->dex = x;
+            ui->dey = y;
+
+            ui->drag_ok = TRUE;
+        }
+
+        if (IS_MOUSE_DRAG(button))
+            return "";                 /* ui updated */
+
+        /*
+         * The drag has been released. Enact it.
+         */
+        if (!ui->drag_ok) {
+            ui->drag_button = -1;
+            return "";                 /* drag was just cancelled */
         }
 
-        sprintf(buf, "%c%d,%d", (int)(v==BLANK ? 'B' :
-                                      v==TENT ? 'T' : 'N'), x, y);
-        return dupstr(buf);
+        xmin = min(ui->dsx, ui->dex);
+        xmax = max(ui->dsx, ui->dex);
+        ymin = min(ui->dsy, ui->dey);
+        ymax = max(ui->dsy, ui->dey);
+        assert(0 <= xmin && xmin <= xmax && xmax < w);
+        assert(0 <= ymin && ymin <= ymax && ymax < h);
+
+        buflen = 0;
+        bufsize = 256;
+        buf = snewn(bufsize, char);
+        sep = "";
+        for (y = ymin; y <= ymax; y++)
+            for (x = xmin; x <= xmax; x++) {
+                int v = drag_xform(ui, x, y, state->grid[y*w+x]);
+                if (state->grid[y*w+x] != v) {
+                    tmplen = sprintf(tmpbuf, "%s%c%d,%d", sep,
+                                     (int)(v == BLANK ? 'B' :
+                                           v == TENT ? 'T' : 'N'),
+                                     x, y);
+                    sep = ";";
+
+                    if (buflen + tmplen >= bufsize) {
+                        bufsize = buflen + tmplen + 256;
+                        buf = sresize(buf, bufsize, char);
+                    }
+
+                    strcpy(buf+buflen, tmpbuf);
+                    buflen += tmplen;
+                }
+            }
+
+        ui->drag_button = -1;          /* drag is terminated */
+
+        if (buflen == 0) {
+            sfree(buf);
+            return "";                 /* ui updated (drag was terminated) */
+        } else {
+            buf[buflen] = '\0';
+            return buf;
+        }
     }
 
     return NULL;
@@ -1674,7 +1810,7 @@ static void game_set_size(drawing *dr, game_drawstate *ds,
     ds->tilesize = tilesize;
 }
 
-static float *game_colours(frontend *fe, game_state *state, int *ncolours)
+static float *game_colours(frontend *fe, int *ncolours)
 {
     float *ret = snewn(3 * NCOLOURS, float);
 
@@ -1834,6 +1970,15 @@ static void int_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
         for (x = 0; x < w; x++) {
             int v = state->grid[y*w+x];
 
+            /*
+             * We deliberately do not take drag_ok into account
+             * here, because user feedback suggests that it's
+             * marginally nicer not to have the drag effects
+             * flickering on and off disconcertingly.
+             */
+            if (ui && ui->drag_button >= 0)
+                v = drag_xform(ui, x, y, v);
+
             if (flashing && (v == TREE || v == TENT))
                 v = NONTENT;
 
@@ -1868,11 +2013,6 @@ static float game_flash_length(game_state *oldstate, game_state *newstate,
     return 0.0F;
 }
 
-static int game_wants_statusbar(void)
-{
-    return FALSE;
-}
-
 static int game_timing_state(game_state *state, game_ui *ui)
 {
     return TRUE;
@@ -1913,7 +2053,7 @@ static void game_print(drawing *dr, game_state *state, int tilesize)
 #endif
 
 const struct game thegame = {
-    "Tents", "games.tents",
+    "Tents", "games.tents", "tents",
     default_params,
     game_fetch_preset,
     decode_params,
@@ -1944,9 +2084,9 @@ const struct game thegame = {
     game_anim_length,
     game_flash_length,
     TRUE, FALSE, game_print_size, game_print,
-    game_wants_statusbar,
+    FALSE,                            /* wants_statusbar */
     FALSE, game_timing_state,
-    0,                                /* mouse_priorities */
+    REQUIRE_RBUTTON,                  /* flags */
 };
 
 #ifdef STANDALONE_SOLVER