Add a `jumble' key (`J') to Net, which scrambles the positions of all unlocked
authorjacob <jacob@cda61777-01e9-0310-a592-d414129be87e>
Wed, 22 Dec 2004 19:27:26 +0000 (19:27 +0000)
committerjacob <jacob@cda61777-01e9-0310-a592-d414129be87e>
Wed, 22 Dec 2004 19:27:26 +0000 (19:27 +0000)
tiles randomly. (Rachel asked for this; it's been being tested for a good few
months now, and Simon didn't care either way, so in it goes :)

As part of this, the front end can now be asked to provide a random random
seed (IYSWIM).

git-svn-id: svn://svn.tartarus.org/sgt/puzzles@5019 cda61777-01e9-0310-a592-d414129be87e

gtk.c
midend.c
net.c
puzzles.but
puzzles.h
windows.c

diff --git a/gtk.c b/gtk.c
index 72fdcdd..d27f739 100644 (file)
--- a/gtk.c
+++ b/gtk.c
@@ -73,6 +73,14 @@ struct frontend {
     GtkWidget *cfgbox;
 };
 
+void get_random_seed(void **randseed, int *randseedsize)
+{
+    time_t *tp = snew(time_t);
+    time(tp);
+    *randseed = (void *)tp;
+    *randseedsize = sizeof(time_t);
+}
+
 void frontend_default_colour(frontend *fe, float *output)
 {
     GdkColor col = fe->window->style->bg[GTK_STATE_NORMAL];
@@ -772,12 +780,10 @@ static frontend *new_window(char *game_id, char **error)
     GtkBox *vbox;
     GtkWidget *menubar, *menu, *menuitem;
     int x, y, n;
-    time_t t;
 
     fe = snew(frontend);
 
-    time(&t);
-    fe->me = midend_new(fe, &t, sizeof(t));
+    fe->me = midend_new(fe);
     if (game_id) {
         *error = midend_game_id(fe->me, game_id, FALSE);
         if (*error) {
index a9cd768..4636fa0 100644 (file)
--- a/midend.c
+++ b/midend.c
@@ -40,9 +40,13 @@ struct midend_data {
     } \
 } while (0)
 
-midend_data *midend_new(frontend *fe, void *randseed, int randseedsize)
+midend_data *midend_new(frontend *fe)
 {
     midend_data *me = snew(midend_data);
+    void *randseed;
+    int randseedsize;
+
+    get_random_seed(&randseed, &randseedsize);
 
     me->frontend = fe;
     me->random = random_init(randseed, randseedsize);
@@ -61,6 +65,8 @@ midend_data *midend_new(frontend *fe, void *randseed, int randseedsize)
     me->dir = 0;
     me->ui = NULL;
 
+    sfree(randseed);
+
     return me;
 }
 
diff --git a/net.c b/net.c
index bb9218f..6849dbc 100644 (file)
--- a/net.c
+++ b/net.c
@@ -344,7 +344,7 @@ game_state *new_game(game_params *params, char *seed)
     state->cx = state->width / 2;
     state->cy = state->height / 2;
     state->wrapping = params->wrapping;
-    state->last_rotate_dir = +1;       /* *shrug* */
+    state->last_rotate_dir = 0;
     state->completed = FALSE;
     state->tiles = snewn(state->width * state->height, unsigned char);
     memset(state->tiles, 0, state->width * state->height);
@@ -767,20 +767,27 @@ static unsigned char *compute_active(game_state *state)
 struct game_ui {
     int cur_x, cur_y;
     int cur_visible;
+    random_state *rs; /* used for jumbling */
 };
 
 game_ui *new_ui(game_state *state)
 {
+    void *seed;
+    int seedsize;
     game_ui *ui = snew(game_ui);
     ui->cur_x = state->width / 2;
     ui->cur_y = state->height / 2;
     ui->cur_visible = FALSE;
+    get_random_seed(&seed, &seedsize);
+    ui->rs = random_init(seed, seedsize);
+    sfree(seed);
 
     return ui;
 }
 
 void free_ui(game_ui *ui)
 {
+    random_free(ui->rs);
     sfree(ui);
 }
 
@@ -842,6 +849,10 @@ game_state *make_move(game_state *state, game_ui *ui, int x, int y, int button)
        else if (button == 'd' || button == 'D')
            button = RIGHT_BUTTON;
         ui->cur_visible = TRUE;
+    } else if (button == 'j' || button == 'J') {
+       /* XXX should we have some mouse control for this? */
+       button = 'J';   /* canonify */
+       tx = ty = -1;   /* shut gcc up :( */
     } else
        return nullret;
 
@@ -856,31 +867,54 @@ game_state *make_move(game_state *state, game_ui *ui, int x, int y, int button)
      * unlocks it.)
      */
     if (button == MIDDLE_BUTTON) {
+
        ret = dup_game(state);
        tile(ret, tx, ty) ^= LOCKED;
+       ret->last_rotate_dir = 0;
        return ret;
-    }
 
-    /*
-     * The left and right buttons have no effect if clicked on a
-     * locked tile.
-     */
-    if (tile(state, tx, ty) & LOCKED)
-       return nullret;
+    } else if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
 
-    /*
-     * Otherwise, turn the tile one way or the other. Left button
-     * turns anticlockwise; right button turns clockwise.
-     */
-    ret = dup_game(state);
-    orig = tile(ret, tx, ty);
-    if (button == LEFT_BUTTON) {
-       tile(ret, tx, ty) = A(orig);
-        ret->last_rotate_dir = +1;
-    } else {
-       tile(ret, tx, ty) = C(orig);
-        ret->last_rotate_dir = -1;
-    }
+        /*
+         * The left and right buttons have no effect if clicked on a
+         * locked tile.
+         */
+        if (tile(state, tx, ty) & LOCKED)
+            return nullret;
+
+        /*
+         * Otherwise, turn the tile one way or the other. Left button
+         * turns anticlockwise; right button turns clockwise.
+         */
+        ret = dup_game(state);
+        orig = tile(ret, tx, ty);
+        if (button == LEFT_BUTTON) {
+            tile(ret, tx, ty) = A(orig);
+            ret->last_rotate_dir = +1;
+        } else {
+            tile(ret, tx, ty) = C(orig);
+            ret->last_rotate_dir = -1;
+        }
+
+    } else if (button == 'J') {
+
+        /*
+         * Jumble all unlocked tiles to random orientations.
+         */
+        int jx, jy;
+        ret = dup_game(state);
+        for (jy = 0; jy < ret->height; jy++) {
+            for (jx = 0; jx < ret->width; jx++) {
+                if (!(tile(ret, jx, jy) & LOCKED)) {
+                    int rot = random_upto(ui->rs, 4);
+                    orig = tile(ret, jx, jy);
+                    tile(ret, jx, jy) = ROT(orig, rot);
+                }
+            }
+        }
+        ret->last_rotate_dir = 0; /* suppress animation */
+
+    } else assert(0);
 
     /*
      * Check whether the game has been completed.
@@ -1253,7 +1287,7 @@ static void draw_tile(frontend *fe, game_state *state, int x, int y, int tile,
 void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
                  game_state *state, int dir, game_ui *ui, float t, float ft)
 {
-    int x, y, tx, ty, frame;
+    int x, y, tx, ty, frame, last_rotate_dir;
     unsigned char *active;
     float angle = 0.0;
 
@@ -1309,9 +1343,11 @@ void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
     }
 
     tx = ty = -1;
-    if (oldstate && (t < ROTATE_TIME)) {
+    last_rotate_dir = dir==-1 ? oldstate->last_rotate_dir :
+                                state->last_rotate_dir;
+    if (oldstate && (t < ROTATE_TIME) && last_rotate_dir) {
         /*
-         * We're animating a tile rotation. Find the turning tile,
+         * We're animating a single tile rotation. Find the turning tile,
          * if any.
          */
         for (x = 0; x < oldstate->width; x++)
@@ -1323,8 +1359,6 @@ void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
         break_label:
 
         if (tx >= 0) {
-            int last_rotate_dir = dir==-1 ? oldstate->last_rotate_dir :
-                                            state->last_rotate_dir;
             angle = last_rotate_dir * dir * 90.0F * (t / ROTATE_TIME);
             state = oldstate;
         }
@@ -1404,17 +1438,26 @@ void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
 
 float game_anim_length(game_state *oldstate, game_state *newstate, int dir)
 {
-    int x, y;
+    int x, y, last_rotate_dir;
 
     /*
-     * If there's a tile which has been rotated, allow time to
-     * animate its rotation.
+     * Don't animate if last_rotate_dir is zero.
      */
-    for (x = 0; x < oldstate->width; x++)
-        for (y = 0; y < oldstate->height; y++)
-            if ((tile(oldstate, x, y) ^ tile(newstate, x, y)) & 0xF) {
-                return ROTATE_TIME;
-            }
+    last_rotate_dir = dir==-1 ? oldstate->last_rotate_dir :
+                                newstate->last_rotate_dir;
+    if (last_rotate_dir) {
+
+        /*
+         * If there's a tile which has been rotated, allow time to
+         * animate its rotation.
+         */
+        for (x = 0; x < oldstate->width; x++)
+            for (y = 0; y < oldstate->height; y++)
+                if ((tile(oldstate, x, y) ^ tile(newstate, x, y)) & 0xF) {
+                    return ROTATE_TIME;
+                }
+
+    }
 
     return 0.0F;
 }
index 44e1ae3..de0bac3 100644 (file)
@@ -203,6 +203,11 @@ controls are:
 also unlock it again, but while it's locked you can't accidentally
 turn it.
 
+\dt \e{Jumble tiles}: \q{J} key
+
+\dd This key turns all tiles that are not locked to random
+orientations.
+
 (All the actions described in \k{common-actions} are also available.)
 
 \H{net-params} \I{parameters, for Net}Net parameters
index b0947fc..027a973 100644 (file)
--- a/puzzles.h
+++ b/puzzles.h
@@ -106,11 +106,12 @@ void end_draw(frontend *fe);
 void deactivate_timer(frontend *fe);
 void activate_timer(frontend *fe);
 void status_bar(frontend *fe, char *text);
+void get_random_seed(void **randseed, int *randseedsize);
 
 /*
  * midend.c
  */
-midend_data *midend_new(frontend *fe, void *randseed, int randseedsize);
+midend_data *midend_new(frontend *fe);
 void midend_free(midend_data *me);
 void midend_set_params(midend_data *me, game_params *params);
 void midend_size(midend_data *me, int *x, int *y);
index 494caae..ac5e20c 100644 (file)
--- a/windows.c
+++ b/windows.c
@@ -121,6 +121,14 @@ void fatal(char *fmt, ...)
     exit(1);
 }
 
+void get_random_seed(void **randseed, int *randseedsize)
+{
+    time_t *tp = snew(time_t);
+    time(tp);
+    *randseed = (void *)tp;
+    *randseedsize = sizeof(time_t);
+}
+
 void status_bar(frontend *fe, char *text)
 {
     SetWindowText(fe->statusbar, text);
@@ -352,12 +360,10 @@ static frontend *new_window(HINSTANCE inst, char *game_id, char **error)
     int x, y;
     RECT r, sr;
     HDC hdc;
-    time_t t;
 
     fe = snew(frontend);
 
-    time(&t);
-    fe->me = midend_new(fe, &t, sizeof(t));
+    fe->me = midend_new(fe);
 
     if (game_id) {
         *error = midend_game_id(fe->me, game_id, FALSE);