Added RandomGenerator:getState and RandomGenerator:setState (resolves issue #831.)

getState returns an implementation-dependent string representing the current state of the RandomGenerator's PRNG. setState sets the PRNG's state to an implementation-dependent string.
This commit is contained in:
Alex Szpakowski
2014-02-08 20:28:41 -04:00
parent de1f105e40
commit d07e31370d
7 changed files with 201 additions and 135 deletions
+20 -20
View File
@@ -53,26 +53,6 @@ public:
virtual ~Math()
{}
inline void setRandomSeed(RandomGenerator::Seed seed)
{
rng.setSeed(seed);
}
inline void setRandomSeed(uint32 low, uint32 high)
{
rng.setSeed(low, high);
}
inline RandomGenerator::Seed getRandomSeed() const
{
return rng.getSeed();
}
inline void getRandomSeed(uint32 &low, uint32 &high) const
{
rng.getSeed(low, high);
}
/**
* @copydoc RandomGenerator::random()
**/
@@ -105,6 +85,26 @@ public:
return rng.randomNormal(stddev);
}
inline void setRandomSeed(RandomGenerator::Seed seed)
{
rng.setSeed(seed);
}
inline RandomGenerator::Seed getRandomSeed() const
{
return rng.getSeed();
}
inline void setRandomState(const std::string &statestr)
{
rng.setState(statestr);
}
inline std::string getRandomState() const
{
return rng.getState();
}
/**
* Create a new random number generator.
**/
+76 -20
View File
@@ -20,8 +20,13 @@
#include "RandomGenerator.h"
// STL
// C++
#include <cmath>
#include <sstream>
#include <iomanip>
// C
#include <cstdlib>
namespace love
{
@@ -36,25 +41,10 @@ RandomGenerator::RandomGenerator()
{
// because it is too big for some compilers to handle ... if you know what
// i mean
#ifdef LOVE_BIG_ENDIAN
seed.b32.a = 0x0139408D;
seed.b32.b = 0xCBBF7A44;
#else
seed.b32.b = 0x0139408D;
seed.b32.a = 0xCBBF7A44;
#endif
rng_state = seed;
}
void RandomGenerator::setSeed(RandomGenerator::Seed newseed)
{
// 0 xor 0 is still 0, so Xorshift can't generate new numbers.
if (newseed.b64 == 0)
throw love::Exception("Invalid random seed.");
seed = newseed;
rng_state = seed;
Seed newseed;
newseed.b32.low = 0xCBBF7A44;
newseed.b32.high = 0x0139408D;
setSeed(newseed);
}
uint64 RandomGenerator::rand()
@@ -83,5 +73,71 @@ double RandomGenerator::randomNormal(double stddev)
return r * sin(phi) * stddev;
}
void RandomGenerator::setSeed(RandomGenerator::Seed newseed)
{
// 0 xor 0 is still 0, so Xorshift can't generate new numbers.
if (newseed.b64 == 0)
throw love::Exception("Invalid random seed.");
seed = newseed;
rng_state = seed;
}
RandomGenerator::Seed RandomGenerator::getSeed() const
{
return seed;
}
void RandomGenerator::setState(const std::string &statestr)
{
// For this implementation we'll accept a hex string representing the
// 64-bit state integer xorshift uses.
Seed state = {};
// Hex string must start with 0x.
if (statestr.find("0x") != 0 || statestr.size() < 3)
throw love::Exception("Invalid random state.");
// standardized strtoull (or 64 bit integer support for stringstream)
// requires C++11's standard library, which we can't use yet.
// I use strtol like this not because it's the best solution, but because
// it's "good enough".
// Convert the hex string to the state integer character-by-character.
for (size_t i = 2; i < statestr.size(); i++)
{
char hex[2] = {statestr[i], 0};
char *end = nullptr;
// Convert the current hex character to a number.
int nibble = strtol(hex, &end, 16);
// Check if strtol failed to convert it.
if (end != nullptr && *end != 0)
throw love::Exception("Invalid random state.");
state.b64 = (state.b64 << 4) + nibble;
}
rng_state = state;
}
std::string RandomGenerator::getState() const
{
// For this implementation we'll return a hex string representing the 64-bit
// state integer xorshift uses.
std::stringstream ss;
ss << "0x";
// Again with the stringstream not dealing with 64 bit integers...
ss << std::setfill('0') << std::setw(8) << std::hex << rng_state.b32.high;
ss << std::setfill('0') << std::setw(8) << std::hex << rng_state.b32.low;
return ss.str();
}
} // math
} // love
+31 -43
View File
@@ -28,8 +28,9 @@
#include "common/int.h"
#include "common/Object.h"
// STL
// C++
#include <limits>
#include <string>
namespace love
{
@@ -45,54 +46,19 @@ public:
uint64 b64;
struct
{
uint32 a;
uint32 b;
#ifdef LOVE_BIG_ENDIAN
uint32 high;
uint32 low;
#else
uint32 low;
uint32 high;
#endif
} b32;
};
RandomGenerator();
virtual ~RandomGenerator() {}
/**
* Set pseudo-random seed.
* It's up to the implementation how to use this.
**/
void setSeed(Seed seed);
/**
* Separately set the low and high bits of the pseudo-random seed.
**/
inline void setSeed(uint32 low, uint32 high)
{
Seed newseed;
#ifdef LOVE_BIG_ENDIAN
newseed.b32.a = high;
newseed.b32.b = low;
#else
newseed.b32.b = high;
newseed.b32.a = low;
#endif
setSeed(newseed);
}
inline Seed getSeed() const
{
return seed;
}
inline void getSeed(uint32 &low, uint32 &high) const
{
#ifdef LOVE_BIG_ENDIAN
high = seed.b32.a;
low = seed.b32.b;
#else
high = seed.b32.b;
low = seed.b32.a;
#endif
}
/**
* Return uniformly distributed pseudo random integer.
*
@@ -138,6 +104,28 @@ public:
**/
double randomNormal(double stddev);
/**
* Set pseudo-random seed.
* It's up to the implementation how to use this.
**/
void setSeed(Seed seed);
/**
* Get the previously set pseudo-random seed.
**/
Seed getSeed() const;
/**
* Set the internal implementation-dependent state value based on a string.
**/
void setState(const std::string &statestr);
/**
* Get a string representation of the implementation-dependent internal
* state value.
**/
std::string getState() const;
private:
Seed seed;
+30 -17
View File
@@ -32,21 +32,6 @@ namespace love
namespace math
{
int w_setRandomSeed(lua_State *L)
{
EXCEPT_GUARD(Math::instance.setRandomSeed(luax_checkrandomseed(L, 1));)
return 0;
}
int w_getRandomSeed(lua_State *L)
{
uint32 low = 0, high = 0;
Math::instance.getRandomSeed(low, high);
lua_pushnumber(L, (lua_Number) low);
lua_pushnumber(L, (lua_Number) high);
return 2;
}
int w_random(lua_State *L)
{
return luax_getrandom(L, 1, Math::instance.random());
@@ -62,6 +47,32 @@ int w_randomNormal(lua_State *L)
return 1;
}
int w_setRandomSeed(lua_State *L)
{
EXCEPT_GUARD(Math::instance.setRandomSeed(luax_checkrandomseed(L, 1));)
return 0;
}
int w_getRandomSeed(lua_State *L)
{
RandomGenerator::Seed s = Math::instance.getRandomSeed();
lua_pushnumber(L, (lua_Number) s.b32.low);
lua_pushnumber(L, (lua_Number) s.b32.high);
return 2;
}
int w_setRandomState(lua_State *L)
{
EXCEPT_GUARD(Math::instance.setRandomState(luax_checkstring(L, 1));)
return 0;
}
int w_getRandomState(lua_State *L)
{
luax_pushstring(L, Math::instance.getRandomState());
return 1;
}
int w_newRandomGenerator(lua_State *L)
{
RandomGenerator::Seed s;
@@ -341,10 +352,12 @@ int w_noise(lua_State *L)
// List of functions to wrap.
static const luaL_Reg functions[] =
{
{ "setRandomSeed", w_setRandomSeed },
{ "getRandomSeed", w_getRandomSeed },
{ "random", w_random },
{ "randomNormal", w_randomNormal },
{ "setRandomSeed", w_setRandomSeed },
{ "getRandomSeed", w_getRandomSeed },
{ "setRandomState", w_setRandomState },
{ "getRandomState", w_getRandomState },
{ "newRandomGenerator", w_newRandomGenerator },
{ "newBezierCurve", w_newBezierCurve },
{ "triangulate", w_triangulate },
+4 -2
View File
@@ -30,10 +30,12 @@ namespace love
namespace math
{
int w_setRandomSeed(lua_State *L);
int w_getRandomSeed(lua_State *L);
int w_random(lua_State *L);
int w_randomNormal(lua_State *L);
int w_setRandomSeed(lua_State *L);
int w_getRandomSeed(lua_State *L);
int w_setRandomState(lua_State *L);
int w_getRandomState(lua_State *L);
int w_newRandomGenerator(lua_State *L);
int w_newBezierCurve(lua_State *L);
int w_triangulate(lua_State *L);
+36 -31
View File
@@ -47,16 +47,8 @@ RandomGenerator::Seed luax_checkrandomseed(lua_State *L, int idx)
if (!lua_isnoneornil(L, idx + 1))
{
uint32 low = checkrandomseed_part<uint32>(L, idx);
uint32 high = checkrandomseed_part<uint32>(L, idx + 1);
#ifdef LOVE_BIG_ENDIAN
s.b32.a = high;
s.b32.b = low;
#else
s.b32.b = high;
s.b32.a = low;
#endif
s.b32.low = checkrandomseed_part<uint32>(L, idx);
s.b32.high = checkrandomseed_part<uint32>(L, idx + 1);
}
else
s.b64 = checkrandomseed_part<uint64>(L, idx);
@@ -95,25 +87,6 @@ RandomGenerator *luax_checkrandomgenerator(lua_State *L, int idx)
return luax_checktype<RandomGenerator>(L, idx, "RandomGenerator", MATH_RANDOM_GENERATOR_T);
}
int w_RandomGenerator_setSeed(lua_State *L)
{
RandomGenerator *rng = luax_checkrandomgenerator(L, 1);
EXCEPT_GUARD(rng->setSeed(luax_checkrandomseed(L, 2));)
return 0;
}
int w_RandomGenerator_getSeed(lua_State *L)
{
RandomGenerator *rng = luax_checkrandomgenerator(L, 1);
uint32 low = 0, high = 0;
rng->getSeed(low, high);
lua_pushnumber(L, (lua_Number) low);
lua_pushnumber(L, (lua_Number) high);
return 2;
}
int w_RandomGenerator_random(lua_State *L)
{
RandomGenerator *rng = luax_checkrandomgenerator(L, 1);
@@ -132,12 +105,44 @@ int w_RandomGenerator_randomNormal(lua_State *L)
return 1;
}
int w_RandomGenerator_setSeed(lua_State *L)
{
RandomGenerator *rng = luax_checkrandomgenerator(L, 1);
EXCEPT_GUARD(rng->setSeed(luax_checkrandomseed(L, 2));)
return 0;
}
int w_RandomGenerator_getSeed(lua_State *L)
{
RandomGenerator *rng = luax_checkrandomgenerator(L, 1);
RandomGenerator::Seed s = rng->getSeed();
lua_pushnumber(L, (lua_Number) s.b32.low);
lua_pushnumber(L, (lua_Number) s.b32.high);
return 2;
}
int w_RandomGenerator_setState(lua_State *L)
{
RandomGenerator *rng = luax_checkrandomgenerator(L, 1);
EXCEPT_GUARD(rng->setState(luax_checkstring(L, 2));)
return 0;
}
int w_RandomGenerator_getState(lua_State *L)
{
RandomGenerator *rng = luax_checkrandomgenerator(L, 1);
luax_pushstring(L, rng->getState());
return 1;
}
static const luaL_Reg functions[] =
{
{ "setSeed", w_RandomGenerator_setSeed },
{ "getSeed", w_RandomGenerator_getSeed },
{ "random", w_RandomGenerator_random },
{ "randomNormal", w_RandomGenerator_randomNormal },
{ "setSeed", w_RandomGenerator_setSeed },
{ "getSeed", w_RandomGenerator_getSeed },
{ "setState", w_RandomGenerator_setState },
{ "getState", w_RandomGenerator_getState },
{ 0, 0 }
};
+4 -2
View File
@@ -36,10 +36,12 @@ RandomGenerator::Seed luax_checkrandomseed(lua_State *L, int idx);
int luax_getrandom(lua_State *L, int startidx, double r);
RandomGenerator *luax_checkrandomgenerator(lua_State *L, int idx);
int w_RandomGenerator_setSeed(lua_State *L);
int w_RandomGenerator_getSeed(lua_State *L);
int w_RandomGenerator_random(lua_State *L);
int w_RandomGenerator_randomNormal(lua_State *L);
int w_RandomGenerator_setSeed(lua_State *L);
int w_RandomGenerator_getSeed(lua_State *L);
int w_RandomGenerator_setState(lua_State *L);
int w_RandomGenerator_getState(lua_State *L);
extern "C" int luaopen_randomgenerator(lua_State *L);
} // math