Check in code for new random number generator
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
|
||||
#include "world.hpp"
|
||||
#include "pprint.hpp"
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
|
||||
static void tangible_getall(LuaStack &LS0, LuaSlot list, const util::IdVector &idv) {
|
||||
LuaVar tangibles, tan;
|
||||
@@ -311,6 +313,161 @@ LuaDefine(tangible_nopredict, "",
|
||||
}
|
||||
}
|
||||
|
||||
LuaDefine(math_random, "(args...)",
|
||||
"|Generate random numbers."
|
||||
"|"
|
||||
"|What it generates depends on the arguments:"
|
||||
"|"
|
||||
"| () - a float in range [0.0, 1.0)"
|
||||
"| (high) - an int between 1 and high inclusive"
|
||||
"| (low, high) - an int between low and high inclusive"
|
||||
"|"
|
||||
"|math.random tries to cooperate with predictive"
|
||||
"|reexecution to be as predictable as possible."
|
||||
"|To achieve predictability, we used an ad-hoc"
|
||||
"|random number generator. It passes a variety of"
|
||||
"|statistical tests, but it's not well-studied."
|
||||
"|"
|
||||
"|If you want actually want nonpredictability, or"
|
||||
"|if you need the assurance of a well-studied random"
|
||||
"|number generator, use math.mtrandom or"
|
||||
"|math.cryptrandom instead.") {
|
||||
// Parse the arguments.
|
||||
// This is hairy because there's a lot of possibilities.
|
||||
bool passed_in_randomstate = false;
|
||||
int arg = 1;
|
||||
if ((lua_gettop(L) >= arg) && (lua_istable(L, arg))) {
|
||||
passed_in_randomstate = true;
|
||||
arg += 1;
|
||||
}
|
||||
bool have_range = false;
|
||||
int64_t low, high;
|
||||
if ((lua_gettop(L) >= arg) && (lua_type(L, arg) == LUA_TNUMBER)) {
|
||||
double lowf, highf;
|
||||
if ((lua_gettop(L) >= arg+1) && (lua_type(L, arg+1) == LUA_TNUMBER)) {
|
||||
lowf = std::floor(lua_tonumber(L, arg));
|
||||
highf = std::floor(lua_tonumber(L, arg + 1));
|
||||
arg += 2;
|
||||
} else {
|
||||
lowf = 1;
|
||||
highf = std::floor(lua_tonumber(L, arg));
|
||||
arg += 1;
|
||||
}
|
||||
if ((lowf < -LuaStack::MAXINT) || (highf > LuaStack::MAXINT)) {
|
||||
luaL_error(L, "math.random range exceeds MAXINT");
|
||||
return 0;
|
||||
}
|
||||
if (lowf > highf) {
|
||||
luaL_error(L, "math.random range low > high");
|
||||
return 0;
|
||||
}
|
||||
low = int64_t(lowf);
|
||||
high = int64_t(highf);
|
||||
have_range = true;
|
||||
}
|
||||
if (lua_gettop(L) >= arg) {
|
||||
luaL_error(L, "math.random accepts an optional randomstate and an optional range");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Generate the seed, count, and salt.
|
||||
// The salt prevents accidental duplication between user-specified
|
||||
// seeds and system-generated seeds.
|
||||
uint64_t seed, count, salt;
|
||||
if (passed_in_randomstate) {
|
||||
lua_pushstring(L, "seed");
|
||||
lua_rawget(L, 1);
|
||||
lua_pushstring(L, "count");
|
||||
lua_rawget(L, 1);
|
||||
if ((lua_type(L, -1) != LUA_TNUMBER) ||
|
||||
(lua_type(L, -2) != LUA_TNUMBER)) {
|
||||
luaL_error(L, "Not a valid randomstate table");
|
||||
return 0;
|
||||
}
|
||||
double dseed = lua_tonumber(L, -2);
|
||||
double dcount = lua_tonumber(L, -1);
|
||||
seed = uint64_t(dseed) & LuaStack::MAXINT;
|
||||
count = uint64_t(dcount) & LuaStack::MAXINT;
|
||||
if (dseed < 0) {
|
||||
salt = 0x35c9a6082a097ade;
|
||||
} else {
|
||||
salt = 0x4785d086ead90c20;
|
||||
}
|
||||
lua_pop(L, 2);
|
||||
lua_pushstring(L, "count");
|
||||
lua_pushnumber(L, double((count + 1) & LuaStack::MAXINT));
|
||||
lua_rawset(L, 1);
|
||||
} else {
|
||||
World *w = World::fetch_global_pointer(L);
|
||||
if (w->lthread_use_ppool_) {
|
||||
Tangible *actor = w->tangible_get(w->lthread_actor_id_);
|
||||
seed = w->lthread_actor_id_;
|
||||
count = actor->id_player_pool_.get_seqno();
|
||||
salt = 0x3ab0fb84aedc3764;
|
||||
} else {
|
||||
// TODO: maybe throw in a 'donotpredict' here.
|
||||
seed = 123456;
|
||||
count = w->id_global_pool_.get_seqno();
|
||||
salt = 0x6f493c90faf0139d;
|
||||
}
|
||||
}
|
||||
|
||||
// Generate the hash and convert to a lua_Number.
|
||||
uint64_t hash = util::hash_ints(seed, count, salt, 456);
|
||||
if (!have_range) {
|
||||
double result = (hash & LuaStack::MAXINT) * 0x1p-53;
|
||||
lua_pushnumber(L, result);
|
||||
} else {
|
||||
uint64_t range = (high - low) + 1;
|
||||
uint64_t offset = (hash & 0x7FFFFFFFFFFFFFFF) % range;
|
||||
int64_t result = low + int64_t(offset);
|
||||
lua_pushnumber(L, result);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
LuaDefine(math_randomstate, "(seed)",
|
||||
"|Create and return a randomstate table."
|
||||
"|This is a lua table that stores the state for a random"
|
||||
"|number generator. A randomstate table can be passed"
|
||||
"|to math.random."
|
||||
"|"
|
||||
"|You can optionally omit the seed, in which case it will"
|
||||
"|pick a seed randomly. Automatically-generated seeds are"
|
||||
"|guaranteed never to be the same as user-specified seeds.") {
|
||||
double seed;
|
||||
if (lua_gettop(L) == 0) {
|
||||
World *w = World::fetch_global_pointer(L);
|
||||
int64_t iseed = (w->id_global_pool_.get_seqno() & LuaStack::MAXINT) + 1;
|
||||
seed = -iseed;
|
||||
} else if (lua_gettop(L) == 1) {
|
||||
if (lua_type(L, 1) != LUA_TNUMBER) {
|
||||
luaL_error(L, "math.randomstate takes an optional integer seed");
|
||||
return 0;
|
||||
}
|
||||
seed = lua_tonumber(L, 1);
|
||||
if ((seed < 0.0) || (seed > LuaStack::MAXINT) || (std::floor(seed) != seed)) {
|
||||
luaL_error(L, "math.randomstate seed must be an integer 0-MAXINT");
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
luaL_error(L, "math.randomstate takes an optional integer seed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
lua_newtable(L);
|
||||
lua_pushstring(L, "seed");
|
||||
lua_pushnumber(L, seed);
|
||||
lua_rawset(L, -3);
|
||||
lua_pushstring(L, "count");
|
||||
lua_pushnumber(L, 0);
|
||||
lua_rawset(L, -3);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LuaSandboxBuiltin(math_randomseed, "", "");
|
||||
|
||||
|
||||
LuaDefine(pprint, "obj1,obj2,...",
|
||||
"|Pretty-print object or objects.") {
|
||||
World *w = World::fetch_global_pointer(L);
|
||||
|
||||
Reference in New Issue
Block a user