Dealing with Blackjack

Of all the casino games, blackjack has one of the lowest edges for the house. Under some rules it is even possible for the player to have an edge on the house. Various books and net resources can provide a system of what to do when certain cards appear. We want to figure out some probabilities that go into making those systems. Namely, we want to know for each upcard the dealer may have showing, what are the probabilities the dealer will end up with a given final count.

In blackjack, face cards (K, Q, J) counts as 10, an ace counts as 1 or 11, and the other cards count their value. The goal is to get as close to 21 as possible without going over, and to beat the dealer's hand. Once all the players finish, the dealer plays his hand according to specified rules. This project is only going to look at the dealer's play.

The algorithm for the dealer can be expressed as follows: Note: in some casinos the dealer also takes a card on a soft 17, ie an Ace plus 6 other points.

The dealer may therefore end up with any of the following scores: 17, 18, 19, 20, 21, blackjack, or bust. The question is, for any given upcard, what are the probabilities the dealer will end up with each of these possible results? A blackjack is a 21 composed of exactly two cards - an Ace and a 10-value card. Since the rules treat blackjacks differently than other 21-counts, it should be considered a separate result for the dealer.

Part I

The first step is creating the deck. The suits do not matter, only the card values. The effective values in one deck are: 4 each of 2 through 9, 16 10's, and 4 that can be 11 or 1. You will have to figure out how to deal with Aces. So an initial deck of 52 cards should have the following values each repeated 4 times: Ace, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10. For this project, use a shoe consisting of six decks. There are many ways to simulate shuffling and dealing cards on a computer. One way to handle shuffling and dealing is to have a second vector of the same size as the deck or shoe, which will contain the answer to the question: has this card been dealt already? The values in this vector will initially be set to all 0's ('false'). When a card is randomly choosen from the shoe the corresponding place in the other vector is checked. If the value is 'false' it has not yet been dealt, and the value is set to 1 ('true') to indicate the card has now been dealt. If the value is already 'true' what needs to be done? How can the simulated deck be reshuffled?

Use a shoe of 6 decks of 52 cards, or 312 total cards. Create a vector of the necessary length, and place the numbers above in the vector so the counts are correct. You should print out this vector of values and verify for yourself that the values are correct.

Next, your program should deal cards from this deck to the dealer's hand until the total is over 16, as the rule above indicates. You will have to be careful how you treat Aces. Print out the values of the cards in the order they are dealt, and verify that the dealing stops as appropriate. You should prompt the user once for a random seed value and generate one deal only. (Therefore if you use the same seed you should get the same hand dealt.) How can you verify that your dealing algorithm is not dealing the same card more than once?

Store this program as 21a.cpp in the lab3 folder of your eng101 space. While you may handle aces differently, your program should work something like this:
ruby% a.out
 Seed ? 1
 Cards dealt: 11 5 10 10 :: total = 26
ruby% a.out
 Seed ? 6
 Cards dealt: 10 10 :: total = 20
ruby% a.out
 Seed ? 9
 Cards dealt: 4 3 3 2 4 10 :: total = 26
ruby% a.out
 Seed ? 12345
 Cards dealt: 11 6 :: total = 17
ruby% a.out
 Seed ? 13
 Cards dealt: 10 11 :: total = 21
ruby% a.out
 Seed ? 1
 Cards dealt: 11 5 10 10 :: total = 26

Part II

Now do the same thing for 1,000,000 deals, and collect statistics on the results. Only prompt once for a seed at the beginning of the deals! For each deal, reshuffle the deck, and treat the first card dealt as the upcard. Deal out a hand for the dealer using the code you wrote for part 1. Each deal generates a data point consisting of two values, the first card and the final dealer result. Keep track of the times each data point occurs using a 2D vector. Remember that any hard total over a 21 is a bust; it does not matter if the dealer gets a 22 or a 24. (What is the highest total the dealer could have?)

Next display these statistics as probabilities in a readable grid structure.

Finally, change the dealer rules so the dealer hits on a soft 17, and generate revised statistics on 1,000,000 deals using the same seed value. Compare the two. Where did you expect differences? Where are there differences? Can you determine which one should be more favorable to the house? If not, would more deals help?

Store your final program using the soft 17 rule as 21b.cpp in the lab3 folder of your eng101 space.

Your program should have the following rough structure: Your program should generate results something like this:
 Dealer results in 1000000 deals
UpCard        17        18        19        20        21      Bust    BlkJk
    1   0.058009  0.143974  0.143987  0.142291  0.065956  0.137050  0.308734
    2   0.129509  0.135729  0.129861  0.125192  0.121202  0.358507         0
    3   0.126504  0.132416  0.125264  0.122732  0.115489  0.377594         0
    4   0.123957  0.126421  0.122380  0.118691  0.114468  0.394082         0
    5   0.119597  0.124278  0.119127  0.112777  0.107366  0.416855         0
    6   0.115530  0.113878  0.114906  0.110339  0.105096  0.440251         0
    7   0.371553  0.135432  0.079290  0.079382  0.074211  0.260132         0
    8   0.129021  0.360571  0.128917  0.070656  0.067057  0.243776         0
    9   0.120687  0.118726  0.349259  0.119700  0.060798  0.230831         0
   10   0.112073  0.111609  0.111833  0.341216  0.034549  0.211309  0.077410 

Questions for thought and discussion

Some of these questions relate to assumptions built into the project, some to decisions made in creating the C++ program, and some to interpreting the results.

Can you tell by looking at the above chart whether it was made with the dealer staying or hitting on soft 17?

The deck is simulated by a vector of 312 values. Should the initial order of values have an effect on the final results? Does it?

This approach reshuffles the deck at the start of each deal. This means the dealer is always taking cards from an initially full, shuffled deck. What is good or bad about this?

This project suggested using a second parallel vector to handle shuffling a deck. How else could shuffling have been handled? How is one way better than another?

In C++ a bool is an int in disguise. Nevertheless in C++ there is a difference between a vector of ints and a vector of bools. What is the difference? What are the benefits of one over the other? Why do you think a vector of ints was suggested?

The dealer gets a 10-value card as an upcard more often than any other card, so the counts are higher for this upcard. Approximately how much higher? Should we do anything to adjust for this?

How do we know how many hands to try? Was 1 million a good choice? Do your probabilities change if you do 10 million hands instead? You may want to use the -O2 compiler option and try this out.

In many casinos, the dealer actually checks for a blackjack before the player plays the hand. If the dealer has a blackjack, it automatically beats anything a player may have except another blackjack, which ties. What if any consequence does this casino rule have on a player using your statistics to make play decisions?

This project recommended only prompting for a seed value once before dealing all of the hands. Certainly you wouldn't want to prompt the user for a million seed numbers! However, another possibility is that for the Nth deal, the random number generator could be seeded with N. Does this make a difference? What might be good or bad about this? How much time does the seeding process take? How are deals #1 and deals #2 related with this approach? Does it assume more or less "randomness" from the random number generator? Does it assume anything about the seeding process? Would seeding with a different function of N matter?