0210c14858
PCG32 seeded RNG; immutable 8x8 GridState with occupancy bitmask; placement legality + anyPlacementExists; simultaneous row/col clears with single-count gem credit; combo scoring with one-move grace; weighted-bag generator with pity bias and depth-3 solvability nudge. All TDD, 51 tests green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
49 lines
1.6 KiB
Dart
49 lines
1.6 KiB
Dart
/// Deterministic seedable PRNG for the game engine.
|
|
///
|
|
/// dart:math's Random is implementation-unspecified across platforms; piece
|
|
/// sequences must be byte-identical on iOS, Android, and in tests, so we own
|
|
/// the algorithm (PCG32, https://www.pcg-random.org).
|
|
class SeededRng {
|
|
SeededRng(int seed, [int sequence = 0])
|
|
: _state = 0,
|
|
_inc = ((sequence << 1) | 1) {
|
|
_nextRaw();
|
|
_state = _state + seed;
|
|
_nextRaw();
|
|
}
|
|
|
|
// Dart native ints are 64-bit with wrapping arithmetic, which is exactly
|
|
// what the PCG32 state transition needs.
|
|
int _state;
|
|
final int _inc;
|
|
|
|
static const _multiplier = 6364136223846793005;
|
|
static const _mask32 = 0xFFFFFFFF;
|
|
|
|
/// One PCG32 step: 32-bit output via xorshift-high + random rotation.
|
|
int _nextRaw() {
|
|
final old = _state;
|
|
_state = old * _multiplier + _inc;
|
|
final xorshifted = (((old >>> 18) ^ old) >>> 27) & _mask32;
|
|
final rot = old >>> 59;
|
|
return ((xorshifted >>> rot) | (xorshifted << ((-rot) & 31))) & _mask32;
|
|
}
|
|
|
|
/// Uniform integer in [0, max), bias-free via rejection sampling.
|
|
int nextInt(int max) {
|
|
assert(max > 0);
|
|
final threshold = (0x100000000 - (0x100000000 % max)) & _mask32;
|
|
if (threshold == 0) return _nextRaw() % max;
|
|
while (true) {
|
|
final r = _nextRaw();
|
|
if (r < threshold) return r % max;
|
|
}
|
|
}
|
|
|
|
/// Uniform double in [0, 1).
|
|
double nextDouble() => _nextRaw() / 4294967296.0;
|
|
|
|
/// Derives an independent deterministic stream (e.g. per retry attempt).
|
|
SeededRng fork(int streamId) => SeededRng(_nextRaw() ^ streamId, streamId);
|
|
}
|