A probability and Python lesson on mathematically fair games, casino house edges, and simulation.
Learning goals
By the end of this lesson, learners should be able to:
Distinguish mathematical fairness from commercial “fair play.”
Calculate expected value, RTP, and house edge.
Design fair and house-edge versions of single- and double-dice games.
Implement and simulate these games in Python.
Interpret results from 10,000 simulated rounds.
This lesson uses fictional money for probability and programming education—not real-money betting.
What is a fair game of chance?
A game of chance is mathematically fair if neither player has an expected long-run advantage.
If a player repeatedly makes the same bet, their average net gain should approach zero over a very large number of plays:
\[
\text{Expected value for player} = 0
\]
For a game with possible outcomes \(i\):
\[
EV = \sum_i P(i) \times \text{net payoff}(i)
\]
A fair game has:
\[
EV = 0
\]
This does not mean that every player breaks even in a single session. A player may win ₹500 in one round and lose ₹500 in the next. Fairness concerns the average outcome over many repetitions.
Mathematical fairness and casino fairness
A casino may describe a game as “fair” when it is honestly run: the rules are clear, outcomes are random, and payouts match the published schedule. But the game is usually not mathematically fair for the player.
\[
EV = \left(\frac{1}{6}\times500\right) + \left(\frac{5}{6}\times-100\right) = \text{₹0}
\]
So this is mathematically fair.
Casino version: 5% house edge
Set \(h=0.05\):
\[
G = \frac{100(1-0.05)}{1/6} = \text{₹570}
\]
The player earns a net ₹470 after a win, rather than ₹500.
Result
Probability
Player net result
Selected face appears
\(1/6\)
+₹470
Any other face appears
\(5/6\)
-₹100
\[
EV = \left(\frac{1}{6}\times470\right) + \left(\frac{5}{6}\times-100\right) = -\text{₹5}
\]
The expected player loss is ₹5 per ₹100 bet, which is a 5% house edge.
Double-dice game
When two dice are rolled, their sums do not have equal probabilities.
There are 36 equally likely ordered outcomes:
\[
6 \times 6 = 36
\]
For example, a total of 7 can occur in six ways:
\[
(1,6), (2,5), (3,4), (4,3), (5,2), (6,1)
\]
Therefore:
\[
P(\text{sum is 7}) = \frac{6}{36} = \frac{1}{6}
\]
A total of 2 can occur only in one way, \((1,1)\):
\[
P(\text{sum is 2}) = \frac{1}{36}
\]
Target sum
Ways to obtain it
Probability
2 or 12
1
\(1/36\)
3 or 11
2
\(2/36\)
4 or 10
3
\(3/36\)
5 or 9
4
\(4/36\)
6 or 8
5
\(5/36\)
7
6
\(6/36\)
Fair double-dice example
A player stakes ₹100 that the total will be 2.
\[
p = \frac{1}{36}
\]
Fair gross payout:
\[
G = \frac{100}{1/36} = \text{₹3,600}
\]
The player receives a net profit of ₹3,500 if the total is 2.
Casino double-dice example: 5% edge
\[
G = \frac{100(0.95)}{1/36} = \text{₹3,420}
\]
The player earns ₹3,320 net on a successful prediction of total 2, rather than ₹3,500.
Python: mathematically fair die game
from secrets import randbelowSTAKE =100CHOSEN_FACE =4win_probability =1/6gross_payout = STAKE / win_probability # ₹600 in a fair gameroll = randbelow(6) +1if roll == CHOSEN_FACE: player_net = gross_payout - STAKEprint(f"Rolled: {roll}")print(f"You won ₹{player_net:.2f}")else: player_net =-STAKEprint(f"Rolled: {roll}")print(f"You lost ₹{-player_net:.2f}")
Rolled: 2
You lost ₹100.00
The program uses secrets.randbelow(6) + 1 to generate a uniformly distributed die result from 1 to 6.
Python: casino die game
This version uses the same unbiased die roll, but sets a 5% house edge by reducing the payout.
from secrets import randbelowSTAKE =100HOUSE_EDGE =0.05CHOSEN_FACE =4win_probability =1/6gross_payout = STAKE * (1- HOUSE_EDGE) / win_probabilitynet_win = gross_payout - STAKEroll = randbelow(6) +1if roll == CHOSEN_FACE:print(f"Rolled: {roll}")print(f"You won ₹{net_win:.2f}")else:print(f"Rolled: {roll}")print(f"You lost ₹{STAKE:.2f}")
Rolled: 5
You lost ₹100.00
The die remains unbiased. The house edge comes from the stated payout—₹570 rather than the fair ₹600—not from secretly changing the result.
Simulating 10,000 rolls
from secrets import randbelowNUM_ROLLS =10_000STAKE =100HOUSE_EDGE =0.05CHOSEN_FACE =4win_probability =1/6gross_payout = STAKE * (1- HOUSE_EDGE) / win_probabilitynet_win = gross_payout - STAKEplayer_profit =0.0casino_profit =0.0wins =0for _ inrange(NUM_ROLLS): roll = randbelow(6) +1if roll == CHOSEN_FACE: wins +=1 player_profit += net_win casino_profit -= net_winelse: player_profit -= STAKE casino_profit += STAKEtotal_wagered = NUM_ROLLS * STAKEobserved_win_rate = wins / NUM_ROLLSobserved_house_edge = casino_profit / total_wageredtheoretical_casino_profit = total_wagered * HOUSE_EDGEprint("DICE CASINO SIMULATION")print("-"*35)print(f"Number of rolls: {NUM_ROLLS:,}")print(f"Stake per roll: ₹{STAKE:.2f}")print(f"House edge: {HOUSE_EDGE:.2%}")print(f"Gross payout on a win: ₹{gross_payout:.2f}")print(f"Number of player wins: {wins:,}")print(f"Observed win rate: {observed_win_rate:.2%}")print()print(f"Total amount wagered: ₹{total_wagered:,.2f}")print(f"Player net result: ₹{player_profit:,.2f}")print(f"Casino profit: ₹{casino_profit:,.2f}")print(f"Observed house edge: {observed_house_edge:.2%}")print(f"Expected casino profit: ₹{theoretical_casino_profit:,.2f}")
DICE CASINO SIMULATION
-----------------------------------
Number of rolls: 10,000
Stake per roll: ₹100.00
House edge: 5.00%
Gross payout on a win: ₹570.00
Number of player wins: 1,670
Observed win rate: 16.70%
Total amount wagered: ₹1,000,000.00
Player net result: ₹-48,100.00
Casino profit: ₹48,100.00
Observed house edge: 4.81%
Expected casino profit: ₹50,000.00
The simulation will not necessarily produce exactly ₹50,000. Random variation means that 10,000 rolls may yield a higher or lower casino profit. Repeating the experiment, or increasing the number of rolls, should make the observed edge tend closer to 5%.
Conclusion
A fair chance game has an expected player gain of zero; its payout exactly matches the probability of a win. A casino-style game can still be procedurally fair—random outcomes, visible rules, and consistent payouts—while giving the operator an advantage through a lower-than-fair payout.
The key design principle is:
\[
\text{Fair payout} = \frac{\text{Stake}}{\text{Probability of winning}}
\]
In a transparent simulation, randomness determines each outcome, while the published payout schedule determines whether the game is mathematically fair or has a house edge.