Make peg removal accessible from the keyboard.
[sgt/puzzles] / rect.c
diff --git a/rect.c b/rect.c
index 229ce18..5852f59 100644 (file)
--- a/rect.c
+++ b/rect.c
@@ -323,7 +323,8 @@ static void remove_number_placement(int w, int h, struct numberdata *number,
 }
 
 static int rect_solver(int w, int h, int nrects, struct numberdata *numbers,
-                       game_state *result, random_state *rs)
+                       unsigned char *hedge, unsigned char *vedge,
+                      random_state *rs)
 {
     struct rectlist *rectpositions;
     int *overlaps, *rectbyplace, *workspace;
@@ -848,25 +849,25 @@ static int rect_solver(int w, int h, int nrects, struct numberdata *numbers,
         assert(rectpositions[i].n > 0);
         if (rectpositions[i].n > 1) {
             ret = FALSE;
-       } else if (result) {
-           /*
-            * Place the rectangle in its only possible position.
-            */
-           int x, y;
-           struct rect *r = &rectpositions[i].rects[0];
-
-           for (y = 0; y < r->h; y++) {
-               if (r->x > 0)
-                   vedge(result, r->x, r->y+y) = 1;
-               if (r->x+r->w < result->w)
-                   vedge(result, r->x+r->w, r->y+y) = 1;
-           }
-           for (x = 0; x < r->w; x++) {
-               if (r->y > 0)
-                   hedge(result, r->x+x, r->y) = 1;
-               if (r->y+r->h < result->h)
-                   hedge(result, r->x+x, r->y+r->h) = 1;
-           }
+        } else if (hedge && vedge) {
+            /*
+             * Place the rectangle in its only possible position.
+             */
+            int x, y;
+            struct rect *r = &rectpositions[i].rects[0];
+
+            for (y = 0; y < r->h; y++) {
+                if (r->x > 0)
+                   vedge[(r->y+y) * w + r->x] = 1;
+                if (r->x+r->w < w)
+                   vedge[(r->y+y) * w + r->x+r->w] = 1;
+            }
+            for (x = 0; x < r->w; x++) {
+                if (r->y > 0)
+                    hedge[r->y * w + r->x+x] = 1;
+                if (r->y+r->h < h)
+                    hedge[(r->y+r->h) * w + r->x+x] = 1;
+            }
        }
     }
 
@@ -1116,14 +1117,8 @@ static void display_grid(game_params *params, int *grid, int *numbers, int all)
 }
 #endif
 
-struct game_aux_info {
-    int w, h;
-    unsigned char *vedge;             /* (w+1) x h */
-    unsigned char *hedge;             /* w x (h+1) */
-};
-
 static char *new_game_desc(game_params *params, random_state *rs,
-                          game_aux_info **aux, int interactive)
+                          char **aux, int interactive)
 {
     int *grid, *numbers = NULL;
     int x, y, y2, y2last, yx, run, i, nsquares;
@@ -1633,7 +1628,7 @@ static char *new_game_desc(game_params *params, random_state *rs,
 
            if (params->unique)
                ret = rect_solver(params->w, params->h, nnumbers, nd,
-                                 NULL, rs);
+                                 NULL, NULL, rs);
            else
                ret = TRUE;            /* allow any number placement at all */
 
@@ -1678,26 +1673,31 @@ static char *new_game_desc(game_params *params, random_state *rs,
     }
 
     /*
-     * Store the rectangle data in the game_aux_info.
+     * Store the solution in aux.
      */
     {
-        game_aux_info *ai = snew(game_aux_info);
+        char *ai;
+        int len;
+
+        len = 2 + (params->w-1)*params->h + (params->h-1)*params->w;
+        ai = snewn(len, char);
 
-        ai->w = params->w;
-        ai->h = params->h;
-        ai->vedge = snewn(ai->w * ai->h, unsigned char);
-        ai->hedge = snewn(ai->w * ai->h, unsigned char);
+        ai[0] = 'S';
+
+        p = ai+1;
 
         for (y = 0; y < params->h; y++)
-            for (x = 1; x < params->w; x++) {
-                vedge(ai, x, y) =
-                    index(params, grid, x, y) != index(params, grid, x-1, y);
-            }
+            for (x = 1; x < params->w; x++)
+                *p++ = (index(params, grid, x, y) !=
+                        index(params, grid, x-1, y) ? '1' : '0');
+
         for (y = 1; y < params->h; y++)
-            for (x = 0; x < params->w; x++) {
-                hedge(ai, x, y) =
-                    index(params, grid, x, y) != index(params, grid, x, y-1);
-            }
+            for (x = 0; x < params->w; x++)
+                *p++ = (index(params, grid, x, y) !=
+                        index(params, grid, x, y-1) ? '1' : '0');
+
+        assert(p - ai == len-1);
+        *p = '\0';
 
         *aux = ai;
     }
@@ -1745,13 +1745,6 @@ static char *new_game_desc(game_params *params, random_state *rs,
     return desc;
 }
 
-static void game_free_aux_info(game_aux_info *ai)
-{
-    sfree(ai->vedge);
-    sfree(ai->hedge);
-    sfree(ai);
-}
-
 static char *validate_desc(game_params *params, char *desc)
 {
     int area = params->w * params->h;
@@ -1852,60 +1845,71 @@ static void free_game(game_state *state)
     sfree(state);
 }
 
-static game_state *solve_game(game_state *state, game_state *currstate,
-                             game_aux_info *ai, char **error)
+static char *solve_game(game_state *state, game_state *currstate,
+                       char *ai, char **error)
 {
-    game_state *ret;
+    unsigned char *vedge, *hedge;
+    int x, y, len;
+    char *ret, *p;
+    int i, j, n;
+    struct numberdata *nd;
 
-    if (!ai) {
-       int i, j, n;
-       struct numberdata *nd;
-
-       /*
-        * Attempt the in-built solver.
-        */
-
-       /* Set up each number's (very short) candidate position list. */
-       for (i = n = 0; i < state->h * state->w; i++)
-           if (state->grid[i])
-               n++;
-
-       nd = snewn(n, struct numberdata);
-
-       for (i = j = 0; i < state->h * state->w; i++)
-           if (state->grid[i]) {
-               nd[j].area = state->grid[i];
-               nd[j].npoints = 1;
-               nd[j].points = snewn(1, struct point);
-               nd[j].points[0].x = i % state->w;
-               nd[j].points[0].y = i / state->w;
-               j++;
-           }
+    if (ai)
+        return dupstr(ai);
 
-       assert(j == n);
+    /*
+     * Attempt the in-built solver.
+     */
 
-       ret = dup_game(state);
-       ret->cheated = TRUE;
+    /* Set up each number's (very short) candidate position list. */
+    for (i = n = 0; i < state->h * state->w; i++)
+        if (state->grid[i])
+            n++;
+
+    nd = snewn(n, struct numberdata);
+
+    for (i = j = 0; i < state->h * state->w; i++)
+        if (state->grid[i]) {
+            nd[j].area = state->grid[i];
+            nd[j].npoints = 1;
+            nd[j].points = snewn(1, struct point);
+            nd[j].points[0].x = i % state->w;
+            nd[j].points[0].y = i / state->w;
+            j++;
+        }
 
-       rect_solver(state->w, state->h, n, nd, ret, NULL);
+    assert(j == n);
 
-       /*
-        * Clean up.
-        */
-       for (i = 0; i < n; i++)
-           sfree(nd[i].points);
-       sfree(nd);
+    vedge = snewn(state->w * state->h, unsigned char);
+    hedge = snewn(state->w * state->h, unsigned char);
+    memset(vedge, 0, state->w * state->h);
+    memset(hedge, 0, state->w * state->h);
 
-       return ret;
-    }
+    rect_solver(state->w, state->h, n, nd, hedge, vedge, NULL);
 
-    assert(state->w == ai->w);
-    assert(state->h == ai->h);
+    /*
+     * Clean up.
+     */
+    for (i = 0; i < n; i++)
+        sfree(nd[i].points);
+    sfree(nd);
 
-    ret = dup_game(state);
-    memcpy(ret->vedge, ai->vedge, ai->w * ai->h * sizeof(unsigned char));
-    memcpy(ret->hedge, ai->hedge, ai->w * ai->h * sizeof(unsigned char));
-    ret->cheated = TRUE;
+    len = 2 + (state->w-1)*state->h + (state->h-1)*state->w;
+    ret = snewn(len, char);
+
+    p = ret;
+    *p++ = 'S';
+    for (y = 0; y < state->h; y++)
+        for (x = 1; x < state->w; x++)
+            *p++ = vedge[y*state->w+x] ? '1' : '0';
+    for (y = 1; y < state->h; y++)
+       for (x = 0; x < state->w; x++)
+           *p++ = hedge[y*state->w+x] ? '1' : '0';
+    *p++ = '\0';
+    assert(p - ret == len);
+
+    sfree(vedge);
+    sfree(hedge);
 
     return ret;
 }
@@ -2148,6 +2152,15 @@ static void free_ui(game_ui *ui)
     sfree(ui);
 }
 
+static char *encode_ui(game_ui *ui)
+{
+    return NULL;
+}
+
+static void decode_ui(game_ui *ui, char *encoding)
+{
+}
+
 static void coord_round(float x, float y, int *xr, int *yr)
 {
     float xs, ys, xv, yv, dx, dy, dist;
@@ -2232,14 +2245,16 @@ static void coord_round(float x, float y, int *xr, int *yr)
     }
 }
 
-static void ui_draw_rect(game_state *state, game_ui *ui,
-                         unsigned char *hedge, unsigned char *vedge, int c)
+/*
+ * Returns TRUE if it has made any change to the grid.
+ */
+static int grid_draw_rect(game_state *state,
+                         unsigned char *hedge, unsigned char *vedge,
+                         int c, int really,
+                         int x1, int y1, int x2, int y2)
 {
     int x, y;
-    int x1 = ui->x1;
-    int y1 = ui->y1;
-    int x2 = ui->x2;
-    int y2 = ui->y2;
+    int changed = FALSE;
 
     /*
      * Draw horizontal edges of rectangles.
@@ -2252,7 +2267,9 @@ static void ui_draw_rect(game_state *state, game_ui *ui,
                     val = c;
                 else if (c == 1)
                     val = 0;
-                index(state,hedge,x,y) = val;
+               changed = changed || (index(state,hedge,x,y) != val);
+               if (really)
+                   index(state,hedge,x,y) = val;
             }
 
     /*
@@ -2266,8 +2283,20 @@ static void ui_draw_rect(game_state *state, game_ui *ui,
                     val = c;
                 else if (c == 1)
                     val = 0;
-                index(state,vedge,x,y) = val;
+               changed = changed || (index(state,vedge,x,y) != val);
+                if (really)
+                   index(state,vedge,x,y) = val;
             }
+
+    return changed;
+}
+
+static int ui_draw_rect(game_state *state, game_ui *ui,
+                       unsigned char *hedge, unsigned char *vedge, int c,
+                       int really)
+{
+    return grid_draw_rect(state, hedge, vedge, c, really,
+                         ui->x1, ui->y1, ui->x2, ui->y2);
 }
 
 static void game_changed_state(game_ui *ui, game_state *oldstate,
@@ -2281,11 +2310,12 @@ struct game_drawstate {
     unsigned long *visible;
 };
 
-static game_state *make_move(game_state *from, game_ui *ui, game_drawstate *ds,
-                             int x, int y, int button) {
+static char *interpret_move(game_state *from, game_ui *ui, game_drawstate *ds,
+                           int x, int y, int button)
+{
     int xc, yc;
     int startdrag = FALSE, enddrag = FALSE, active = FALSE;
-    game_state *ret;
+    char buf[80], *ret;
 
     button &= ~MOD_MASK;
 
@@ -2343,44 +2373,24 @@ static game_state *make_move(game_state *from, game_ui *ui, game_drawstate *ds,
     if (enddrag) {
        if (xc >= 0 && xc <= 2*from->w &&
            yc >= 0 && yc <= 2*from->h) {
-           ret = dup_game(from);
 
            if (ui->dragged) {
-               ui_draw_rect(ret, ui, ret->hedge, ret->vedge, 1);
+               if (ui_draw_rect(from, ui, from->hedge,
+                                from->vedge, 1, FALSE)) {
+                   sprintf(buf, "R%d,%d,%d,%d",
+                           ui->x1, ui->y1, ui->x2 - ui->x1, ui->y2 - ui->y1);
+                   ret = dupstr(buf);
+               }
            } else {
                if ((xc & 1) && !(yc & 1) && HRANGE(from,xc/2,yc/2)) {
-                   hedge(ret,xc/2,yc/2) = !hedge(ret,xc/2,yc/2);
+                   sprintf(buf, "H%d,%d", xc/2, yc/2);
+                   ret = dupstr(buf);
                }
                if ((yc & 1) && !(xc & 1) && VRANGE(from,xc/2,yc/2)) {
-                   vedge(ret,xc/2,yc/2) = !vedge(ret,xc/2,yc/2);
+                   sprintf(buf, "V%d,%d", xc/2, yc/2);
+                   ret = dupstr(buf);
                }
            }
-
-           if (!memcmp(ret->hedge, from->hedge, from->w*from->h) &&
-               !memcmp(ret->vedge, from->vedge, from->w*from->h)) {
-               free_game(ret);
-               ret = NULL;
-           }
-
-            /*
-             * We've made a real change to the grid. Check to see
-             * if the game has been completed.
-             */
-            if (ret && !ret->completed) {
-                int x, y, ok;
-                unsigned char *correct = get_correct(ret);
-
-                ok = TRUE;
-                for (x = 0; x < ret->w; x++)
-                    for (y = 0; y < ret->h; y++)
-                        if (!index(ret, correct, x, y))
-                            ok = FALSE;
-
-                sfree(correct);
-
-                if (ok)
-                    ret->completed = TRUE;
-            }
        }
 
        ui->drag_start_x = -1;
@@ -2398,11 +2408,84 @@ static game_state *make_move(game_state *from, game_ui *ui, game_drawstate *ds,
     if (ret)
        return ret;                    /* a move has been made */
     else if (active)
-        return from;                   /* UI activity has occurred */
+        return "";                    /* UI activity has occurred */
     else
        return NULL;
 }
 
+static game_state *execute_move(game_state *from, char *move)
+{
+    game_state *ret;
+    int x1, y1, x2, y2, mode;
+
+    if (move[0] == 'S') {
+       char *p = move+1;
+       int x, y;
+
+       ret = dup_game(from);
+       ret->cheated = TRUE;
+
+       for (y = 0; y < ret->h; y++)
+           for (x = 1; x < ret->w; x++) {
+               vedge(ret, x, y) = (*p == '1');
+               if (*p) p++;
+           }
+       for (y = 1; y < ret->h; y++)
+           for (x = 0; x < ret->w; x++) {
+               hedge(ret, x, y) = (*p == '1');
+               if (*p) p++;
+           }
+
+       return ret;
+
+    } else if (move[0] == 'R' &&
+       sscanf(move+1, "%d,%d,%d,%d", &x1, &y1, &x2, &y2) == 4 &&
+       x1 >= 0 && x2 >= 0 && x1+x2 <= from->w &&
+       y1 >= 0 && y2 >= 0 && y1+y2 <= from->h) {
+       x2 += x1;
+       y2 += y1;
+       mode = move[0];
+    } else if ((move[0] == 'H' || move[0] == 'V') &&
+              sscanf(move+1, "%d,%d", &x1, &y1) == 2 &&
+              (move[0] == 'H' ? HRANGE(from, x1, y1) :
+               VRANGE(from, x1, y1))) {
+       mode = move[0];
+    } else
+       return NULL;                   /* can't parse move string */
+
+    ret = dup_game(from);
+
+    if (mode == 'R') {
+       grid_draw_rect(ret, ret->hedge, ret->vedge, 1, TRUE, x1, y1, x2, y2);
+    } else if (mode == 'H') {
+       hedge(ret,x1,y1) = !hedge(ret,x1,y1);
+    } else if (mode == 'V') {
+       vedge(ret,x1,y1) = !vedge(ret,x1,y1);
+    }
+
+    /*
+     * We've made a real change to the grid. Check to see
+     * if the game has been completed.
+     */
+    if (!ret->completed) {
+       int x, y, ok;
+       unsigned char *correct = get_correct(ret);
+
+       ok = TRUE;
+       for (x = 0; x < ret->w; x++)
+           for (y = 0; y < ret->h; y++)
+               if (!index(ret, correct, x, y))
+                   ok = FALSE;
+
+       sfree(correct);
+
+       if (ok)
+           ret->completed = TRUE;
+    }
+
+    return ret;
+}
+
 /* ----------------------------------------------------------------------
  * Drawing routines.
  */
@@ -2559,7 +2642,7 @@ static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
         vedge = snewn(state->w*state->h, unsigned char);
         memcpy(hedge, state->hedge, state->w*state->h);
         memcpy(vedge, state->vedge, state->w*state->h);
-        ui_draw_rect(state, ui, hedge, vedge, 2);
+        ui_draw_rect(state, ui, hedge, vedge, 2, TRUE);
     } else {
         hedge = state->hedge;
         vedge = state->vedge;
@@ -2698,7 +2781,6 @@ const struct game thegame = {
     TRUE, game_configure, custom_params,
     validate_params,
     new_game_desc,
-    game_free_aux_info,
     validate_desc,
     new_game,
     dup_game,
@@ -2707,8 +2789,11 @@ const struct game thegame = {
     TRUE, game_text_format,
     new_ui,
     free_ui,
+    encode_ui,
+    decode_ui,
     game_changed_state,
-    make_move,
+    interpret_move,
+    execute_move,
     game_size,
     game_colours,
     game_new_drawstate,