Skip to content

Instantly share code, notes, and snippets.

@thomcc
Last active August 29, 2015 14:06
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thomcc/a9c42b0fabd5880e0f2c to your computer and use it in GitHub Desktop.
Save thomcc/a9c42b0fabd5880e0f2c to your computer and use it in GitHub Desktop.
Serializable RNG
// This is free and unencumbered software released into the public domain.
// For more information, please refer to <http://unlicense.org/>
import 'dart:math' as math;
/// A class used to store the state of a [ResumableRandom].
class RngState {
final int originalSeed, a, b, c, d;
RngState(this.originalSeed, this.a, this.b, this.c, this.d);
}
/// A [math.Random] implementation that can save and reload it's state to a
/// [RngState].
///
/// Caveat: This class throws if you run the dart vm with
/// --throw-on-javascript-int-overflow. I couldn't fix this, unfortunately.
class ResumableRandom implements math.Random {
// implementation based on http://burtleburtle.net/bob/rand/smallprng.html
int _originalSeed;
int _a, _b, _c, _d;
/// Construct a [ResumableRandom] with a seed value.
///
/// If [seed] is not provided, one will be created based on the current time.
ResumableRandom([int seed]) {
if (seed == null) {
seed = new DateTime.now().millisecondsSinceEpoch;
}
seed = seed.toUnsigned(32);
_originalSeed = seed;
_a = 0xf1ea5eed;
_b = _c = _d = seed;
for (int i = 0; i < 20; ++i) {
_next(); // warm up the rng
}
}
/// The seed this [ResumableRandom] was initialized with.
int get seed => _originalSeed;
/// The state of the [ResumableRandom], for serialization.
RngState get state =>
new RngState(_originalSeed, _a, _b, _c, _d);
void set state(RngState s) {
_originalSeed = s.originalSeed;
_a = s.a;
_b = s.b;
_c = s.c;
_d = s.d;
}
static int _rot(int v, int n) =>
((v << n) |(v >> (32 - n))).toUnsigned(32);
int _next() {
int e = (_a - _rot(_b, 27)).toUnsigned(32);
_a = (_b ^ _rot(_c, 17)).toUnsigned(32);
_b = (_c + _d).toUnsigned(32);
_c = (_d + e).toUnsigned(32);
_d = (e + _a).toUnsigned(32);
return _d;
}
int _nextBits(int bits) {
return _next() & ((1 << bits)-1);
}
/// Generates a random integer between 0 inclusive and [max] exclusive, without bias.
///
/// [max] must be between 0 and 2^32-1.
int nextInt(int max) {
if (max <= 0 || max > 0xffffffff) {
throw new RangeError("Argument to nextInt out of range: $max");
}
if ((max & (max-1)) == 0) {
return _next() & (max-1);
}
int part = (0xffffffff ~/ max).toUnsigned(32);
for (;;) {
int bits = _next();
int val = (bits ~/ part).toUnsigned(32);
if (val < max) {
return val;
}
}
}
/// Generates a random boolean value.
bool nextBool() => (_next() & 1) == 0;
/// Generate a random double between 0.0 inclusive, and 1.0 exclusive.
double nextDouble() {
return (_nextBits(26) * (1.0 * (1 << 27)) + _nextBits(27)) / (1.0 * 0x20000000000000);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment