Enhancements to mkfiles.pl and Recipe to arrange for the auxiliary
[sgt/puzzles] / pattern.c
index 419e4aa..e9b6f98 100644 (file)
--- a/pattern.c
+++ b/pattern.c
@@ -11,9 +11,6 @@
 
 #include "puzzles.h"
 
-#define max(x,y) ( (x)>(y) ? (x):(y) )
-#define min(x,y) ( (x)<(y) ? (x):(y) )
-
 enum {
     COL_BACKGROUND,
     COL_EMPTY,
@@ -23,15 +20,17 @@ enum {
     NCOLOURS
 };
 
-#define BORDER 18
+#define PREFERRED_TILE_SIZE 24
+#define TILE_SIZE (ds->tilesize)
+#define BORDER (3 * TILE_SIZE / 4)
 #define TLBORDER(d) ( (d) / 5 + 2 )
-#define GUTTER 12
-#define TILE_SIZE 24
+#define GUTTER (TILE_SIZE / 2)
 
 #define FROMCOORD(d, x) \
         ( ((x) - (BORDER + GUTTER + TILE_SIZE * TLBORDER(d))) / TILE_SIZE )
 
 #define SIZE(d) (2*BORDER + GUTTER + TILE_SIZE * (TLBORDER(d) + (d)))
+#define GETTILESIZE(d, w) ((double)w / (2.0 + (double)TLBORDER(d) + (double)(d)))
 
 #define TOCOORD(d, x) (BORDER + GUTTER + TILE_SIZE * (TLBORDER(d) + (x)))
 
@@ -62,24 +61,26 @@ static game_params *default_params(void)
     return ret;
 }
 
+static const struct game_params pattern_presets[] = {
+    {10, 10},
+    {15, 15},
+    {20, 20},
+#ifndef SLOW_SYSTEM
+    {25, 25},
+    {30, 30},
+#endif
+};
+
 static int game_fetch_preset(int i, char **name, game_params **params)
 {
     game_params *ret;
     char str[80];
-    static const struct { int x, y; } values[] = {
-        {10, 10},
-        {15, 15},
-        {20, 20},
-        {25, 25},
-        {30, 30},
-    };
-
-    if (i < 0 || i >= lenof(values))
+
+    if (i < 0 || i >= lenof(pattern_presets))
         return FALSE;
 
     ret = snew(game_params);
-    ret->w = values[i].x;
-    ret->h = values[i].y;
+    *ret = pattern_presets[i];
 
     sprintf(str, "%dx%d", ret->w, ret->h);
 
@@ -166,12 +167,8 @@ static game_params *custom_params(config_item *cfg)
 
 static char *validate_params(game_params *params)
 {
-    if (params->w <= 0 && params->h <= 0)
+    if (params->w <= 0 || params->h <= 0)
        return "Width and height must both be greater than zero";
-    if (params->w <= 0)
-       return "Width must be greater than zero";
-    if (params->h <= 0)
-       return "Height must be greater than zero";
     return NULL;
 }
 
@@ -469,13 +466,8 @@ static unsigned char *generate_soluble(random_state *rs, int w, int h)
     return grid;
 }
 
-struct game_aux_info {
-    int w, h;
-    unsigned char *grid;
-};
-
 static char *new_game_desc(game_params *params, random_state *rs,
-                          game_aux_info **aux, int interactive)
+                          char **aux, int interactive)
 {
     unsigned char *grid;
     int i, j, max, rowlen, *rowdata;
@@ -487,15 +479,22 @@ static char *new_game_desc(game_params *params, random_state *rs,
     rowdata = snewn(max, int);
 
     /*
-     * Save the solved game in an aux_info.
+     * Save the solved game in aux.
      */
     {
-       game_aux_info *ai = snew(game_aux_info);
+       char *ai = snewn(params->w * params->h + 2, char);
+
+        /*
+         * String format is exactly the same as a solve move, so we
+         * can just dupstr this in solve_game().
+         */
 
-       ai->w = params->w;
-       ai->h = params->h;
-       ai->grid = snewn(ai->w * ai->h, unsigned char);
-       memcpy(ai->grid, grid, ai->w * ai->h);
+        ai[0] = 'S';
+
+        for (i = 0; i < params->w * params->h; i++)
+            ai[i+1] = grid[i] ? '1' : '0';
+
+        ai[params->w * params->h + 1] = '\0';
 
        *aux = ai;
     }
@@ -550,15 +549,10 @@ static char *new_game_desc(game_params *params, random_state *rs,
     assert(desc[desclen-1] == '/');
     desc[desclen-1] = '\0';
     sfree(rowdata);
+    sfree(grid);
     return desc;
 }
 
-static void game_free_aux_info(game_aux_info *aux)
-{
-    sfree(aux->grid);
-    sfree(aux);
-}
-
 static char *validate_desc(game_params *params, char *desc)
 {
     int i, n, rowspace;
@@ -668,69 +662,67 @@ static void free_game(game_state *state)
     sfree(state);
 }
 
-static game_state *solve_game(game_state *state, game_aux_info *ai,
-                             char **error)
+static char *solve_game(game_state *state, game_state *currstate,
+                       char *ai, char **error)
 {
-    game_state *ret;
-
-    ret = dup_game(state);
-    ret->completed = ret->cheated = TRUE;
+    unsigned char *matrix;
+    int w = state->w, h = state->h;
+    int i;
+    char *ret;
+    int done_any, max;
+    unsigned char *workspace;
+    int *rowdata;
 
     /*
-     * If we already have the solved state in an aux_info, copy it
-     * out.
+     * If we already have the solved state in ai, copy it out.
      */
-    if (ai) {
+    if (ai)
+        return dupstr(ai);
 
-       assert(ret->w == ai->w);
-       assert(ret->h == ai->h);
-       memcpy(ret->grid, ai->grid, ai->w * ai->h);
+    matrix = snewn(w*h, unsigned char);
+    max = max(w, h);
+    workspace = snewn(max*3, unsigned char);
+    rowdata = snewn(max+1, int);
 
-    } else {
-       int w = state->w, h = state->h, i, j, done_any, max;
-       unsigned char *matrix, *workspace;
-       int *rowdata;
+    memset(matrix, 0, w*h);
 
-       matrix = snewn(w*h, unsigned char);
-       max = max(w, h);
-       workspace = snewn(max*3, unsigned char);
-       rowdata = snewn(max+1, int);
-
-        memset(matrix, 0, w*h);
+    do {
+        done_any = 0;
+        for (i=0; i<h; i++) {
+            memcpy(rowdata, state->rowdata + state->rowsize*(w+i),
+                   max*sizeof(int));
+            rowdata[state->rowlen[w+i]] = 0;
+            done_any |= do_row(workspace, workspace+max, workspace+2*max,
+                               matrix+i*w, w, 1, rowdata);
+        }
+        for (i=0; i<w; i++) {
+            memcpy(rowdata, state->rowdata + state->rowsize*i, max*sizeof(int));
+            rowdata[state->rowlen[i]] = 0;
+            done_any |= do_row(workspace, workspace+max, workspace+2*max,
+                               matrix+i, h, w, rowdata);
+        }
+    } while (done_any);
 
-        do {
-            done_any = 0;
-            for (i=0; i<h; i++) {
-               memcpy(rowdata, state->rowdata + state->rowsize*(w+i),
-                      max*sizeof(int));
-               rowdata[state->rowlen[w+i]] = 0;
-                done_any |= do_row(workspace, workspace+max, workspace+2*max,
-                                   matrix+i*w, w, 1, rowdata);
-            }
-            for (i=0; i<w; i++) {
-               memcpy(rowdata, state->rowdata + state->rowsize*i, max*sizeof(int));
-               rowdata[state->rowlen[i]] = 0;
-                done_any |= do_row(workspace, workspace+max, workspace+2*max,
-                                   matrix+i, h, w, rowdata);
-            }
-        } while (done_any);
+    sfree(workspace);
+    sfree(rowdata);
 
-       for (i = 0; i < h; i++) {
-           for (j = 0; j < w; j++) {
-               int c = (matrix[i*w+j] == BLOCK ? GRID_FULL :
-                        matrix[i*w+j] == DOT ? GRID_EMPTY : GRID_UNKNOWN);
-               ret->grid[i*w+j] = c;
-               if (c == GRID_UNKNOWN)
-                   ret->completed = FALSE;
-           }
-       }
+    for (i = 0; i < w*h; i++) {
+        if (matrix[i] != BLOCK && matrix[i] != DOT) {
+            sfree(matrix);
+            *error = "Solving algorithm cannot complete this puzzle";
+            return NULL;
+        }
+    }
 
-       if (!ret->completed) {
-           free_game(ret);
-           *error = "Solving algorithm cannot complete this puzzle";
-           return NULL;
-       }
+    ret = snewn(w*h+2, char);
+    ret[0] = 'S';
+    for (i = 0; i < w*h; i++) {
+       assert(matrix[i] == BLOCK || matrix[i] == DOT);
+       ret[i+1] = (matrix[i] == BLOCK ? '1' : '0');
     }
+    ret[w*h+1] = '\0';
+
+    sfree(matrix);
 
     return ret;
 }
@@ -764,17 +756,36 @@ static void free_ui(game_ui *ui)
     sfree(ui);
 }
 
-static game_state *make_move(game_state *from, game_ui *ui,
-                            int x, int y, int button)
+static char *encode_ui(game_ui *ui)
 {
-    game_state *ret;
+    return NULL;
+}
+
+static void decode_ui(game_ui *ui, char *encoding)
+{
+}
+
+static void game_changed_state(game_ui *ui, game_state *oldstate,
+                               game_state *newstate)
+{
+}
 
+struct game_drawstate {
+    int started;
+    int w, h;
+    int tilesize;
+    unsigned char *visible;
+};
+
+static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds,
+                           int x, int y, int button)
+{
     button &= ~MOD_MASK;
 
-    x = FROMCOORD(from->w, x);
-    y = FROMCOORD(from->h, y);
+    x = FROMCOORD(state->w, x);
+    y = FROMCOORD(state->h, y);
 
-    if (x >= 0 && x < from->w && y >= 0 && y < from->h &&
+    if (x >= 0 && x < state->w && y >= 0 && y < state->h &&
         (button == LEFT_BUTTON || button == RIGHT_BUTTON ||
          button == MIDDLE_BUTTON)) {
 
@@ -797,7 +808,7 @@ static game_state *make_move(game_state *from, game_ui *ui,
         ui->drag_start_x = ui->drag_end_x = x;
         ui->drag_start_y = ui->drag_end_y = y;
 
-        return from;                   /* UI activity occurred */
+        return "";                    /* UI activity occurred */
     }
 
     if (ui->dragging && button == ui->drag) {
@@ -820,13 +831,13 @@ static game_state *make_move(game_state *from, game_ui *ui,
 
         if (x < 0) x = 0;
         if (y < 0) y = 0;
-        if (x >= from->w) x = from->w - 1;
-        if (y >= from->h) y = from->h - 1;
+        if (x >= state->w) x = state->w - 1;
+        if (y >= state->h) y = state->h - 1;
 
         ui->drag_end_x = x;
         ui->drag_end_y = y;
 
-        return from;                   /* UI activity occurred */
+        return "";                    /* UI activity occurred */
     }
 
     if (ui->dragging && button == ui->release) {
@@ -840,75 +851,117 @@ static game_state *make_move(game_state *from, game_ui *ui,
 
         for (yy = y1; yy <= y2; yy++)
             for (xx = x1; xx <= x2; xx++)
-                if (from->grid[yy * from->w + xx] != ui->state)
+                if (state->grid[yy * state->w + xx] != ui->state)
                     move_needed = TRUE;
 
         ui->dragging = FALSE;
 
         if (move_needed) {
-            ret = dup_game(from);
-            for (yy = y1; yy <= y2; yy++)
-                for (xx = x1; xx <= x2; xx++)
-                    ret->grid[yy * ret->w + xx] = ui->state;
-
-            /*
-             * An actual change, so check to see if we've completed
-             * the game.
-             */
-            if (!ret->completed) {
-                int *rowdata = snewn(ret->rowsize, int);
-                int i, len;
-
-                ret->completed = TRUE;
-
-                for (i=0; i<ret->w; i++) {
-                    len = compute_rowdata(rowdata,
-                                          ret->grid+i, ret->h, ret->w);
-                    if (len != ret->rowlen[i] ||
-                        memcmp(ret->rowdata+i*ret->rowsize, rowdata,
-                               len * sizeof(int))) {
-                        ret->completed = FALSE;
-                        break;
-                    }
-                }
-                for (i=0; i<ret->h; i++) {
-                    len = compute_rowdata(rowdata,
-                                          ret->grid+i*ret->w, ret->w, 1);
-                    if (len != ret->rowlen[i+ret->w] ||
-                        memcmp(ret->rowdata+(i+ret->w)*ret->rowsize, rowdata,
-                               len * sizeof(int))) {
-                        ret->completed = FALSE;
-                        break;
-                    }
-                }
-
-                sfree(rowdata);
-            }
-
-            return ret;
+           char buf[80];
+           sprintf(buf, "%c%d,%d,%d,%d",
+                   (char)(ui->state == GRID_FULL ? 'F' :
+                          ui->state == GRID_EMPTY ? 'E' : 'U'),
+                   x1, y1, x2-x1+1, y2-y1+1);
+           return dupstr(buf);
         } else
-            return from;               /* UI activity occurred */
+            return "";                /* UI activity occurred */
     }
 
     return NULL;
 }
 
+static game_state *execute_move(game_state *from, char *move)
+{
+    game_state *ret;
+    int x1, x2, y1, y2, xx, yy;
+    int val;
+
+    if (move[0] == 'S' && strlen(move) == from->w * from->h + 1) {
+       int i;
+
+       ret = dup_game(from);
+
+       for (i = 0; i < ret->w * ret->h; i++)
+           ret->grid[i] = (move[i+1] == '1' ? GRID_FULL : GRID_EMPTY);
+
+       ret->completed = ret->cheated = TRUE;
+
+       return ret;
+    } else if ((move[0] == 'F' || move[0] == 'E' || move[0] == 'U') &&
+       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;
+       val = (move[0] == 'F' ? GRID_FULL :
+                move[0] == 'E' ? GRID_EMPTY : GRID_UNKNOWN);
+
+       ret = dup_game(from);
+       for (yy = y1; yy < y2; yy++)
+           for (xx = x1; xx < x2; xx++)
+               ret->grid[yy * ret->w + xx] = val;
+
+       /*
+        * An actual change, so check to see if we've completed the
+        * game.
+        */
+       if (!ret->completed) {
+           int *rowdata = snewn(ret->rowsize, int);
+           int i, len;
+
+           ret->completed = TRUE;
+
+           for (i=0; i<ret->w; i++) {
+               len = compute_rowdata(rowdata,
+                                     ret->grid+i, ret->h, ret->w);
+               if (len != ret->rowlen[i] ||
+                   memcmp(ret->rowdata+i*ret->rowsize, rowdata,
+                          len * sizeof(int))) {
+                   ret->completed = FALSE;
+                   break;
+               }
+           }
+           for (i=0; i<ret->h; i++) {
+               len = compute_rowdata(rowdata,
+                                     ret->grid+i*ret->w, ret->w, 1);
+               if (len != ret->rowlen[i+ret->w] ||
+                   memcmp(ret->rowdata+(i+ret->w)*ret->rowsize, rowdata,
+                          len * sizeof(int))) {
+                   ret->completed = FALSE;
+                   break;
+               }
+           }
+
+           sfree(rowdata);
+       }
+
+       return ret;
+    } else
+       return NULL;
+}
+
 /* ----------------------------------------------------------------------
  * Drawing routines.
  */
 
-struct game_drawstate {
-    int started;
-    int w, h;
-    unsigned char *visible;
-};
-
-static void game_size(game_params *params, int *x, int *y)
+static void game_compute_size(game_params *params, int tilesize,
+                             int *x, int *y)
 {
+    /* Ick: fake up `ds->tilesize' for macro expansion purposes */
+    struct { int tilesize; } ads, *ds = &ads;
+    ads.tilesize = tilesize;
+
     *x = SIZE(params->w);
     *y = SIZE(params->h);
 }
 
+static void game_set_size(game_drawstate *ds, game_params *params,
+                         int tilesize)
+{
+    ds->tilesize = tilesize;
+}
+
 static float *game_colours(frontend *fe, game_state *state, int *ncolours)
 {
     float *ret = snewn(3 * NCOLOURS, float);
@@ -943,6 +996,7 @@ static game_drawstate *game_new_drawstate(game_state *state)
     ds->w = state->w;
     ds->h = state->h;
     ds->visible = snewn(ds->w * ds->h, unsigned char);
+    ds->tilesize = 0;                  /* not decided yet */
     memset(ds->visible, 255, ds->w * ds->h);
 
     return ds;
@@ -977,8 +1031,8 @@ static void grid_square(frontend *fe, game_drawstate *ds,
 }
 
 static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
-                 game_state *state, int dir, game_ui *ui,
-                 float animtime, float flashtime)
+                        game_state *state, int dir, game_ui *ui,
+                        float animtime, float flashtime)
 {
     int i, j;
     int x1, x2, y1, y2;
@@ -1125,7 +1179,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,
@@ -1134,8 +1187,12 @@ const struct game thegame = {
     FALSE, game_text_format,
     new_ui,
     free_ui,
-    make_move,
-    game_size,
+    encode_ui,
+    decode_ui,
+    game_changed_state,
+    interpret_move,
+    execute_move,
+    PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
     game_colours,
     game_new_drawstate,
     game_free_drawstate,
@@ -1144,6 +1201,7 @@ const struct game thegame = {
     game_flash_length,
     game_wants_statusbar,
     FALSE, game_timing_state,
+    0,                                /* mouse_priorities */
 };
 
 #ifdef STANDALONE_SOLVER
@@ -1159,8 +1217,6 @@ void draw_text(frontend *fe, int x, int y, int fonttype, int fontsize,
                int align, int colour, char *text) {}
 void draw_rect(frontend *fe, int x, int y, int w, int h, int colour) {}
 void draw_line(frontend *fe, int x1, int y1, int x2, int y2, int colour) {}
-void draw_polygon(frontend *fe, int *coords, int npoints,
-                  int fill, int colour) {}
 void clip(frontend *fe, int x, int y, int w, int h) {}
 void unclip(frontend *fe) {}
 void start_draw(frontend *fe) {}
@@ -1187,15 +1243,12 @@ int main(int argc, char **argv)
 {
     game_params *p;
     game_state *s;
-    int recurse = TRUE;
     char *id = NULL, *desc, *err;
-    int y, x;
-    int grade = FALSE;
 
     while (--argc > 0) {
         char *p = *++argv;
        if (*p == '-') {
-            fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0]);
+            fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p);
             return 1;
         } else {
             id = p;
@@ -1221,7 +1274,7 @@ int main(int argc, char **argv)
         fprintf(stderr, "%s: %s\n", argv[0], err);
         return 1;
     }
-    s = new_game(p, desc);
+    s = new_game(NULL, p, desc);
 
     {
        int w = p->w, h = p->h, i, j, done_any, max;