ref: aa9a8e8c7eecc2de77690b872931e88951622813
parent: 6e42ddd31b5ca71f48c6260b01fc49b2451d0a56
author: Simon Tatham <anakin@pobox.com>
date: Mon May 3 05:10:52 EDT 2004
The Windows RNG turns out to only give about 16 bits at a time. This 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]
--- 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
--- a/cube.c
+++ b/cube.c
@@ -560,7 +560,7 @@
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 @@
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 @@
/*
* 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);
--- a/fifteen.c
+++ b/fifteen.c
@@ -131,7 +131,7 @@
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 @@
used[i] = FALSE;
}
- gap = rand_upto(n);
+ gap = random_upto(rs, n);
tiles[gap] = 0;
used[0] = TRUE;
@@ -157,7 +157,7 @@
* 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++)
--- a/gtk.c
+++ b/gtk.c
@@ -688,10 +688,12 @@
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 @@
int main(int argc, char **argv)
{
- srand(time(NULL));
-
gtk_init(&argc, &argv);
(void) new_window();
gtk_main();
--- 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 @@
} \
} 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 @@
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 *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
--- 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;
--- a/net.c
+++ b/net.c
@@ -254,7 +254,7 @@
* 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 @@
* understand it and do something completely different.)
*/
char buf[40];
- sprintf(buf, "%d", rand());
+ sprintf(buf, "%lu", random_bits(rs, 32));
return dupstr(buf);
}
--- a/nullgame.c
+++ b/nullgame.c
@@ -76,7 +76,7 @@
return NULL;
}
-char *new_game_seed(game_params *params)
+char *new_game_seed(game_params *params, random_state *rs)
{
return dupstr("FIXME");
}
--- a/puzzles.h
+++ b/puzzles.h
@@ -103,7 +103,7 @@
/*
* 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,7 +138,6 @@
/*
* misc.c
*/
-int rand_upto(int limit);
void free_cfg(config_item *cfg);
/*
@@ -145,6 +144,7 @@
* 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 @@
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);
--- a/random.c
+++ b/random.c
@@ -231,7 +231,7 @@
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 @@
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;
}
--- a/sixteen.c
+++ b/sixteen.c
@@ -151,7 +151,7 @@
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 @@
* 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++)
--- a/windows.c
+++ b/windows.c
@@ -311,9 +311,13 @@
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);
@@ -949,8 +953,6 @@
int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
{
MSG msg;
-
- srand(time(NULL));
InitCommonControls();