Casino Games Simulator

Introduction

Overview

This a detailed description a simulator for casino games. It allows someone to model different player bettting strategies for common games. It gathers some statistics that I find useful for comparing the betting strategies.

Introduction

This document describes the Architecture of the simulator in general. The Architecture section has a roadmap through the various modules. This document reviews the Design of each module and class in detail. It also lists some Extensions that can be applied to this simulator to create more value.

Technology

This simulator is a Python application. You’ll need the Python interpreter appropriate for your platform. This has been tested under Windows 98 and MacOS 8.x. I have complete confidence that the various ports of Python are of such high quality that this will run everywhere.

A quick note on the use of an interpreter. The “slowness” factor is more than offset by the leverage from garbage collection, built-in data structure features, reliability and debugging power.

A quick note on Python. While Python programs don’t have the robust compile-time checking available in Java, they do permit a quick assembly of a working application. For a reliable, robust, high-performance application, Java may be a better choice. For a simulation like this, however, Python permits rapid implementation.

The diagrams were produced with Visio 2000 in a few minutes; very little care was taken to make them complete or easy-to-read. They were exported as JPEG’s, by Visio, again, as rapidly as possible.

This document was done in BBEdit 5.

References

The basic rules and some of the player strategies are stolen without any permission of any kind from The Wizard of Odds. His site is a brilliant analysis of casino games.

Architecture

The overall architecture is shown in the following component diagram.

Component Diagram

This diagram shows the dependencies among the Python modules. Each module is described in detail in the Design section.

  • The Driver module contains the main() function and a utility process() function. These decode command-line prompts and run the request simulations.
  • The Simulator module contains the classes which provide the basic simulation and analysis capabilities.
  • The Casino module contains superclasses for all casino games. This includes Bet, Table, Game and Player.
  • The Cards module contains classes for card games in particular. The basic Card, Deck and Hand classes have unique subclasses for Blackjack and Caribbean Stud poker.
  • The Simlog module is a general-purpose logging module used throughout the simulator.
  • The modules sim1 (Craps), sim2 (Roulette), sim3 (Blackjak) and sim4 (Caribbean Stud Poker) are standard casino games. Each is built as a set of subclasses of the parent classes defined in Casino.

Design

driver

The driver module is shown in the following static structure diagram.

Driver Structure

The driver uses classes from the simulator module and subclasses of the casino module to run a simulation.

driver.main()

Decodes the command-line parameters.

driver.process()

Requires a specific game name, a number of samples to create, a limit on the number of rounds a player will play and a list of player names; optionally a report file. It is a kind of Factory, and decodes the game name to create the required GameTable, Game, and PlayerFactory. It uses the PlayerFactory and list of player names to create the actual pool of Player objects that will play the game. It creates an Analyzer to hold raw data and do reporting. It creates a Simulator to create the required samples, given the Game and Players.

Source: driver.

simulator

The simulator module is shown in the following static structure diagram.

Simulator Structure

A Simulator is associated with an Analyzer. The simulator appends samples to an analyzer. The simulator requires a Player and a Game; it runs the game with the player for the required number of samples.

An Analyzer collects raw data; creates summary statistics or frequency distributions; and writes the final report.

A Statistic keeps a specific statistic for each named sample set. Typical statistics include final stake. Sample sets are usually each player strategy being simulated.

A Distribution is a subclass of statistic. It keeps a set of values are broken into buckets to create a frequency distribution for each named sample set.

Source: simulator.

class simulator.Simulator
__init__(self, players, game, analyzer)

Initialize this simulator with a specific set of Player instances, a CasinoGame instance, an Analyzer instance and a Simlog instance.

oneRound(self)

Run the Game for a single round of play

runSession(self)

Run the Game for a player’s session - max rounds or out of money

sample(self, games, player)

Collect statistics for a number of samples of a given player

generate(self, games, rounds= 100)

Generate a database of stats for a number of players

performance(self)

Games per second

class simulator.Statistic
value(self, sample, value)

Save the value with the given sample name

report(self, samples)

For the given set of sample names, report each sample of this statistic as a tab-delimited string.

class simulator.Distribution
value(self, sample, value)

Break the set of values into buckets and save with the given sample name

report(self, samples)

For the given set of sample names, report each sample of this distribution as a comma-delimited string.

reportCell(self, index, samples)

For a given set of sample names and a bucket of the distribution, report each sample of this distribution as a tab-delimited string.

class simulator.Analyzer
outcome(self, outMinMaxDoc)

Given the tuple (final, minStake, maxStake, doc), log the outcome

report(self, file= sys.stdout)

Report the collected data showing all samples for all statistics

The following statistics are analyzed. The finals are the final stakes after the number of rounds of play (or bankruptcy). The minimums are the lowest stake recorded during play. The maximums are the highest stake recorded during play.

  • “avg final” - average of final stake samples
  • “sdv final” - standard deviation of final stake samples
  • “min final” - minimum final stake sample
  • “max final” - maximum final stake sample
  • “avg minimum” - average of the minimum stakes recorded during play
  • “min minimum” - lowest of the minimum stakes recorded during play
  • “avg maximum” - average of the maximum stakes recorded during play
  • “max maximum” - highest of the maximum stakes recorded during play
  • “dist final” - distribution of final outcomes
  • “dist minimum” - distribution of minimum stakes recorded during play

casino

The casino module is shown in the following static structure diagram.

Casino Structure

A GameTable holds the population of Bets for a CasinoGame.

A Bet is a specific proposition, optionally with an amount wagered on that proposition by a Player. If the bet wins, the player’s stake increases. If the bet loses the player’s stake decreases.

A CasinoGame contains the mechanics of play for the game. A round of play will present game state and wagering opportunities to the player. The player responds with an instance of Command, which either makes a play choice (e.g., hit or stand in blackjack) or places a bet.

A Player is a betting strategy. Each game simulation can provide multiple subclasses of Player to represent different betting strategies.

A PlayerFactory must be subclassed to create the various Player subclasses from string names. This allows a list of strings to represent a set of player strategies. A generic simulator can use an instance of a subclass of PlayerFactory to generate the Players for a Game.

Source: casino.

class casino.Bet
__init__(self, name, odds)

Define a bet as a name, an array of payout odds tuples and an instance of the Simlog. The odds array has the form { roll : (lose,win), ... } for each roll. The default (or only) roll value must be zero. Where there are alternative winning payouts based on roll, this index is used by the win method to select the payout odds.

place(self, player, amount)

Place a specific amount on this bet

wins(self, roll)

The bet is a winner, pay it; computing odds for this bet based on the “roll”. For Craps the roll is used to simplify describing the bets. For example, a “Pass Line Odds” bet is really “Pass Line Odds Point 4 or 10”, “Pass Line Odds Point 5 or 9”, “Pass Line Odds Point 6 or 8”. Rather than track each bet separately, these are collectively as “Pass Line Odds” with different payouts depending on the winning roll. In Blackjack, this is used to pay blackjack (21) at 2:1, and other wins at 1:1. In Caribbean Stud poker, it allows paying the raise depending on the hand.

lose(self)

The bet is a loser, reduce the player’s stake

push(self)

The bet is a push - the money is returned to the player

working(self)

Is this bet working?

clear(self)

Clear the bet, this is part of moving a bet; usually used to move “Come” bets when a point is established.

class casino.GameTable
clearAll(self, player)

clear all bets and establish a player betting strategy object for this table.

place(self, betName, amount)

Place a specific bet with a specific amount

move(self, fromName, toName)

Move bet from one place to another, typically used in Craps to move Come Bet to a Point

combine(self, fromName, toName)

Combine one bet’s amount into abother, typically used in Blackjack to handle a double-down

win(self, betName, roll)

this bet is a winner (delegates work to the bet)

lose(self, betName)

this bet is a pitiful loser (delegates work to the bet)

push(self, betName)

this bet is a push (delegates work to the bet)

working(self, betName)

is this bet working? (delegates work to the bet)

state(self)

return the current table state: a list of bets working

class casino.CasinoGame
seatPlayer(self, player)

associate a player with the game, reset all bets on the table

state(self)

return a tuple with the current game state

place(self, bet, amount)

place the named bet and amount (delegates work to the game table)

working(self, bet)

is this bet working? (delegates work to the game table)

play(self)

play 1 round of this game, offering betting opportunities to the seated player. Calls player’s startRound, inRound and endRound methods repeatedly to start the game, offering in-game betting and play opportunities, and endRound for any cleanup required.

class casino.Player
__doc__

“short description of the betting strategy” - this is used as sample identification for Statistics created by an Analyzer

__init__(self, limit)

Initialize the player with a specific initial stake (or betting limit).

start(self, rounds)

Initialize this player prior to running the game for a finite number of rounds to gather a sample.

def status( self )

Current status of the player.

name(self)

Return the __doc__ string as the name of this player; subclasses will override this if the __doc__ is not a complete description of the strategy.

canPlay(self)

Is the player’s stake still positive and is the number of rounds left still positive?

startRound(self, game)

Return a command to place any start-of-round bets - ante in card games or place pass-line (or don’t pass) bets for craps or roulette

inRound(self, game)

Return a command to place in-round bets - raise, double or split in card games, come bets in craps. No inRound bets in roulette.

endRound(self, game)

Check table state or game state if the player that keeps history

wins(self, win)

Add the win amount to the player’s stake.

lose(self, amount)

Remove the loss amount from the player’s stake.

finalNet(self)

return final stake less original stake; this is the net outcome of the game

minNet(self)

return the minimum stake recorded during play less original stake; this is the lowest point of the game

maxNet(self)

return the maximum stake recorded during play less original stake; this is the highest point of the game

class casino.PlayerFactory
newPlayer(self, name)

Return a Player instance with the given name

playerSet(self, names)

Given a list of player names, map the newPlayer method, returning a list of Player instances.

class casino.Command
do(self, game)

Abstract method for placing bet or making play.

class casino.CommandBet
do(self, game)

Place this bet (using game.place).

class casino.CommandSequence
addCommand(self, command)

Add the given command to this sequence; this is used in Roulette or Craps to place a number of bets simultaneously.

do(self, game)

Perform the do method of each command in this sequence.

cards

The Cards module is shown in the following static structure diagram.

Cards Structure

A Card is a realtively lightweight object, it has a rank, suit and a number of points. Poker cards have points from 2 to King (13) and Ace (14). Blackjack cards have points from 2 to 10, J, Q, K (all 10), and Ace (11). The Ace can also be one, depending on the hand.

A Deck is a set of cards. A standard Deck instance is 52 cards, created using the Card Factory to get the right subclass of cards for the deck.

A Shoe is a subclass of Deck with multiple standard decks.

A Hand is a set of cards which can be scored. Scoring a blackjack hand involves interpreting the Aces as 1’s or 11’s. Scoring a Poker hand involves determining wether the hand is a straight flush, four of a kind, straight, flush, full-house, three of a kind, two pair, pair, ace-king or stiff.

Source: ` card <../../_static/gamesim/cards.py>`_.

class cards.Card

Card is a simplistic structure, with overrides for __str__, __cmp__ and __rcmp__. This makes an object with attributes that can be transformed into a string, and compared for purposes of sorting and equality testing.

class cards.PokerCard(Card)

Subclass of Card; this adds a points attribute with values from 2 through Ace (14)

class cards.BJCard(Card)

Subclass of Card; this adds a points attribute with values from Ace (1) through Ace (11), with 10 and face cards all being 10.

class cards.CardFactory
__init__(self, game)

Initialize this factory to create cards of the appropriate subclass (PokerCard or BJCard) of Card.

newCard(self, suit, rank)

Return a new card of the given subclass (PokerCard or BJCard).

class cards.Deck
__init__(self, game)

Initialize the deck to create cards of the appropriate subclass (PokerCard or BJCard) or Card. Create the 52 cards of a standard deck.

shuffle(self)

Shuffle the deck prior to play.

isMore(self)

Are there undealt cards?

next(self)

Return the next card from the deck.

class cards.PokerShoe(Deck)

Since this is a subclass of Deck, it inherits shuffle, isMore and next for shuffling and dealing.

__init__(self, decks)

Create a Deck, extending it with additional Decks.

burn(self)

“Burn” a card somewhere in the last deck; this is the limit on dealing.

class cards.BJShoe(Deck)

Since this is a subclass of Deck, it inherits shuffle, isMore and next for shuffling and dealing.

__init__(self, decks)

Create a Deck, extending it with additional Decks.

burn(self)

“Burn” a card somewhere in the last deck; this is the limit on dealing.

class cards.Hand
reset(self)

Clear out the hand

addCard(self, card)

Append a card to the hand

count(self)

Count the number of cards in the hand.

card(self, number)

Return a specific card

contains(self, rank)

Does the hand contain a card of the given rank?

class cards.PokerHand(Hand)
flush(self)

return 1 if this hand is a flush

straight(self)

return 1 if this hand is a straight

royal(self)

return 1 if this hand is a straight 10-J-Q-K-A

highestCard(self)

return the highest ranking card (for tie-breaking)

hasRank(self, rank)

locate another card with this rank (or return None)

hand(self)

Score this hand, returning a tuple with (“name”, level, highestCard )

class cards.BJHand(Hand)
points(self)

return the total number of points, changing the valuation of aces from 11 to 1 when necessary.

split(self)

Split the hand; this hand becomes a 1-card hand, the hand which is returned is another 1-card hand.

simlog

The Simlog module is shown in the following static structure diagram.

Simlog Structure

Source: simlog.

class simlog.Simlog

This is a singleton class - only one instance is ever needed.

instance()

returns the one and only instance of this class.

summary()

disables all logging.

detail()

enables all logging.

watch()

takes a list of search-pattern strings and watches messages that match the search patterns.

msg()

takes a message name and message payload. If detail() was called and all messages are logged or if the message matches one of the watch patterns, the payload is printed.

sim1 (Craps)

The Sim1 (Craps) module is shown in the following static structure diagram.

Craps Structure

The elements of the Craps game are subclasses of general Casino classes.

Source: sim1.

class sim1.CrapsTable(casino.GameTable)

CrapsTable is a subclass of casino.GameTable. It contains a domain of all Bets.

class sim1.CrapsGame(casino.CasinoGame)

CrapsGame is a subclass of casino.CasinoGame. It overrides state to return a state tuple of ( “state”, point, ( d1, d2 ) ). The state is either “come out” or “point”. For a state of “come out” the point is zero, otherwise the point is the point required to win the round. The (d1,d2) tuple is the dice just rolled.

The game state is an instance of CrapsState. There are two subclass to reflect the state change from come out roll to point roll.

class sim1.CrapsState
__init__(self, point= 0)

Initialize the game state with a specific point; 0 indicates a come out roll.

evalDice(self, game, d1, d2)

Determine if the round is over; calling back to the Game as various bets win or lose.

class sim1.CrapsStateComeOutRoll(CrapsState)

Lose on 2, 3, 12. Win on 7 or 11. Change state to CrapsStatePointRoll on any other roll. Pay 1-roll propositions.

class sim1.CrapsStatePointRoll(CrapsState)

Lose on 7, win in point being rolled; change state to CrapsStateComeOutRoll. No action on 2, 3, 11, 12. Pay 1-roll propositions.

class sim1.CrapsPlayer(casino.Player)

The game is played by offering a startRound betting opportunity, the player places their bets - typically a PassLine bet. The inRound betting opportunity is used for bets prior to point rolls, odds bets and come bets.

Most of the bets on a Craps table are propositions that don’t pay correct odds. The only bets that do pay correct odds are the “behind the line” odds bets on the Come numbers and the Pass Line. The Don’t Come and the Don’t Pass bets are similar, and can be ignored.

The variations on play are used to evaluate the number of Come bets that can be working along with the Pass Line bets. The open issue is not how much can be lost at one throw of the dice, but how likely is that kind of exposure. A little bit of study seems to show a consistent standard deviation, leading to a conclusion that a reasonable approach is to stop playing when the stake is plus or minus one standard deviation.

sim2 (Roulette)

The Sim2 (Roulette) module is shown in the following static structure diagram.

Roulette Structure

The elements of the Roulette game are subclasses of general Casino classes.

Source: sim2.

class sim2.RouletteTable(casino.GameTable)

RouletteTable is a subclass of casino.GameTable. It contains a domain of all Bets. To simplify play, each number on the wheel is represented as a list of all bets that pay out for that number. For instance, 5 pays [ “number 5”, “red”, “odd”, “low”, “grp1”, “col2”, “pair 45”, “pair 56”, “pair 25”, “pair 58”, “three 456”, “quad 1245”, “quad 2356”, “quad 4578”, “quad 5689”, “hex 123456”, “hex 456789” ].

class sim2.RouletteGame(casino.CasinoGame)

RouletteGame is a subclass of casino.CasinoGame. It overrides state to return the last number rolled. Roulette is stateless, so the state is more of a historical fact. If the number is 0 or 00, the list has the bet name and the color (“green”). If the number is 1-36, the list has a variable number of elements, including some of the following:

  • number n
  • color (“red” or “black”)
  • even (“even” or “odd”)
  • hi-lo (<= 18 or >18)
  • grp n, where 1 is 1-12, 2 is 13-24, and 3 is 25-36.
  • col c, where c=1 for the 1,4,7,... column, c=2 for the 2,5,8,... column and c=3 for the 3,6,9,... column.
  • pair nm, for all pair bets between n and m. This includes horizontal pairs (like 1-2) and vertical pairs (like 1-4). A number (like 5) can be part of at most four pairs (4-5, 5-6, 2-5 and 5-8). A number (like 1) may be part of only two pairs (1-2 and 1-4).
  • three nmo, for any of the 12 rows of three (1-2-3, 4-5-6, ...)
  • quad mnop, for all quad bets between n, m, o and p. For example, 5-6-8-9. Some numbers (like 5) are part of four quad bets. Other numbers (like 1) are only part of one quad bet.
  • hex mnopqr, for all hex bets. A hex bet is a double row of three (1-2-3-4-5-6, 4-5-6-7-8-9, ...)

The state list will have between 12 and 17 elements, depending on how many pair and quad bets are possible.

class sim2.RoulettePlayer(casino.Player)

The game is played by offering a startRound betting opportunity, the player places their bets. There is no inRound betting opportunity. The endRound method allows the Player to collect history on the various bets which have won recently.

The roulette game is very heavily stacked against the player. It is not possible to win, since each spin is completely independent. However, it is fun to use a betting system, like “wait for 7 blacks in a row and bet red” and hope to win. The important part of the simulation is to get a standard deviation so you can limit your losses.

You can consider your stake and the number of game rounds to give you a location on a two-dimensional grid. As the game round number increases, your stake moves either down a little or up a lot. On average, it will hover right around zero. Sometimes it will be positive, other times negative. If you leave when it is positive, you are a winner. A negative swing will eventually be counterbalanced by a positive swing. The two don’t balance exactly, and this is the house edge. The question is, when will a negative swing be unlikely to recover given a finite bankroll and finite time? It appears that one standard deviation is a good working rule.

sim3 (Blackjack)

The Sim3 (Blackjack) module is shown in the following static structure diagram.

Blackjack Structure

The elements of the Blackjack game are subclasses of general Casino classes.

Source: sim3.

class sim3.BlackjackTable(casino.GameTable)

BlackjackTable is a subclass of casino.GameTable. It contains a domain of all Bets, including the player options hit and stand. The insurance bet is not simulated, since it is heavily weighted against the player.

class sim3.BlackjackGame(casino.CasinoGame)

BlackjackGame is a subclass of casino.CasinoGame. It overrides state to return the dealer’s up card and the player’s hand. In the event of a split, multiple tables are used, one for each split hand. The game issues cards to dealer and player, offers inRound betting opportunities to the player. It handles the various player requests: hit, stand, split and double. If the player is not bust or blackjack, it plays out the dealer’s hand, paying off as appropriate.

class sim3.BlackjackPlayer(casino.Player)

The Blackjack subclass of casino.Player makes decisions based on the dealer’s card and their hand. The rules are relatively well understood, and sources differ slightly on some subtle issues, like how to play the soft hands A2, A3, A4 and A5. This simulator allows evaluation of various subtle rule variations.

sim4 (CaribPoker)

The Sim4 (CaribPoker) module is shown in the following static structure diagram.

Caribbean Poker Structure

The elements of the Caribbean Stud Poker game are subclasses of general Casino classes.

Source: sim4.

class sim4.CaribPokerTable(casino.GameTable)

CaribPokerTable is a subclass of casino.GameTable. It contains a domain of all Bets, including the player options raise and fold. The progressive jackpot bet is not simulated, since it is heavily weighted against the player.

class sim4.CaribPokerGame(casino.CasinoGame)

CaribPokerGame is a subclass of casino.CasinoGame. It overrides state to return the dealer’s up card and the player’s hand. The game issues cards to dealer and player, offers one inRound betting opportunity to the player. It handles the player requests: raise or fold. If the dealer has AK, it plays out the dealer’s hand, paying off as appropriate.

class sim4.CaribPokerPlayer(casino.Player)

The CaribPoker player subclass of casino.Player makes decisions based on the dealer’s card and their hand. The rules are complex, and sources differ on a useful subset of these rules. Variations involve raising on pairs or better, raising on AK or better, raising on AKJ83 or better, and the Wizard of Odds rules:

  • Fold on stiff
  • Raise on pairs or better
  • AK, dealer has 2-Q and we hold a card that matches the dealer’s card
  • AKx, dealer has A or K and x is J or Q
  • AKQx, where x beats dealer’s up card

Extensions

Currently, extending the simulations to incorporate additional player strategies is not very convenient. Each game module should be split into two sections, one with the basic game rules as subclasses of GameTable and CasinoGame; the other with the subclasses of Player.

The introspection capabilities of Python may make the PlayerFactories superfluous.

version:2
date:2/28/02

Table Of Contents

Previous topic

Casino Games

Next topic

Blackjack Flashcards