chiark / gitweb /
UI change to Filling: allow multiple squares to be set at once.
authorJacob Nevins <jacobn@chiark.greenend.org.uk>
Sun, 10 Feb 2008 18:43:29 +0000 (18:43 +0000)
committerJacob Nevins <jacobn@chiark.greenend.org.uk>
Sun, 10 Feb 2008 18:43:29 +0000 (18:43 +0000)
(This change adds a new possibility to the save format, such that new save
files won't necessarily be loadable by old binaries. I think that's acceptable
-- it's certainly happened before -- but I couldn't find anything in the
developer docs explicitly blessing it.)

[originally from svn r7849]

filling.c
puzzles.but

index cccd89839de734dd358f8e120810feee711d55b1..48874b7195e5ef06eaadf7f7f36681e5242898c3 100644 (file)
--- a/filling.c
+++ b/filling.c
@@ -976,20 +976,22 @@ static char *solve_game(game_state *state, game_state *currstate,
  *****************************************************************************/
 
 struct game_ui {
-    int x, y; /* highlighted square, or (-1, -1) if none */
+    int *sel; /* w*h highlighted squares, or NULL */
 };
 
 static game_ui *new_ui(game_state *state)
 {
     game_ui *ui = snew(game_ui);
 
-    ui->x = ui->y = -1;
+    ui->sel = NULL;
 
     return ui;
 }
 
 static void free_ui(game_ui *ui)
 {
+    if (ui->sel)
+        sfree(ui->sel);
     sfree(ui);
 }
 
@@ -1005,6 +1007,11 @@ static void decode_ui(game_ui *ui, char *encoding)
 static void game_changed_state(game_ui *ui, game_state *oldstate,
                                game_state *newstate)
 {
+    /* Clear any selection */
+    if (ui->sel) {
+        sfree(ui->sel);
+        ui->sel = NULL;
+    }
 }
 
 #define PREFERRED_TILE_SIZE 32
@@ -1029,23 +1036,34 @@ static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds,
     const int tx = (x + TILE_SIZE - BORDER) / TILE_SIZE - 1;
     const int ty = (y + TILE_SIZE - BORDER) / TILE_SIZE - 1;
 
+    char *move = NULL;
+    int i;
+
     assert(ui);
     assert(ds);
 
     button &= ~MOD_MASK;
 
-    if (tx >= 0 && tx < w && ty >= 0 && ty < h) {
+    if (button == LEFT_BUTTON || button == LEFT_DRAG) {
+        /* A left-click anywhere will clear the current selection. */
         if (button == LEFT_BUTTON) {
-            if ((tx == ui->x && ty == ui->y) || state->shared->clues[w*ty+tx])
-                ui->x = ui->y = -1;
-            else ui->x = tx, ui->y = ty;
-            return ""; /* redraw */
+            if (ui->sel) {
+                sfree(ui->sel);
+                ui->sel = NULL;
+            }
+        }
+        if (tx >= 0 && tx < w && ty >= 0 && ty < h) {
+            if (!ui->sel) {
+                ui->sel = snewn(w*h, int);
+                memset(ui->sel, 0, w*h*sizeof(int));
+            }
+            if (!state->shared->clues[w*ty+tx])
+                ui->sel[w*ty+tx] = 1;
         }
+        return ""; /* redraw */
     }
 
-    assert((ui->x == -1) == (ui->y == -1));
-    if (ui->x == -1) return NULL;
-    assert(state->shared->clues[w*ui->y + ui->x] == 0);
+    if (!ui->sel) return NULL;
 
     switch (button) {
       case ' ':
@@ -1061,17 +1079,32 @@ static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds,
         if (button > (w == 2 && h == 2? 3: max(w, h))) return NULL;
     }
 
-    {
-        const int i = w*ui->y + ui->x;
-        char buf[64];
-        ui->x = ui->y = -1;
-       if (state->board[i] == button) {
-           return "";                 /* no change - just update ui */
-       } else {
-           sprintf(buf, "%d_%d", i, button);
-           return dupstr(buf);
-       }
+    for (i = 0; i < w*h; i++) {
+        char buf[32];
+        if (ui->sel[i]) {
+            assert(state->shared->clues[i] == 0);
+            if (state->board[i] != button) {
+                sprintf(buf, "%s%d", move ? "," : "", i);
+                if (move) {
+                    move = srealloc(move, strlen(move)+strlen(buf)+1);
+                    strcat(move, buf);
+                } else {
+                    move = smalloc(strlen(buf)+1);
+                    strcpy(move, buf);
+                }
+            }
+        }
+    }
+    if (move) {
+        char buf[32];
+        sprintf(buf, "_%d", button);
+        move = srealloc(move, strlen(move)+strlen(buf)+1);
+        strcat(move, buf);
     }
+    sfree(ui->sel);
+    ui->sel = NULL;
+    /* Need to update UI at least, as we cleared the selection */
+    return move ? move : "";
 }
 
 static game_state *execute_move(game_state *state, char *move)
@@ -1085,18 +1118,22 @@ static game_state *execute_move(game_state *state, char *move)
         for (++move; i < sz; ++i) new_state->board[i] = move[i] - '0';
         new_state->cheated = TRUE;
     } else {
-        char *endptr;
-        const int i = strtol(move, &endptr, 0);
         int value;
-        if (endptr == move) return NULL;
-        if (*endptr != '_') return NULL;
-        move = endptr + 1;
-        value = strtol(move, &endptr, 0);
-        if (endptr == move) return NULL;
-        if (*endptr != '\0') return NULL;
-        if (i < 0 || i >= sz || value < 0 || value > 9) return NULL;
+        char *endptr, *delim = strchr(move, '_');
+        if (!delim) return NULL;
+        value = strtol(delim+1, &endptr, 0);
+        if (*endptr || endptr == delim+1) return NULL;
+        if (value < 0 || value > 9) return NULL;
         new_state = dup_game(state);
-        new_state->board[i] = value;
+        while (*move) {
+            const int i = strtol(move, &endptr, 0);
+            if (endptr == move) return NULL;
+            if (i < 0 || i >= sz) return NULL;
+            new_state->board[i] = value;
+            if (*endptr == '_') break;
+            if (*endptr != ',') return NULL;
+            move = endptr + 1;
+        }
     }
 
     /*
@@ -1414,7 +1451,7 @@ static void draw_grid(drawing *dr, game_drawstate *ds, game_state *state,
 
             if (flashy || !shading) {
                 /* clear all background flags */
-            } else if (x == ui->x && y == ui->y) {
+            } else if (ui->sel && ui->sel[y*w+x]) {
                 flags |= CURSOR_BG;
             } else if (v) {
                 int size = dsf_size(ds->dsf_scratch, y*w+x);
index 42e9203dcb177b82454f2de54eca82a71d6ccef9..5d41c47e8a41a09493bb7dcff49487119f0979dc 100644 (file)
@@ -2233,9 +2233,11 @@ Filling was contributed to this collection by Jonas K\u00F6{oe}lker.
 \H{filling-controls} \I{controls, for Filling}Filling controls
 
 To play Filling, simply click the mouse in any empty square and then
-type a digit on the keyboard to fill that square.  If you make a
-mistake, click the mouse in the incorrect square and press 0, Space,
-Backspace or Enter to clear it again (or use the Undo feature).
+type a digit on the keyboard to fill that square. By dragging the
+mouse, you can select multiple squares to fill with a single keypress.
+If you make a mistake, click the mouse in the incorrect square and
+press 0, Space, Backspace or Enter to clear it again (or use the Undo
+feature).
 
 (All the actions described in \k{common-actions} are also available.)