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]
This commit is contained in:
Simon Tatham
2004-05-03 09:10:52 +00:00
parent 6e42ddd31b
commit aa9a8e8c7e
12 changed files with 39 additions and 45 deletions

4
Recipe
View File

@ -13,8 +13,8 @@
!makefile cygwin Makefile.cyg !makefile cygwin Makefile.cyg
WINDOWS = windows user32.lib gdi32.lib comctl32.lib WINDOWS = windows user32.lib gdi32.lib comctl32.lib
COMMON = midend misc malloc COMMON = midend misc malloc random
NET = net random tree234 NET = net tree234
net : [X] gtk COMMON NET net : [X] gtk COMMON NET
cube : [X] gtk COMMON cube cube : [X] gtk COMMON cube

6
cube.c
View File

@ -560,7 +560,7 @@ static void classify_grid_square_callback(void *ctx, struct grid_square *sq)
data->squareindex++; data->squareindex++;
} }
char *new_game_seed(game_params *params) char *new_game_seed(game_params *params, random_state *rs)
{ {
struct grid_data data; struct grid_data data;
int i, j, k, m, area, facesperclass; 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 (i = 0; i < data.nclasses; i++) {
for (j = 0; j < facesperclass; j++) { 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]]); assert(!flags[data.gridptrs[i][n]]);
flags[data.gridptrs[i][n]] = TRUE; 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. * 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(data.gridptrs[0]);
sfree(flags); sfree(flags);

View File

@ -131,7 +131,7 @@ int perm_parity(int *perm, int n)
return ret; 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 gap, n, i, x;
int x1, x2, p1, p2, parity; int x1, x2, p1, p2, parity;
@ -149,7 +149,7 @@ char *new_game_seed(game_params *params)
used[i] = FALSE; used[i] = FALSE;
} }
gap = rand_upto(n); gap = random_upto(rs, n);
tiles[gap] = 0; tiles[gap] = 0;
used[0] = TRUE; used[0] = TRUE;
@ -157,7 +157,7 @@ char *new_game_seed(game_params *params)
* Place everything else except the last two tiles. * Place everything else except the last two tiles.
*/ */
for (x = 0, i = n-1; i > 2; i--) { for (x = 0, i = n-1; i > 2; i--) {
int k = rand_upto(i); int k = random_upto(rs, i);
int j; int j;
for (j = 0; j < n; j++) for (j = 0; j < n; j++)

6
gtk.c
View File

@ -688,10 +688,12 @@ static frontend *new_window(void)
GtkBox *vbox; GtkBox *vbox;
GtkWidget *menubar, *menu, *menuitem; GtkWidget *menubar, *menu, *menuitem;
int x, y, n; int x, y, n;
time_t t;
fe = snew(frontend); fe = snew(frontend);
fe->me = midend_new(fe); time(&t);
fe->me = midend_new(fe, &t, sizeof(t));
midend_new_game(fe->me); midend_new_game(fe->me);
fe->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); fe->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
@ -855,8 +857,6 @@ static frontend *new_window(void)
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
srand(time(NULL));
gtk_init(&argc, &argv); gtk_init(&argc, &argv);
(void) new_window(); (void) new_window();
gtk_main(); gtk_main();

View File

@ -13,6 +13,8 @@
struct midend_data { struct midend_data {
frontend *frontend; frontend *frontend;
random_state *random;
char *seed; char *seed;
int fresh_seed; int fresh_seed;
int nstates, statesize, statepos; int nstates, statesize, statepos;
@ -36,11 +38,12 @@ struct midend_data {
} \ } \
} while (0) } while (0)
midend_data *midend_new(frontend *frontend) midend_data *midend_new(frontend *fe, void *randseed, int randseedsize)
{ {
midend_data *me = snew(midend_data); 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->nstates = me->statesize = me->statepos = 0;
me->states = NULL; me->states = NULL;
me->params = default_params(); me->params = default_params();
@ -88,7 +91,7 @@ void midend_new_game(midend_data *me)
if (!me->fresh_seed) { if (!me->fresh_seed) {
sfree(me->seed); sfree(me->seed);
me->seed = new_game_seed(me->params); me->seed = new_game_seed(me->params, me->random);
} else } else
me->fresh_seed = FALSE; me->fresh_seed = FALSE;
@ -252,7 +255,7 @@ float *midend_colours(midend_data *me, int *ncolours)
float *ret; float *ret;
if (me->nstates == 0) { 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); state = new_game(me->params, seed);
sfree(seed); sfree(seed);
} else } else

17
misc.c
View File

@ -7,23 +7,6 @@
#include "puzzles.h" #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) void free_cfg(config_item *cfg)
{ {
config_item *i; config_item *i;

4
net.c
View File

@ -254,7 +254,7 @@ char *validate_params(game_params *params)
* Randomly select a new game seed. * 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 * 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.) * understand it and do something completely different.)
*/ */
char buf[40]; char buf[40];
sprintf(buf, "%d", rand()); sprintf(buf, "%lu", random_bits(rs, 32));
return dupstr(buf); return dupstr(buf);
} }

View File

@ -76,7 +76,7 @@ char *validate_params(game_params *params)
return NULL; return NULL;
} }
char *new_game_seed(game_params *params) char *new_game_seed(game_params *params, random_state *rs)
{ {
return dupstr("FIXME"); return dupstr("FIXME");
} }

View File

@ -103,7 +103,7 @@ void status_bar(frontend *fe, char *text);
/* /*
* midend.c * 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_free(midend_data *me);
void midend_set_params(midend_data *me, game_params *params); void midend_set_params(midend_data *me, game_params *params);
void midend_size(midend_data *me, int *x, int *y); void midend_size(midend_data *me, int *x, int *y);
@ -138,13 +138,13 @@ char *dupstr(char *s);
/* /*
* misc.c * misc.c
*/ */
int rand_upto(int limit);
void free_cfg(config_item *cfg); void free_cfg(config_item *cfg);
/* /*
* random.c * random.c
*/ */
random_state *random_init(char *seed, int len); 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); unsigned long random_upto(random_state *state, unsigned long limit);
void random_free(random_state *state); void random_free(random_state *state);
@ -160,7 +160,7 @@ game_params *dup_params(game_params *params);
config_item *game_configure(game_params *params); config_item *game_configure(game_params *params);
game_params *custom_params(config_item *cfg); game_params *custom_params(config_item *cfg);
char *validate_params(game_params *params); 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); char *validate_seed(game_params *params, char *seed);
game_state *new_game(game_params *params, char *seed); game_state *new_game(game_params *params, char *seed);
game_state *dup_game(game_state *state); game_state *dup_game(game_state *state);

View File

@ -231,7 +231,7 @@ random_state *random_init(char *seed, int len)
unsigned long random_bits(random_state *state, int bits) unsigned long random_bits(random_state *state, int bits)
{ {
int ret = 0; unsigned long ret = 0;
int n; int n;
for (n = 0; n < bits; n += 8) { 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 = (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; return ret;
} }

View File

@ -151,7 +151,7 @@ int perm_parity(int *perm, int n)
return ret; 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 stop, n, i, x;
int x1, x2, p1, p2; int x1, x2, p1, p2;
@ -181,7 +181,7 @@ char *new_game_seed(game_params *params)
* Place everything except (possibly) the last two tiles. * Place everything except (possibly) the last two tiles.
*/ */
for (x = 0, i = n; i > stop; i--) { 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; int j;
for (j = 0; j < n; j++) for (j = 0; j < n; j++)

View File

@ -311,9 +311,13 @@ static frontend *new_window(HINSTANCE inst)
int x, y; int x, y;
RECT r, sr; RECT r, sr;
HDC hdc; HDC hdc;
time_t t;
fe = snew(frontend); fe = snew(frontend);
fe->me = midend_new(fe);
time(&t);
fe->me = midend_new(fe, &t, sizeof(t));
fe->inst = inst; fe->inst = inst;
midend_new_game(fe->me); midend_new_game(fe->me);
midend_size(fe->me, &x, &y); midend_size(fe->me, &x, &y);
@ -950,8 +954,6 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
{ {
MSG msg; MSG msg;
srand(time(NULL));
InitCommonControls(); InitCommonControls();
if (!prev) { if (!prev) {