Simulating 10 Million Blackjack Hands in Python
Blackjack is simple enough to simulate in about 150 lines of Python and complicated enough that the answer is not obvious. We wrote a small simulator, ran ten million hands with basic strategy and ten million with a Hi-Lo count, and published the code so you can check our numbers.
The rules we simulated
- Six decks, reshuffled after 75% of the shoe has been dealt
- Dealer stands on soft 17; blackjack pays 3:2
- Double on any first two cards, double after split allowed
- Split once (no re-splitting); split aces receive one card each
- No surrender, no insurance
These are common, but slightly less generous than the best six-deck games: most real tables allow re-splitting pairs, and some allow surrender. Both would move the numbers below a little in the player's favour.
How the simulator works
The whole thing is three parts:
- A shoe. A shuffled list of 312 card values, with aces stored as 11. Drawing a card also updates the Hi-Lo running count.
- A strategy. Basic strategy written as three small functions: one for hard totals, one for soft totals, and one that decides whether to split a pair.
- A round. Deal, check for blackjacks, play the player's hand or hands, play the dealer, and settle every bet.
Hand totals use the usual trick: add everything up with aces as 11, then subtract 10 for each ace while the total is over 21.
def hand_value(cards):
total = sum(cards)
aces = cards.count(11)
while total > 21 and aces:
total -= 10
aces -= 1
return total, aces > 0 # (total, is_soft)
For the counting run, the bet is chosen from the true count (running count divided by decks remaining, rounded down) just before each round:
true_count = floor(running_count / decks_remaining)
bet = 1 if true_count <= 1 else [2, 4, 6, 8, 10, 12][min(true_count, 7) - 2]
That is 1 unit up to a true count of +1, 2 units at +2, and 2 more for each point after that, up to 12 units at +7 and above. The counting run uses no playing deviations, so the gain comes only from bet sizing.
The full script is here: blackjack_sim.txt (save it as a .py file to run it). It uses nothing but Python's standard library and runs about 200,000 hands a second on an ordinary machine.
Results
Each strategy was run as four independent batches of 2.5 million hands with different random seeds.
| Basic strategy, flat bet | Hi-Lo, 1–12 spread | |
|---|---|---|
| Hands | 10,000,000 | 10,000,000 |
| Average bet (units) | 1.00 | 1.50 |
| Net result (units) | −52,299 | +50,027 |
| Result per unit bet | −0.52% | +0.33% |
| Standard error | ±0.04% | ±0.05% |
| Standard deviation per hand (units) | 1.15 | 2.51 |
Reading the numbers
The basic-strategy figure is about right. A house edge of around half a percent is what published calculators give for a six-deck, stand-on-soft-17 game with no re-splitting and no surrender. If a simulator disagrees wildly with published figures, the bug is almost always in the strategy tables or in how split hands are settled.
The counting edge is real but small. A modest 1-to-12 spread turns a −0.52% game into a +0.33% game, measured against the total amount bet. That works out to half a unit won per hundred hands.
Variance more than doubles. Bigger bets at high counts mean bigger swings. A standard deviation of 2.5 units a hand against an expected win of 0.005 units a hand is why counters need large bankrolls. See variance and risk of ruin.
Ten million hands is a lot, and still not infinite. The standard errors in the table are what you get after ten million hands. A real player sees perhaps 100 hands an hour. Ten million hands is about 50 years of full-time play.
Things worth trying with the code
- Change the blackjack payout from 1.5 to 1.2 (a 6:5 table) and watch the edge collapse.
- Lower penetration from 0.75 to 0.5 and see how much of the counting edge disappears.
- Add the insurance deviation (take insurance at a true count of +3 or more).
- Replace the random module with NumPy and vectorise the shoe for speed.
- random: generate pseudo-random numbers, Python documentation
- Monte Carlo method, Wikipedia
- Standard error, Wikipedia
- Blackjack basic strategy calculator, Wizard of Odds