January 12, 2023

Gambler's Ruin

Authors: Ryan Peters*
* Core Contributor; Correspondence to ryanirl@icloud.com;

The Gambler’s Ruin is a classic problem in probability that goes as follows: Say we have two gamblers A and B, who are competing against each other in a game. Each gambler initially starts with some amount of money, and at each round of the game, one player either wins a dollar from the other player, or loses a dollar to the other player. They play as many independent rounds as they need until one player runs out of money (gets ruined).

Alt Text

Figure 1: One in a series of five oil paintings created by the French artist Paul Cézanne during the early 1890’s titled The Card Players. This painting in particular can be found in the Musée d’Orsay in Paris, France.

To begin formulating this problem, let’s define some variables:

  • Let player A start with ii dollars and player B starts with (Ni)(N - i) dollars. Therefore N, the total amount of dollars in the game, is constant and does NOT change throughout the game (make sure to convince yourself of this before moving on).
  • Let player A have probability pp of winning any given round and player B have probability q=1pq = 1 - p of winning any given round. In a fair game p=q=0.5p = q = 0.5.
  • Given that player A has ii dollars at the start of an arbitrary round, let us define xix_i as the probability that player A wins the entire game (all N dollars). This is a convenient notation because if player A losses that first round, then the probability that player A wins the game at the next round would be xi1x_{i-1} because they lost 1 dollar in the previous round (if they won the first round, it would instead be xi+1x_{i+1}). This is the probability that we want to solve for. That is, we want to know at any point in time what xix_i is, given the starting state defined by the variables pp, ii, and NN.

As a side note, here are two definitions and a theorem that we will need later.

  • Mutually Exclusive: Two events E0E_0 and E1E_1 are mutually exclusive if they cannot occur at the same time. That is E0E1=E_0 \cap E_1 = \emptyset.
  • Exhaustive: Given a sample space SS, events E0,E1,,EnE_0, E_1, \ldots, E_n are exhaustive if i=0n(Ei)=S\bigcup_{i = 0}^{n} (E_i) = S.

Theorem (Law of Total Probability): Suppose E0,E1,,EnE_0, E_1, \ldots, E_n are mutually exclusive and exhaustive events in a sample space SS. Then for any event ASA \in S it must be that

P(A)=P(AE0)++P(AEn)=P(AE0)P(E0)++P(AEn)P(En)=i=0nP(AEi)P(Ei)\begin{aligned} P(A) &= P(A \cap E_0) + \ldots + P(A \cap E_n) \\ &= P(A | E_0) P(E_0) + \ldots + P(A | E_n) P(E_n) \\ &= \sum_{i = 0}^{n} P(A | E_i) P(E_i) \end{aligned}

Now, back to the problem at hand. Previously it was established that we want to solve for xix_i, the probability that player A wins the game starting with ii dollars. First notice that we also have two boundary conditions, x0x_0 and xNx_N. x0x_0, the probability that player AA wins when they have 00 dollars is 00 (because they have already lost). xNx_N, the probability that player AA wins when they have NN dollars is 11 (because they have already won).

This is progress, but now let’s dig deeper and define two events:

  • WW: The event that A wins a round.
  • LL: The event that A loses a round.

Notice that events WW and LL are mutually exlusive and exhaustive. Mutually exclusive because if AA wins a round, then they cannot also lose that round. Exhaustive because player AA can either win or lose a round, nothing more and nothing less. Therefore we can apply the law of total probability (previously defined above) to get the following equation for xix_i:

xi=P(xiW)P(W)+P(xiL)P(L)xi=xi+1p+xi1q\begin{align*} x_i &= P(x_i | W) P(W) + P(x_i | L) P(L) \\ x_i &= x_{i + 1} p + x_{i - 1} q \\ \end{align*}

Let’s break this down a bit. We can interpret P(xiW)P(x_i | W) as “the probability that player AA wins the whole game when starting with ii dollars (xix_i) given that player AA wins the first round (event WW)“. Therefore P(xiW)=xi+1P(x_i | W) = x_{i + 1}, and inversely P(xiL)=xi1P(x_i | L) = x_{i - 1}.

As a reminder pp is the probability that player AA wins any round and q=p1q = p - 1 is the probability that player A will lose a single round (or the probability that player B wins a round). Typically p=q=0.5p = q = 0.5 in a fair game.

Equations of this form are called difference equations (not to be confused with differential equations). I will cover two ways we can solve this equation: A clever and more brute force method, and a more principled method that involves guessing a solution to the difference equation.

Method 1: Clever Brute Force

First, notice that because p+q=1p + q = 1 we can reorganize the difference equation to the following:

xi=xi+1p+xi1q(p+q)xi=xi+1p+xi1qxi+1xi=qp(xixi1)\begin{align*} x_i &= x_{i + 1} p + x_{i - 1} q \\ (p + q) x_i &= x_{i + 1} p + x_{i - 1} q \\ x_{i + 1} - x_{i} &= \frac{q}{p} (x_{i} - x_{i - 1}) \end{align*}

This form is still a bit confusing, so let’s plug in a couple values to see if we can find any sort of pattern that arises from this equation. First let’s try i=1i = 1:

x2x1=qp(x1x0)=qp(x1)\begin{align*} x_{2} - x_{1} &= \frac{q}{p} (x_{1} - x_{0}) \\ &= \frac{q}{p} (x_{1}) \end{align*}

Now, let’s try i=2i = 2:

x3x2=qp(x2x1)=qp(qp(x1))=(qp)2(x1)\begin{align*} x_{3} - x_{2} &= \frac{q}{p} (x_{2} - x_{1}) \\ &= \frac{q}{p} ( \frac{q}{p} (x_{1})) \\ &= (\frac{q}{p})^{2} (x_{1}) \\ \end{align*}

Immediately we can see a pattern arise. Feel free to try i=3i = 3 on your own, but it turns out this equation does generalizes to:

xi+1xi=(qp)i(x1)\begin{align*} x_{i + 1} - x_{i} &= (\frac{q}{p})^{i} (x_{1}) \\ \end{align*}

This will be an important equation for later. For now, we still need one more piece of the puzzle before things start coming together. Notice one more thing:

xi+1x1=(xi+1xi)+(xixi1)++(x2x1)xi+1x1=k=1i(xk+1xk)xi+1x1=x1k=1i(qp)kxi+1=x1+x1k=1i(qp)kxi+1=x1k=0i(qp)k\begin{aligned} x_{i + 1} - x_{1} &= (x_{i + 1} - x_{i}) + (x_{i} - x_{i - 1}) + \ldots + (x_{2} - x_{1}) \\ x_{i + 1} - x_{1} &= \sum_{k = 1}^{i} (x_{k + 1} - x_{k}) \\ x_{i + 1} - x_{1} &= x_1 \sum_{k = 1}^{i} (\frac{q}{p})^{k} \\ x_{i + 1} &= x_{1} + x_1 \sum_{k = 1}^{i} (\frac{q}{p})^{k} \\ x_{i + 1} &= x_1 \sum_{k = 0}^{i} (\frac{q}{p})^{k} \\ \end{aligned}

This is going to be the clever part of the “clever brute force method” as it’s not obvious how one would come up with this on the spot.

Plugging this equation into the previously derived equation, we can see the following:

xi+1xi=(qp)i(x1)x1k=0i(qp)kxi=(qp)i(x1)xi=x1k=0i(qp)k(qp)i(x1)\begin{align*} x_{i + 1} - x_{i} &= (\frac{q}{p})^{i} (x_{1}) \\ x_1 \sum_{k = 0}^{i} (\frac{q}{p})^{k} - x_{i} &= (\frac{q}{p})^{i} (x_{1}) \\ x_{i} &= x_1 \sum_{k = 0}^{i} (\frac{q}{p})^{k} - (\frac{q}{p})^{i} (x_{1}) \\ \end{align*}

Note the following fact about the geometric series:

k=0iai=1ai+11a\begin{align*} \sum_{k=0}^{i} a^i &= \frac{1 - a^{i+1}}{1-a} \\ \end{align*}

Using this we can simplify the above equation to:

xi=x1k=0i(qp)k(qp)i(x1)xi=x1(k=0i(qp)k(qp)i)xi=x1(1(qp)i+11qp(qp)i)xi=x1(1(qp)i1qp)\begin{align*} x_{i} &= x_1 \sum_{k = 0}^{i} (\frac{q}{p})^{k} - (\frac{q}{p})^{i} (x_{1}) \\ x_{i} &= x_1 (\sum_{k = 0}^{i} (\frac{q}{p})^{k} - (\frac{q}{p})^{i} ) \\ x_{i} &= x_1 (\frac{1-(\frac{q}{p})^{i+1}}{1-\frac{q}{p}} - (\frac{q}{p})^{i}) \\ x_{i} &= x_1 (\frac{1 - (\frac{q}{p})^i}{1-\frac{q}{p}}) \\ \end{align*}

Although, notice that this is actually not defined in the case that p=q=0.5p = q = 0.5. Thus, we need to go back to the original series and see that in the case that p=q=0.5p = q = 0.5 we get the following (feel free to verify this on your own):

xi={x11(qp)i1qpif    pqx1iif    p=q\begin{align*} x_i = \begin{cases} x_1 \frac{1 - (\frac{q}{p})^i}{1-\frac{q}{p}} & \text{if} \;\; p \neq q \\ x_1 i & \text{if} \;\; p = q \end{cases} \end{align*}

Dealing with just the pqp \neq q case, let’s try and figure out x1x_1. To do so, remember that we know the solutions to the edge cases, so let’s plug in the case in which i=Ni = N.

xN=x1(1(qp)N1qp)1=x1(1(qp)N1qp)x1=1qp1(qp)N\begin{align*} x_{N} &= x_1 (\frac{1 - (\frac{q}{p})^N}{1-\frac{q}{p}}) \\ 1 &= x_1 (\frac{1 - (\frac{q}{p})^N}{1-\frac{q}{p}}) \\ x_1 &= \frac{1-\frac{q}{p}}{1 - (\frac{q}{p})^N} \\ \end{align*}

Now that we have x1x_1, plugging it back into the original equation we get:

xi=(1qp1(qp)N)(1(qp)i1qp)xi=1(qp)i1(qp)N\begin{align*} x_{i} &= (\frac{1-\frac{q}{p}}{1 - (\frac{q}{p})^N}) (\frac{1 - (\frac{q}{p})^i}{1-\frac{q}{p}}) \\ x_{i} &= \frac{1 - (\frac{q}{p})^i}{1 - (\frac{q}{p})^N} \\ \end{align*}

This is the final solution to the case where pqp \neq q. To handle the case in which p=q=0.5p = q = 0.5, we get that x1=1Nx_1 = \frac{1}{N}. Plugging this back into the original equation we get that xi=iNx_i = \frac{i}{N}, and thus the solution is:

xi={1(qp)i1(qp)Nif    pqiNif    p=q\begin{align*} x_i = \begin{cases} \frac{1 - (\frac{q}{p})^i}{1 - (\frac{q}{p})^N} & \text{if} \;\; p \neq q \\ \frac{i}{N} & \text{if} \;\; p = q \end{cases} \end{align*}

Method 2: Principled Difference Equation Solution

Another way we can solve for xix_i from the form xi=xi+1p+xi1qx_i = x_{i + 1} p + x_{i - 1} q is to notice that this equation is a pretty standard difference equation (discrete analog of a differential equation). Thus, we can use textbook ideas from differential equations to solve for xix_i.

To do so, let’s guess a possible solution, and first try xi=xix_i = x^i. Then:

xi=xi+1p+xi1qxi=xi+1p+xi1qx=px2+q0=px2x+qx=1±14pq2p\begin{align*} x_i &= x_{i + 1} p + x_{i - 1} q \\ x^i &= x^{i + 1} p + x^{i - 1} q \\ x &= p x^2 + q \\ 0 &= p x^2 - x + q \\ x &= \frac{1 \pm \sqrt{1 - 4pq}}{2p} \end{align*}

Simplifying a bit, notice that 14pq=14p(1p)=14p+4p2=(2p1)21 - 4pq = 1 - 4p(1 - p) = 1 - 4p + 4p^2 = (2p - 1)^2. Therefore:

x=1±14pq2p=1±(2p1)2p\begin{align*} x &= \frac{1 \pm \sqrt{1 - 4pq}}{2p} \\ &= \frac{1 \pm (2p - 1)}{2p} \end{align*}

This has two solutions, 11 and qp\frac{q}{p}. Therefore, we should take the linear combination of these roots. This gives a solution of:

xi=C0+C1(qp)i\begin{align*} x_i = C_0 + C_1 (\frac{q}{p})^i \end{align*}

For some constants C0C_0 and C1C_1. Well, to solve for C0C_0 and C1C_1 we need to plug in our boundary conditions x0=0x_0 = 0 and xN=1x_N = 1. Starting with x0=0x_0 = 0 we obtain:

x0=C0+C1(qp)00=C0+C1C1=C0\begin{align*} x_0 &= C_0 + C_1 (\frac{q}{p})^0 \\ 0 &= C_0 + C_1 \\ C_1 &= -C_0 \end{align*}

Handling xN=1x_N = 1 next, we obtain:

xN=C0+C1(qp)N1=C0+C1(qp)N1=C0C0(qp)N1=C0(1(qp)N)C1=11(qp)N\begin{align*} x_N &= C_0 + C_1 (\frac{q}{p})^N \\ 1 &= C_0 + C_1 (\frac{q}{p})^N \\ 1 &= C_0 - C_0 (\frac{q}{p})^N \\ 1 &= C_0 (1 - (\frac{q}{p})^N) \\ C_1 &= \frac{1}{1 - (\frac{q}{p})^N} \\ \end{align*}

Therefore C0=11(qp)NC_0 = \frac{-1}{1 - (\frac{q}{p})^N}. Notice that in this case, pp cannot equal qq or else we get a divide by 00. Which is bad. Therefore, we will have to solve for the case that pp equals qq later and treat it as a separate case. For now, when pqp \neq q we get:

xi=11(qp)N11(qp)N(qpi)=11(qp)N(1(qpi))=1(qp)i1(qp)N\begin{align*} x_i &= \frac{1}{1 - (\frac{q}{p})^N} - \frac{1}{1 - (\frac{q}{p})^N}(\frac{q}{p}^i) \\ &= \frac{1}{1 - (\frac{q}{p})^N} ( 1 - (\frac{q}{p}^i)) \\ &= \frac{1 - (\frac{q}{p})^i}{1 - (\frac{q}{p})^N} \end{align*}

Now handling the case in which p=qp = q, notice that the roots of the differential equation become 11 and 11. That is, we have repeated roots. What shall we do in this situation? Well, classic theory of differential would tell us to consider the solution: xi=C0+C1ix_i = C_0 + C_1 i. Doing so and plugging in our boundary cases gives:

x0=C0+C1(0)C0=0\begin{align*} x_0 &= C_0 + C_1 (0) \\ C_0 &= 0 \\ \end{align*}

And finally:

xN=C0+C1(N)1=C1(N)C1=1N\begin{align*} x_N &= C_0 + C_1 (N) \\ 1 &= C_1 (N) \\ C_1 &= \frac{1}{N} \\ \end{align*}

Therefore our solution for the case when p=qp = q is: xi=1Ni=iNx_i = \frac{1}{N} i = \frac{i}{N}, and the final solution becomes:

xi={1(qp)i1(qp)Nif    pqiNif    p=q\begin{align*} x_i = \begin{cases} \frac{1 - (\frac{q}{p})^i}{1 - (\frac{q}{p})^N} & \text{if} \;\; p \neq q \\ \frac{i}{N} & \text{if} \;\; p = q \end{cases} \end{align*}

And that’s it for the formal derivation of the solution to this problem. In both methods for solving the problem we came to the same solution.

Analysis

Now that we have a closed-form solution for xix_i, the probability that player A will win the entire game when starting with ii dollars of NN total dollars when having a probability of pp for winning every independent round, let’s see how different values of ii, pp, and NN impact the probability of player A winning the game.

To do so, we need to first write some code to compute the closed-form solution. Below is that implementation. This handles some edge cases in which numerical stability is a problem, though quickly breaks if N is set too large due to overflow.

gamblers_ruin.py
def true_probability(p: float, i: int, N: int, eps: float = 1e-12) -> float:
"""Returns the true probability that player A wins the game if they
have probability `p` of winning any given round, start with `i` dollars
of `N` total dollars in the game.
"""
assert 0.0 <= p <= 1.0, "`p` must be a probability between 0 and 1."
assert 0 <= i <= N, "`i` an integer between 0 and N."
q = 1.0 - p
# Base cases in which player A has already won or lost.
if i == 0: return 0.0
if i == N: return 1.0
# Another edge case and some cases with numerical instability.
if (p <= eps): return 0.0
if (p >= 1.0 - eps): return 1.0
if (p == 0.5): return i / N
return (1.0 - ((q / p) ** i)) / (1.0 - ((q / p) ** N))

Now let’s visualize different values of pp, ii, and NN (the code to do so can be found on my GitHub here):

Figure 2: A visualization of the probabilities of player A winning the game given differing values of p, i, and N. The white line illustrates the mean probability for every value of i. Brighter colors illustrate a higher probability and darker colors illustrate a lower probability.

Looking at the above, it seems the more money in the game (NN) the sharper the cutoff between the probabilities. This makes a lot of sense because winning by luck is less likely when there is more money in the game since more rounds are played. That is, given even the slightest edge in pp you’re almost guaranteed to win if NN is large and iN2i \approx \frac{N}{2}.

Monte Carlo Simulation

Just for fun, another thing we can do to validate our closed-form solution is to compare it against a bunch of simulated games. You would assume that if we simulated n_simulation games then as n_simulation approaches infinity that the ratio of games that player A won would approach our analytical solution (Figure 3).

Figure 3: The error (RMSE) in the estimation of xix_i using the monte carlo simulation and the true probability using the analytical solution. Notice that as we increase the number of simulations ran, the average error decreases.

To do this, we need to first write code to simulate the playing of a game given the following variables:

  • pp, the probability that player AA wins any independent round.
  • ii, the amount of money player AA starts with.
  • NN, the total amount of money in the game.

Below is the code to do that:

gamblers_ruin.py
import random
from typing import Optional
class GamblersRuin:
def __init__(self, p: float, i: int, N: int) -> None:
self.p = p
self.i = i
self.N = N
self.history = [i]
@property
def ruined(self) -> bool:
if self.i <= 0:
return True
return False
@property
def won(self) -> bool:
if self.i >= self.N:
return True
return False
@property
def game_over(self) -> bool:
if self.ruined or self.won:
return True
return False
@property
def victor(self) -> Optional[int]:
if self.won:
return 1
if self.ruined:
return 0
return None
def play_round(self) -> None:
if self.game_over:
print("Cannot play round, the game is over.")
return
if random.uniform(0, 1) <= self.p: # Player A wins this round.
self.i += 1
else:
self.i -= 1
# Keep track of the history of the game for analysis.
self.history.append(self.i)
def play_game(self) -> int:
while not self.game_over:
self.play_round()
return self.victor

Now that we have a way to simulate a single game, let’s simulate some number of games and compute the probability of xix_i as the ratio of games that player A won against the total number of games played. Therefore, the analog of the true_probability() function for estimating the probability based on Monte Carlo simulations would be:

gamblers_ruin.py
def estimated_probability(p: float, i: int, N: int, n_sims: int = 1_000) -> float:
wins = []
for _ in range(n_sims):
game = GamblersRuin(p = p, i = i, N = N)
victor = game.play_game()
wins.append(victor)
return sum(wins) / len(wins)

Now that we have both a closed-form and estimated solution to this problem, let’s compare the results of both using 1000 Monte Carlo simulations.

gamblers_ruin.py
def compare(p: float, i: int, N: int, n_sims: int = 1_000) -> None:
true_p = true_probability(p, i, N)
est_p = estimated_probability(p, i, N, n_sims)
print(f"p: {p:.02f} | i: {i} | N: {N:>3} | true: {true_p:.3f} | est: {est_p:.3f}")
compare(p = 0.50, i = 50, N = 100)
compare(p = 0.49, i = 50, N = 100)
compare(p = 0.49, i = 70, N = 100)
compare(p = 0.40, i = 90, N = 100)
p: 0.50 | i: 50 | N: 100 | true: 0.500 | est: 0.510
p: 0.49 | i: 50 | N: 100 | true: 0.119 | est: 0.115
p: 0.49 | i: 70 | N: 100 | true: 0.288 | est: 0.262
p: 0.40 | i: 90 | N: 100 | true: 0.017 | est: 0.018

The results look really good! In general, both the estimated and true probability seem to give very similar results (which is expected). Of course, as we increase n_sims the difference between the true and estimated probabilities should converge to each other.

Since we keep track of the histories of the game, one last thing we can do is plot the histories of each game. Doing so with 100 games, and the above settings gives:

Figure 4: The history of 5 fair games played with parameters i = 50, N = 100, p = 0.5. Even though it was a fair game, player A still lost 4 games, tough luck. The colors represent each game played (5 total).

And this is pretty cool, you can see the random walk behavior clearly.

In summary, we explored the Gambler’s Ruin problem, a classic problem in probability theory where two players gamble until one player runs out of money. We derived a closed-form solution for the probability of one player winning the entire game, using both a clever brute force method and a more principled approach based on difference equations. This solution reveals how the initial stake, total money in play, and the probability of winning each round affect the overall chances of winning. Next, we implemented the game in Python, and ran a Monte Carlo simulation to experimentally validate the closed form solution. In doing so, we found that the closed-form solution and the Monte Carlo simulation provided similar results that converged upon increasing the number of simulated games. Finally, we ran an analysis to show how different values of initial parameters impacted the probability of one player winning, and also showed figures demonstrating the random-walk nature of these games.

References

  1. Blitzstein, J. (2013). Lecture 7: Gambler's Ruin and Random Variables | Statistics 110. Link
  2. Sigman, K. (2009). Gambler’s Ruin Problem. Link

Figures

  1. The Card Players. Link.
  2. Custom. Probability of winning given various starting conditions. Link.
  3. Custom. Error of the Monte Carlo simulation given a varying number of simulations. Link.
  4. Custom. Five examples of games played, and their history. Link.