summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkwolekr <kwolekr@minetest.net>2015-03-22 00:01:46 -0400
committerkwolekr <kwolekr@minetest.net>2015-03-22 00:48:08 -0400
commit3993093f51544d4eb44efb57c973e29107ea2f7a (patch)
tree0533167edce0dbd2cb8f03c37b880e57cfd7916d
parent7679396ebbb38115eedbfb8e9636dff50cdf2075 (diff)
downloadminetest-3993093f51544d4eb44efb57c973e29107ea2f7a.tar.gz
minetest-3993093f51544d4eb44efb57c973e29107ea2f7a.tar.bz2
minetest-3993093f51544d4eb44efb57c973e29107ea2f7a.zip
Add support for the PCG32 PRNG algo (and associated script APIs)
-rw-r--r--doc/lua_api.txt16
-rw-r--r--src/mapgen.cpp10
-rw-r--r--src/mg_ore.cpp2
-rw-r--r--src/noise.cpp101
-rw-r--r--src/noise.h71
-rw-r--r--src/script/lua_api/l_noise.cpp260
-rw-r--r--src/script/lua_api/l_noise.h50
-rw-r--r--src/script/scripting_game.cpp1
-rw-r--r--src/util/numeric.cpp59
-rw-r--r--src/util/numeric.h8
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);
/*