diff options
-rw-r--r-- | doc/lua_api.txt | 16 | ||||
-rw-r--r-- | src/mapgen.cpp | 10 | ||||
-rw-r--r-- | src/mg_ore.cpp | 2 | ||||
-rw-r--r-- | src/noise.cpp | 101 | ||||
-rw-r--r-- | src/noise.h | 71 | ||||
-rw-r--r-- | src/script/lua_api/l_noise.cpp | 260 | ||||
-rw-r--r-- | src/script/lua_api/l_noise.h | 50 | ||||
-rw-r--r-- | src/script/scripting_game.cpp | 1 | ||||
-rw-r--r-- | src/util/numeric.cpp | 59 | ||||
-rw-r--r-- | src/util/numeric.h | 8 |
10 files changed, 407 insertions, 171 deletions
diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 81a35976b..fbacb07d9 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -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)` diff --git a/src/mapgen.cpp b/src/mapgen.cpp index fd4fe5bb0..851f018ee 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -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); diff --git a/src/mg_ore.cpp b/src/mg_ore.cpp index c62f05860..850f25516 100644 --- a/src/mg_ore.cpp +++ b/src/mg_ore.cpp @@ -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) diff --git a/src/noise.cpp b/src/noise.cpp index 5223450dc..e6a9b7395 100644 --- a/src/noise.cpp +++ b/src/noise.cpp @@ -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) { diff --git a/src/noise.h b/src/noise.h index e59e73b23..d2287835e 100644 --- a/src/noise.h +++ b/src/noise.h @@ -30,47 +30,67 @@ #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); diff --git a/src/script/lua_api/l_noise.cpp b/src/script/lua_api/l_noise.cpp index 5a82b6485..bf3dca589 100644 --- a/src/script/lua_api/l_noise.cpp +++ b/src/script/lua_api/l_noise.cpp @@ -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} }; diff --git a/src/script/lua_api/l_noise.h b/src/script/lua_api/l_noise.h index 3e22ac7a0..56d2d59f8 100644 --- a/src/script/lua_api/l_noise.h +++ b/src/script/lua_api/l_noise.h @@ -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); }; diff --git a/src/script/scripting_game.cpp b/src/script/scripting_game.cpp index e716bc979..5bcd2a33d 100644 --- a/src/script/scripting_game.cpp +++ b/src/script/scripting_game.cpp @@ -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); diff --git a/src/util/numeric.cpp b/src/util/numeric.cpp index 19f927134..a1f1fd0ab 100644 --- a/src/util/numeric.cpp +++ b/src/util/numeric.cpp @@ -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; diff --git a/src/util/numeric.h b/src/util/numeric.h index 42d9a87a9..ccc9fbee4 100644 --- a/src/util/numeric.h +++ b/src/util/numeric.h @@ -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); /* |