diff --git a/src/modules/math/MathModule.h b/src/modules/math/MathModule.h index 0765a0e9c..e9df716ac 100644 --- a/src/modules/math/MathModule.h +++ b/src/modules/math/MathModule.h @@ -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. **/ diff --git a/src/modules/math/RandomGenerator.cpp b/src/modules/math/RandomGenerator.cpp index 1f7b58a47..0e26d3e8d 100644 --- a/src/modules/math/RandomGenerator.cpp +++ b/src/modules/math/RandomGenerator.cpp @@ -20,8 +20,13 @@ #include "RandomGenerator.h" -// STL +// C++ #include +#include +#include + +// C +#include 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 diff --git a/src/modules/math/RandomGenerator.h b/src/modules/math/RandomGenerator.h index 830a9506c..aab0f9277 100644 --- a/src/modules/math/RandomGenerator.h +++ b/src/modules/math/RandomGenerator.h @@ -28,8 +28,9 @@ #include "common/int.h" #include "common/Object.h" -// STL +// C++ #include +#include 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; diff --git a/src/modules/math/wrap_Math.cpp b/src/modules/math/wrap_Math.cpp index 773ecf040..50b78f956 100644 --- a/src/modules/math/wrap_Math.cpp +++ b/src/modules/math/wrap_Math.cpp @@ -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 }, diff --git a/src/modules/math/wrap_Math.h b/src/modules/math/wrap_Math.h index f4c73fcbb..d75afe4ae 100644 --- a/src/modules/math/wrap_Math.h +++ b/src/modules/math/wrap_Math.h @@ -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); diff --git a/src/modules/math/wrap_RandomGenerator.cpp b/src/modules/math/wrap_RandomGenerator.cpp index 78cf53a04..ea029df2a 100644 --- a/src/modules/math/wrap_RandomGenerator.cpp +++ b/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)) { - uint32 low = checkrandomseed_part(L, idx); - uint32 high = checkrandomseed_part(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(L, idx); + s.b32.high = checkrandomseed_part(L, idx + 1); } else s.b64 = checkrandomseed_part(L, idx); @@ -95,25 +87,6 @@ RandomGenerator *luax_checkrandomgenerator(lua_State *L, int idx) return luax_checktype(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 } }; diff --git a/src/modules/math/wrap_RandomGenerator.h b/src/modules/math/wrap_RandomGenerator.h index 07e82894e..ad5201d17 100644 --- a/src/modules/math/wrap_RandomGenerator.h +++ b/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); 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