Generating Random Numbers for Instant Games
Overview
The process for generating random numbers in instant games involves two main steps:
- Generate a 512-bit (64-byte) random array using: - RandomBytes = HMACSHA512(Active_Server_Seed, Active_Client_Seed:Nonce:Cursor) 
- Extract the needed value—either an integer or a floating point number—from the generated bytes. 
📌 Note: This process differs from how random numbers are generated for scheduled games.
Generating Random Bytes
The core function is generateRandomBytes(). Here is the pseudocode:
byte[] byteGenerator(serverSeed, clientSeed, nonce, cursor) {
    // 1. Create an HMAC512 hasher with server seed as the key
    let hasher = HMAC512(serverSeed)
    // 2. Format the message as: clientSeed:nonce:cursor
    let message = clientSeed:nonce:cursor
    // 3. Hash the message
    let randomBytes = hasher.Hash(message)
    // 4. Increment the cursor
    cursor = cursor + 1
    // 5. Return the random bytes
    return randomBytes
}These randomBytes are then passed into either:
- generateDouble()→ for floating point numbers
- NextPositiveInt32()→ for integers
Generating Floating Point Numbers
Floating Point Structure (IEEE 754 - 64-bit double):
- 1 bit: Sign (positive/negative) 
- 11 bits: Exponent 
- 52 bits: Mantissa/Fraction 
The mantissa controls the precision for values between 0 and 1.
Algorithm
- Generate 7 random bytes 
- Combine into a 56-bit number 
- Mask to keep the lowest 52 bits 
- Divide by - 2^52→ Gives a uniform value between 0 (inclusive) and 1 (exclusive)
Pseudocode:
double generateDouble() {
    bytes = generateRandomBytes(7)
    bits = 0
    for each byte in bytes:
        bits = (bits << 8) OR byte
    masked = bits AND (2^52 - 1)
    randomDouble = masked / 2^52
    return randomDouble
}✅ This ensures maximum precision and equal probability for all outcomes in the range [0, 1).
Generating Integers
Unlike some operators who derive integers from floats (which can cause bias), this method directly generates uniform integers, ensuring fairness and accuracy.
Why This Matters:
- Mapping floats to integers (e.g., - floor(float * N)) introduces rounding issues
- This method uses rejection sampling to avoid bias 
How It Works:
- Draw a 32-bit random unsigned integer ( - value)
- Define a rejection limit: - limit = MAX_UINT32 - (MAX_UINT32 % maxExclusive) 
- If - value < limit, use:- result = value % maxExclusive 
- If not, discard and draw new bytes 
🛑 Rejection Sampling ensures perfectly uniform results in [0, maxExclusive)
Pseudocode:
int NextPositiveInt32(maxExclusive) {
    if maxExclusive not provided:
        maxExclusive = MAX_UINT32
    limit = MAX_UINT32 - (MAX_UINT32 mod maxExclusive)
    byteCount = HashingBitSize / 8
    loop forever:
        bytes = GenerateNextBytes(byteCount)
        for i from 0 to bytes.length - 4 step 4:
            value = convert 4 bytes at i to unsigned int
            if value < limit:
                return value % maxExclusive
}