Expected Value (EV) Calculation
Command line options
-c, --custom Run custom user-defined movers and betters that require special initialization.
--cores CORES How many cores to use in the calculation. (default: 1, use -1 for all cores)
-m MOVER, --mover MOVER
Use a predefined mover. Can also be the name of the class of a user-defined mover. (possible values: card-count, basic-strategy-deviations, basic-strategy, perfect, simple;
default: card-count)
-b BETTER, --better BETTER
Use a predefined better. Can also be the name of the class of a user-defined better. (possible values: card-count, conservative-card-count,
wonging-card-count, wonging-conservative-card-count, simple; default: card-count)
-s SIMULATIONS, --simulations SIMULATIONS
How many simulations to run. Running more simulations gives more accurate results but they are slower to calculate. (default: 100,000)
--decks DECKS How many decks the shoe starts with. (default: 6)
--deck-penetration DECK_PENETRATION
When to reshuffle the shoe. Reshuffles when cards remaining < starting cards * deck penetration. (default: 0.25)
--stand17 Dealer should stand on soft 17. (default: true)
--hit17 Dealer should hit on soft 17. (default: false)
--das Allow double after split. (default: true)
--no-das Don't allow double after split. (default: false)
--peek Dealer peeks for blackjack. (default: true)
--no-peek Dealer doesn't peek for blackjack. (default: false)
--surrender Allow surrendering. (default: true)
--no-surrender Don't allow surrendering. (default: false)
--units UNITS The number of units in total. (default: 200)
--hands-played HANDS_PLAYED
How many hands to play before checking the risk of ruin. (default: 1000)
See the help by running python expected_value.py -h.
Calculate the expected value
- expected_value.expected_value(action_class, betting_class, simulations, deck_number=6, shoe_penetration=0.25, dealer_peeks_for_blackjack=True, das=True, dealer_stands_soft_17=True, surrender_allowed=True, units=200, hands_played=1000, plot_profits=True, print_info=True)
Estimate the expected value of a strategy.
- Parameters:
action_class (BaseMover) – The class that chooses the action.
betting_class (BaseBetter) – The class that chooses the bet.
simulations (int) – How many hands to play.
deck_number (int) – The number of decks in the initial shoe.
shoe_penetration (float) – When to reshuffle the shoe. Reshuffles when cards remaining < starting cards * deck penetration.
dealer_peeks_for_blackjack (bool) – Whether the dealer peeks for blackjack.
das (bool) – Whether we can double after splitting.
dealer_stands_soft_17 (bool) – Whether the dealer stands on soft 17.
surrender_allowed (bool) – Whether the game rules allow surrendering.
units (int) – The number of units in total.
hands_played (int) – How many hands to play before checking the risk of ruin.
plot_profits (bool) – Whether a plot showing how the profit changed over time should be made at the end.
print_info (bool) – Whether to print information about the progress of the simulation. Disabled for multithreading.
- Returns:
The total return, the average return of a game, the average bet size, and the risk of ruin.
- Return type:
tuple[float, float, float, float, float]
Example:
from expected_value import expected_value
from action_strategies import BaseMover
from betting_strategies import BaseBetter
from random import randint, choice
class RandomBetter(BaseBetter): # Create your own movers and betters.
@staticmethod
def get_bet(cards_seen: list[int], deck_number: int) -> int:
return randint(1, 10)
class RandomMover(BaseMover):
@staticmethod
def get_move(hand_value: int, hand_has_ace: bool, dealer_up_card: int, can_double: bool, can_split: bool,
can_surrender: bool, can_insure: bool, hand_cards: list[int], cards_seen: list[int], deck_number: int,
dealer_peeks_for_blackjack: bool, das: bool, dealer_stands_soft_17: bool) -> tuple[str, bool]:
return choice(["s", "h"]), False # Randomly select between stand ("s") and hit ("h"), and never take insurance (False is don't take insurance).
mover = RandomMover()
better = RandomBetter()
average_profit = expected_value(action_class=mover, betting_class=better, simulations=1_000_000, deck_number=6,
shoe_penetration=.2, dealer_peeks_for_blackjack=True, das=True,
dealer_stands_soft_17=True, surrender_allowed=False, plot_profits=False)
- expected_value.expected_value_multithreading(action_class, betting_class, total_simulations, cores=2, deck_number=6, shoe_penetration=0.25, dealer_peeks_for_blackjack=True, das=True, dealer_stands_soft_17=True, surrender_allowed=True, units=200, hands_played=1000)
Estimate the expected value of a strategy, using multithreading to speed up the process. Can’t plot the results.
- Parameters:
action_class (BaseMover) – The class that chooses the action.
betting_class (BaseBetter) – The class that chooses the bet.
total_simulations (int) – How many hands to play in total.
cores (int) – How many cores to use for the simulation.
deck_number (int) – The number of decks in the initial shoe.
shoe_penetration (float) – When to reshuffle the shoe. Reshuffles when cards remaining < starting cards * deck penetration.
dealer_peeks_for_blackjack (bool) – Whether the dealer peeks for blackjack.
das (bool) – Whether we can double after splitting.
dealer_stands_soft_17 (bool) – Whether the dealer stands on soft 17.
surrender_allowed (bool) – Whether the game rules allow surrendering.
units (int) – The number of units in total.
hands_played (int) – How many hands to play before checking the risk of ruin.
- Returns:
The total return, the average return of a game, the average bet size, and the risk of ruin.
- Return type:
tuple[float, float, float, float]
Simulate one hand
- expected_value.simulate_hand(action_class, cards, dealer_up_card, dealer_down_card, shoe, splits_remaining, deck_number, dealer_peeks_for_blackjack=True, das=True, dealer_stands_soft_17=True, surrender_allowed=True)
Play one hand.
- Parameters:
action_class (BaseMover) – The class that chooses the action.
cards (list[int]) – The cards in our hand.
dealer_up_card (int) – The dealer’s up card.
dealer_down_card (int) – The dealer’s down card.
shoe (list[int]) – The shoe.
splits_remaining (int) – How many more splits we can do.
deck_number (int) – The number of decks in the initial shoe.
dealer_peeks_for_blackjack (bool) – Whether the dealer peeks for blackjack.
das (bool) – Whether we can double after splitting.
dealer_stands_soft_17 (bool) – Whether the dealer stands on soft 17.
surrender_allowed (bool) – Whether the game rules allow surrendering.
- Returns:
The profit/loss from the hand, and how many times we split.
- Return type:
float
- expected_value.play_hand(action_class, hand_cards, dealer_up_card, dealer_down_card, shoe, splits_remaining, deck_number, dealer_peeks_for_blackjack=True, das=True, dealer_stands_soft_17=True)
Play hands but don’t play the dealer.
- Parameters:
action_class (BaseMover) – The class that chooses the action.
hand_cards (list[list[int]]) – The cards in our hand.
dealer_up_card (int) – The dealer’s up card.
dealer_down_card (int) – The dealer’s down card.
shoe (list[int]) – The shoe.
splits_remaining (int) – How many more splits we can do.
deck_number (int) – The number of decks in the initial shoe.
dealer_peeks_for_blackjack (bool) – Whether the dealer peeks for blackjack.
das (bool) – Whether we can double after splitting.
dealer_stands_soft_17 (bool) – Whether the dealer stands on soft 17.
- Returns:
The hands played out.
- Return type:
tuple[list[list[int]], int]
- expected_value.get_mover_and_better(mover_name, better_name)
Get the mover and the better from the arguments passed by the user.
- Parameters:
mover_name (str) – The name of the mover to use. If it isn’t one of the recognized names, then in checks if there is a class of that name.
better_name (str) – The name of the better to use. If it isn’t one of the recognized names, then in checks if there is a class of that name.
- Returns:
The mover and the better to use, already set up.
- Return type:
tuple[BaseMover, BaseBetter]
- expected_value.play_dealer(dealer_cards, shoe, dealer_stands_soft_17)
Play the dealers hand to get its final value.
- Parameters:
dealer_cards (Iterable[int]) – The cards the dealer already has.
shoe (list[int]) – The shoe.
dealer_stands_soft_17 (bool) – Whether the dealer stands on soft 17.
- Returns:
The final value of the dealer’s hand. If the dealer busted, the value is 0.
- Return type:
int
Utilities
- expected_value.get_card_from_shoe(shoe)
Get a card from the shoe. Always returns the last item from the shoe, so the shoe must be shuffled before.
- Parameters:
shoe (list[int]) – The shoe to get a card from.
- Returns:
The card we got from the shoe.
- Return type:
int
- class expected_value.Hand(cards)
Hold information about the hand of the player and the dealer.
Save the initial cards.
- Parameters:
cards (Iterable[int]) – The cards the hand started with.
- aces()
Return the number of aces that count as 11.
- Returns:
The number of aces counted as 11.
- Return type:
int
- add_card(card)
Add a new card to the hand.
- Parameters:
card (int) – The new card to add for the hand (an ace is symbolised as 11).
- Return type:
None
- value()
Return the value of a hand.
- Returns:
The hand’s value.
- Return type:
int
- value_ace()
Return the value of a hand and how many aces that count as 11 it has.
- Returns:
The hand’s value and how many aces are counted as 11 (0 or 1).
- Return type:
tuple[int, int]
Wrapper for multithreading
- expected_value._expected_value_multithreading_wrapper(results, action_class, betting_class, simulations, deck_number=6, shoe_penetration=0.25, dealer_peeks_for_blackjack=True, das=True, dealer_stands_soft_17=True, surrender_allowed=True, units=200, hands_played=1000)
Estimate the expected value of a strategy. Used inside multithreading. Don’t use this function directly.
- Parameters:
action_class (action_strategies.BaseMover) – The class that chooses the action.
betting_class (betting_strategies.BaseBetter) – The class that chooses the bet.
simulations (int) – How many hands to play.
deck_number (int) – The number of decks in the initial shoe.
shoe_penetration (float) – When to reshuffle the shoe. Reshuffles when cards remaining < starting cards * deck penetration.
dealer_peeks_for_blackjack (bool) – Whether the dealer peeks for blackjack.
das (bool) – Whether we can double after splitting.
dealer_stands_soft_17 (bool) – Whether the dealer stands on soft 17.
surrender_allowed (bool) – Whether the game rules allow surrendering.
units (int) – The number of units in total.
hands_played (int) – How many hands to play before checking the risk of ruin.
results (multiprocessing.Queue[tuple[float, float, float, float, float]])
- Return type:
None