//+------------------------------------------------------------------+ //| Random.mqh | //| AnimateDread | //| MQL5's MathRand() is the 15-bit MSVC LCG: 32768 distinct values, | //| period 2^31, and the lattice structure every LCG of that shape | //| has. That is tolerable for a jittered sleep and wrong for the | //| two places this project actually leans on randomness: | //+------------------------------------------------------------------+ #ifndef WARRIOR_SYSTEM_RANDOM_MQH #define WARRIOR_SYSTEM_RANDOM_MQH #include CHighQualityRandState g_warriorRandState; //--- Distinguishes two seedings that share a salt AND a millisecond. Never reset. int g_warriorRandSeedCount = 0; bool g_warriorRandReady = false; //+------------------------------------------------------------------+ //| Seed the shared stream. `salt` should identify the caller - the | //| model id is what every current call site passes. | //+------------------------------------------------------------------+ void WarriorRandSeed(const string salt) { g_warriorRandSeedCount++; uint h1 = 2166136261; int len = StringLen(salt); for(int i = 0; i < len; i++) { h1 ^= (uint)StringGetCharacter(salt, i); h1 *= 16777619; } uint tick = GetTickCount(); uint h2 = h1 ^ (tick * 2654435761) ^ ((uint)g_warriorRandSeedCount * 40503); h1 ^= tick + (uint)g_warriorRandSeedCount; int s1 = (int)(h1 & 0x7FFFFFFF); int s2 = (int)(h2 & 0x7FFFFFFF); CHighQualityRand::HQRndSeed(MathMax(s1, 1), MathMax(s2, 1), g_warriorRandState); g_warriorRandReady = true; } //+------------------------------------------------------------------+ //| Lazily seed, so a draw can never read an uninitialised state. | //| CHighQualityRand asserts on that rather than returning garbage, | //| which would be a hard stop mid-training. | //+------------------------------------------------------------------+ void WarriorRandEnsureSeeded(void) { if(!g_warriorRandReady) WarriorRandSeed("warrior"); } //--- Uniform on [0,1]. Both endpoints are attainable - callers that cannot take 0 must say so. double WarriorRandUniform(void) { WarriorRandEnsureSeeded(); return CHighQualityRand::HQRndUniformR(g_warriorRandState); } //--- Uniform on [-1,1] - the shape every weight-init site wants before scaling by its fan-in bound. double WarriorRandSymmetric(void) { return (WarriorRandUniform() - 0.5) * 2.0; } //--- Uniform integer on [0,n). Exactly uniform, not modulo-folded. int WarriorRandInt(const int n) { if(n <= 1) return 0; WarriorRandEnsureSeeded(); return CHighQualityRand::HQRndUniformI(g_warriorRandState, n); } //--- Standard normal. Unused by the uniform init sites, but this is the draw a Gaussian He/Xavier //--- init needs, and hand-rolling Box-Muller beside a library that already has it would be silly. double WarriorRandNormal(void) { WarriorRandEnsureSeeded(); return CHighQualityRand::HQRndNormal(g_warriorRandState); } #endif