Add support for the PCG32 PRNG algo (and associated script APIs)
authorkwolekr <kwolekr@minetest.net>
Sun, 22 Mar 2015 04:01:46 +0000 (00:01 -0400)
committerkwolekr <kwolekr@minetest.net>
Sun, 22 Mar 2015 04:48:08 +0000 (00:48 -0400)
doc/lua_api.txt
src/mapgen.cpp
src/mg_ore.cpp
src/noise.cpp
src/noise.h
src/script/lua_api/l_noise.cpp
src/script/lua_api/l_noise.h
src/script/scripting_game.cpp
src/util/numeric.cpp
src/util/numeric.h

index 81a35976bb8f98e97be948412f81842349469c59..fbacb07d953560fb77a044aaeea209f892f15d5e 100644 (file)
@@ -2515,7 +2515,8 @@ an itemstring, a table or `nil`.
   Returns taken `ItemStack`.
 
 ### `PseudoRandom`
-A pseudorandom number generator.
+A 16-bit pseudorandom number generator.
+Uses a well-known LCG algorithm introduced by K&R.
 
 It can be created via `PseudoRandom(seed)`.
 
@@ -2525,6 +2526,19 @@ It can be created via `PseudoRandom(seed)`.
     * `((max - min) == 32767) or ((max-min) <= 6553))` must be true
       due to the simple implementation making bad distribution otherwise.
 
+### `PcgRandom`
+A 32-bit pseudorandom number generator.
+Uses PCG32, an algorithm of the permuted congruential generator family, offering very strong randomness.
+
+It can be created via `PcgRandom(seed)` or `PcgRandom(seed, sequence)`.
+
+#### Methods
+* `next()`: return next integer random number [`-2147483648`...`2147483647`]
+* `next(min, max)`: return next integer random number [`min`...`max`]
+* `rand_normal_dist(min, max, num_trials=6)`: return normally distributed random number [`min`...`max`]
+    * This is only a rough approximation of a normal distribution with mean=(max-min)/2 and variance=1
+    * Increasing num_trials improves accuracy of the approximation
+
 ### `PerlinNoise`
 A perlin noise generator.
 It can be created via `PerlinNoise(seed, octaves, persistence, scale)`
index fd4fe5bb03a83b1329272eefc305e8126445d34d..851f018ee2f9f86d03bdaf8ffe8b12e79413cb74 100644 (file)
@@ -515,14 +515,10 @@ void MapgenParams::load(const Settings &settings)
        std::string seed_str;
        const char *seed_name = (&settings == g_settings) ? "fixed_map_seed" : "seed";
 
-       if (settings.getNoEx(seed_name, seed_str) && !seed_str.empty()) {
+       if (settings.getNoEx(seed_name, seed_str) && !seed_str.empty())
                seed = read_seed(seed_str.c_str());
-       } else {
-               seed = ((u64)(myrand() & 0xFFFF) << 0) |
-                       ((u64)(myrand() & 0xFFFF) << 16) |
-                       ((u64)(myrand() & 0xFFFF) << 32) |
-                       ((u64)(myrand() & 0xFFFF) << 48);
-       }
+       else
+               myrand_bytes(&seed, sizeof(seed));
 
        settings.getNoEx("mg_name", mg_name);
        settings.getS16NoEx("water_level", water_level);
index c62f05860edb72078c98bfcb2e88630d75b2df9c..850f2551688afa3e6692d9b0ba80961ecea928d7 100644 (file)
@@ -308,7 +308,7 @@ void OreVein::generate(MMVManip *vm, int mapseed, u32 blockseed,
                }
 
                // randval ranges from -1..1
-               float randval   = (float)pr.next() / (PSEUDORANDOM_MAX / 2) - 1.f;
+               float randval   = (float)pr.next() / (pr.RANDOM_RANGE / 2) - 1.f;
                float noiseval  = contour(noise->result[index]);
                float noiseval2 = contour(noise2->result[index]);
                if (noiseval * noiseval2 + randval * random_factor < nthresh)
index 5223450dcf25f1d6a06fd64babce115c469d44c7..e6a9b7395b17ab1e10b7892e6f33f16bbb754f5f 100644 (file)
@@ -62,6 +62,107 @@ FlagDesc flagdesc_noiseparams[] = {
 
 ///////////////////////////////////////////////////////////////////////////////
 
+PcgRandom::PcgRandom(u64 state, u64 seq)
+{
+       seed(state, seq);
+}
+
+void PcgRandom::seed(u64 state, u64 seq)
+{
+       m_state = 0U;
+       m_inc = (seq << 1u) | 1u;
+       next();
+       m_state += state;
+       next();
+}
+
+
+u32 PcgRandom::next()
+{
+       u64 oldstate = m_state;
+       m_state = oldstate * 6364136223846793005ULL + m_inc;
+
+       u32 xorshifted = ((oldstate >> 18u) ^ oldstate) >> 27u;
+       u32 rot = oldstate >> 59u;
+       return (xorshifted >> rot) | (xorshifted << ((-rot) & 31));
+}
+
+
+u32 PcgRandom::range(u32 bound)
+{
+       /*
+       If the bound is not a multiple of the RNG's range, it may cause bias,
+       e.g. a RNG has a range from 0 to 3 and we take want a number 0 to 2.
+       Using rand() % 3, the number 0 would be twice as likely to appear.
+       With a very large RNG range, the effect becomes less prevalent but
+       still present.  This can be solved by modifying the range of the RNG
+       to become a multiple of bound by dropping values above the a threshhold.
+       In our example, threshhold == 4 - 3 = 1 % 3 == 1, so reject 0, thus
+       making the range 3 with no bias.
+
+       This loop looks dangerous, but will always terminate due to the
+       RNG's property of uniformity.
+       */
+       u32 threshhold = -bound % bound;
+       u32 r;
+
+       while ((r = next()) < threshhold);
+
+       return r % bound;
+}
+
+
+s32 PcgRandom::range(s32 min, s32 max)
+{
+       assert(max >= min);
+       u32 bound = max - min + 1;
+       return range(bound) + min;
+}
+
+
+void PcgRandom::bytes(void *out, size_t len)
+{
+       u32 r;
+       u8 *outb = (u8 *)out;
+
+       size_t len_alignment = (uintptr_t)out % sizeof(u32);
+       if (len_alignment) {
+               r = next();
+               while (len_alignment--) {
+                       *outb = r & 0xFF;
+                       outb++;
+                       r >>= 8;
+               }
+       }
+
+       size_t len_dwords = len / sizeof(u32);
+       while (len_dwords--) {
+               r = next();
+               *(u32 *)outb = next();
+               outb += sizeof(u32);
+       }
+
+       size_t len_remaining = len % sizeof(u32);
+       if (len_remaining) {
+               r = next();
+               while (len_remaining--) {
+                       *outb = r & 0xFF;
+                       outb++;
+                       r >>= 8;
+               }
+       }
+}
+
+
+s32 PcgRandom::randNormalDist(s32 min, s32 max, int num_trials)
+{
+       u32 accum = 0;
+       for (int i = 0; i != num_trials; i++)
+               accum += range(min, max);
+       return ((float)accum / num_trials) + 0.5f;
+}
+
+///////////////////////////////////////////////////////////////////////////////
 
 float noise2d(int x, int y, int seed)
 {
index e59e73b233d14e99e70f02de2ea6e3d684e995df..d2287835ea9966dd618602c683e782e1f186cef1 100644 (file)
 #include "irr_v3d.h"
 #include "util/string.h"
 
-#define PSEUDORANDOM_MAX 32767
-
 extern FlagDesc flagdesc_noiseparams[];
 
-class PseudoRandom
-{
+// Note: this class is not polymorphic so that its high level of
+// optimizability may be preserved in the common use case
+class PseudoRandom {
 public:
-       PseudoRandom(): m_next(0)
-       {
-       }
-       PseudoRandom(int seed): m_next(seed)
+       const static u32 RANDOM_RANGE = 32767;
+
+       inline PseudoRandom(int seed=0):
+               m_next(seed)
        {
        }
-       void seed(int seed)
+
+       inline void seed(int seed)
        {
                m_next = seed;
        }
-       // Returns 0...PSEUDORANDOM_MAX
-       int next()
+
+       inline int next()
        {
                m_next = m_next * 1103515245 + 12345;
-               return((unsigned)(m_next/65536) % (PSEUDORANDOM_MAX + 1));
+               return (unsigned)(m_next / 65536) % (RANDOM_RANGE + 1);
        }
-       int range(int min, int max)
+
+       inline int range(int min, int max)
        {
-               if (max-min > (PSEUDORANDOM_MAX + 1) / 10)
-               {
-                       //dstream<<"WARNING: PseudoRandom::range: max > 32767"<<std::endl;
-                       assert("Something wrong with random number" == NULL);
-               }
-               if(min > max)
-               {
-                       assert("Something wrong with random number" == NULL);
-                       //return max;
-               }
-               return (next()%(max-min+1))+min;
+               assert(max >= min);
+               /*
+               Here, we ensure the range is not too large relative to RANDOM_MAX,
+               as otherwise the effects of bias would become noticable.  Unlike
+               PcgRandom, we cannot modify this RNG's range as it would change the
+               output of this RNG for reverse compatibility.
+               */
+               assert((u32)(max - min) <= (RANDOM_RANGE + 1) / 10);
+
+               return (next() % (max - min + 1)) + min;
        }
+
 private:
        int m_next;
 };
 
+class PcgRandom {
+public:
+       const static s32 RANDOM_MIN   = -0x7fffffff - 1;
+       const static s32 RANDOM_MAX   = 0x7fffffff;
+       const static u32 RANDOM_RANGE = 0xffffffff;
+
+       PcgRandom(u64 state=0x853c49e6748fea9bULL, u64 seq=0xda3e39cb94b95bdbULL);
+       void seed(u64 state, u64 seq=0xda3e39cb94b95bdbULL);
+       u32 next();
+       u32 range(u32 bound);
+       s32 range(s32 min, s32 max);
+       void bytes(void *out, size_t len);
+       s32 randNormalDist(s32 min, s32 max, int num_trials=6);
+
+private:
+       u64 m_state;
+       u64 m_inc;
+};
+
 #define NOISE_FLAG_DEFAULTS    0x01
 #define NOISE_FLAG_EASED       0x02
 #define NOISE_FLAG_ABSVALUE    0x04
@@ -89,7 +109,8 @@ struct NoiseParams {
        float lacunarity;
        u32 flags;
 
-       NoiseParams() {
+       NoiseParams()
+       {
                offset     = 0.0f;
                scale      = 1.0f;
                spread     = v3f(250, 250, 250);
index 5a82b6485bcbddf76e500799b2292d752171e9d0..bf3dca58968f4f3accdc42ae9a738fa3de4cd3da 100644 (file)
@@ -23,12 +23,19 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "common/c_content.h"
 #include "log.h"
 
-// garbage collector
-int LuaPerlinNoise::gc_object(lua_State *L)
+///////////////////////////////////////
+/*
+  LuaPerlinNoise
+*/
+
+LuaPerlinNoise::LuaPerlinNoise(NoiseParams *params) :
+       np(*params)
+{
+}
+
+
+LuaPerlinNoise::~LuaPerlinNoise()
 {
-       LuaPerlinNoise *o = *(LuaPerlinNoise **)(lua_touserdata(L, 1));
-       delete o;
-       return 0;
 }
 
 
@@ -54,19 +61,6 @@ int LuaPerlinNoise::l_get3d(lua_State *L)
 }
 
 
-LuaPerlinNoise::LuaPerlinNoise(NoiseParams *params) :
-       np(*params)
-{
-}
-
-
-LuaPerlinNoise::~LuaPerlinNoise()
-{
-}
-
-
-// LuaPerlinNoise(seed, octaves, persistence, scale)
-// Creates an LuaPerlinNoise and leaves it on top of stack
 int LuaPerlinNoise::create_object(lua_State *L)
 {
        NO_MAP_LOCK_REQUIRED;
@@ -91,14 +85,22 @@ int LuaPerlinNoise::create_object(lua_State *L)
 }
 
 
-LuaPerlinNoise* LuaPerlinNoise::checkobject(lua_State *L, int narg)
+int LuaPerlinNoise::gc_object(lua_State *L)
+{
+       LuaPerlinNoise *o = *(LuaPerlinNoise **)(lua_touserdata(L, 1));
+       delete o;
+       return 0;
+}
+
+
+LuaPerlinNoise *LuaPerlinNoise::checkobject(lua_State *L, int narg)
 {
        NO_MAP_LOCK_REQUIRED;
        luaL_checktype(L, narg, LUA_TUSERDATA);
        void *ud = luaL_checkudata(L, narg, className);
        if (!ud)
                luaL_typerror(L, narg, className);
-       return *(LuaPerlinNoise**)ud;  // unbox pointer
+       return *(LuaPerlinNoise **)ud;
 }
 
 
@@ -111,7 +113,7 @@ void LuaPerlinNoise::Register(lua_State *L)
 
        lua_pushliteral(L, "__metatable");
        lua_pushvalue(L, methodtable);
-       lua_settable(L, metatable);  // hide metatable from Lua getmetatable()
+       lua_settable(L, metatable);
 
        lua_pushliteral(L, "__index");
        lua_pushvalue(L, methodtable);
@@ -121,12 +123,11 @@ void LuaPerlinNoise::Register(lua_State *L)
        lua_pushcfunction(L, gc_object);
        lua_settable(L, metatable);
 
-       lua_pop(L, 1);  // drop metatable
+       lua_pop(L, 1);
 
-       luaL_openlib(L, 0, methods, 0);  // fill methodtable
-       lua_pop(L, 1);  // drop methodtable
+       luaL_openlib(L, 0, methods, 0);
+       lua_pop(L, 1);
 
-       // Can be created from Lua (PerlinNoise(seed, octaves, persistence)
        lua_register(L, className, create_object);
 }
 
@@ -138,16 +139,26 @@ const luaL_reg LuaPerlinNoise::methods[] = {
        {0,0}
 };
 
-
+///////////////////////////////////////
 /*
-  PerlinNoiseMap
- */
+  LuaPerlinNoiseMap
+*/
 
-int LuaPerlinNoiseMap::gc_object(lua_State *L)
+LuaPerlinNoiseMap::LuaPerlinNoiseMap(NoiseParams *params, int seed, v3s16 size)
 {
-       LuaPerlinNoiseMap *o = *(LuaPerlinNoiseMap **)(lua_touserdata(L, 1));
-       delete o;
-       return 0;
+       m_is3d = size.Z > 1;
+       np = *params;
+       try {
+               noise = new Noise(&np, seed, size.X, size.Y, size.Z);
+       } catch (InvalidNoiseParamsException &e) {
+               throw LuaError(e.what());
+       }
+}
+
+
+LuaPerlinNoiseMap::~LuaPerlinNoiseMap()
+{
+       delete noise;
 }
 
 
@@ -251,26 +262,6 @@ int LuaPerlinNoiseMap::l_get3dMap_flat(lua_State *L)
 }
 
 
-LuaPerlinNoiseMap::LuaPerlinNoiseMap(NoiseParams *params, int seed, v3s16 size)
-{
-       m_is3d = size.Z > 1;
-       np = *params;
-       try {
-               noise = new Noise(&np, seed, size.X, size.Y, size.Z);
-       } catch (InvalidNoiseParamsException &e) {
-               throw LuaError(e.what());
-       }
-}
-
-
-LuaPerlinNoiseMap::~LuaPerlinNoiseMap()
-{
-       delete noise;
-}
-
-
-// LuaPerlinNoiseMap(np, size)
-// Creates an LuaPerlinNoiseMap and leaves it on top of stack
 int LuaPerlinNoiseMap::create_object(lua_State *L)
 {
        NoiseParams np;
@@ -286,6 +277,14 @@ int LuaPerlinNoiseMap::create_object(lua_State *L)
 }
 
 
+int LuaPerlinNoiseMap::gc_object(lua_State *L)
+{
+       LuaPerlinNoiseMap *o = *(LuaPerlinNoiseMap **)(lua_touserdata(L, 1));
+       delete o;
+       return 0;
+}
+
+
 LuaPerlinNoiseMap *LuaPerlinNoiseMap::checkobject(lua_State *L, int narg)
 {
        luaL_checktype(L, narg, LUA_TUSERDATA);
@@ -294,7 +293,7 @@ LuaPerlinNoiseMap *LuaPerlinNoiseMap::checkobject(lua_State *L, int narg)
        if (!ud)
                luaL_typerror(L, narg, className);
 
-       return *(LuaPerlinNoiseMap **)ud;  // unbox pointer
+       return *(LuaPerlinNoiseMap **)ud;
 }
 
 
@@ -307,7 +306,7 @@ void LuaPerlinNoiseMap::Register(lua_State *L)
 
        lua_pushliteral(L, "__metatable");
        lua_pushvalue(L, methodtable);
-       lua_settable(L, metatable);  // hide metatable from Lua getmetatable()
+       lua_settable(L, metatable);
 
        lua_pushliteral(L, "__index");
        lua_pushvalue(L, methodtable);
@@ -317,12 +316,11 @@ void LuaPerlinNoiseMap::Register(lua_State *L)
        lua_pushcfunction(L, gc_object);
        lua_settable(L, metatable);
 
-       lua_pop(L, 1);  // drop metatable
+       lua_pop(L, 1);
 
-       luaL_openlib(L, 0, methods, 0);  // fill methodtable
-       lua_pop(L, 1);  // drop methodtable
+       luaL_openlib(L, 0, methods, 0);
+       lua_pop(L, 1);
 
-       // Can be created from Lua (PerlinNoiseMap(np, size)
        lua_register(L, className, create_object);
 }
 
@@ -336,32 +334,23 @@ const luaL_reg LuaPerlinNoiseMap::methods[] = {
        {0,0}
 };
 
+///////////////////////////////////////
 /*
        LuaPseudoRandom
 */
 
-// garbage collector
-int LuaPseudoRandom::gc_object(lua_State *L)
-{
-       LuaPseudoRandom *o = *(LuaPseudoRandom **)(lua_touserdata(L, 1));
-       delete o;
-       return 0;
-}
-
-
-// next(self, min=0, max=32767) -> get next value
 int LuaPseudoRandom::l_next(lua_State *L)
 {
        NO_MAP_LOCK_REQUIRED;
        LuaPseudoRandom *o = checkobject(L, 1);
        int min = 0;
        int max = 32767;
-       lua_settop(L, 3); // Fill 2 and 3 with nil if they don't exist
-       if(!lua_isnil(L, 2))
+       lua_settop(L, 3);
+       if (lua_isnumber(L, 2))
                min = luaL_checkinteger(L, 2);
-       if(!lua_isnil(L, 3))
+       if (lua_isnumber(L, 3))
                max = luaL_checkinteger(L, 3);
-       if(max < min){
+       if (max < min) {
                errorstream<<"PseudoRandom.next(): max="<<max<<" min="<<min<<std::endl;
                throw LuaError("PseudoRandom.next(): max < min");
        }
@@ -378,34 +367,107 @@ int LuaPseudoRandom::l_next(lua_State *L)
 }
 
 
-LuaPseudoRandom::LuaPseudoRandom(int seed):
-       m_pseudo(seed)
+int LuaPseudoRandom::create_object(lua_State *L)
 {
+       int seed = luaL_checknumber(L, 1);
+       LuaPseudoRandom *o = new LuaPseudoRandom(seed);
+       *(void **)(lua_newuserdata(L, sizeof(void *))) = o;
+       luaL_getmetatable(L, className);
+       lua_setmetatable(L, -2);
+       return 1;
+}
+
+
+int LuaPseudoRandom::gc_object(lua_State *L)
+{
+       LuaPseudoRandom *o = *(LuaPseudoRandom **)(lua_touserdata(L, 1));
+       delete o;
+       return 0;
 }
 
 
-LuaPseudoRandom::~LuaPseudoRandom()
+LuaPseudoRandom *LuaPseudoRandom::checkobject(lua_State *L, int narg)
 {
+       luaL_checktype(L, narg, LUA_TUSERDATA);
+       void *ud = luaL_checkudata(L, narg, className);
+       if (!ud)
+               luaL_typerror(L, narg, className);
+       return *(LuaPseudoRandom **)ud;
+}
+
+
+void LuaPseudoRandom::Register(lua_State *L)
+{
+       lua_newtable(L);
+       int methodtable = lua_gettop(L);
+       luaL_newmetatable(L, className);
+       int metatable = lua_gettop(L);
+
+       lua_pushliteral(L, "__metatable");
+       lua_pushvalue(L, methodtable);
+       lua_settable(L, metatable);
+
+       lua_pushliteral(L, "__index");
+       lua_pushvalue(L, methodtable);
+       lua_settable(L, metatable);
+
+       lua_pushliteral(L, "__gc");
+       lua_pushcfunction(L, gc_object);
+       lua_settable(L, metatable);
+
+       lua_pop(L, 1);
+
+       luaL_openlib(L, 0, methods, 0);
+       lua_pop(L, 1);
+
+       lua_register(L, className, create_object);
 }
 
 
-const PseudoRandom& LuaPseudoRandom::getItem() const
+const char LuaPseudoRandom::className[] = "PseudoRandom";
+const luaL_reg LuaPseudoRandom::methods[] = {
+       luamethod(LuaPseudoRandom, next),
+       {0,0}
+};
+
+///////////////////////////////////////
+/*
+       LuaPcgRandom
+*/
+
+int LuaPcgRandom::l_next(lua_State *L)
 {
-       return m_pseudo;
+       NO_MAP_LOCK_REQUIRED;
+
+       LuaPcgRandom *o = checkobject(L, 1);
+       u32 min = lua_isnumber(L, 2) ? lua_tointeger(L, 2) : o->m_rnd.RANDOM_MIN;
+       u32 max = lua_isnumber(L, 3) ? lua_tointeger(L, 3) : o->m_rnd.RANDOM_MAX;
+
+       lua_pushinteger(L, o->m_rnd.range(min, max));
+       return 1;
 }
 
-PseudoRandom& LuaPseudoRandom::getItem()
+
+int LuaPcgRandom::l_rand_normal_dist(lua_State *L)
 {
-       return m_pseudo;
+       NO_MAP_LOCK_REQUIRED;
+
+       LuaPcgRandom *o = checkobject(L, 1);
+       u32 min = lua_isnumber(L, 2) ? lua_tointeger(L, 2) : o->m_rnd.RANDOM_MIN;
+       u32 max = lua_isnumber(L, 3) ? lua_tointeger(L, 3) : o->m_rnd.RANDOM_MAX;
+       int num_trials = lua_isnumber(L, 4) ? lua_tointeger(L, 4) : 6;
+
+       lua_pushinteger(L, o->m_rnd.randNormalDist(min, max, num_trials));
+       return 1;
 }
 
 
-// LuaPseudoRandom(seed)
-// Creates an LuaPseudoRandom and leaves it on top of stack
-int LuaPseudoRandom::create_object(lua_State *L)
+int LuaPcgRandom::create_object(lua_State *L)
 {
-       int seed = luaL_checknumber(L, 1);
-       LuaPseudoRandom *o = new LuaPseudoRandom(seed);
+       lua_Integer seed = luaL_checknumber(L, 1);
+       LuaPcgRandom *o  = lua_isnumber(L, 2) ?
+               new LuaPcgRandom(seed, lua_tointeger(L, 2)) :
+               new LuaPcgRandom(seed);
        *(void **)(lua_newuserdata(L, sizeof(void *))) = o;
        luaL_getmetatable(L, className);
        lua_setmetatable(L, -2);
@@ -413,17 +475,25 @@ int LuaPseudoRandom::create_object(lua_State *L)
 }
 
 
-LuaPseudoRandom* LuaPseudoRandom::checkobject(lua_State *L, int narg)
+int LuaPcgRandom::gc_object(lua_State *L)
+{
+       LuaPcgRandom *o = *(LuaPcgRandom **)(lua_touserdata(L, 1));
+       delete o;
+       return 0;
+}
+
+
+LuaPcgRandom *LuaPcgRandom::checkobject(lua_State *L, int narg)
 {
        luaL_checktype(L, narg, LUA_TUSERDATA);
        void *ud = luaL_checkudata(L, narg, className);
        if (!ud)
                luaL_typerror(L, narg, className);
-       return *(LuaPseudoRandom**)ud;  // unbox pointer
+       return *(LuaPcgRandom **)ud;
 }
 
 
-void LuaPseudoRandom::Register(lua_State *L)
+void LuaPcgRandom::Register(lua_State *L)
 {
        lua_newtable(L);
        int methodtable = lua_gettop(L);
@@ -432,7 +502,7 @@ void LuaPseudoRandom::Register(lua_State *L)
 
        lua_pushliteral(L, "__metatable");
        lua_pushvalue(L, methodtable);
-       lua_settable(L, metatable);  // hide metatable from Lua getmetatable()
+       lua_settable(L, metatable);
 
        lua_pushliteral(L, "__index");
        lua_pushvalue(L, methodtable);
@@ -442,18 +512,18 @@ void LuaPseudoRandom::Register(lua_State *L)
        lua_pushcfunction(L, gc_object);
        lua_settable(L, metatable);
 
-       lua_pop(L, 1);  // drop metatable
+       lua_pop(L, 1);
 
-       luaL_openlib(L, 0, methods, 0);  // fill methodtable
-       lua_pop(L, 1);  // drop methodtable
+       luaL_openlib(L, 0, methods, 0);
+       lua_pop(L, 1);
 
-       // Can be created from Lua (LuaPseudoRandom(seed))
        lua_register(L, className, create_object);
 }
 
 
-const char LuaPseudoRandom::className[] = "PseudoRandom";
-const luaL_reg LuaPseudoRandom::methods[] = {
-       luamethod(LuaPseudoRandom, next),
+const char LuaPcgRandom::className[] = "PcgRandom";
+const luaL_reg LuaPcgRandom::methods[] = {
+       luamethod(LuaPcgRandom, next),
+       luamethod(LuaPcgRandom, rand_normal_dist),
        {0,0}
 };
index 3e22ac7a0a7199db213cd591e800ca99fcb46784..56d2d59f8fb1a335f602727096dc552a0011b460 100644 (file)
@@ -64,6 +64,9 @@ class LuaPerlinNoiseMap : public ModApiBase {
        static const char className[];
        static const luaL_reg methods[];
 
+       // Exported functions
+
+       // garbage collector
        static int gc_object(lua_State *L);
 
        static int l_get2dMap(lua_State *L);
@@ -104,18 +107,51 @@ private:
        static int l_next(lua_State *L);
 
 public:
-       LuaPseudoRandom(int seed);
-
-       ~LuaPseudoRandom();
-
-       const PseudoRandom& getItem() const;
-       PseudoRandom& getItem();
+       LuaPseudoRandom(int seed) :
+               m_pseudo(seed) {}
 
        // LuaPseudoRandom(seed)
        // Creates an LuaPseudoRandom and leaves it on top of stack
        static int create_object(lua_State *L);
 
-       static LuaPseudoRandom* checkobject(lua_State *L, int narg);
+       static LuaPseudoRandom *checkobject(lua_State *L, int narg);
+
+       static void Register(lua_State *L);
+};
+
+/*
+       LuaPcgRandom
+*/
+class LuaPcgRandom : public ModApiBase {
+private:
+       PcgRandom m_rnd;
+
+       static const char className[];
+       static const luaL_reg methods[];
+
+       // Exported functions
+
+       // garbage collector
+       static int gc_object(lua_State *L);
+
+       // next(self, min=-2147483648, max=2147483647) -> get next value
+       static int l_next(lua_State *L);
+
+       // rand_normal_dist(self, min=-2147483648, max=2147483647, num_trials=6) ->
+       // get next normally distributed random value
+       static int l_rand_normal_dist(lua_State *L);
+
+public:
+       LuaPcgRandom(u64 seed) :
+               m_rnd(seed) {}
+       LuaPcgRandom(u64 seed, u64 seq) :
+               m_rnd(seed, seq) {}
+
+       // LuaPcgRandom(seed)
+       // Creates an LuaPcgRandom and leaves it on top of stack
+       static int create_object(lua_State *L);
+
+       static LuaPcgRandom *checkobject(lua_State *L, int narg);
 
        static void Register(lua_State *L);
 };
index e716bc979489bc357617ec2f2d8f4cce73ff95d8..5bcd2a33df8693b047b1c16a19c48b195197b794 100644 (file)
@@ -92,6 +92,7 @@ void GameScripting::InitializeModApi(lua_State *L, int top)
        LuaPerlinNoise::Register(L);
        LuaPerlinNoiseMap::Register(L);
        LuaPseudoRandom::Register(L);
+       LuaPcgRandom::Register(L);
        LuaVoxelManip::Register(L);
        NodeMetaRef::Register(L);
        NodeTimerRef::Register(L);
index 19f927134b5e33404a18f173b6ee174c66d60cb5..a1f1fd0ab1d308cec13a6d6c94a18d6be3f13f30 100644 (file)
@@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #include "log.h"
 #include "../constants.h" // BS, MAP_BLOCKSIZE
+#include "../noise.h" // PseudoRandom, PcgRandom
 #include <string.h>
 #include <iostream>
 
@@ -115,36 +116,32 @@ void FacePositionCache::generateFacePosition(u16 d)
     myrand
 */
 
-static unsigned long next = 1;
+PcgRandom g_pcgrand;
 
-/* RAND_MAX assumed to be 32767 */
-int myrand(void)
+u32 myrand()
 {
-   next = next * 1103515245 + 12345;
-   return((unsigned)(next/65536) % 32768);
+       return g_pcgrand.next();
 }
 
-void mysrand(unsigned seed)
+void mysrand(unsigned int seed)
 {
-   next = seed;
+       g_pcgrand.seed(seed);
+}
+
+void myrand_bytes(void *out, size_t len)
+{
+       g_pcgrand.bytes(out, len);
 }
 
 int myrand_range(int min, int max)
 {
-       if(max-min > MYRAND_MAX)
-       {
-               errorstream<<"WARNING: myrand_range: max-min > MYRAND_MAX"<<std::endl;
-        max = min + MYRAND_MAX;
-       }
-       if(min > max)
-       {
-               errorstream<<"WARNING: myrand_range: min > max"<<std::endl;
-               return max;
-       }
-       return (myrand()%(max-min+1))+min;
+       return g_pcgrand.range(min, max);
 }
 
-// 64-bit unaligned version of MurmurHash
+
+/*
+       64-bit unaligned version of MurmurHash
+*/
 u64 murmur_hash_64_ua(const void *key, int len, unsigned int seed)
 {
        const u64 m = 0xc6a4a7935bd1e995ULL;
@@ -159,12 +156,12 @@ u64 murmur_hash_64_ua(const void *key, int len, unsigned int seed)
                memcpy(&k, data, sizeof(u64));
                data++;
 
-               k *= m; 
-               k ^= k >> r; 
-               k *= m; 
-               
+               k *= m;
+               k ^= k >> r;
+               k *= m;
+
                h ^= k;
-               h *= m; 
+               h *= m;
        }
 
        const unsigned char *data2 = (const unsigned char *)data;
@@ -178,13 +175,13 @@ u64 murmur_hash_64_ua(const void *key, int len, unsigned int seed)
                case 1: h ^= (u64)data2[0];
                                h *= m;
        }
+
        h ^= h >> r;
        h *= m;
        h ^= h >> r;
-       
+
        return h;
-} 
+}
 
 
 /*
@@ -197,7 +194,7 @@ bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir,
                f32 camera_fov, f32 range, f32 *distance_ptr)
 {
        v3s16 blockpos_nodes = blockpos_b * MAP_BLOCKSIZE;
-       
+
        // Block center position
        v3f blockpos(
                        ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
@@ -213,7 +210,7 @@ bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir,
 
        if(distance_ptr)
                *distance_ptr = d;
-       
+
        // If block is far away, it's not in sight
        if(d > range)
                return false;
@@ -221,7 +218,7 @@ bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir,
        // Maximum radius of a block.  The magic number is
        // sqrt(3.0) / 2.0 in literal form.
        f32 block_max_radius = 0.866025403784 * MAP_BLOCKSIZE * BS;
-       
+
        // If block is (nearly) touching the camera, don't
        // bother validating further (that is, render it anyway)
        if(d < block_max_radius)
@@ -242,7 +239,7 @@ bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir,
        // Cosine of the angle between the camera direction
        // and the block direction (camera_dir is an unit vector)
        f32 cosangle = dforward / blockpos_adj.getLength();
-       
+
        // If block is not in the field of view, skip it
        if(cosangle < cos(camera_fov / 2))
                return false;
index 42d9a87a91a7af8d416b938fc6615057cc5d605b..ccc9fbee4192112b4b2b914269cc6b063cbad346 100644 (file)
@@ -239,10 +239,10 @@ inline float wrapDegrees_180(float f)
 /*
        Pseudo-random (VC++ rand() sucks)
 */
-int myrand(void);
-void mysrand(unsigned seed);
-#define MYRAND_MAX 32767
-
+#define MYRAND_RANGE 0xffffffff
+u32 myrand();
+void mysrand(unsigned int seed);
+void myrand_bytes(void *out, size_t len);
 int myrand_range(int min, int max);
 
 /*