OS-016 · Stochastic processes

Gambler's Ruin Simulation

A gambler with i units bets 1 unit at a time, winning each bet with probability p, and stops on reaching N units or going broke. The chance of success has an exact formula — and it shows how lowering the win probability by a single percentage point turns a coin flip into a clear loss.

Interactive model

OS-016 · gamblers-ruin · seeded
reached Nruined at 0starting capital
running shareexact probability±2 standard errors
Parameters
Capped at N − 1
Play stops at 0 or N
Capped at 5 million bets in total
Same seed, same run — here, in simulation.js and in simulation.py.
Games played
2,000
Reached N (simulated)
40.55%
Reached N (exact)
40.13%
Standard error
1.10%
Mean bets per game (simulated)
99.9
Mean bets per game (exact)
98.7
Longest game
832

Example run: default parameters, seed 42.

A small edge, a large effect

With a fair coin (p = 1/2), starting at 10 and aiming for 20 succeeds exactly half the time. Lower p to 0.49 — an expected loss of 2% per bet, less than the 2.7% house edge on red at a single-zero roulette wheel — and the chance drops to about 40%. Each bet is nearly fair, but a game lasts about a hundred bets, and the small drift accumulates over all of them. Move the target further away and it gets worse fast: from 50 aiming for 100 at p = 0.49, success falls to about 12%.

The flip side is bold play. For an unfavorable game, fewer, larger bets are better. Betting 2 units at a time from 10 toward 20 is the same as the game from 5 toward 10, which succeeds with probability 45.0% instead of 40.1%; a single all-in bet succeeds with probability p = 49%. Try i = 5, N = 10 and then i = 1, N = 2 in the lab. When p > 1/2 the advice reverses: small bets let the favorable drift work.

The exact formulas

Let Pi be the probability of reaching N before 0 from capital i, and q = 1 − p. Conditioning on the first bet gives Pi = p·Pi+1 + q·Pi−1 with P0 = 0 and PN = 1. This linear recurrence solves to

Pi = [1 − (q/p)i] / [1 − (q/p)N]   (p ≠ ½),   Pi = i/N   (p = ½)

The expected number of bets Di satisfies the same recurrence plus one bet, Di = 1 + p·Di+1 + q·Di−1, with D0 = DN = 0:

Di = i/(q − p) − [N/(q − p)] · Pi   (p ≠ ½),   Di = i(N − i)   (p = ½)

The unit tests check both formulas against a direct numerical solution of the recurrences. The fair case has a one-line explanation: in a fair game the expected capital never changes, so i = N·Pi + 0·(1 − Pi). Two consequences are worth knowing. Against an opponent with unlimited money (N → ∞), ruin is certain whenever p ≤ ½, even in a fair game. And fair games are long: from 50 toward 100 the expected length is 2,500 bets.

What the simulation adds

Each game is a simple random walk with absorbing barriers at 0 and N — a finite Markov chain with two absorbing states. The lab plays the games one bet at a time and records whether each reached N and how many bets it took. The path plot shows how different individual games are: most end quickly, a few wander for hundreds of bets, which is why the longest game is several times the mean. The running share of successes settles inside the ±2 standard-error band around the exact value, at the usual 1/√n rate.

The simulation is the check here, not the answer, but it extends easily to rules without a formula — variable stakes, a stop-loss, a cap on the number of bets. To keep the page responsive, the number of games is reduced when the expected total would exceed about 5 million bets; the readout reports how many were played.

Example result

Starting with 10 units, aiming for 20 at p = 0.49, 40.55% of the 2,000 simulated games reached the target (standard error 1.10%), against the exact 40.13%. Games lasted 99.9 bets on average, against the exact expectation of 98.7; the longest took 832.

Output of gamblers-ruin with start=10 target=20 p=0.49 trials=2000 seed=42
Games played2,000
Reached N (simulated)40.55%
Reached N (exact)40.13%
Standard error1.10%
Mean bets per game (simulated)99.9
Mean bets per game (exact)98.7
Longest game832

Implementation

The page runs simulation.js from the package below; the Python file is a line-by-line port with the same random-number generator, so a given seed produces the same result in both languages. The excerpts show the core of each.

JavaScript — simulation.js
// One game: bet 1 unit at a time, win with probability p, lose with q = 1 − p,
// until the capital hits 0 (ruin) or the target N. Returns the outcome and length.
export function playGame(i, n, p, rng, onBet) {
  let x = i, bets = 0;
  while (x > 0 && x < n) {
    x += rng.bernoulli(p) ? 1 : -1;
    bets++;
    if (onBet) onBet(x, bets);
  }
  return { won: x === n, bets };
}

// Probability of reaching N before 0 from i (classical ruin problem, Feller ch. XIV).
export function exactSuccess(i, n, p) {
  if (fair(p)) return i / n;
  const r = (1 - p) / p;
  return (1 - r ** i) / (1 - r ** n);
}

// Expected number of bets until absorption at 0 or N.
export function exactDuration(i, n, p) {
  if (fair(p)) return i * (n - i);
  const q = 1 - p;
  return i / (q - p) - (n / (q - p)) * exactSuccess(i, n, p);
}
Python — simulation.py
def play_game(i, n, p, rng):
    """Bet 1 unit at a time (win with probability p) until capital hits 0 or N."""
    x, bets = i, 0
    while 0 < x < n:
        x += 1 if rng.bernoulli(p) else -1
        bets += 1
    return x == n, bets


def exact_success(i, n, p):
    """P(reach N before 0 | start at i)."""
    if _fair(p):
        return i / n
    r = (1 - p) / p
    return (1 - r ** i) / (1 - r ** n)


def exact_duration(i, n, p):
    """Expected number of bets until absorption."""
    if _fair(p):
        return i * (n - i)
    q = 1 - p
    return i / (q - p) - (n / (q - p)) * exact_success(i, n, p)

Run the full model from a terminal:

Shell
node simulation.js --start 10 --target 20 --seed 42
python simulation.py --start 10 --target 20 --seed 42

Download the model

gamblers-ruin.zip6 files · 12.0 KBDownload .zip
Files in gamblers-ruin.zip
FileContentsSize
README.mdHow to run it, parameters, example result2.3 KB
model.jsonParameters with ranges and defaults, outputs, the seed-42 example2.3 KB
simulation.jsThe model used on this page plus a command-line runner (Node 18+)8.1 KB
simulation.pyPython 3.9+ port, standard library only6.5 KB
example.csvResult table from the example run8.0 KB
LICENSE.txtMIT License1.0 KB

Assumptions & limitations

  • Independent, identical bets. Every bet wins with the same probability p, independent of the past. Real games can have changing odds, and card games like blackjack have memory.
  • Unit stakes, fixed stopping rule. The gambler always bets 1 unit and stops only at 0 or N. No betting system changes the expected loss per bet, but stake sizes do change the chance of reaching a target.
  • Even-money payoffs. A win adds 1 unit and a loss removes 1. Games with other payouts are random walks with unequal steps and need a different analysis.
  • Capped work. The number of games is reduced if the expected total number of bets exceeds 5 million.
  • Pseudo-random bets. Outcomes come from a seeded generator; see pseudo-random numbers and seeds.

References

  1. Feller, W. (1968). An Introduction to Probability Theory and Its Applications, Vol. 1, 3rd ed., chapter XIV. Wiley.
  2. Grinstead, C. M. & Snell, J. L. Introduction to Probability, 2nd rev. ed., chapter 12. American Mathematical Society. math.dartmouth.edu/~prob/prob/prob.pdf