chiark / gitweb /
Better mouse button handling in Mines:
[sgt-puzzles.git] / netslide.c
index 6ae956902b85e421614cf1578aeaa097a120e8db..5697b2ffaa9dcd0429799d0300ee188bc4a91803 100644 (file)
@@ -13,8 +13,6 @@
 #include "puzzles.h"
 #include "tree234.h"
 
-#define PI 3.141592653589793238462643383279502884197169399
-
 #define MATMUL(xr,yr,m,x,y) do { \
     float rx, ry, xx = (x), yy = (y), *mat = (m); \
     rx = mat[0] * xx + mat[2] * yy; \
@@ -80,6 +78,7 @@ struct game_params {
     int height;
     int wrapping;
     float barrier_probability;
+    int movetarget;
 };
 
 struct game_aux_info {
@@ -90,7 +89,7 @@ struct game_aux_info {
 struct game_state {
     int width, height, cx, cy, wrapping, completed;
     int used_solve, just_used_solve;
-    int move_count;
+    int move_count, movetarget;
 
     /* position (row or col number, starting at 0) of last move. */
     int last_move_row, last_move_col;
@@ -157,6 +156,7 @@ static game_params *default_params(void)
     ret->height = 3;
     ret->wrapping = FALSE;
     ret->barrier_probability = 1.0;
+    ret->movetarget = 0;
 
     return ret;
 }
@@ -185,6 +185,7 @@ static int game_fetch_preset(int i, char **name, game_params **params)
     ret->height = values[i].y;
     ret->wrapping = values[i].wrap;
     ret->barrier_probability = values[i].bprob;
+    ret->movetarget = 0;
 
     sprintf(str, "%dx%d%s", ret->width, ret->height,
             values[i].desc);
@@ -212,6 +213,7 @@ static void decode_params(game_params *ret, char const *string)
 
     ret->wrapping = FALSE;
     ret->barrier_probability = 0.0;
+    ret->movetarget = 0;
 
     ret->width = atoi(p);
     while (*p && isdigit(*p)) p++;
@@ -221,8 +223,13 @@ static void decode_params(game_params *ret, char const *string)
         while (*p && isdigit(*p)) p++;
         if ( (ret->wrapping = (*p == 'w')) != 0 )
             p++;
-        if (*p == 'b')
-            ret->barrier_probability = atof(p+1);
+        if (*p == 'b') {
+            ret->barrier_probability = atof(++p);
+            while (*p && (isdigit(*p) || *p == '.')) p++;
+        }
+        if (*p == 'm') {
+            ret->movetarget = atoi(++p);
+        }
     } else {
         ret->height = ret->width;
     }
@@ -238,6 +245,10 @@ static char *encode_params(game_params *params, int full)
         ret[len++] = 'w';
     if (full && params->barrier_probability)
         len += sprintf(ret+len, "b%g", params->barrier_probability);
+    /* Shuffle limit is part of the limited parameters, because we have to
+     * provide the target move count. */
+    if (params->movetarget)
+        len += sprintf(ret+len, "m%d", params->movetarget);
     assert(len < lenof(ret));
     ret[len] = '\0';
 
@@ -249,7 +260,7 @@ static config_item *game_configure(game_params *params)
     config_item *ret;
     char buf[80];
 
-    ret = snewn(5, config_item);
+    ret = snewn(6, config_item);
 
     ret[0].name = "Width";
     ret[0].type = C_STRING;
@@ -274,11 +285,17 @@ static config_item *game_configure(game_params *params)
     ret[3].sval = dupstr(buf);
     ret[3].ival = 0;
 
-    ret[4].name = NULL;
-    ret[4].type = C_END;
-    ret[4].sval = NULL;
+    ret[4].name = "Number of shuffling moves";
+    ret[4].type = C_STRING;
+    sprintf(buf, "%d", params->movetarget);
+    ret[4].sval = dupstr(buf);
     ret[4].ival = 0;
 
+    ret[5].name = NULL;
+    ret[5].type = C_END;
+    ret[5].sval = NULL;
+    ret[5].ival = 0;
+
     return ret;
 }
 
@@ -290,6 +307,7 @@ static game_params *custom_params(config_item *cfg)
     ret->height = atoi(cfg[1].sval);
     ret->wrapping = cfg[2].ival;
     ret->barrier_probability = (float)atof(cfg[3].sval);
+    ret->movetarget = atoi(cfg[4].sval);
 
     return ret;
 }
@@ -314,7 +332,7 @@ static char *validate_params(game_params *params)
  */
 
 static char *new_game_desc(game_params *params, random_state *rs,
-                          game_aux_info **aux)
+                          game_aux_info **aux, int interactive)
 {
     tree234 *possibilities, *barriertree;
     int w, h, x, y, cx, cy, nbarriers;
@@ -538,7 +556,11 @@ static char *new_game_desc(game_params *params, random_state *rs,
 
     /*
      * Now shuffle the grid.
-     * FIXME - this simply does a set of random moves to shuffle the pieces.
+     * FIXME - this simply does a set of random moves to shuffle the pieces,
+     * although we make a token effort to avoid boring cases by avoiding moves
+     * that directly undo the previous one, or that repeat so often as to
+     * turn into fewer moves.
+     *
      * A better way would be to number all the pieces, generate a placement
      * for all the numbers as for "sixteen", observing parity constraints if
      * neccessary, and then place the pieces according to their numbering.
@@ -549,18 +571,43 @@ static char *new_game_desc(game_params *params, random_state *rs,
         int i;
         int cols = w - 1;
         int rows = h - 1;
-        for (i = 0; i < cols * rows * 2; i++) {
+        int moves = params->movetarget;
+        int prevdir = -1, prevrowcol = -1, nrepeats = 0;
+        if (!moves) moves = cols * rows * 2;
+        for (i = 0; i < moves; /* incremented conditionally */) {
             /* Choose a direction: 0,1,2,3 = up, right, down, left. */
             int dir = random_upto(rs, 4);
+            int rowcol;
             if (dir % 2 == 0) {
                 int col = random_upto(rs, cols);
-                if (col >= cx) col += 1;
+                if (col >= cx) col += 1;    /* avoid centre */
+                if (col == prevrowcol) {
+                    if (dir == 2-prevdir)
+                        continue;   /* undoes last move */
+                    else if ((nrepeats+1)*2 > h)
+                        continue;   /* makes fewer moves */
+                }
                 slide_col_int(w, h, tiles, 1 - dir, col);
+                rowcol = col;
             } else {
                 int row = random_upto(rs, rows);
-                if (row >= cy) row += 1;
+                if (row >= cy) row += 1;    /* avoid centre */
+                if (row == prevrowcol) {
+                    if (dir == 4-prevdir)
+                        continue;   /* undoes last move */
+                    else if ((nrepeats+1)*2 > w)
+                        continue;   /* makes fewer moves */
+                }
                 slide_row_int(w, h, tiles, 2 - dir, row);
+                rowcol = row;
             }
+            if (dir == prevdir && rowcol == prevrowcol)
+                nrepeats++;
+            else
+                nrepeats = 1;
+            prevdir = dir;
+            prevrowcol = rowcol;
+            i++;    /* if we got here, the move was accepted */
         }
     }
 
@@ -645,6 +692,7 @@ static char *new_game_desc(game_params *params, random_state *rs,
         }
     }
     assert(p - desc <= w*h*3);
+    *p = '\0';
 
     sfree(tiles);
     sfree(barriers);
@@ -688,7 +736,7 @@ static char *validate_desc(game_params *params, char *desc)
  * Construct an initial game state, given a description and parameters.
  */
 
-static game_state *new_game(game_params *params, char *desc)
+static game_state *new_game(midend_data *me, game_params *params, char *desc)
 {
     game_state *state;
     int w, h, x, y;
@@ -705,6 +753,7 @@ static game_state *new_game(game_params *params, char *desc)
     state->cx = state->width / 2;
     state->cy = state->height / 2;
     state->wrapping = params->wrapping;
+    state->movetarget = params->movetarget;
     state->completed = 0;
     state->used_solve = state->just_used_solve = FALSE;
     state->move_count = 0;
@@ -823,6 +872,7 @@ static game_state *dup_game(game_state *state)
     ret->cx = state->cx;
     ret->cy = state->cy;
     ret->wrapping = state->wrapping;
+    ret->movetarget = state->movetarget;
     ret->completed = state->completed;
     ret->used_solve = state->used_solve;
     ret->just_used_solve = state->just_used_solve;
@@ -999,12 +1049,14 @@ static void slide_col(game_state *state, int dir, int col)
 }
 
 static game_state *make_move(game_state *state, game_ui *ui,
-                            int x, int y, int button)
+                             game_drawstate *ds, int x, int y, int button)
 {
     int cx, cy;
     int n, dx, dy;
     game_state *ret;
 
+    button &= ~MOD_MASK;
+
     if (button != LEFT_BUTTON && button != RIGHT_BUTTON)
         return NULL;
 
@@ -1616,6 +1668,10 @@ static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
                    (state->completed ? "COMPLETED! " : ""),
                    (state->completed ? state->completed : state->move_count));
 
+        if (state->movetarget)
+            sprintf(statusbuf + strlen(statusbuf), " (target %d)",
+                    state->movetarget);
+
        sprintf(statusbuf + strlen(statusbuf), " Active: %d/%d", a, n);
 
        status_bar(fe, statusbuf);
@@ -1625,7 +1681,7 @@ static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
 }
 
 static float game_anim_length(game_state *oldstate,
-                             game_state *newstate, int dir)
+                             game_state *newstate, int dir, game_ui *ui)
 {
     /*
      * Don't animate an auto-solve move.
@@ -1638,7 +1694,7 @@ static float game_anim_length(game_state *oldstate,
 }
 
 static float game_flash_length(game_state *oldstate,
-                              game_state *newstate, int dir)
+                              game_state *newstate, int dir, game_ui *ui)
 {
     /*
      * If the game has just been completed, we display a completion
@@ -1667,6 +1723,11 @@ static int game_wants_statusbar(void)
     return TRUE;
 }
 
+static int game_timing_state(game_state *state)
+{
+    return FALSE;
+}
+
 #ifdef COMBINED
 #define thegame netslide
 #endif
@@ -1700,4 +1761,6 @@ const struct game thegame = {
     game_anim_length,
     game_flash_length,
     game_wants_statusbar,
+    FALSE, game_timing_state,
+    0,                                /* mouse_priorities */
 };