Hmm, I wrote a program to duplicate the first chart on this page, and I can't figure out why some values match and others don't. (This is not Monte Carlo but rather direct calculation through brute force.) I've omitted the entries where dealer or player has possibility for blackjack, because I haven't yet determined what rules are being used there, but I'd like to get the other numbers to match first. Either I'm doing something wrong or the website is wrong... anyone care to comment? I suspected precision errors but got the same result using greater precision.

For example, here is one row that I get (rounded to 4 decimal places), compared with the appendix:

Code:

Mine:
2 3 4 5 6 7 8 9
20 0.6410 0.6512 0.6620 0.6704 0.7040 0.7732 0.7918 0.7584
Appendix:
2 3 4 5 6 7 8 9
20 0.6400 0.6503 0.6610 0.6704 0.7040 0.7732 0.7918 0.7584

Java:

Code:

import java.util.ArrayList;
public class BlackjackStrat {
static double expRet = 0;
public static void main(String[] args) {
for (int i = 16; i < 21; i++) {
for (int j = 2; j < 10; j++) {
expRet = 0;
ArrayList<Integer> cards = new ArrayList<Integer>();
cards.add(j);
recurse(1, i, j, false, cards);
System.out.println("(" + i + ", " + j + ") " + expRet);
}
}
}
public static void recurse(double p, int playerTot, int dealerTot, boolean dealerHasAce, ArrayList<Integer> cards) {
// assume playerTot <= 21
if (dealerTot > 21) {
if (!dealerHasAce) {
// dealer bust
expRet += p;
//System.out.println("dealer bust " + cards + " (" + playerTot + ", " + dealerTot + ") " + expRet);
return;
}
dealerTot -= 10;
dealerHasAce = false;
}
if (dealerTot >= 17) {
// we do not need to check dealerHasAce because dealer must stand on all 17s
if (dealerTot > playerTot) {
expRet -= p;
//System.out.println("player low " + cards + " (" + playerTot + ", " + dealerTot + ") " + expRet);
}
if (dealerTot == playerTot) {
//System.out.println("push " + cards + " (" + playerTot + ", " + dealerTot + ") " + expRet);
}
if (dealerTot < playerTot) {
expRet += p;
//System.out.println("dealer low " + cards + " (" + playerTot + ", " + dealerTot + ") " + expRet);
}
return;
}
// iterate through all possible cards
double nextP = p / 13.0;
for (int i = 1; i < 14; i++) {
int nextDealerTot = dealerTot;
if (i == 1) nextDealerTot += 11;
else if (i > 9) nextDealerTot += 10;
else nextDealerTot += i;
boolean nextDealerHasAce = dealerHasAce;
if (i == 1) nextDealerHasAce = true;
ArrayList<Integer> nextCards = new ArrayList<Integer>(cards);
nextCards.add(i);
recurse(nextP, playerTot, nextDealerTot, nextDealerHasAce, nextCards);
}
}
}

Python:

Code:

from decimal import *
currExpRet = Decimal(0)
def main():
global currExpRet
for i in xrange(16,21):
for j in xrange(2,10):
currExpRet = Decimal(0)
cards = [j]
recurse(Decimal(1), i, j, False, cards)
print '(' + str(i) + ', ' + str(j) + ') ' + str(currExpRet)
return
def recurse(p, playerTot, dealerTot, dealerHasAce, cards):
global currExpRet
# assume playerTot <= 21
if dealerTot > 21:
if not dealerHasAce:
# dealer bust
currExpRet += p
return
dealerTot -= 10
dealerHasAce = False
if dealerTot >= 17:
# no need to check dealerHasAce; stand on soft 17
if dealerTot > playerTot: currExpRet -= p
if dealerTot < playerTot: currExpRet += p
return
# iterate through all possible cards
nextP = p / Decimal(13)
for i in xrange(1,14):
nextDealerTot = dealerTot
nextDealerHasAce = dealerHasAce
if i == 1:
nextDealerTot += 11
nextDealerHasAce = True
elif i > 9: nextDealerTot += 10
else: nextDealerTot += i
nextCards = cards[:]
nextCards.append(i)
recurse(nextP, playerTot, nextDealerTot, nextDealerHasAce, nextCards)
return
main()

Anyone care to comment? I'll keep looking. I think either I missed a rule, or the website is wrong... but the website seemed pretty reputable.. and I haven't been able to find another chart like it on the internet to compare against.

Update: I found a pdf here that confirms The Wizard of Odds's results, but I still haven't found my error.

Update 2: Now I suspect there is an error involving when the dealer gets two aces, and my program only recognizes one of them, which would cause the dealer to bust more often making my results slightly higher than the correct values.. I'll rework it in a bit.