chiark / gitweb /
The Windows RNG turns out to only give about 16 bits at a time. This
authorSimon Tatham <anakin@pobox.com>
Mon, 3 May 2004 09:10:52 +0000 (09:10 +0000)
committerSimon Tatham <anakin@pobox.com>
Mon, 3 May 2004 09:10:52 +0000 (09:10 +0000)
is (a) pretty feeble, and (b) means that although Net seeds transfer
between platforms and still generate the same game, there's a
suspicious discrepancy in the typical seed _generated_ by each
platform.
I have a better RNG kicking around in this code base already, so
I'll just use it. Each midend has its own random_state, which it
passes to new_game_seed() as required. A handy consequence of this
is that initial seed data is now passed to midend_new(), which means
that new platform implementors are unlikely to forget to seed the
RNG because failure to do so causes a compile error!

[originally from svn r4187]

12 files changed:
Recipe
cube.c
fifteen.c
gtk.c
midend.c
misc.c
net.c
nullgame.c
puzzles.h
random.c
sixteen.c
windows.c

diff --git a/Recipe b/Recipe
index a2a5bfcb20e81c2ba54bd39d3b2a5e54cd1e04c7..9d7c1e90fbed9838d74673390904a565f090b81b 100644 (file)
--- a/Recipe
+++ b/Recipe
@@ -13,8 +13,8 @@
 !makefile cygwin Makefile.cyg
 
 WINDOWS  = windows user32.lib gdi32.lib comctl32.lib
-COMMON   = midend misc malloc
-NET      = net random tree234
+COMMON   = midend misc malloc random
+NET      = net tree234
 
 net      : [X] gtk COMMON NET
 cube     : [X] gtk COMMON cube
diff --git a/cube.c b/cube.c
index 6c2658f520431704947e3187b17ef41f9db90fc6..ccc8689322849f3ec3c1d30083a45c2c7d9cee55 100644 (file)
--- a/cube.c
+++ b/cube.c
@@ -560,7 +560,7 @@ static void classify_grid_square_callback(void *ctx, struct grid_square *sq)
        data->squareindex++;
 }
 
-char *new_game_seed(game_params *params)
+char *new_game_seed(game_params *params, random_state *rs)
 {
     struct grid_data data;
     int i, j, k, m, area, facesperclass;
@@ -605,7 +605,7 @@ char *new_game_seed(game_params *params)
 
     for (i = 0; i < data.nclasses; i++) {
        for (j = 0; j < facesperclass; j++) {
-            int n = rand_upto(data.nsquares[i]);
+            int n = random_upto(rs, data.nsquares[i]);
 
            assert(!flags[data.gridptrs[i][n]]);
            flags[data.gridptrs[i][n]] = TRUE;
@@ -653,7 +653,7 @@ char *new_game_seed(game_params *params)
     /*
      * Choose a non-blue square for the polyhedron.
      */
-    sprintf(p, ":%d", data.gridptrs[0][rand_upto(m)]);
+    sprintf(p, ":%d", data.gridptrs[0][random_upto(rs, m)]);
 
     sfree(data.gridptrs[0]);
     sfree(flags);
index 74fd4fc5970db564237d7a31e9d8ef601ab69b02..3965844da94baf8909d90101d12a44cd84a2d7c5 100644 (file)
--- a/fifteen.c
+++ b/fifteen.c
@@ -131,7 +131,7 @@ int perm_parity(int *perm, int n)
     return ret;
 }
 
-char *new_game_seed(game_params *params)
+char *new_game_seed(game_params *params, random_state *rs)
 {
     int gap, n, i, x;
     int x1, x2, p1, p2, parity;
@@ -149,7 +149,7 @@ char *new_game_seed(game_params *params)
         used[i] = FALSE;
     }
 
-    gap = rand_upto(n);
+    gap = random_upto(rs, n);
     tiles[gap] = 0;
     used[0] = TRUE;
 
@@ -157,7 +157,7 @@ char *new_game_seed(game_params *params)
      * Place everything else except the last two tiles.
      */
     for (x = 0, i = n-1; i > 2; i--) {
-        int k = rand_upto(i);
+        int k = random_upto(rs, i);
         int j;
 
         for (j = 0; j < n; j++)
diff --git a/gtk.c b/gtk.c
index 6906f6c3061f129ac5195924b9823161eb720c14..5cbdbe4add9ca33c863aed848948a3100daa2004 100644 (file)
--- a/gtk.c
+++ b/gtk.c
@@ -688,10 +688,12 @@ static frontend *new_window(void)
     GtkBox *vbox;
     GtkWidget *menubar, *menu, *menuitem;
     int x, y, n;
+    time_t t;
 
     fe = snew(frontend);
 
-    fe->me = midend_new(fe);
+    time(&t);
+    fe->me = midend_new(fe, &t, sizeof(t));
     midend_new_game(fe->me);
 
     fe->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
@@ -855,8 +857,6 @@ static frontend *new_window(void)
 
 int main(int argc, char **argv)
 {
-    srand(time(NULL));
-
     gtk_init(&argc, &argv);
     (void) new_window();
     gtk_main();
index 818b2785884663d3a5ada7bb08126b31117c97b4..cacd66e4a752a2af83c192b1dd352d4ba7a82851 100644 (file)
--- a/midend.c
+++ b/midend.c
@@ -13,6 +13,8 @@
 
 struct midend_data {
     frontend *frontend;
+    random_state *random;
+
     char *seed;
     int fresh_seed;
     int nstates, statesize, statepos;
@@ -36,11 +38,12 @@ struct midend_data {
     } \
 } while (0)
 
-midend_data *midend_new(frontend *frontend)
+midend_data *midend_new(frontend *fe, void *randseed, int randseedsize)
 {
     midend_data *me = snew(midend_data);
 
-    me->frontend = frontend;
+    me->frontend = fe;
+    me->random = random_init(randseed, randseedsize);
     me->nstates = me->statesize = me->statepos = 0;
     me->states = NULL;
     me->params = default_params();
@@ -88,7 +91,7 @@ void midend_new_game(midend_data *me)
 
     if (!me->fresh_seed) {
        sfree(me->seed);
-       me->seed = new_game_seed(me->params);
+       me->seed = new_game_seed(me->params, me->random);
     } else
        me->fresh_seed = FALSE;
 
@@ -252,7 +255,7 @@ float *midend_colours(midend_data *me, int *ncolours)
     float *ret;
 
     if (me->nstates == 0) {
-        char *seed = new_game_seed(me->params);
+        char *seed = new_game_seed(me->params, me->random);
         state = new_game(me->params, seed);
         sfree(seed);
     } else
diff --git a/misc.c b/misc.c
index 48088da140d3edb011222511bdff33cbd6150f24..73b9a7ba48097e1647ef9bbb75623fe0828b1478 100644 (file)
--- a/misc.c
+++ b/misc.c
@@ -7,23 +7,6 @@
 
 #include "puzzles.h"
 
-int rand_upto(int limit)
-{
-    unsigned long divisor = RAND_MAX / (unsigned)limit;
-    unsigned long max = divisor * (unsigned)limit;
-    unsigned long n;
-
-    assert(limit > 0);
-
-    do {
-        n = rand();
-    } while (n >= max);
-
-    n /= divisor;
-
-    return (int)n;
-}
-
 void free_cfg(config_item *cfg)
 {
     config_item *i;
diff --git a/net.c b/net.c
index cf3fe3dcbb00379981acf727c83d322002958720..8b74e7b1d28486ad4661524701553d3556ebe8bf 100644 (file)
--- a/net.c
+++ b/net.c
@@ -254,7 +254,7 @@ char *validate_params(game_params *params)
  * Randomly select a new game seed.
  */
 
-char *new_game_seed(game_params *params)
+char *new_game_seed(game_params *params, random_state *rs)
 {
     /*
      * The full description of a Net game is far too large to
@@ -268,7 +268,7 @@ char *new_game_seed(game_params *params)
      * understand it and do something completely different.)
      */
     char buf[40];
-    sprintf(buf, "%d", rand());
+    sprintf(buf, "%lu", random_bits(rs, 32));
     return dupstr(buf);
 }
 
index bc0c3866aa5a731f2b127197be3aa740200b7625..8df09e9967b6a51bae4a07eb8ca51be6c0a8ae9b 100644 (file)
@@ -76,7 +76,7 @@ char *validate_params(game_params *params)
     return NULL;
 }
 
-char *new_game_seed(game_params *params)
+char *new_game_seed(game_params *params, random_state *rs)
 {
     return dupstr("FIXME");
 }
index 10250a0f4c2a6901d3bb2ae3b51773c697345dde..035ff24a1770cf8679bcb4fb96162a38cb5d06af 100644 (file)
--- a/puzzles.h
+++ b/puzzles.h
@@ -103,7 +103,7 @@ void status_bar(frontend *fe, char *text);
 /*
  * midend.c
  */
-midend_data *midend_new(frontend *fe);
+midend_data *midend_new(frontend *fe, void *randseed, int randseedsize);
 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);
@@ -138,13 +138,13 @@ char *dupstr(char *s);
 /*
  * misc.c
  */
-int rand_upto(int limit);
 void free_cfg(config_item *cfg);
 
 /*
  * random.c
  */
 random_state *random_init(char *seed, int len);
+unsigned long random_bits(random_state *state, int bits);
 unsigned long random_upto(random_state *state, unsigned long limit);
 void random_free(random_state *state);
 
@@ -160,7 +160,7 @@ game_params *dup_params(game_params *params);
 config_item *game_configure(game_params *params);
 game_params *custom_params(config_item *cfg);
 char *validate_params(game_params *params);
-char *new_game_seed(game_params *params);
+char *new_game_seed(game_params *params, random_state *rs);
 char *validate_seed(game_params *params, char *seed);
 game_state *new_game(game_params *params, char *seed);
 game_state *dup_game(game_state *state);
index e0a179e38ac226ae86c535532ee74ce84d5b0046..664b11cd6284e03b0c8bb061ec7c521ee447a2cc 100644 (file)
--- a/random.c
+++ b/random.c
@@ -231,7 +231,7 @@ random_state *random_init(char *seed, int len)
 
 unsigned long random_bits(random_state *state, int bits)
 {
-    int ret = 0;
+    unsigned long ret = 0;
     int n;
 
     for (n = 0; n < bits; n += 8) {
@@ -251,7 +251,13 @@ unsigned long random_bits(random_state *state, int bits)
        ret = (ret << 8) | state->databuf[state->pos++];
     }
 
-    ret &= (1 << bits) - 1;
+    /*
+     * `(1 << bits) - 1' is not good enough, since if bits==32 on a
+     * 32-bit machine, behaviour is undefined and Intel has a nasty
+     * habit of shifting left by zero instead. We'll shift by
+     * bits-1 and then separately shift by one.
+     */
+    ret &= (1 << (bits-1)) * 2 - 1;
     return ret;
 }
 
index 78bd851fda25fd603a04eb5aae7773a0e2538af2..f0acff513ffc8a9029eb7779a0142f02c25d3fe4 100644 (file)
--- a/sixteen.c
+++ b/sixteen.c
@@ -151,7 +151,7 @@ int perm_parity(int *perm, int n)
     return ret;
 }
 
-char *new_game_seed(game_params *params)
+char *new_game_seed(game_params *params, random_state *rs)
 {
     int stop, n, i, x;
     int x1, x2, p1, p2;
@@ -181,7 +181,7 @@ char *new_game_seed(game_params *params)
      * Place everything except (possibly) the last two tiles.
      */
     for (x = 0, i = n; i > stop; i--) {
-        int k = i > 1 ? rand_upto(i) : 0;
+        int k = i > 1 ? random_upto(rs, i) : 0;
         int j;
 
         for (j = 0; j < n; j++)
index 8fbcf8eecccc32b0d4e1b8608797bac1108cd915..87f4284f10cc7f51a504be4fe2f5b99f0e8c4e3c 100644 (file)
--- a/windows.c
+++ b/windows.c
@@ -311,9 +311,13 @@ static frontend *new_window(HINSTANCE inst)
     int x, y;
     RECT r, sr;
     HDC hdc;
+    time_t t;
 
     fe = snew(frontend);
-    fe->me = midend_new(fe);
+
+    time(&t);
+    fe->me = midend_new(fe, &t, sizeof(t));
+
     fe->inst = inst;
     midend_new_game(fe->me);
     midend_size(fe->me, &x, &y);
@@ -950,8 +954,6 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
 {
     MSG msg;
 
-    srand(time(NULL));
-
     InitCommonControls();
 
     if (!prev) {