The House Always Wins

GurpritGurprit
5 min read

As someone who enjoys playing video games, I’ve experienced the highs, the lows, and everything in between. But recently, I’ve noticed something deeply troubling. Many competitive multiplayer games — like CS2 and Rust, to name a few — have spawned third-party websites that use gambling mechanics cleverly disguised as loot boxes, skin cases, or prize wheels. These systems promise players a chance to win rare in-game items, but they’re often rigged and designed to exploit. Even more surprising is how lightly regulated they are. The gambling side of gaming has grown into a multi-billion dollar industry, yet in most places, it remains legal. And while players spend real money chasing these virtual rewards, cashing out is rarely straightforward.

Today, we’ll dissect how one such system works under the hood — specifically the algorithm that powers many of these “pulls.” You’ll see how these games use predictable pseudo-random number generators (PRNGs) to create the illusion of fairness, and how that very predictability can be exploited to manipulate outcomes and dupe unsuspecting players.

🎰 The Algorithm Behind the “Luck”

What most players don’t realize is that games of chance — whether it's skin cases, loot crates, or spin wheels — are powered by pseudo-random number generators (PRNGs), not true randomness. While the results look unpredictable on the surface, these systems are deterministic. That means, given the same inputs, they will always produce the same outputs.

Let’s break down the code behind one such algorithm, allegedly used by popular case-opening websites.

define('ROLL_CHARS', 15);
define('ROLL_MAX', 100000);

function generateRoll(string $serverSeed, string $clientSeed, int $nonce): int
{
  $hash = hash_hmac('sha512', $serverSeed, "{$clientSeed}-{$nonce}");
  echo $hash . "\n";

  $subHash = substr($hash, 0, ROLL_CHARS);
  echo $subHash . "\n";
  $roll = hexdec($subHash) % ROLL_MAX;
  echo hexdec($subHash) . "\n";
  echo $roll . "\n";

  // because we have [0; 99999] but need [1; 100000]
  return $roll + 1;
}

This algorithm uses HMAC-SHA512 (a cryptographic hash function) to produce a long hexadecimal string. It then takes the first 15 characters of that hash, converts it to a decimal using hexdec, and finally reduces it into a usable range for the roll outcome.

🤔 Can this be trusted ?

If the serverSeed is generated fairly and verifiably before the game, the system does approach true randomness — at least in spirit. But this assumes trust in the server’s integrity.

Let’s challenge that assumption.

Using the following Go script, I tested whether it was possible to brute-force a client seed that produces a high-value roll — say, 95000 or above.

package main

import (
    "crypto/hmac"
    "crypto/sha512"
    "encoding/hex"
    "fmt"
    "math/big"
)

const (
    ROLL_CHARS   = 5 // Adjust based on your needs
    ROLL_MAX     = 100000
    desiredRoll  = 95000
    serverSeed   = "add your server seed here"
    maxIterations = 1000000
)

func main() {
    for i := 0; i < maxIterations; i++ {
        clientSeed := fmt.Sprintf("seed%d", i)
        roll := generateRoll(serverSeed, clientSeed, 1)

        if roll >= desiredRoll {
            fmt.Printf("Found seed: %s produces %d\n", clientSeed, roll)
            break
        }
    }
}

func generateRoll(serverSeed, clientSeed string, nonce int) int {
    message := fmt.Sprintf("%s-%d", clientSeed, nonce)
    mac := hmac.New(sha512.New, []byte(serverSeed))
    mac.Write([]byte(message))
    hash := mac.Sum(nil)
    hashHex := hex.EncodeToString(hash)

    // Get first ROLL_CHARS characters
    subHash := hashHex[:ROLL_CHARS]

    // Convert hex to big integer
    rollBigInt := new(big.Int)
    rollBigInt.SetString(subHash, 16)

    // Calculate roll value
    roll := int(rollBigInt.Mod(rollBigInt, big.NewInt(ROLL_MAX)).Int64())

    // Adjust range from [0, 99999] to [1, 100000]
    return roll + 1
}

🎭 Manipulation in the Name of “Fairness”

When I tested this approach against real case-opening platforms, I noticed something suspicious:

  • The server seed often changed right before I was expected to get a high roll by changing client seed

  • The seed appeared to depend on how much I bet or how long I had been playing

  • The server DB dumps included data about how much each I had invested and what amount a user was winning.

You're made to feel you're getting close, just enough to keep you coming back, but the house always recalibrates.

What surprised me most was how game developers actively leveraged gambling-style mechanics often through partnerships with shady third-party case-opening sites and influencers to maximize profits. I’ll cover the full extent of this in a future post, but to give you a sense of scale: the market value of one of these games has surpassed $4 billion. That’s the equivalent of creating a virtual money-printing machine. Even more concerning, studies show that a large portion of participants in this ecosystem are underage users, many of whom gain access through their parents’ credit cards.

Why True Randomness Is Hard

Generating truly random numbers on a computer is surprisingly difficult. That’s because computers are deterministic machines — they follow predictable instructions. What we usually get are pseudo-random numbers, generated by algorithms that only appear random but are entirely reproducible if you know the seed.

To get real randomness, you need to tap into unpredictable physical phenomena. One example is Random.org, a service that generates random numbers using atmospheric noise — tiny variations in radio signals caused by natural fluctuations in the environment. Unlike PRNGs, these values aren’t derived from code but from the real world, making them truly random and non-reproducible.

So next time you call Math.random() and think you're dealing with true randomness… think again!

📚 Suggested Followup

💬

If you’ve made it this far, thank you! I’d love to hear your thoughts. If you have suggestions, corrections, or just want to chat — feel free to reach out or leave a comment.

0
Subscribe to my newsletter

Read articles from Gurprit directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Gurprit
Gurprit

I write about software engineering with a focus on clarity, performance, and the underlying ideas that drive good code. Whether it’s system design, algorithms, or language internals, I explore how things work—and how to build them better.