Provide seedable (CS)PRNG implementations to SLua scripts for predictable RNG
tracked
SungAli Resident
Having math.randomseed start a consistent pseudorandom sequence in each script when calling math.random allows saving and restoring, or transmitting any procedurally generated object simply by storing, recalling, or transmitting the seed, assuming knowledge of the procedure used to generate the object, be it an instance of a puzzle, a maze, a level in a game or whatever. Without this, one is obliged to either save, restore, and/or transmit potentially a great deal of data, or to create one's own pseudorandom number generator that does not depend on math.random.
Also, when debugging a script, being able to repeat a psuedorandom sequence to get consistent results among trials can be invaluable when dealing with intermittent faults and tracking down uncommon errors.
I like the idea that if math.randomseed has not been called, that the effective seed is itself random as is presently the case. Nevertheless, for the above reasons, I feel that it is valuable to be able to stipulate a seed that applies specifically to the script in question and does not affect and is not affect by sequences being generated by any other scripts concurrently running.
On possible option that might be less painful than completely replacing the current implementation, could be to add a second math.rand and math.randseed pair that performed as described above, using a relatively simple, fast algorithm that might not be guaranteed to be quite as well randomized as math.random, but that is adequate for randomized procedural object generation.
Log In
H
Harold Linden
marked this post as
tracked
Thanks for sending this in!
Yep, there's absolutely a usecase for seedable PRNGs within user scripts. However, we're not able to honor that contract with
math.random()
for a number of reasons:1)
math.random()
uses a _global_ PRNG state which is shared across scripts, changing that would create drift with Luau and it would probably require replacing most of math.random()
to make that state script-specific (I explored this a bit a while ago and it was hairy).2)
math.random()
offers to output random numbers, but it doesn't guarantee any particular algorithm, which is necessary if you want behavior to be deterministic even 10 years into the future. Currently PCG32 is used, but Luas have used Xoshiro256, Mersenne-Twister and others in the past, so there's precedent for the PRNG getting swapped out.3) Even if
math.randomseed()
is supported, that only gives you a single PRNG whose state you have little control over. Games often use _multiple_ PRNG instances, one for terrain generation, one for loot drops, etc. to prevent being able to force the PRNG into an advantageous state by doing something unrelated.4) Any additional work on PRNGs should address the usecase where people want _crytographically secure_ output. The PRNG state can be pretty trivially recovered with a very small number of outputs, though this is probably also the case with
llFrand()
.So even if we add a second set of functions that take in an explicit PRNG state as a mutable
buffer
or something like that, we still have to contend with the fact that the underlying PRNG may change.So basically I agree with you. I think there's a strong usecase for seedable PRNGs in SLua in general, but providing a formal contract for one that will still be sensible 10+ years from now requires a lot of careful thought and design, and wouldn't be anything like
math.randomseed()
. math.randomseed()
is stubbed out because I'm certain it would be a footgun, and I know that I wouldn't be able to focus on a suitable replacement before SLua goes live without pushing things back. It is, however, tracked internally for post-GA.In the meantime, Xoshiro128** is pretty trivial to implement in SLua (50~ lines for a
math.random()
/ math.randomseed()
polyfill, can attach if you like), so that's what I recommend if you need a deterministic, non-cryptographically secure PRNG. If you're okay with something that's slower but secure, ChaCha8 would probably work and be about as simple.