/black-jack-resolver

Black Jack's game resolver. A determinist solver that produce the best strategy depending on house rules and give the whole game expected value. For each house rules set, best moves and expected values can be generated.

Primary LanguagePythonMIT LicenseMIT

                 ... Black Jack Resolver ...
                 
                            _ _   
                           | A | 
                           | _ | 
                         bank card
                           
                     _ _                _ _
                 _ _| J |           _ _| K | 
                | A | _ |          | Q | _ |
                | _ |              | _ |
                            _ _     [Stand]  
                         _ _| 2 | 
                        | 5 | _ |                   
                        | _ |   
                          [Hit]

Description

Black Jack resolver is a simple python app that compute and display expected values and best player move for each bank's start cards

Solving is determinist and based on a directed acyclic graph representation of hand states A monte-carlo validation is implemented as unit tests to check some pre-computed values. Not the whole game have been modeled using a montecarlo validation

Hypothesis are:

  • An infinite number of decks, at any time the probability of drawing card that is not ten-valued is 1/13 and 4/13 for a 10-valued card.
  • No possibility to double after a split, this is not yet implemented because the impact on EV is judged as very low

Options:

  • Dealer's peeked: mostly use in America where dealer stop the game before serving players if the face down card would reveal a blackjack (disable using --no-peek)
  • Stand or hit on soft 17: control weither dealer stand or hit on a soft 17 (enable hit on soft 17 using --hos option)
  • No draw on aces split: only one card is dealt on each ace when a pocket aces hand is splitted (enable it using --ace-no-draw option)
  • No black jack on aces split: a ten valued with an ace do not give a blackjack when a pocket aces hand is splitted, you get 21 instead (enable it using --ace-no-bj option)

Here are the computed game expected values depending on house rules, using the best strategy:

dealer's peeked hit on soft 17 ace no draw ace no BJ EV
1.00206
X 1.012566
X 1.000686
X X 1.010486
X X 0.975495
X X X 0.983872
X X X 0.974743
X X X X 0.982916

* using the case where double after a split is not allowed

All best moves and expected values tables computable using this resolver are available in the tables directory.

EV tables

Best moves table (see below) is issued from the EV tables for each bank card and player's start hand probabilities (see the START_HAND_WEIGHTS variable in blackjack/constants.py)

EV table approach is to compute, for each possible state:

  • the standing EV
  • the EV of hitting only 1 card then standing
  • the maximum EV you can reach from this state using the best strategy

In the example below, best move is to surrender, because:

  • the maximum EV you can reach from this state is 0.493.
  • if you stand, EV is 0.231
  • if you surrender, you get an EV of 0.5 (dealer take half of your bet)

Consequently, best move is to surrender if possible, else hit.

-------------------------------------------------------------------------------
Player state        EV stand       EV hit & stand      Max EV         Best move      
-------------------------------------------------------------------------------
12                  0.231          0.437               0.493          U-H   

In the other example, best move is to double if possible, else stand, because:

  • stand EV is 0.828
  • max EV is 1.154, net gain is 0.154
  • hit only 1 card then stand EV is 1.118, net gain if you double from here is 2 times 0.118 = 0.236

Consequently, because 0.236 is greater than 0.154 the maximum net gain is obtained using a double strategy.

If the EV of hitting only 1 card and standing would have been lesser than 0.154, best move would have be to hit.

-------------------------------------------------------------------------------
Player state        EV stand       EV hit & stand      Max EV         Best move      
-------------------------------------------------------------------------------
9                   0.828          1.118               1.154          D-H            

Contributions

If you want to contribute, feel free to suggest and/or ask any questions.

Here are some ideas of things that could be done:

  • an external review of this code base to validate the approach.
  • a full montecarlo game model to validate strategy expected values.
  • a way to estimate the game variance to determine how many hands should be played to ensure player have a good chance to be wining.

Install using poetry

poetry install

Run tests

pytest  # run on a single thread
pytest -s  # run on a single thread and display logs
pytest -n 8  # run on 8 cores using pytest-xdist

Usage

Activate poetry env

poetry shell

Then run jack.py to display expected values for a given bank card or best moves table

For example, to display best moves table and strategy total expected value, use:

python jack.py best_moves

Default is to have the dealer's peeked option and stand on soft 17 .

This can be changed options --no-peek (disable dealer's peeked) and --hos (activate hit on soft 17) options.

Example output for standard use case (no hit on soft 17 and dealer peeked):

-----------------------------------------------------------------------------------
Player best move for each bank card (first line) and each state (first column)
-----------------------------------------------------------------------------------
	2	3	4	5	6	7	8	9	F	A	
-----------------------------------------------------------------------------------
20	S	S	S	S	S	S	S	S	S	S	
19	S	S	S	S	S	S	S	S	S	S	
18	S	S	S	S	S	S	S	S	S	S	
17	S	S	S	S	S	S	S	S	S	U-S	
16	S	S	S	S	S	H	H	U-H	U-H	U-H	
15	S	S	S	S	S	H	H	H	H	U-H	
14	S	S	S	S	S	H	H	H	H	U-H	
13	S	S	S	S	S	H	H	H	H	U-H	
12	H	H	S	S	S	H	H	H	H	U-H	
11	D-H	D-H	D-H	D-H	D-H	D-H	D-H	D-H	D-H	H	
10	D-H	D-H	D-H	D-H	D-H	D-H	D-H	D-H	H	H	
9	H	D-H	D-H	D-H	D-H	H	H	H	H	H	
8	H	H	H	H	H	H	H	H	H	H	
7	H	H	H	H	H	H	H	H	H	H	
6	H	H	H	H	H	H	H	H	H	H	
5	H	H	H	H	H	H	H	H	H	H	
4	H	H	H	H	H	H	H	H	H	H	
-----------------------------------------------------------------------------------
10-20	S	S	S	S	S	S	S	S	S	S	
9-19	S	S	S	S	S	S	S	S	S	S	
8-18	S	D-S	D-S	D-S	D-S	S	S	H	H	H	
7-17	H	D-H	D-H	D-H	D-H	H	H	H	H	H	
6-16	H	H	D-H	D-H	D-H	H	H	H	H	H	
5-15	H	H	H	D-H	D-H	H	H	H	H	H	
4-14	H	H	H	D-H	D-H	H	H	H	H	H	
3-13	H	H	H	H	D-H	H	H	H	H	H	
2-12	H	H	H	H	H	H	H	H	H	H	
-----------------------------------------------------------------------------------
1-1	Sp	Sp	Sp	Sp	Sp	Sp	Sp	Sp	Sp	Sp	
10-10	S	S	S	S	S	S	S	S	S	S	
9-9	Sp	Sp	Sp	Sp	Sp	S	Sp	Sp	S	S	
8-8	Sp	Sp	Sp	Sp	Sp	Sp	Sp	Sp	Sp	U-H	
7-7	Sp	Sp	Sp	Sp	Sp	Sp	H	H	H	U-H	
6-6	H	Sp	Sp	Sp	Sp	H	H	H	H	U-H	
5-5	D-H	D-H	D-H	D-H	D-H	D-H	D-H	D-H	H	H	
4-4	H	H	H	H	H	H	H	H	H	H	
3-3	H	H	Sp	Sp	Sp	Sp	H	H	H	H	
2-2	H	H	Sp	Sp	Sp	Sp	H	H	H	H	
-----------------------------------------------------------------------------------
legend:
	S: Stand
	H: Hit
	Sp: Split
	D-S: Double if possible else stand
	D-H: Double if possible else hit
	U-S: Surrender if possible else stand
	U-H: Surrender if possible else hit
	U-Sp: Surrender if possible else split
-----------------------------------------------------------------------------------
Total expected value using this strategy if double, split and surrender are allowed is: 1.00715
(you win a total of 1.00715 every time you do an initial bet of 1)
-----------------------------------------------------------------------------------

To display expected values table for an Ace on the bank, use:

python jack.py --ev-table -card A

Possible values are: 2, 3,4,5,6,7,8,9,F (a ten-valued card),

Output:

-------------------------------------------------------------------------------
Bank card: A
-------------------------------------------------------------------------------
Player state        EV stand       EV hit & stand      Max EV         Best move      
-------------------------------------------------------------------------------
2                   0.231          0.231               0.552          H              
3                   0.231          0.231               0.535          H              
4                   0.231          0.231               0.517          H              
5                   0.231          0.231               0.502          H              
6                   0.231          0.241               0.482          U-H            
7                   0.231          0.301               0.478          U-H            
8                   0.231          0.412               0.556          H              
9                   0.231          0.542               0.647          H              
10                  0.231          0.687               0.749          H              
F                   0.231          0.742               0.803          H              
11                  0.231          0.73                0.791          H              
12                  0.231          0.405               0.45           U-H            
13                  0.231          0.387               0.418          U-H            
14                  0.231          0.37                0.388          U-H            
15                  0.231          0.352               0.36           U-H            
16                  0.231          0.334               0.334          U-H            
17                  0.361          0.306               0.361          U-S            
18                  0.623          0.259               0.623          S              
19                  0.885          0.191               0.885          S              
20                  1.146          0.102               1.146          S              
A                   0.231          0.948               1.117          H              
2-12                0.231          0.476               0.678          H              
3-13                0.231          0.476               0.653          H              
4-14                0.231          0.476               0.627          H              
5-15                0.231          0.476               0.602          H              
6-16                0.231          0.476               0.578          H              
7-17                0.361          0.506               0.568          H              
8-18                0.623          0.567               0.628          H              
9-19                0.885          0.627               0.885          S              
10-20               1.146          0.687               1.146          S              
21                  1.331          0                   1.331          S              
1-1                 0.231          0.476               0.678          Sp             
2-2                 0.231          0.231               0.517          H              
3-3                 0.231          0.241               0.482          U-H            
4-4                 0.231          0.412               0.556          H              
5-5                 0.231          0.687               0.749          H              
6-6                 0.231          0.405               0.45           U-H            
7-7                 0.231          0.37                0.388          U-H            
8-8                 0.231          0.334               0.334          U-H            
9-9                 0.623          0.259               0.623          S              
10-10               1.146          0.102               1.146          S              
BJ                  2.038          0                   2.038          S       
-------------------------------------------------------------------------------
Legend:
  [Player state] State of player game:
    10: a score of ten that can't lead to a black jack, for example 8 & 2 or 5 & 5 or 6 & 4, ...
    F: a single figure, this can be obtained only by splitting a pocket figure hand
  [EV stand] Give the expected value of standing in this position (1.0 = even)
  [EV hit & stand] Give the expected value of hit and stand from this position (useful to evaluate if a double is relevant)
  [Max EV] Give the maximum expected value that can be obtained from this position using only stand or hit choices
  [Best move] Give the best move from this position:
    S: Stand
    H: Hit
    Sp: Split
    D-S: Double if possible else stand
    D-H: Double if possible else hit
    U-S: Surrender if possible else stand
    U-H: Surrender if possible else hit
    U-Sp: Surrender if possible else split
-------------------------------------------------------------------------------