Warrior_EA/System/Random.mqh
AnimateDread b91c7b1f7a refactor(comments): box headers to stdlib length
The //| box blocks were excluded from 0b06f8e and 5efdb48 and were what
remained: 160 of them ran to 10+ lines, the longest to 88. Compressed to their
leading topic sentences - 5 lines for a function header, 8 for a file header -
keeping the box format and the standard MQL5 name/author lines verbatim.

Verified at the BYTE level this time, across every in-scope file: the list of
non-comment lines is byte-identical to HEAD and braces balance. The first check
compared a locale-decoded 'git show' against a UTF-8 read and flagged 25 files
that had not changed at all - every BOM and every non-ASCII line mismatched.

47,696 -> 40,665 lines in scope; comment share 38% -> 26%.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-22 00:30:14 -04:00

76 lines
3.1 KiB
MQL5

//+------------------------------------------------------------------+
//| 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 <Math\Alglib\ap.mqh>
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