lperkins2
Joined: 23 Apr 2016 Posts: 1
|
Posted: Mon Apr 25, 2016 23:26 Post subject: Replacing rand() |
|
|
There have been a few efforts in the past to provide better pseudo-random number generation for scripts in nwnx; but, this requires changing and recompiling scripts to take advantage of the new random function. Also, new random functions can't replace the random numbers used in attack rolls and other core-engine functions. I decided to try to provide a replacement PRNG that does override the core dice rolling functionality.
Turns out (on the linux build at least), it's just making a call to the system prng rand (or std::rand in C++). This can easily be verified by declaring an LD_PRELOAD with
`int rand() {return 42;}` which will make all D20s come up `3`. After that, it's a simple matter of creating a rand function which, on its first invocation, will initialize a mersenne twister prng from the system time and using it to feed random numbers.
To avoid modulo bias, the highest number returned from rand() should be (n*dsize)-1, since rand needs to work perfectly for all standard dice sizes, I'm using 191999 (8*12*20*100 - 1), which satisfies n*d-1 for all D&D values of d. The extra factor of 20 wouldn't be necessary, but rand() should be capable of returning numbers higher than 32767.
Code: |
// Licens: LGPL2
#include <random>
#include <cstdio>
#include <cstdlib>
#include <chrono>
std::mt19937 my_generator;
std::uniform_int_distribution<int> my_distribution(0,191999);
int initted=0;
typedef std::chrono::high_resolution_clock myclock;
int rand(){
if (!initted){
initted=1;
myclock::time_point my_time = myclock::now();
auto seed = my_time.time_since_epoch().count();
my_generator.seed(seed);
printf("Using mt19937 mersenne_twister_engine with seed: %Li\n", seed);
}
int my_rn = my_distribution(my_generator);
if (getenv( "DEBUG\0" )){
printf("my rn: %i\n", my_rn);
}
return my_rn;
}
|
Compiled with gcc-4.8.5 on gentoo
Code: |
g++ -fPIC -m32 -mcpu=i386 -o rand.so rand.cpp --shared -std=c++11
|
If there's interest, I can provide a binary build, but if you're running it on linux you can probably just compile it, right
To use it, simply add it to the LD_PRELOAD in the startup script,
Code: |
LD_PRELOAD=./rand.so:./nwnx2.so
|
Or for the SP client
Code: |
LD_PRELOAD=./rand.so:./nwmovies.so
|
|
|