Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a pseudo-random number generator in C
- Loading branch information
Showing
6 changed files
with
199 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
/* | ||
This file is part of Iceball. | ||
Iceball is free software: you can redistribute it and/or modify | ||
it under the terms of the GNU General Public License as published by | ||
the Free Software Foundation, either version 3 of the License, or | ||
(at your option) any later version. | ||
Iceball is distributed in the hope that it will be useful, | ||
but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
GNU General Public License for more details. | ||
You should have received a copy of the GNU General Public License | ||
along with Iceball. If not, see <http://www.gnu.org/licenses/>. | ||
*/ | ||
|
||
// Notes: | ||
// * There's no `get_seed` function because state is stored in a uint64, which | ||
// we can't shove into a double. We could return the original seed and steps | ||
// taken, but without a way to take arbitrary steps, this is pretty useless. | ||
// It would also require storing more state, but it's only 128 bits at the | ||
// moment, which is already pretty small for a PRNG. | ||
// * We could add a clone function that creates a new one with the same state. | ||
// Useless for networking though. | ||
|
||
int icelua_fn_cl_prng_seed(lua_State *L) | ||
{ | ||
int top = icelua_assert_stack(L, 0, 2); | ||
uint64_t seed = 0; // TODO: Seed from time or something, if not given | ||
uint64_t stream = 0; | ||
if (top >= 1) { | ||
seed = (uint64_t)lua_tointeger(L, 1); | ||
if (top >= 2) { | ||
stream = (uint64_t)lua_tointeger(L, 2); | ||
} | ||
} | ||
prng_t *rng = lua_touserdata(L, lua_upvalueindex(1)); | ||
prng_seed(rng, seed, stream); | ||
return 0; | ||
} | ||
|
||
// Simulates Lua's math.random | ||
// 0 args: [0-1] | ||
// 1 args: [0-max] | ||
// 2 args: [min-max] | ||
int icelua_fn_cl_prng_random(lua_State *L) | ||
{ | ||
int top = icelua_assert_stack(L, 0, 2); | ||
prng_t *rng = lua_touserdata(L, lua_upvalueindex(1)); | ||
double result; | ||
if (top == 0) { | ||
result = prng_random_double(rng); | ||
} else { | ||
double minimum; | ||
double maximum; | ||
if (top == 1) { | ||
minimum = 0; | ||
maximum = lua_tonumber(L, 1); | ||
} else { | ||
minimum = lua_tonumber(L, 1); | ||
maximum = lua_tonumber(L, 2); | ||
} | ||
result = prng_random_double_range(rng, minimum, maximum); | ||
} | ||
lua_pushnumber(L, result); | ||
return 1; | ||
} | ||
|
||
int icelua_fn_common_prng_new(lua_State *L) | ||
{ | ||
int top = icelua_assert_stack(L, 0, 2); | ||
uint64_t seed = 0; // TODO: Seed from time or something, if not given | ||
uint64_t stream = 0; | ||
if (top >= 1) { | ||
seed = (uint64_t)lua_tointeger(L, 1); | ||
if (top >= 2) { | ||
stream = (uint64_t)lua_tointeger(L, 2); | ||
} | ||
} | ||
|
||
// "this" table | ||
lua_createtable(L, 0, 4); | ||
|
||
// PRNG state - uses upvalues, not visible to Lua, but hey, GC | ||
prng_t *rng = lua_newuserdata(L, sizeof(prng_t)); | ||
prng_seed(rng, seed, stream); | ||
|
||
lua_pushstring(L, "seed"); // Function name | ||
lua_pushvalue(L, -2); // Duplicate RNG reference to top of stack | ||
lua_pushcclosure(L, &icelua_fn_cl_prng_seed, 1); // Create closure, 1 upvalue (the RNG state) | ||
lua_settable(L, -4); // Insert closure into table | ||
|
||
lua_pushstring(L, "random"); // Function name | ||
lua_pushvalue(L, -2); // Duplicate RNG reference to top of stack | ||
lua_pushcclosure(L, &icelua_fn_cl_prng_random, 1); // Create closure, 1 upvalue (the RNG state) | ||
lua_settable(L, -4); // Insert closure into table | ||
|
||
// Pop the RNG state off the stack. | ||
lua_pop(L, 1); | ||
|
||
return 1; | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
/* | ||
This file is part of Iceball. | ||
Iceball is free software: you can redistribute it and/or modify | ||
it under the terms of the GNU General Public License as published by | ||
the Free Software Foundation, either version 3 of the License, or | ||
(at your option) any later version. | ||
Iceball is distributed in the hope that it will be useful, | ||
but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
GNU General Public License for more details. | ||
You should have received a copy of the GNU General Public License | ||
along with Iceball. If not, see <http://www.gnu.org/licenses/>. | ||
*/ | ||
|
||
#include "common.h" | ||
|
||
// Basic implementation of the PCG psuedorandom number generator. | ||
// Specifically, PCG-XSH-RR, as that's what the paper recommends for general use. | ||
// This may diverge from the official implementations, but I wanted to give credit. | ||
// TODO: We could drop the stream part and use a set value. We have no real use | ||
// for it, and it complicates the API. It does give us more possible sequences though. | ||
// On the other hand, using a set value does allow us to ensure that a relatively | ||
// good value is chosen (co-primes, etc.). | ||
|
||
#define PRNG_MULTIPLIER 6364136223846793005ULL | ||
|
||
void prng_seed(prng_t *rng, uint64_t seed, uint64_t stream) { | ||
rng->state = 0; | ||
// This must be odd (although it will still work sub-optimally if even) | ||
rng->stream = (stream << 1) | 1; | ||
// Initialise the state | ||
prng_random(rng); | ||
rng->state += seed; | ||
// Properly initialise the state (diverge the streams) | ||
prng_random(rng); | ||
} | ||
|
||
uint32_t prng_random(prng_t *rng) { | ||
uint64_t state = rng->state; | ||
|
||
// Update stored state | ||
rng->state = state * PRNG_MULTIPLIER + rng->stream; | ||
|
||
// Generate number | ||
// Top 5 bits specify rotation (for 32 bit result): | ||
// * 64 - 5 = 59 | ||
// * 32 - 5 = 27 | ||
// * (5 + 32) / 2 = 18 | ||
uint32_t xor_shifted = (uint32_t)(((state >> 18) ^ state) >> 27); | ||
uint32_t rotation = (uint32_t)(state >> 59); | ||
return (xor_shifted >> rotation) | (xor_shifted << (-rotation & 31)); | ||
} | ||
|
||
double prng_random_double(prng_t *rng) { | ||
uint32_t random = prng_random(rng); | ||
return (double)random / UINT32_MAX; | ||
} | ||
|
||
double prng_random_double_range(prng_t *rng, double minimum, double maximum) { | ||
uint32_t random = prng_random(rng); | ||
double d = (double)random / UINT32_MAX; | ||
return minimum + d * (maximum - minimum); | ||
} |