Browse Source

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.
Alex Szpakowski 11 years ago
parent
commit
d07e31370d

+ 20 - 20
src/modules/math/MathModule.h

@@ -53,26 +53,6 @@ public:
 	virtual ~Math()
 	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()
 	 * @copydoc RandomGenerator::random()
 	 **/
 	 **/
@@ -105,6 +85,26 @@ public:
 		return rng.randomNormal(stddev);
 		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.
 	 * Create a new random number generator.
 	 **/
 	 **/

+ 76 - 20
src/modules/math/RandomGenerator.cpp

@@ -20,8 +20,13 @@
 
 
 #include "RandomGenerator.h"
 #include "RandomGenerator.h"
 
 
-// STL
+// C++
 #include <cmath>
 #include <cmath>
+#include <sstream>
+#include <iomanip>
+
+// C
+#include <cstdlib>
 
 
 namespace love
 namespace love
 {
 {
@@ -36,25 +41,10 @@ RandomGenerator::RandomGenerator()
 {
 {
 	// because it is too big for some compilers to handle ... if you know what
 	// because it is too big for some compilers to handle ... if you know what
 	// i mean
 	// 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()
 uint64 RandomGenerator::rand()
@@ -83,5 +73,71 @@ double RandomGenerator::randomNormal(double stddev)
 	return r * sin(phi) * 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
 } // math
 } // love
 } // love

+ 31 - 43
src/modules/math/RandomGenerator.h

@@ -28,8 +28,9 @@
 #include "common/int.h"
 #include "common/int.h"
 #include "common/Object.h"
 #include "common/Object.h"
 
 
-// STL
+// C++
 #include <limits>
 #include <limits>
+#include <string>
 
 
 namespace love
 namespace love
 {
 {
@@ -45,54 +46,19 @@ public:
 		uint64 b64;
 		uint64 b64;
 		struct
 		struct
 		{
 		{
-			uint32 a;
-			uint32 b;
+#ifdef LOVE_BIG_ENDIAN
+			uint32 high;
+			uint32 low;
+#else
+			uint32 low;
+			uint32 high;
+#endif
 		} b32;
 		} b32;
 	};
 	};
 
 
 	RandomGenerator();
 	RandomGenerator();
 	virtual ~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.
 	 * Return uniformly distributed pseudo random integer.
 	 *
 	 *
@@ -138,6 +104,28 @@ public:
 	 **/
 	 **/
 	double randomNormal(double stddev);
 	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:
 private:
 
 
 	Seed seed;
 	Seed seed;

+ 27 - 14
src/modules/math/wrap_Math.cpp

@@ -32,6 +32,21 @@ namespace love
 namespace math
 namespace math
 {
 {
 
 
+int w_random(lua_State *L)
+{
+	return luax_getrandom(L, 1, Math::instance.random());
+}
+
+int w_randomNormal(lua_State *L)
+{
+	double stddev = luaL_optnumber(L, 1, 1.0);
+	double mean = luaL_optnumber(L, 2, 0.0);
+	double r = Math::instance.randomNormal(stddev);
+
+	lua_pushnumber(L, r + mean);
+	return 1;
+}
+
 int w_setRandomSeed(lua_State *L)
 int w_setRandomSeed(lua_State *L)
 {
 {
 	EXCEPT_GUARD(Math::instance.setRandomSeed(luax_checkrandomseed(L, 1));)
 	EXCEPT_GUARD(Math::instance.setRandomSeed(luax_checkrandomseed(L, 1));)
@@ -40,25 +55,21 @@ int w_setRandomSeed(lua_State *L)
 
 
 int w_getRandomSeed(lua_State *L)
 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);
+	RandomGenerator::Seed s = Math::instance.getRandomSeed();
+	lua_pushnumber(L, (lua_Number) s.b32.low);
+	lua_pushnumber(L, (lua_Number) s.b32.high);
 	return 2;
 	return 2;
 }
 }
 
 
-int w_random(lua_State *L)
+int w_setRandomState(lua_State *L)
 {
 {
-	return luax_getrandom(L, 1, Math::instance.random());
+	EXCEPT_GUARD(Math::instance.setRandomState(luax_checkstring(L, 1));)
+	return 0;
 }
 }
 
 
-int w_randomNormal(lua_State *L)
+int w_getRandomState(lua_State *L)
 {
 {
-	double stddev = luaL_optnumber(L, 1, 1.0);
-	double mean = luaL_optnumber(L, 2, 0.0);
-	double r = Math::instance.randomNormal(stddev);
-
-	lua_pushnumber(L, r + mean);
+	luax_pushstring(L, Math::instance.getRandomState());
 	return 1;
 	return 1;
 }
 }
 
 
@@ -341,10 +352,12 @@ int w_noise(lua_State *L)
 // List of functions to wrap.
 // List of functions to wrap.
 static const luaL_Reg functions[] =
 static const luaL_Reg functions[] =
 {
 {
-	{ "setRandomSeed", w_setRandomSeed },
-	{ "getRandomSeed", w_getRandomSeed },
 	{ "random", w_random },
 	{ "random", w_random },
 	{ "randomNormal", w_randomNormal },
 	{ "randomNormal", w_randomNormal },
+	{ "setRandomSeed", w_setRandomSeed },
+	{ "getRandomSeed", w_getRandomSeed },
+	{ "setRandomState", w_setRandomState },
+	{ "getRandomState", w_getRandomState },
 	{ "newRandomGenerator", w_newRandomGenerator },
 	{ "newRandomGenerator", w_newRandomGenerator },
 	{ "newBezierCurve", w_newBezierCurve },
 	{ "newBezierCurve", w_newBezierCurve },
 	{ "triangulate", w_triangulate },
 	{ "triangulate", w_triangulate },

+ 4 - 2
src/modules/math/wrap_Math.h

@@ -30,10 +30,12 @@ namespace love
 namespace math
 namespace math
 {
 {
 
 
-int w_setRandomSeed(lua_State *L);
-int w_getRandomSeed(lua_State *L);
 int w_random(lua_State *L);
 int w_random(lua_State *L);
 int w_randomNormal(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_newRandomGenerator(lua_State *L);
 int w_newBezierCurve(lua_State *L);
 int w_newBezierCurve(lua_State *L);
 int w_triangulate(lua_State *L);
 int w_triangulate(lua_State *L);

+ 32 - 27
src/modules/math/wrap_RandomGenerator.cpp

@@ -47,16 +47,8 @@ RandomGenerator::Seed luax_checkrandomseed(lua_State *L, int idx)
 
 
 	if (!lua_isnoneornil(L, idx + 1))
 	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
 	else
 		s.b64 = checkrandomseed_part<uint64>(L, idx);
 		s.b64 = checkrandomseed_part<uint64>(L, idx);
@@ -95,6 +87,24 @@ RandomGenerator *luax_checkrandomgenerator(lua_State *L, int idx)
 	return luax_checktype<RandomGenerator>(L, idx, "RandomGenerator", MATH_RANDOM_GENERATOR_T);
 	return luax_checktype<RandomGenerator>(L, idx, "RandomGenerator", MATH_RANDOM_GENERATOR_T);
 }
 }
 
 
+int w_RandomGenerator_random(lua_State *L)
+{
+	RandomGenerator *rng = luax_checkrandomgenerator(L, 1);
+	return luax_getrandom(L, 2, rng->random());
+}
+
+int w_RandomGenerator_randomNormal(lua_State *L)
+{
+	RandomGenerator *rng = luax_checkrandomgenerator(L, 1);
+
+	double stddev = luaL_optnumber(L, 2, 1.0);
+	double mean = luaL_optnumber(L, 3, 0.0);
+	double r = rng->randomNormal(stddev);
+
+	lua_pushnumber(L, r + mean);
+	return 1;
+}
+
 int w_RandomGenerator_setSeed(lua_State *L)
 int w_RandomGenerator_setSeed(lua_State *L)
 {
 {
 	RandomGenerator *rng = luax_checkrandomgenerator(L, 1);
 	RandomGenerator *rng = luax_checkrandomgenerator(L, 1);
@@ -105,39 +115,34 @@ int w_RandomGenerator_setSeed(lua_State *L)
 int w_RandomGenerator_getSeed(lua_State *L)
 int w_RandomGenerator_getSeed(lua_State *L)
 {
 {
 	RandomGenerator *rng = luax_checkrandomgenerator(L, 1);
 	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);
+	RandomGenerator::Seed s = rng->getSeed();
+	lua_pushnumber(L, (lua_Number) s.b32.low);
+	lua_pushnumber(L, (lua_Number) s.b32.high);
 	return 2;
 	return 2;
 }
 }
 
 
-int w_RandomGenerator_random(lua_State *L)
+int w_RandomGenerator_setState(lua_State *L)
 {
 {
 	RandomGenerator *rng = luax_checkrandomgenerator(L, 1);
 	RandomGenerator *rng = luax_checkrandomgenerator(L, 1);
-	return luax_getrandom(L, 2, rng->random());
+	EXCEPT_GUARD(rng->setState(luax_checkstring(L, 2));)
+	return 0;
 }
 }
 
 
-int w_RandomGenerator_randomNormal(lua_State *L)
+int w_RandomGenerator_getState(lua_State *L)
 {
 {
 	RandomGenerator *rng = luax_checkrandomgenerator(L, 1);
 	RandomGenerator *rng = luax_checkrandomgenerator(L, 1);
-
-	double stddev = luaL_optnumber(L, 2, 1.0);
-	double mean = luaL_optnumber(L, 3, 0.0);
-	double r = rng->randomNormal(stddev);
-
-	lua_pushnumber(L, r + mean);
+	luax_pushstring(L, rng->getState());
 	return 1;
 	return 1;
 }
 }
 
 
 static const luaL_Reg functions[] =
 static const luaL_Reg functions[] =
 {
 {
-	{ "setSeed", w_RandomGenerator_setSeed },
-	{ "getSeed", w_RandomGenerator_getSeed },
 	{ "random", w_RandomGenerator_random },
 	{ "random", w_RandomGenerator_random },
 	{ "randomNormal", w_RandomGenerator_randomNormal },
 	{ "randomNormal", w_RandomGenerator_randomNormal },
+	{ "setSeed", w_RandomGenerator_setSeed },
+	{ "getSeed", w_RandomGenerator_getSeed },
+	{ "setState", w_RandomGenerator_setState },
+	{ "getState", w_RandomGenerator_getState },
 	{ 0, 0 }
 	{ 0, 0 }
 };
 };
 
 

+ 4 - 2
src/modules/math/wrap_RandomGenerator.h

@@ -36,10 +36,12 @@ RandomGenerator::Seed luax_checkrandomseed(lua_State *L, int idx);
 int luax_getrandom(lua_State *L, int startidx, double r);
 int luax_getrandom(lua_State *L, int startidx, double r);
 
 
 RandomGenerator *luax_checkrandomgenerator(lua_State *L, int idx);
 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_random(lua_State *L);
 int w_RandomGenerator_randomNormal(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);
 extern "C" int luaopen_randomgenerator(lua_State *L);
 
 
 } // math
 } // math