/* * We don't need crypto-strength randomness here. We could just use * random(), but that supports only 32 bits of seed. 4 billion * distinct games is probably enough in most respects, but birthday * paradox collisions can be expected after around 64K games, which, * while certainly tolerable, is perhaps a little uncomfortably low. * * So, we clone random()'s internal logic, but with 64-bit integers * instead of 32-bit integers, and use the high 31 bits of them (or * occasionally the high 63 bits; see subtick()). This gives us the * ability to use a 64-bit seed and gives us better random numbers. */ #include #include #include #include #include #include extern const char *__progname; // This polynomial comes from Philip Koopman's CRC Polynomal Zoo, // http://users.ece.cmu.edu/~koopman/crc/crc64.html #define CRC_POLYNOMIAL 0xc96c5795d7870f42ULL #include "pline.h" #include "debug.h" #include "dice.h" static unsigned long long int dice_seed_value = 0; #define DEGREE 31 #define SEP 3 static unsigned long long int rp_buf[DEGREE]; static int rp_h; static int rp_m; static unsigned long long int rcore(void) { unsigned long long int v; v = rp_buf[rp_h] += rp_buf[rp_m]; rp_h ++; if (rp_h >= DEGREE) { rp_h = 0; rp_m ++; } else { rp_m ++; if (rp_m >= DEGREE) rp_m = 0; } return(v); } static unsigned long long int r63(void) { return(rcore()>>1); } static unsigned int r(void) { return(rcore()>>33); } static void sr(unsigned long long int seed) { int i; rp_buf[0] = seed; for (i=1;i0;i--) r(); } /* * Return the randomness seed value, for debugging. */ unsigned long long int get_dice_seed(void) { return(dice_seed_value); } /* * Set the randomness seed value, for debugging. */ void set_dice_seed(unsigned long long int v) { dice_seed_value = v; } /* * Initialize the randomness subsystem. We sr() with a hash of a * handful of values we can get quickly and easily, at least some of * which will typically vary from run to run. We hash them (with a * CRC) to spread out the entropy across the available bits at least * somewhat; it is not intended to be a strong-crypto hash, nor does * it need to be. * * We save the resulting seed, for debugging. */ void initrandom(void) { struct timeval tv; unsigned int uiv; unsigned char buf[(3*sizeof(unsigned int))+sizeof(struct timeval)]; int x; int i; int j; while (dice_seed_value == 0) { x = 0; uiv = getpid(); bcopy(&uiv,&buf[x],sizeof(uiv)); x += sizeof(uiv); uiv = getuid(); bcopy(&uiv,&buf[x],sizeof(uiv)); x += sizeof(uiv); uiv = gethostid(); bcopy(&uiv,&buf[x],sizeof(uiv)); x += sizeof(uiv); gettimeofday(&tv,(struct timezone *)0); bcopy(&tv,&buf[x],sizeof(tv)); x += sizeof(tv); if (x != sizeof(buf)) { fprintf(stderr,"%s: random seed buffer size mismatch\n",__progname); exit(1); } dice_seed_value = 0x123456789abcdef0ULL; for (i=sizeof(buf)-1;i>=0;i--) { dice_seed_value ^= buf[i]; for (j=8;j>0;j--) dice_seed_value = (dice_seed_value >> 1) ^ ((dice_seed_value & 1) ? CRC_POLYNOMIAL : 0); } } trace_seed(dice_seed_value); sr(dice_seed_value); } /* * rnd(N) - random number in [0..N). Very simple. */ int rnd(int n) { return(r()%n); } /* * onein(N) - return true 1/N of the time. The only notable thing here * is the test on the argument; it's convenient elsewhere to not have * to special-case arguments less than 2. */ int onein(int n) { return((n>1)?!rnd(n):1); } /* * Roll N dice with S sides (numbered [1..S]) and return the total. * This is internal to roll(), below. */ static int d(int n, int sides) { int total; total = 0; for (;n>0;n--) { total += 1 + (r() % sides); } return(total); } /* * Roll dice. This consists of a sum of simple numbers and dice rolls * (all but the first can actually be negated). For example, this * could roll "4d8" or "2d100+50" or "100d2-50" or "2d8+d10+4d3". * Note in particular that there is no requirement that the side * counts of the dice be physically reasonable ones; it's perfectly * fine to use "d1000" or "d7". */ int roll(const char *str) { int total; int ndice; int nsides; const char *cp; int neg; cp = str; neg = 0; total = 0; while (1) { if (isdigit((unsigned char)*cp)) { ndice = 0; while (isdigit((unsigned char)*cp)) { ndice = (10 * ndice) + (*cp - '0'); cp ++; } } else if (*cp == 'd') { ndice = 1; } if (*cp == 'd') { cp ++; nsides = 0; while (isdigit((unsigned char)*cp)) { nsides = (10 * nsides) + (*cp - '0'); cp ++; } if (neg) { total -= d(ndice,nsides); } else { total += d(ndice,nsides); } } else { if (neg) { total -= ndice; } else { total += ndice; } } neg = 0; if (*cp == '+') { cp ++; } else if (*cp == '-') { neg = 1; cp ++; } else if (*cp == '\0') { break; } else { panic("Bad dice spec `%s' to roll()",str); } } return(total); } /* * Generate a random sub-tick value, [0..1) tick. */ TIME subtick(void) { return(r63()&(TIMESCALE-1)); }