Best Move Analysis
Command line options
-c CARDS, --cards CARDS
The cards the player has. (examples: A,10 or 2,8,4)
-d DEALER_CARD, --dealer-card DEALER_CARD
The up card of the dealer (examples: A or 3)
--splits SPLITS How many times the player can split (min: 1=fastest, fairly accurate; max: 3=slowest, super
accurate; default: 1)
--decks DECKS How many decks the shoe starts with. (default: 6)
--shoe SHOE The cards in the shoe before the cards were dealt. Overrides --decks. (format:
2,6,5,8,6,2,A,10,9,...)
--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)
See the help by running python best_move.py -h.
Calculate the best action
- best_move.perfect_mover_cache(cards, dealer_up_card, cards_not_seen, can_double=True, can_insure=True, can_surrender=True, max_splits=3, dealer_peeks_for_blackjack=True, das=True, dealer_stands_soft_17=True, return_all_profits=False, print_profits=False, plot_profits=False)
Increase cache hits in perfect_mover. Use this instead of perfect_mover for faster results.
- Parameters:
cards (Iterable[int]) – The cards in the player’s hand.
dealer_up_card (int) – The dealer’s up card.
cards_not_seen (Iterable[int]) – The cards the player hasn’t already seen.
can_double (bool) – Whether the player can double.
can_insure (bool) – Whether the player can take insurance.
can_surrender (bool) – Whether the player can surrender.
max_splits (int) – How many times the player can split their hand.
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.
return_all_profits (bool) – Whether we should return the expected returns of all 6 possible actions, or only the best expected return, the action that gets this return, and the expected profit of taking insurance.
print_profits (bool) – Whether we should print the expected returns of all the actions.
plot_profits (bool) – Whether we should create a table showing the expected returns of all possible actions, with the best move highlighted.
- Returns:
The expected returns of all 6 possible actions, or only the best expected return, the action that gets this return, and the expected profit of taking insurance.
- Return type:
tuple[float, …] | tuple[float, str, float]
Example:
from best_move import perfect_mover_cache
from utils import DECK
shoe = DECK * 6
for card in (3, 5, 11): # Remove the cards the player and the dealer have.
shoe.remove(card)
expected_return, best_action, insurance_return = perfect_mover_cache(cards=(3, 5), dealer_up_card=11, cards_not_seen=shoe,
can_double=True, can_insure=True, can_surrender=True,
max_splits=3, dealer_peeks_for_blackjack=False, das=False,
dealer_stands_soft_17=False, return_all_profits=False,
print_profits=True, plot_profits=True)
- best_move.perfect_mover(cards, dealer_up_card, cards_not_seen, can_double=True, can_insure=True, can_surrender=True, max_splits=3, dealer_peeks_for_blackjack=True, das=True, dealer_stands_soft_17=True)
Get the best move to play by taking into account even the cards that we have already seen.
- Parameters:
cards (tuple[int, ...]) – The cards in the player’s hand.
dealer_up_card (int) – The dealer’s up card.
cards_not_seen (tuple[int, ...]) – The cards the player hasn’t already seen.
can_double (bool) – Whether the player can double.
can_insure (bool) – Whether the player can take insurance.
can_surrender (bool) – Whether the player can surrender.
max_splits (int) – How many times the player can split their hand.
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 expected returns of all 6 possible actions (stand, hit, double, split, surrender, and insurance, in that order).
- Return type:
tuple[float, …]
Calculate our chances of beating the dealer
- best_move.chances_of_beating_dealer(hand_value, dealer_value, dealer_has_ace, counts, dealer_more_than_1_card, dealer_peeks_for_blackjack, dealer_stands_soft_17)
Assumes that if the player got 21, it wasn’t a blackjack. If it was a blackjack, then we don’t call this function.
If dealer_peeks_for_blackjack is true, then the dealer can’t have blackjack.
- Parameters:
hand_value (int) – The player’s final hand value.
dealer_value (int) – The dealer’s hand value at the moment.
dealer_has_ace (bool) – Whether the dealer has an ace that is counted as 11.
counts (tuple[int, ...]) – How many times each card is in the deck. (e.g. (0, 0, 12, 11, 15, …))
dealer_more_than_1_card (bool) – Whether the dealer has more than 1 card (not including the down card).
dealer_peeks_for_blackjack (bool) – Whether the dealer peeks for blackjack.
dealer_stands_soft_17 (bool) – Whether the dealer stands on soft 17.
- Returns:
The chances of beating the dealer.
- Return type:
float
Adjust the probabilities if the dealer peeks for blackjack
- best_move.probabilities_if_dealer_peeks_for_blackjack(counts, dealer_up_card)
Account for the fact that if the dealer has an ace and doesn’t have blackjack, then we know they don’t have a 10.
As such, our chances of getting a 10 are slightly higher and out chances of getting any other card are slightly lower. Similarly, if the dealer has a 10 and doesn’t have blackjack, then we know they don’t have an ace.
- Parameters:
counts (dict[int, int]) – How many times each card is in the deck. (e.g. {2: 12, 3: 11, 4: 15, …})
dealer_up_card (int) – The dealer’s up card.
- Returns:
The probabilities of getting each card if the dealer peeks for blackjack.
- Return type:
dict[int, float]
Utilities
- best_move.create_deck_from_counts_cache(counts)
Create a deck from the counts of every card.
- Parameters:
counts (dict[int, int]) – How many times each card is in the deck. (e.g. {2: 12, 3: 11, 4: 15, …})
- Returns:
A shoe with these cards.
- Return type:
tuple[int, …]
- best_move.create_deck_from_counts(counts)
Create a deck from the counts of every card.
- Parameters:
counts (tuple[int, ...]) – How many times each card is in the deck. (e.g. (0, 0, 12, 11, 15, …))
- Returns:
A shoe with these cards.
- Return type:
tuple[int, …]
- best_move.argmax(*profits)
Return the maximum profit and the action that gets you that profit.
- Parameters:
profits (float) – The profits generated by each action.
- Returns:
The best profit, and the best action.
- Return type:
tuple[float, str]
- best_move.dict_to_tuple(counts)
Convert the dict of the counts of every card to a tuple, so it can be used with @cache.
- Parameters:
counts (dict[int, int]) – How many times each card is in the deck. (e.g. {2: 12, 3: 11, 4: 15, …})
- Returns:
A tuple containing the number of times each card is in the shoe, but the keys are now the indices. (e.g. {2: 12, 3: 11, 4: 15, …} becomes (0, 0, 12, 11, 15, …))
- Return type:
tuple[int, …]
- best_move.can_never_split(cards)
Get if the player can’t split.
- Parameters:
cards (tuple[int, ...]) – The player’s hand.
- Returns:
Whether the player will not be able to split their hand now and in the future.
- Return type:
bool
- best_move.tuple_sort(cards)
Sort a tuple. Used to increase cache hits.
- Parameters:
cards (Iterable[int]) – The cards in the shoe.
- Returns:
The shoe with its cards sorted.
- Return type:
tuple[int, …]
- class best_move.HandPlayer(cards)
Hold information about the player’s hand.
Calculate the value of the hand the number of aces it has.
- Parameters:
cards (tuple[int, ...]) – The cards the hand has at the moment.
- class best_move.HandDealer(value, aces)
Hold information about the dealer’s hand.
Calculate the value of the hand the number of aces it has.
- Parameters:
value (int) – The hand’s value (can be over 21).
aces (int) – How many aces that are counted as 11 the hand has.