0

Blackjack probabilties

 3 years ago
source link: https://easylang.online/apps/tut_blackj.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
Blackjack probabilties

Blackjack - Probabilties, Card Counting

We will calculate here what are the probabilities of winning at blackjack. Unfortunately, there are different rule variations. Here we use the following rules:

Rules

The dealer draws cards to 16. You can double down on 9, 10 and ace. You can split all cards, with ace you get only one card, split cards you can not split again and you can not double. When splitting 2 aces, you get only one card each. A blackjack is paid 1.5 times the bet.

Okay, so how can I calculate an optimal strategy and what do my winning propositions look like?

Probabilities Dealer

We start with the dealer. There is a simple rule: Buy to 16. For simplicity we assume fixed probabilities for the card values, i.e. the probability of getting a 9 is 1/13. This is usually only exactly correct for the first card of a full deck. The probability of getting a card depends, of course, on which card has already been turned over and how many card decks are used. These changing probabilities can be used to your advantage, as we will see later. Since you usually play with at least 6 decks, this is a good approximation.

In our calculations only the card value is taken into account, i.e. a 10, Jacks, Queens and Kings count as 10. But the probability for this card value is then of course 4/13.

The dealer serves a card, which from now on we will call card1. Based on this card and your own cards, you can then make your own game decision.

First we calculate for each card1 the probabilities that the bank stops at 17, 18, 19, 20 or 21 or bust with 22 or more. The bank keeps drawing cards until 17 or more is reached. The probability for the reached sum is multiplied over all cards (except the first one).

For each card1 we store these probabilities in the field p_dealer[].

len p_card[] 12
# 
func prepare . .
  # probabilities to get a card from 1 to 11 
  for i = 1 to 11
    p_card[i] = 1 / 13
  .
  p_card[10] = 4 / 13
.
# 
call prepare
# 
func dealer_draw p card cardsum n_ace . p_dealer[] .
  if card = 11
    n_ace += 1
  .
  cardsum += card
  if cardsum > 21 and n_ace > 0
    cardsum -= 10
    n_ace -= 1
  .
  if cardsum >= 17
    cardsum = lower cardsum 22
    p_dealer[cardsum] += p
  elif p > 0.00001
    for i = 2 to 11
      call dealer_draw p * p_card[i] i cardsum n_ace p_dealer[]
    .
  .
.
func p_dealer . .
  len p_dealer[] 23
  numb_fmt 5 0
  write ""
  for i = 17 to 22
    write i
  .
  print ""
  for card1 = 2 to 11
    for i = 17 to 22
      p_dealer[i] = 0
    .
    call dealer_draw 1 card1 0 0 p_dealer[]
    # 
    numb_fmt 2 0
    write card1 & ":"
    numb_fmt 5 2
    for i = 17 to 22
      write p_dealer[i] & ""
    .
    print ""
  .
.
call p_dealer

We see that probilities for the dealers first card 2 to 6 are relatively high to bust. Therefore, the players game strategy can be more cautious for these constellations.

Strategy Hard Hand

Now we try to determine an optimal playing strategy based on the probabilities of getting a card. The first part deals with the hard hand, i.e. we have a card sum without an ace (with an ace it is a soft hand because a ace can count 1 instead of 11) - and we then make a decision to take a card (hit) or not (stand). We do this by the expected value, if we stand say at 18 and the dealer has a 7, then the expected value is the sum of the probabilities, where we win

p_dealer[7][17] + p_dealer[7][22]

minus the probabilities where we lose

p_dealer[7][19] + p_dealer[7][20] + p_dealer[7][21]

Now we calculate the expected value if we take a card. This is the sum of the expected values of the card sums to which we get if we add the card values from 1 to 11, each multiplied by the chance to get there, which is 1/13 and at 10 4/13. From 22 the expected value is of course -1.

Then we choose the path with the best prediction (with the best expected value).

# Strategy Hard Hand
# 
len p_card[] 12
len ev_stand[][] 12
len ev_hard[][] 12
len strat_hard$[][] 12
# 
func prepare . .
  for i = 1 to 11
    p_card[i] = 1 / 13
  .
  p_card[10] = 4 / 13
  # 
  for i = 2 to 11
    len ev_stand[i][] 22
    len ev_hard[i][] 22
    len strat_hard$[i][] 22
  .
.
call prepare
# 
func dealer_draw p card cardsum n_ace . p_dealer[] .
  if card = 11
    n_ace += 1
  .
  cardsum += card
  if cardsum > 21 and n_ace > 0
    cardsum -= 10
    n_ace -= 1
  .
  if cardsum >= 17
    cardsum = lower cardsum 22
    p_dealer[cardsum] += p
  elif p > 0.00001
    for i = 2 to 11
      call dealer_draw p * p_card[i] i cardsum n_ace p_dealer[]
    .
  .
.
func ev_stand . .
  len p_dealer[] 23
  for card1 = 2 to 11
    len ev_stand[card1][] 22
    for i = 17 to 22
      p_dealer[i] = 0
    .
    call dealer_draw 1 card1 0 0 p_dealer[]
    # 
    for card_sum = 21 downto 12
      ev_stand[card1][card_sum] = p_dealer[22]
      for i = 17 to card_sum - 1
        ev_stand[card1][card_sum] += p_dealer[i]
      .
      for i = card_sum + 1 to 21
        ev_stand[card1][card_sum] -= p_dealer[i]
      .
    .
  .
.
call ev_stand
# 
for card1 = 2 to 11
  for card_sum = 21 downto 12
    ev_hit = 0.0
    for i = 1 to 21 - card_sum
      ev_hit += p_card[i] * ev_hard[card1][i + card_sum]
    .
    for i = 21 - card_sum + 1 to 10
      ev_hit -= p_card[i]
    .
    if ev_hit > ev_stand[card1][card_sum]
      strat_hard$[card1][card_sum] = "H"
      ev_hard[card1][card_sum] = ev_hit
    else
      strat_hard$[card1][card_sum] = "S"
      ev_hard[card1][card_sum] = ev_stand[card1][card_sum]
    .
  .
.
numb_fmt 2 2
func out_strat . .
  print "Strategy Hard Hand:"
  print ""
  write ""
  for i = 12 to 20
    write i & ""
  .
  print ""
  for card1 = 2 to 11
    write "" & card1 & ": "
    for i = 12 to 20
      write strat_hard$[card1][i] & ""
    .
    print ""
  .
.
call out_strat

We see that if the dealer has a six, you should not take a card with a sum of 12. If the dealer has a 7, you have to risk another card until you have more than 16.

    12 13 14 15 16 17 18 19 20 
  -----------------------------
  6: S  S  S  S  S  S  S  S  S  
  7: H  H  H  H  H  S  S  S  S  

Generally, buy to 16 if the dealer has at least a 7.

Strategy Soft Hand

Now let's deal with the soft hand, which is a sum with an ace, 11 point count. If we bust, then the ace counts 1 and we have a hard hand.

# Strategy Soft Hand
# 
len p_card[] 12
len ev_hard[][] 12
len ev_stand[][] 12
len ev_soft[][] 12
len strat_soft$[][] 12
# 
func prepare . .
  for i = 1 to 11
    p_card[i] = 1 / 13
  .
  p_card[10] = 4 / 13
  # 
  for i = 2 to 11
    len ev_stand[i][] 22
    len ev_hard[i][] 22
    len ev_soft[i][] 22
    len strat_soft$[i][] 22
  .
.
call prepare
# 
func dealer_draw p card cardsum n_ace . p_dealer[] .
  if card = 11
    n_ace += 1
  .
  cardsum += card
  if cardsum > 21 and n_ace > 0
    cardsum -= 10
    n_ace -= 1
  .
  if cardsum >= 17
    cardsum = lower cardsum 22
    p_dealer[cardsum] += p
  elif p > 0.00001
    for i = 2 to 11
      call dealer_draw p * p_card[i] i cardsum n_ace p_dealer[]
    .
  .
.
func ev_stand . .
  len p_dealer[] 23
  for card1 = 2 to 11
    len ev_stand[card1][] 22
    for i = 17 to 22
      p_dealer[i] = 0
    .
    call dealer_draw 1 card1 0 0 p_dealer[]
    # 
    for card_sum = 21 downto 12
      ev_stand[card1][card_sum] = p_dealer[22]
      for i = 17 to card_sum - 1
        ev_stand[card1][card_sum] += p_dealer[i]
      .
      for i = card_sum + 1 to 21
        ev_stand[card1][card_sum] -= p_dealer[i]
      .
    .
  .
.
call ev_stand
# 
func calc_ev . .
  for card1 = 2 to 11
    # 
    # hard hand
    # 
    for card_sum = 21 downto 12
      ev_hit = 0.0
      for i = 21 - card_sum + 1 to 10
        ev_hit -= p_card[i]
      .
      for i = 1 to 21 - card_sum
        ev_hit += p_card[i] * ev_hard[card1][i + card_sum]
      .
      ev_hard[card1][card_sum] = higher ev_hit ev_stand[card1][card_sum]
    .
    # 
    # soft hand
    # 
    for card_sum = 21 downto 12
      ev_stand = ev_stand[card1][card_sum]
      if card_sum = 11
        ev_stand = ev_stand[card1][16]
      .
      ev_hit = 0.0
      if card_sum = 11
        for i = 1 to 20 - card_sum
          ev_hit += p_card[i] * ev_soft[card1][i + card_sum]
        .
        # black jack
        ev_hit += p_card[10] * 1.5 * ev_soft[card1][21]
      else
        for i = 1 to 21 - card_sum
          ev_hit += p_card[i] * ev_soft[card1][i + card_sum]
        .
        for i = 21 - card_sum + 1 to 10
          ev_hit += p_card[i] * ev_hard[card1][i + card_sum - 10]
        .
      .
      if ev_hit > ev_stand
        strat_soft$[card1][card_sum] = "H"
      else
        strat_soft$[card1][card_sum] = "S"
      .
    .
  .
.
call calc_ev
# 
numb_fmt 2 2
func out_strat . .
  print "Strategy Soft Hand:"
  print ""
  write ""
  for i = 12 to 20
    write i & ""
  .
  print ""
  for card1 = 2 to 11
    write "" & card1 & ": "
    for i = 12 to 20
      write strat_soft$[card1][i] & ""
    .
    print ""
  .
.
call out_strat

Buy up to 16, if the dealer has at least 8 also buy at 17.

Strategy Double Down

If we get 9, 10 or 11 with 2 cards. We can double the bet. But then we get only one card. Depending on card1 this can make sense, i.e. the expected value is then higher.

# Strategy - Double Down
# 
len p_card[] 12
len ev_stand[][] 12
len ev_hard[][] 12
len ev_soft[][] 12
len ev_dbl[][] 12
len strat_dbl$[][] 12
# 
func prepare . .
  for i = 1 to 11
    p_card[i] = 1 / 13
  .
  p_card[10] = 4 / 13
  for i = 2 to 11
    len ev_stand[i][] 22
    len ev_hard[i][] 22
    len ev_soft[i][] 22
    len ev_dbl[i][] 12
    len strat_dbl$[i][] 12
  .
.
call prepare
# 
func dealer_draw p card cardsum n_ace . p_dealer[] .
  if card = 11
    n_ace += 1
  .
  cardsum += card
  if cardsum > 21 and n_ace > 0
    cardsum -= 10
    n_ace -= 1
  .
  if cardsum >= 17
    cardsum = lower cardsum 22
    p_dealer[cardsum] += p
  elif p > 0.00001
    for i = 2 to 11
      call dealer_draw p * p_card[i] i cardsum n_ace p_dealer[]
    .
  .
.
func ev_stand . .
  len p_dealer[] 23
  for card1 = 2 to 11
    len ev_stand[card1][] 22
    for i = 17 to 22
      p_dealer[i] = 0
    .
    call dealer_draw 1 card1 0 0 p_dealer[]
    # 
    for card_sum = 21 downto 12
      ev_stand[card1][card_sum] = p_dealer[22]
      for i = 17 to card_sum - 1
        ev_stand[card1][card_sum] += p_dealer[i]
      .
      for i = card_sum + 1 to 21
        ev_stand[card1][card_sum] -= p_dealer[i]
      .
    .
  .
.
call ev_stand
# 
func calc_ev . .
  for card1 = 2 to 11
    # 
    # hard hand
    # 
    for card_sum = 21 downto 12
      ev_hit = 0.0
      for i = 21 - card_sum + 1 to 10
        ev_hit -= p_card[i]
      .
      for i = 1 to 21 - card_sum
        ev_hit += p_card[i] * ev_hard[card1][i + card_sum]
      .
      ev_hard[card1][card_sum] = higher ev_hit ev_stand[card1][card_sum]
    .
    # 
    # soft hand
    # 
    for card_sum = 21 downto 12
      ev_stand = ev_stand[card1][card_sum]
      if card_sum = 11
        ev_stand = ev_stand[card1][16]
      .
      ev_hit = 0.0
      if card_sum = 11
        for i = 1 to 20 - card_sum
          ev_hit += p_card[i] * ev_soft[card1][i + card_sum]
        .
        # black jack
        ev_hit += p_card[10] * 1.5 * ev_soft[card1][21]
      else
        for i = 1 to 21 - card_sum
          ev_hit += p_card[i] * ev_soft[card1][i + card_sum]
        .
        for i = 21 - card_sum + 1 to 10
          ev_hit += p_card[i] * ev_hard[card1][i + card_sum - 10]
        .
      .
      ev_soft[card1][card_sum] = higher ev_hit ev_stand
    .
    # 
    # expected values for cardsum 9, 10, 11
    # 
    for card_sum = 11 downto 9
      ev_hit = 0.0
      if card_sum = 11
        for i = 1 to 10
          ev_hit += p_card[i] * ev_hard[card1][i + 11]
        .
      else
        for i = 2 to 10
          ev_hit += p_card[i] * ev_hard[card1][i + card_sum]
        .
        ev_hit += p_card[11] * ev_soft[card1][card_sum + 11]
      .
      ev_hard[card1][card_sum] = ev_hit
      ev_stand[card1][card_sum] = ev_stand[card1][16]
    .
    # 
    # expected values for double
    # 
    for card_sum = 9 to 11
      ev_dbl = 0.0
      if card_sum = 9 or card_sum = 10
        for i = 2 to 11
          ev_dbl += p_card[i] * ev_stand[card1][i + card_sum]
        .
      else
        for i = 1 to 10
          ev_dbl += p_card[i] * ev_stand[card1][i + card_sum]
        .
      .
      ev_dbl *= 2
      if ev_dbl > ev_hard[card1][card_sum]
        strat_dbl$[card1][card_sum] = "D"
      else
        strat_dbl$[card1][card_sum] = "-"
      .
    .
  .
.
call calc_ev
# 
func out_strat . .
  numb_fmt 2 2
  print "Strategy Double Down"
  print ""
  write ""
  for i = 9 to 11
    write i & ""
  .
  print ""
  for card1 = 2 to 11
    write "" & card1 & ": "
    for i = 9 to 11
      write strat_dbl$[card1][i] & ""
    .
    print ""
  .
.
call out_strat

Double down on a 10 or an ace, unless the dealer has a 10 or an ace. On a 9 only if the dealer has a poor card (3 to 6).

Strategy Split

If we get 2 equal cards, we can split them and continue playing with each card. Depending on the dealer first card, this can also be advantageous.

# Strategy Split
# 
len p_card[] 12
len ev_stand[][] 12
len ev_hard[][] 12
len ev_soft[][] 12
len strat_split$[][] 12
# 
func prepare . .
  for i = 1 to 11
    p_card[i] = 1 / 13
  .
  p_card[10] = 4 / 13
  for i = 2 to 11
    len ev_stand[i][] 22
    len ev_hard[i][] 22
    len ev_soft[i][] 22
    len strat_split$[i][] 12
  .
.
call prepare
# 
func dealer_draw p card cardsum n_ace . p_dealer[] .
  if card = 11
    n_ace += 1
  .
  cardsum += card
  if cardsum > 21 and n_ace > 0
    cardsum -= 10
    n_ace -= 1
  .
  if cardsum >= 17
    cardsum = lower cardsum 22
    p_dealer[cardsum] += p
  elif p > 0.00001
    for i = 2 to 11
      call dealer_draw p * p_card[i] i cardsum n_ace p_dealer[]
    .
  .
.
func ev_stand . .
  len p_dealer[] 23
  for card1 = 2 to 11
    len ev_stand[card1][] 22
    for i = 17 to 22
      p_dealer[i] = 0
    .
    call dealer_draw 1 card1 0 0 p_dealer[]
    # 
    for card_sum = 21 downto 12
      ev_stand[card1][card_sum] = p_dealer[22]
      for i = 17 to card_sum - 1
        ev_stand[card1][card_sum] += p_dealer[i]
      .
      for i = card_sum + 1 to 21
        ev_stand[card1][card_sum] -= p_dealer[i]
      .
    .
  .
.
call ev_stand
# 
func calc_ev . .
  for card1 = 2 to 11
    # 
    # hard hand
    # 
    for card_sum = 21 downto 12
      ev_hit = 0.0
      for i = 21 - card_sum + 1 to 10
        ev_hit -= p_card[i]
      .
      for i = 1 to 21 - card_sum
        ev_hit += p_card[i] * ev_hard[card1][i + card_sum]
      .
      ev_hard[card1][card_sum] = higher ev_hit ev_stand[card1][card_sum]
    .
    # 
    # soft hand
    # 
    for card_sum = 21 downto 12
      ev_stand = ev_stand[card1][card_sum]
      if card_sum = 11
        ev_stand = ev_stand[card1][16]
      .
      ev_hit = 0.0
      if card_sum = 11
        for i = 1 to 20 - card_sum
          ev_hit += p_card[i] * ev_soft[card1][i + card_sum]
        .
        # black jack
        ev_hit += p_card[10] * 1.5 * ev_soft[card1][21]
      else
        for i = 1 to 21 - card_sum
          ev_hit += p_card[i] * ev_soft[card1][i + card_sum]
        .
        for i = 21 - card_sum + 1 to 10
          ev_hit += p_card[i] * ev_hard[card1][i + card_sum - 10]
        .
      .
      ev_soft[card1][card_sum] = higher ev_hit ev_stand
    .
    # 
    # normal expected values for cardsum 2 to 11
    # 
    for card_sum = 11 downto 2
      ev_hit = 0.0
      if card_sum = 11
        for i = 1 to 10
          ev_hit += p_card[i] * ev_hard[card1][i + 11]
        .
      else
        for i = 2 to 10
          ev_hit += p_card[i] * ev_hard[card1][i + card_sum]
        .
        ev_hit += p_card[11] * ev_soft[card1][card_sum + 11]
      .
      ev_hard[card1][card_sum] = ev_hit
      ev_stand[card1][card_sum] = ev_stand[card1][16]
    .
    # 
    # expected values for split
    # 
    for card_sum = 2 to 11
      if card_sum = 11
        # only one more card
        ev_split = 0.0
        for i = 1 to 10
          ev_split += p_card[i] * ev_stand[card1][i + card_sum]
        .
        ev_split *= 2
        ev_hit = ev_soft[card1][12]
      else
        ev_split = 2 * ev_hard[card1][card_sum]
        ev_hit = ev_hard[card1][2 * card_sum]
      .
      if ev_split > ev_hit
        strat_split$[card1][card_sum] = "X"
      else
        strat_split$[card1][card_sum] = "-"
      .
    .
  .
.
call calc_ev
# 
func out_strat . .
  numb_fmt 2 2
  print "Strategy Split:"
  print ""
  write ""
  for i = 2 to 11
    write i & ""
  .
  print ""
  for card1 = 2 to 11
    write "" & card1 & ": "
    for i = 2 to 11
      write strat_split$[card1][i] & ""
    .
    print ""
  .
.
call out_strat

We see that we should never split two 10-values, aces always unless the dealer also has an ace.

Total Expected Value

We now have an optimal playing strategy,. The question is now do we win at blackjack with this strategy. For this we calculate the expected value.

We calculate for all combinations of three cards, one from the dealer and two from us, the expected values - which we got when calculating the optimal strategy - multiplied by the probability of getting these 3 cards.

# Total Expected Value
# 
len p_card[] 12
len ev_stand[][] 12
len ev_hard[][] 12
len ev_soft[][] 12
len ev_dbl[][] 12
len ev_split[][] 12
# 
func prepare . .
  for i = 1 to 11
    p_card[i] = 1 / 13
  .
  p_card[10] = 4 / 13
  for i = 2 to 11
    len ev_stand[i][] 22
    len ev_hard[i][] 22
    len ev_soft[i][] 22
    len ev_dbl[i][] 12
    len ev_split[i][] 12
  .
.
call prepare
# 
# 
func dealer_draw p card cardsum n_ace . p_dealer[] .
  if card = 11
    n_ace += 1
  .
  cardsum += card
  if cardsum > 21 and n_ace > 0
    cardsum -= 10
    n_ace -= 1
  .
  if p = -1
    p = 1
  else
    p *= p_card[card]
  .
  if p > 0.00000001
    if cardsum >= 17
      if cardsum > 22
        cardsum = 22
      .
      p_dealer[cardsum] += p
    else
      for i = 2 to 11
        call dealer_draw p i cardsum n_ace p_dealer[]
      .
    .
  .
.
func ev_stand . .
  len p_dealer[] 23
  for card1 = 2 to 11
    for i = 17 to 22
      p_dealer[i] = 0
    .
    call dealer_draw -1 card1 0 0 p_dealer[]
    # 
    for card_sum = 21 downto 11
      ev_stand[card1][card_sum] = p_dealer[22]
      for i = 17 to card_sum - 1
        ev_stand[card1][card_sum] += p_dealer[i]
      .
      for i = card_sum + 1 to 21
        ev_stand[card1][card_sum] -= p_dealer[i]
      .
    .
  .
.
# 
func calc_ev . .
  call ev_stand
  # 
  for card1 = 2 to 11
    # 
    # hard hand
    # 
    for card_sum = 21 downto 11
      ev_stand = ev_stand[card1][card_sum]
      ev_hit = 0.0
      for i = 21 - card_sum + 1 to 10
        ev_hit -= p_card[i]
      .
      for i = 1 to 21 - card_sum
        ev_hit += p_card[i] * ev_hard[card1][i + card_sum]
      .
      ev_hard[card1][card_sum] = higher ev_hit ev_stand
    .
    # 
    # soft hand
    # 
    for card_sum = 21 downto 12
      ev_stand = ev_stand[card1][card_sum]
      ev_hit = 0.0
      for i = 1 to 21 - card_sum
        ev_hit += p_card[i] * ev_soft[card1][i + card_sum]
      .
      for i = 21 - card_sum + 1 to 10
        ev_hit += p_card[i] * ev_hard[card1][i + card_sum - 10]
      .
      ev_soft[card1][card_sum] = higher ev_hit ev_stand
    .
    # 
    # normal expected values for cardsum 2 to 11
    # 
    for card_sum = 11 downto 2
      ev_hit = 0.0
      if card_sum = 11
        for i = 1 to 10
          ev_hit += p_card[i] * ev_hard[card1][i + 11]
        .
      else
        for i = 2 to 10
          ev_hit += p_card[i] * ev_hard[card1][i + card_sum]
        .
        ev_hit += p_card[11] * ev_soft[card1][card_sum + 11]
      .
      ev_hard[card1][card_sum] = ev_hit
      ev_stand[card1][card_sum] = ev_stand[card1][16]
    .
    # 
    # expected values for double
    # 
    for card_sum = 9 to 11
      ev_dbl = 0.0
      if card_sum = 9 or card_sum = 10
        for i = 2 to 11
          ev_dbl += p_card[i] * ev_stand[card1][i + card_sum]
        .
      else
        for i = 1 to 10
          ev_dbl += p_card[i] * ev_stand[card1][i + card_sum]
        .
      .
      ev_dbl *= 2
      ev_dbl[card1][card_sum] = higher ev_dbl ev_hard[card1][card_sum]
    .
    # 
    # expected values for split
    # 
    for card_sum = 2 to 11
      if card_sum = 11
        # only one more card
        ev_split = 0.0
        for i = 1 to 10
          ev_split += p_card[i] * ev_stand[card1][i + card_sum]
        .
        ev_split *= 2
        ev_hit = ev_soft[card1][12]
      else
        ev_split = 2 * ev_hard[card1][card_sum]
        ev_hit = ev_hard[card1][2 * card_sum]
      .
      ev_split[card1][card_sum] = higher ev_split ev_hit
    .
  .
  # 
  # total expected value
  # 
  ev_total = 0.0
  for i = 2 to 11
    for j = 2 to 11
      f = p_card[i] * p_card[j]
      card_sum = i + j
      if card_sum = 22
        card_sum = 12
      .
      for card1 = 2 to 11
        f1 = p_card[card1]
        if card_sum = 21
          if card1 < 10
            ev_total += f * f1 * 1.5
          elif card1 = 10
            ev_total += f * f1 * 1.5 * (1 - p_card[11])
          elif card1 = 11
            ev_total += f * f1 * 1.5 * (1 - p_card[10])
          .
        elif 9 <= card_sum and card_sum <= 11
          ev_total += f * f1 * ev_dbl[card1][card_sum]
        elif i = j
          ev_total += f * f1 * ev_split[card1][i]
        elif i = 11 or j = 11
          ev_total += f * f1 * ev_soft[card1][card_sum]
        else
          ev_total += f * f1 * ev_hard[card1][card_sum]
        .
      .
    .
  .
  numb_fmt 0 4
  print "EV: " & ev_total
.
# 
call calc_ev

Okay we got a number, unfortunately it is negative, which means that we lose in blackjack even if we play optimally. But at least we don't lose as much as in Roulette, where the expected value is -0.027.

In many casinos shuffling machines are used, which shuffle the played cards into the deck after each game, in which case the probabilities remain almost the same.

Simulation

We simulate 500000 games. We bet $10 each time. There are 6 decks. After each round, the cards played are shuffled back in.

# Simulation
# 
n_deck = 6
len cards[] 52 * n_deck
len n_cards[] 12
global n_cards .
# 
len strat_hard$[][] 12
len strat_soft$[][] 12
len strat_dbl$[][] 12
len strat_split$[][] 12
# 
func init . .
  for i = 2 to 11
    len strat_hard$[i][] 22
    s$ = input
    for j range 10
      strat_hard$[i][j + 12] = substr s$ j * 3 1
    .
  .
  s$ = input
  for i = 2 to 11
    len strat_soft$[i][] 22
    s$ = input
    for j range 10
      strat_soft$[i][j + 12] = substr s$ j * 3 1
    .
  .
  s$ = input
  for i = 2 to 11
    len strat_dbl$[i][] 12
    s$ = input
    for j range 3
      strat_dbl$[i][j + 9] = substr s$ j * 3 1
    .
  .
  s$ = input
  for i = 2 to 11
    len strat_split$[i][] 12
    s$ = input
    for j range 10
      strat_split$[i][j + 2] = substr s$ j * 3 1
    .
  .
  for i = 2 to 11
    if i = 10
      n_cards[i] = n_deck * 16
    else
      n_cards[i] = n_deck * 4
    .
    n_cards = n_deck * 52
    for i range n_cards
      card = i div (n_deck * 4) + 2
      if card > 11
        card = 10
      .
      cards[i] = card
    .
  .
.
call init
# 
func shuffle . .
  for i = n_deck * 52 - 1 downto n_cards
    j = random (i + 1)
    h = cards[j]
    cards[j] = cards[i]
    cards[i] = h
  .
  n_cards = n_deck * 52
.
n_cards = 0
call shuffle
# 
subr game_init
  dealer_sum = 0
  dealer_ace = 0
  dealer_blackj = 0
  player_sum = 0
  player_ace = 0
  player_blackj = 0
.
# 
func take . card .
  n_cards -= 1
  card = cards[n_cards]
  n_cards[card] -= 1
.
# 
func dealer_take . .
  call take card
  if card = 11
    dealer_ace += 1
  .
  dealer_sum += card
  if dealer_sum > 21 and dealer_ace > 0
    dealer_ace -= 1
    dealer_sum -= 10
  .
.
func dealer_turn . .
  call dealer_take
  if dealer_sum = 21
    dealer_blackj = 1
  .
  while dealer_sum < 17
    call dealer_take
  .
.
func player_take . .
  call take card
  if card = 11
    player_ace += 1
  .
  player_sum += card
  if player_sum > 21 and player_ace > 0
    player_ace -= 1
    player_sum -= 10
  .
.
func player_turn . .
  while 1 = 1
    if player_ace = 0
      h$ = strat_hard$[dealer_sum][player_sum]
    else
      h$ = strat_soft$[dealer_sum][player_sum]
    .
    if h$ = "-"
      break 1
    .
    call player_take
    if player_sum > 21
      break 1
    .
  .
.
func play n_games . .
  money = 0
  for games range n_games
    call shuffle
    set = 10
    call game_init
    call player_take
    player_card1 = player_sum
    call player_take
    if player_sum = 21
      player_blackj = 1
    .
    call dealer_take
    # 
    player_sum_spl = 0
    if player_sum >= 9 and player_sum <= 11 and strat_dbl$[dealer_sum][player_sum] = "D"
      set *= 2
      call player_take
      # 
    elif player_card1 * 2 = player_sum and strat_split$[dealer_sum][player_card1] = "X"
      if player_card1 = 11
        # 2 aces, therefor only one card each
        player_sum = 11
        player_ace = 0
        call player_take
        player_sum_spl = player_sum
        # 
        player_sum = 11
        player_ace = 0
        call player_take
      else
        player_sum = player_card1
        call player_turn
        player_sum_spl = player_sum
        # 
        player_sum = player_card1
        player_ace = 0
        call player_turn
      .
    else
      call player_turn
    .
    call dealer_turn
    # 
    if player_sum_spl = 0 and player_blackj = 1 and dealer_blackj = 0
      money += 3 * set / 2
    elif player_sum > 21
      money -= set
    elif dealer_sum > 21 or player_sum > dealer_sum
      money += set
    elif player_sum < dealer_sum
      money -= set
    .
    if player_sum_spl > 0
      if player_sum_spl > 21
        money -= set
      elif dealer_sum > 21 or player_sum_spl > dealer_sum
        money += set
      elif player_sum_spl < dealer_sum
        money -= set
      .
    .
  .
  print "$" & money
  numb_fmt 0 4
  print "EV: " & money / (10 * n_games)
.
call play 500000
# 
input_data
H  -  -  -  -  -  -  -  -  -  
H  -  -  -  -  -  -  -  -  -  
-  -  -  -  -  -  -  -  -  -  
-  -  -  -  -  -  -  -  -  -  
-  -  -  -  -  -  -  -  -  -  
H  H  H  H  H  -  -  -  -  -  
H  H  H  H  H  -  -  -  -  -  
H  H  H  H  H  -  -  -  -  -  
H  H  H  H  H  -  -  -  -  -  
H  H  H  H  H  -  -  -  -  -  
*
H  H  H  H  H  H  -  -  -  -  
H  H  H  H  H  H  -  -  -  -  
H  H  H  H  H  H  -  -  -  -  
H  H  H  H  H  H  -  -  -  -  
H  H  H  H  H  H  -  -  -  -  
H  H  H  H  H  H  -  -  -  -  
H  H  H  H  H  H  H  -  -  -  
H  H  H  H  H  H  H  -  -  -  
H  H  H  H  H  H  H  -  -  -  
H  H  H  H  H  H  H  -  -  -  
*
-  D  D  
D  D  D  
D  D  D  
D  D  D  
D  D  D  
-  D  D  
-  D  D  
-  D  D  
-  -  -  
-  -  -  
*
-  -  -  -  -  X  X  X  -  X  
-  -  -  -  X  X  X  X  -  X  
X  X  -  -  X  X  X  X  -  X  
X  X  -  -  X  X  X  X  -  X  
X  X  -  -  X  X  X  X  -  X  
X  X  -  -  -  X  X  -  -  X  
-  -  -  -  -  -  X  X  -  X  
-  -  -  -  -  -  X  X  -  X  
-  -  -  -  -  -  -  -  -  X  
-  -  -  -  -  -  -  -  -  -  

The simulated expected value is quite close to the calculated expected value.

A Stack with Good Cards

When the deck, which often consists of 6 decks of 52 cards, is played until about a fourth of the cards are left, the probabilities of getting certain cards change as the game goes on. Perhaps the expected value can also change to plus.

Let's assume that in the course of the game 5 cards from cards 2 to 7 were played, 4 cards from cards 8 to 9, and 3 cards from the others (all 10 values together 12). In a stack with 6 decks, the probability of drawing a card is calculated as follows.

len p_card[] 12
removed_cards[] = [ 0 0 5 5 5 5 5 5 4 4 12 3 ]
# 
n_deck = 6
n_cards = n_deck * 52
for i = 2 to 11
  n_cards -= removed_cards[i]
.
for i = 2 to 11
  p_card[i] = (n_deck * 4 - removed_cards[i]) / n_cards
.
p_card[10] = (n_deck * 16 - removed_cards[10]) / n_cards
# 
numb_fmt 0 3
print p_card[]

Now we calculate the expected value with this stack of cards.

# Good cards
# 
len p_card[] 12
len ev_stand[][] 12
len ev_hard[][] 12
len ev_soft[][] 12
len ev_dbl[][] 12
len ev_split[][] 12
# 
func prepare . .
  for i = 1 to 11
    p_card[i] = 1 / 13
  .
  p_card[10] = 4 / 13
  for i = 2 to 11
    len ev_stand[i][] 22
    len ev_hard[i][] 22
    len ev_soft[i][] 22
    len ev_dbl[i][] 12
    len ev_split[i][] 12
  .
.
call prepare
# 
# 
func dealer_draw p card cardsum n_ace . p_dealer[] .
  if card = 11
    n_ace += 1
  .
  cardsum += card
  if cardsum > 21 and n_ace > 0
    cardsum -= 10
    n_ace -= 1
  .
  if p = -1
    p = 1
  else
    p *= p_card[card]
  .
  if p > 0.00000001
    if cardsum >= 17
      if cardsum > 22
        cardsum = 22
      .
      p_dealer[cardsum] += p
    else
      for i = 2 to 11
        call dealer_draw p i cardsum n_ace p_dealer[]
      .
    .
  .
.
func ev_stand . .
  len p_dealer[] 23
  for card1 = 2 to 11
    for i = 17 to 22
      p_dealer[i] = 0
    .
    call dealer_draw -1 card1 0 0 p_dealer[]
    # 
    for card_sum = 21 downto 11
      ev_stand[card1][card_sum] = p_dealer[22]
      for i = 17 to card_sum - 1
        ev_stand[card1][card_sum] += p_dealer[i]
      .
      for i = card_sum + 1 to 21
        ev_stand[card1][card_sum] -= p_dealer[i]
      .
    .
  .
.
# 
func calc_ev . .
  call ev_stand
  # 
  for card1 = 2 to 11
    # 
    # hard hand
    # 
    for card_sum = 21 downto 11
      ev_stand = ev_stand[card1][card_sum]
      ev_hit = 0.0
      for i = 21 - card_sum + 1 to 10
        ev_hit -= p_card[i]
      .
      for i = 1 to 21 - card_sum
        ev_hit += p_card[i] * ev_hard[card1][i + card_sum]
      .
      ev_hard[card1][card_sum] = higher ev_hit ev_stand
    .
    # 
    # soft hand
    # 
    for card_sum = 21 downto 12
      ev_stand = ev_stand[card1][card_sum]
      ev_hit = 0.0
      for i = 1 to 21 - card_sum
        ev_hit += p_card[i] * ev_soft[card1][i + card_sum]
      .
      for i = 21 - card_sum + 1 to 10
        ev_hit += p_card[i] * ev_hard[card1][i + card_sum - 10]
      .
      ev_soft[card1][card_sum] = higher ev_hit ev_stand
    .
    # 
    # normal expected values for cardsum 2 to 11
    # 
    for card_sum = 11 downto 2
      ev_hit = 0.0
      if card_sum = 11
        for i = 1 to 10
          ev_hit += p_card[i] * ev_hard[card1][i + 11]
        .
      else
        for i = 2 to 10
          ev_hit += p_card[i] * ev_hard[card1][i + card_sum]
        .
        ev_hit += p_card[11] * ev_soft[card1][card_sum + 11]
      .
      ev_hard[card1][card_sum] = ev_hit
      ev_stand[card1][card_sum] = ev_stand[card1][16]
    .
    # 
    # expected values for double
    # 
    for card_sum = 9 to 11
      ev_dbl = 0.0
      if card_sum = 9 or card_sum = 10
        for i = 2 to 11
          ev_dbl += p_card[i] * ev_stand[card1][i + card_sum]
        .
      else
        for i = 1 to 10
          ev_dbl += p_card[i] * ev_stand[card1][i + card_sum]
        .
      .
      ev_dbl *= 2
      ev_dbl[card1][card_sum] = higher ev_dbl ev_hard[card1][card_sum]
    .
    # 
    # expected values for split
    # 
    for card_sum = 2 to 11
      if card_sum = 11
        # only one more card
        ev_split = 0.0
        for i = 1 to 10
          ev_split += p_card[i] * ev_stand[card1][i + card_sum]
        .
        ev_split *= 2
        ev_hit = ev_soft[card1][12]
      else
        ev_split = 2 * ev_hard[card1][card_sum]
        ev_hit = ev_hard[card1][2 * card_sum]
      .
      ev_split[card1][card_sum] = higher ev_split ev_hit
    .
  .
  # 
  # total expected value
  # 
  ev_total = 0.0
  for i = 2 to 11
    for j = 2 to 11
      f = p_card[i] * p_card[j]
      card_sum = i + j
      if card_sum = 22
        card_sum = 12
      .
      for card1 = 2 to 11
        f1 = p_card[card1]
        if card_sum = 21
          if card1 < 10
            ev_total += f * f1 * 1.5
          elif card1 = 10
            ev_total += f * f1 * 1.5 * (1 - p_card[11])
          elif card1 = 11
            ev_total += f * f1 * 1.5 * (1 - p_card[10])
          .
        elif 9 <= card_sum and card_sum <= 11
          ev_total += f * f1 * ev_dbl[card1][card_sum]
        elif i = j
          ev_total += f * f1 * ev_split[card1][i]
        elif i = 11 or j = 11
          ev_total += f * f1 * ev_soft[card1][card_sum]
        else
          ev_total += f * f1 * ev_hard[card1][card_sum]
        .
      .
    .
  .
  numb_fmt 0 4
  print "EV: " & ev_total
.
# 
func change_stack . removed_cards[] .
  n_deck = 6
  n_cards = n_deck * 52
  for i = 2 to 11
    n_cards -= removed_cards[i]
  .
  for i = 2 to 11
    p_card[i] = (n_deck * 4 - removed_cards[i]) / n_cards
  .
  p_card[10] = (n_deck * 16 - removed_cards[10]) / n_cards
  p_card[1] = (n_deck * 4 - removed_cards[11]) / n_cards
.
# 
removed_cards[] = [ 0 0 5 5 5 5 5 5 4 4 12 3 ]
call change_stack removed_cards[]
call calc_ev

Whoopee, the expected value is positive. We can win at blackjack if we only bet the minimum while the expected value is negative, and when it becomes positive, bet the maximum amount allowed at the table.

Card Counting

This is the principle of card counting in blackjack. Only we need a simpler method - which can be used in the mind - to determine or estimate when the expected value becomes positive.

A simple method is the "Hi-Lo-Strategy". When there are many 10s and aces in the stack, the expected value increases. You start counting at 0, for each high card (10s or Aces) you decrease the counter, for each low card (2 to 6) you increase the counter by 1.

if card >= 2 and card <= 6
  count += 1
elif card >= 10
  count -= 1
.

A positive value counts more if there are fewer cards in the game. We therefore multiply the "count" by the number of cards in the deck divided by the cards currently present - this can also be estimated. If this value, the "true count", is at least 6, we place the largest possible bet.

set = 10
true_count = count * (n_deck * 52 / n_cards) 
if true_count >= 6
  set = 1000
.

We simulate 500000 games. If the "true count" is at least 6, we bet $1000, otherwise $10. There are 6 decks that are shuffled when three quarters of the cards have been played. We also adjusted the game strategy a bit to the card probabilities at positive expected value.

# Card counting simulation
# 
n_deck = 6
len cards[] 52 * n_deck
len n_cards[] 12
global n_cards .
# 
len strat_hard$[][] 12
len strat_soft$[][] 12
len strat_dbl$[][] 12
len strat_split$[][] 12
# 
func init . .
  for i = 2 to 11
    len strat_hard$[i][] 22
    s$ = input
    for j range 10
      strat_hard$[i][j + 12] = substr s$ j * 3 1
    .
  .
  s$ = input
  for i = 2 to 11
    len strat_soft$[i][] 22
    s$ = input
    for j range 10
      strat_soft$[i][j + 12] = substr s$ j * 3 1
    .
  .
  s$ = input
  for i = 2 to 11
    len strat_dbl$[i][] 12
    s$ = input
    for j range 3
      strat_dbl$[i][j + 9] = substr s$ j * 3 1
    .
  .
  s$ = input
  for i = 2 to 11
    len strat_split$[i][] 12
    s$ = input
    for j range 10
      strat_split$[i][j + 2] = substr s$ j * 3 1
    .
  .
  for i = 2 to 11
    if i = 10
      n_cards[i] = n_deck * 16
    else
      n_cards[i] = n_deck * 4
    .
    for i range n_deck * 52
      card = i div (n_deck * 4) + 2
      if card > 11
        card = 10
      .
      cards[i] = card
    .
  .
.
call init
# 
global count .
# 
func shuffle . .
  n_cards = n_deck * 52
  for i = n_cards - 1 downto 1
    j = random (i + 1)
    h = cards[j]
    cards[j] = cards[i]
    cards[i] = h
  .
  count = 0
.
call shuffle
# 
subr game_init
  dealer_sum = 0
  dealer_ace = 0
  dealer_blackj = 0
  player_sum = 0
  player_ace = 0
  player_blackj = 0
.
# 
func take . card .
  n_cards -= 1
  card = cards[n_cards]
  n_cards[card] -= 1
  # 
  if card >= 2 and card <= 6
    count += 1
  elif card >= 10
    count -= 1
  .
  # 
.
# 
func dealer_take . .
  call take card
  if card = 11
    dealer_ace += 1
  .
  dealer_sum += card
  if dealer_sum > 21 and dealer_ace > 0
    dealer_ace -= 1
    dealer_sum -= 10
  .
.
func dealer_turn . .
  call dealer_take
  if dealer_sum = 21
    dealer_blackj = 1
  .
  while dealer_sum < 17
    call dealer_take
  .
.
func player_take . .
  call take card
  if card = 11
    player_ace += 1
  .
  player_sum += card
  if player_sum > 21 and player_ace > 0
    player_ace -= 1
    player_sum -= 10
  .
.
func player_turn . .
  while 1 = 1
    if player_ace = 0
      h$ = strat_hard$[dealer_sum][player_sum]
    else
      h$ = strat_soft$[dealer_sum][player_sum]
    .
    if h$ = "-"
      break 1
    .
    call player_take
    if player_sum > 21
      break 1
    .
  .
.
func play . .
  money = 0
  for games range 500000
    if n_cards < n_deck * 52 / 4
      call shuffle
    .
    set = 10
    true_count = count * (n_deck * 52 / n_cards)
    if true_count >= 6
      set = 1000
    .
    call game_init
    call player_take
    player_card1 = player_sum
    call player_take
    if player_sum = 21
      player_blackj = 1
    .
    call dealer_take
    # 
    player_sum_spl = 0
    if player_sum >= 9 and player_sum <= 11 and strat_dbl$[dealer_sum][player_sum] = "D"
      set *= 2
      call player_take
      # 
    elif player_card1 * 2 = player_sum and strat_split$[dealer_sum][player_card1] = "X"
      if player_card1 = 11
        # 2 aces, therefor only one card each
        player_sum = 11
        player_ace = 0
        call player_take
        player_sum_spl = player_sum
        # 
        player_sum = 11
        player_ace = 0
        call player_take
      else
        player_sum = player_card1
        call player_turn
        player_sum_spl = player_sum
        # 
        player_sum = player_card1
        player_ace = 0
        call player_turn
      .
    else
      call player_turn
    .
    call dealer_turn
    # 
    if player_sum_spl = 0 and player_blackj = 1 and dealer_blackj = 0
      money += 3 * set / 2
    elif player_sum > 21
      money -= set
    elif dealer_sum > 21 or player_sum > dealer_sum
      money += set
    elif player_sum < dealer_sum
      money -= set
    .
    if player_sum_spl > 0
      if player_sum_spl > 21
        money -= set
      elif dealer_sum > 21 or player_sum_spl > dealer_sum
        money += set
      elif player_sum_spl < dealer_sum
        money -= set
      .
    .
  .
  print "$" & money
.
call play
# 
input_data
H  -  -  -  -  -  -  -  -  -  
H  -  -  -  -  -  -  -  -  -  
-  -  -  -  -  -  -  -  -  -  
-  -  -  -  -  -  -  -  -  -  
-  -  -  -  -  -  -  -  -  -  
H  H  H  H  H  -  -  -  -  -  
H  H  H  H  H  -  -  -  -  -  
H  H  H  H  H  -  -  -  -  -  
H  H  H  H  H  -  -  -  -  -  
H  H  H  H  H  -  -  -  -  -  
*
H  H  H  H  H  H  -  -  -  -  
H  H  H  H  H  H  -  -  -  -  
H  H  H  H  H  H  -  -  -  -  
H  H  H  H  H  H  -  -  -  -  
H  H  H  H  H  H  -  -  -  -  
H  H  H  H  H  H  -  -  -  -  
H  H  H  H  H  H  -  -  -  -  
H  H  H  H  H  H  H  -  -  -  
H  H  H  H  H  H  H  -  -  -  
H  H  H  H  H  H  H  -  -  -  
*
D  D  D  
D  D  D  
D  D  D  
D  D  D  
D  D  D  
-  D  D  
-  D  D  
-  D  D  
-  -  D  
-  -  -  
*
-  -  -  -  -  X  X  X  -  X  
-  -  -  -  X  X  X  X  -  X  
X  X  -  -  X  X  X  X  -  X  
X  X  -  -  X  X  X  X  -  X  
X  X  -  -  X  X  X  X  -  X  
X  X  -  -  -  X  X  -  -  X  
-  -  -  -  -  -  X  X  -  X  
-  -  -  -  -  -  X  X  -  X  
-  -  -  -  -  -  -  -  -  X  
-  -  -  -  -  -  -  -  -  -  

So in blackjack you can win if you have plenty of time and the conditions are right - no automatic shuffler, no kicking out when counting.

With roulette, it's a completely different story.

Monte Carlo Methods or Why it's a Bad Idea to Go to the Casino


The examples were created with easylang.online

[email protected]


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK