Blackjack strategy: Difference between revisions
(Added Wren) |
m (→{{header|Phix}}: minor tidy, duplicate {{trans|Go}} removed) |
||
Line 2,518: | Line 2,518: | ||
{{trans|Go}} |
{{trans|Go}} |
||
<!--<lang Phix>--> |
<!--<lang Phix>--> |
||
<span style="color: #000080;font-style:italic;">-- |
<span style="color: #000080;font-style:italic;">-- demo/rosetta/blackjack.exw</span> |
||
-- demo/rosetta/blackjack.exw |
|||
-- ========================== |
|||
--{{trans|Go}}</span> |
|||
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span> |
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span> |
||
Revision as of 09:30, 8 November 2021
The objective of this task is to recreate and explore the following strategy charts for the game of blackjack (which is known by many other names as well).
Assume that my casino:
- uses a single deck,
- does not allow Double after split,
- pays out 3 to 2 for Blackjack, and
- uses these rules.
Begin by assuming the player's dealt hand contains no aces and is not a pair. Create functions which given the players dealt cards and the dealers exposed card returns the number of wins and losses for all possible continuations when the player either sticks or hits. Gather the results together, set a threshold at which you consider it wise to Double the bet and reconstruct the Hard Totals Table enhanced with precise probabilities.
Enhance your analysis by considering the case when the player's hand contains an Ace. Again by considering all continuations recreate the Soft Totals Table again enhanced with precise probabilities.
Finally complete your analysis by considering the case when the player's hand contains a pair. Again by considering all continuations recreate the Pair Splitting Table again enhanced with precise probabilities.
You should now create a function which randomly deals hands. Assuming I play 50 hands at a visit and visit everyday for a year, applying the strategy defined by the tables you have created, answer the following questions:
- How many days can I expect to win/lose?
- What can I expect to be my biggest win?
- What can I expect to be my biggest loss?
- What can I expect to win/lose over the year?
Go
As the dealer plays automatically, the first thing I did was to write a function which calculates the probabilities of the dealer ending up with various scores according to the Rules. I then checked the resulting table against a similar one on an 'active' online gambling site (which I'd better not link to here) and the results agreed to 6 decimal places.
The task asks us to calculate precise probabilities for all possible continuations after the player stands, hits, doubles or splits but this is impossible if the player 'hits' or 'splits' as we don't know what decisions (s)he will make subsequently. To be reasonably realistic, I decided to anticipate some further strategy tables I've computed for rounds after the initial deal and to assume that further decisions (to hit or stand) will be made in accordance with these tables.
The criterion I've used for doubling down is that this gives a better expected gain (positive or negative) than the other alternatives, taking into account the doubled stake.
Using these assumptions, I've been able to reproduce the 'hard' strategy table exactly and my 'soft' strategy table only differs in one case (A7/A) where I have 'stand' rather than 'hit' though the underlying figures are quite close.
The trickiest part of this task is dealing with the splitting of pairs (I've assumed re-splitting is not allowed though the Rules aren't explicit on this). The criterion used again is that this gives a better expected gain than the alternatives, taking into account the doubled stake.
I decided, given the other uncertainties, to make the simplifying assumption that, after calculating the expected gain for the first split hand as if the second one isn't completed, the expected gain for the second hand will then be exactly the same. This, of course, is not quite right since both hands need to be completed and the probabilities for the second hand will depend on what cards have been drawn for the first hand and the dealer's probabilities will depend on what cards have been drawn for both hands, a very complicated calculation.
However, the 'true' figures are unlikely to be much different from the figures I've actually used which is borne out by my 'pairs' strategy table only differing from the original in 4 cases out of 100 (33/3, 55/A, 77/T and 88/T). Of these, 2 cases have nothing to do with splitting, 1 case (55/A) is extremely marginal and the other 3 are quite close too.
Finally, I've done 10 years of simulations as the basic statistics can vary quite a bit from year to year. However, it will be seen that % loss varies over a narrower range - between about 0.3 and 1.8% for this particular run - which seems reasonable given the casino's edge even after basic strategy is utilized. <lang go>package main
import (
"fmt" "math/rand" "time"
)
type Deck [11]int // 0:deck size, 1 to 10: number of cards of that denomination
type ActionGain struct {
action string gain float64
}
func NewDeck() Deck {
return Deck{52, 4, 4, 4, 4, 4, 4, 4, 4, 4, 16}
}
// Returns probabilities of dealer eventually getting: // 0: 17, 1: 18, 2: 19, 3: 20, 4: 21 (non-blackjack), 5: blackjack (nil), 6: bust. // It is assumed that the dealer has already checked for blackjack, that one deck is used // and that the dealer stands on 'soft' 17. func dealerProbs(upCard int, startDeck Deck) []float64 {
res := make([]float64, 7) // results decks := make([]Deck, 9) // decks for each level scores := make([]int, 9) // scores for each level elevens := make([]int, 9) // number of aces for each level scored as 11 probs := make([]float64, 9) // probs for each level decks[0] = startDeck scores[0] = upCard if upCard == 1 { // an ace scores[0] = 11 elevens[0] = 1 } probs[0] = 1.0 var f func(lev int) // recursive closure f = func(lev int) { for c := 1; c < 11; c++ { if decks[lev][c] == 0 { continue // card no longer present in deck } // temporary variables for current level deck, score, eleven, prob := decks[lev], scores[lev], elevens[lev], probs[lev] score += c // add card to score if c == 1 { // score all aces initially as 11 score += 10 eleven++ } prob *= float64(deck[c]) / float64(deck[0]) if score > 21 && eleven > 0 { score -= 10 // bust but can demote an ace eleven-- } if lev == 0 && ((upCard == 1 && c == 10) || (upCard == 10 && c == 1)) { res[5] += prob // blackjack, allow for now } else if score >= 17 && score <= 21 { res[score-17] += prob // 17 to (non-blackjack) 21 } else if score > 21 && eleven == 0 { res[6] += prob // bust } else { deck[c]-- // remove card from deck deck[0]-- // decrement deck size lev2 := lev + 1 decks[lev2], scores[lev2], elevens[lev2], probs[lev2] = deck, score, eleven, prob f(lev2) // get another card } } } f(0) // but can't have blackjack, so adjust probabilities accordingly pnbj := 1 - res[5] for i := 0; i < 7; i++ { res[i] /= pnbj } res[5] = 0 return res
}
// Prints chart of dealer probabilities (as a check against an external source). func dealerChart() {
fmt.Println("Dealer Probabilities, Stands on Soft 17, 1 Deck, U.S Rules") fmt.Println("Up Card 17 18 19 20 21 Bust") fmt.Println("-------------------------------------------------------------------") deck := NewDeck() deck[0] = 51 for uc := 1; uc < 11; uc++ { deck2 := deck deck2[uc]-- dp := dealerProbs(uc, deck2) if uc > 1 { fmt.Printf("%3d ", uc) } else { fmt.Print("Ace ") } fmt.Printf("%f %f %f %f %f %f\n", dp[0], dp[1], dp[2], dp[3], dp[4], dp[6]) }
}
// Returns player's expected gain per unit staked after hitting once and then standing. func playerGain(card1, card2, uc int, startDeck Deck) float64 {
eg := 0.0 deck := startDeck score := card1 + card2 eleven := false if card1 == 1 || card2 == 1 { // an ace score += 10 eleven = true } for c := 1; c < 11; c++ { // get another card if deck[c] == 0 { continue // card no longer present in deck } // temporary variables for current card deck2, score2, eleven2 := deck, score, eleven score2 += c // add card to score if c == 1 { // score all aces initially as 11 score2 += 10 eleven2 = true } prob := float64(deck2[c]) / float64(deck2[0]) deck2[c]-- // remove card from deck deck2[0]-- // decrement deck size if score2 > 21 && eleven2 { score2 -= 10 // bust but can demote an ace } if score2 <= 21 { dp := dealerProbs(uc, deck2) eg += calcGain(score2, dp) * prob } else { // bust eg -= prob } } return eg
}
// Returns player's expected gain per unit staked after hitting once and then continuing in accordance // with the tables for rounds >= 2. func playerGain2(card1, card2, uc int, startDeck Deck) float64 {
eg := 0.0 // result decks := make([]Deck, 9) // decks for each level scores := make([]int, 9) // scores for each level elevens := make([]int, 9) // number of aces for each level scored as 11 probs := make([]float64, 9) // probs for each level decks[0] = startDeck scores[0] = card1 + card2 if card1 == 1 || card2 == 1 { // an ace scores[0] += 10 elevens[0] = 1 } probs[0] = 1.0 var f func(lev int) // recursive closure f = func(lev int) { for c := 1; c < 11; c++ { if decks[lev][c] == 0 { continue // card no longer present in deck } // temporary variables for current level deck, score, eleven, prob := decks[lev], scores[lev], elevens[lev], probs[lev] score += c // add card to score if c == 1 { // score all aces initially as 11 score += 10 eleven++ } prob *= float64(deck[c]) / float64(deck[0]) if score > 21 && eleven > 0 { score -= 10 // bust but can demote an ace eleven-- } deck[c]-- // remove card from deck deck[0]-- // decrement deck size if (eleven == 0 && (score >= 17 || (score >= 13 && uc < 7)) || (eleven == 0 && score == 12 && uc >= 4 && uc <= 6) || (eleven > 0 && score == 18 && uc != 9 && uc != 10) || (eleven > 0 && score >= 19)) && score <= 21 { dp := dealerProbs(uc, deck) eg += calcGain(score, dp) * prob } else if score > 21 && eleven == 0 { // bust eg -= prob } else { lev2 := lev + 1 decks[lev2], scores[lev2], elevens[lev2], probs[lev2] = deck, score, eleven, prob f(lev2) // get another card } } } f(0) return eg
}
// Calculates gain per unit staked for a given scenario (helper function). func calcGain(pscore int, dp []float64) float64 {
eg := 0.0 switch pscore { case 17: eg += dp[6] // dealer is bust eg -= dp[1] + dp[2] + dp[3] + dp[4] // dealer has 18 to 21 case 18: eg += dp[0] + dp[6] // dealer has 17 or is bust eg -= dp[2] + dp[3] + dp[4] // dealer has 19 to 21 case 19: eg += dp[0] + dp[1] + dp[6] // dealer has 17, 18 or is bust eg -= dp[3] + dp[4] // dealer has 20 or 21 case 20: eg += dp[0] + dp[1] + dp[2] + dp[6] // dealer has 17 to 19 or is bust eg -= dp[4] // dealer has (non-blackjack) 21 case 21: eg += dp[0] + dp[1] + dp[2] + dp[3] + dp[6] // dealer has 17 to 20 or is bust case 22: // notional eg += 1.5 // player blackjack case 23: // notional eg -= 1 // player bust, loses stake irrespective of what dealer has default: // player has less than 17 eg += dp[6] // dealer is bust eg -= (1 - dp[6]) // dealer isn't bust } return eg
}
// Returns player's expected gains per unit staked, for each dealer up-card, after standing. func stand(card1, card2 int) [10]float64 {
deck := NewDeck() deck[card1]-- deck[card2]-- deck[0] = 50 pscore := card1 + card2 // player score if card1 == 1 || card2 == 1 { pscore += 10 } var egs [10]float64 // results for uc := 1; uc < 11; uc++ { // dealer's up-card deck2 := deck deck2[uc]-- deck2[0]-- dp := dealerProbs(uc, deck2) eg := calcGain(pscore, dp) // expected gain for this up-card if uc > 1 { egs[uc-2] = eg } else { // dealer has Ace egs[9] = eg // ace comes last in tables } } return egs
}
// Returns player's expected gains per unit staked, for each dealer up-card, after hitting once and // then either standing (once == true) or continuing as per the round >= 2 tables (once == false). func hit(card1, card2 int, once bool) [10]float64 {
deck := NewDeck() deck[card1]-- deck[card2]-- deck[0] = 50 var egs [10]float64 // results for uc := 1; uc < 11; uc++ { // dealer's up-card deck2 := deck deck2[uc]-- deck2[0] = 49 var peg float64 // player's expected gain for this up-card if once { peg = playerGain(card1, card2, uc, deck2) } else { peg = playerGain2(card1, card2, uc, deck2) } if uc > 1 { egs[uc-2] = peg } else { // dealer has Ace egs[9] = peg } } return egs
}
// Returns player's expected gains per unit oiginally staked, for each dealer up-card, after // doubling i.e. hitting once and then standing with a doubled stake. func double(card1, card2 int) [10]float64 {
egs := hit(card1, card2, true) // hit once and then stand for i := 0; i < 10; i++ { egs[i] *= 2 } return egs
}
// Returns player's expected gains per unit originally staked, for each dealer up-card, after // splitting a pair and doubling the stake, getting a second card for each hand and then continuing // in accordace with the rounds >= 2 tables. It is assumed that a player cannot double or re-split // following a split. It is also assumed (in the interests of simplicity) that the expected gains // for each split hand (after calculating the gains for the first hand as though the second hand // is not completed) are exactly the same. func split(card int) [10]float64 {
deck := NewDeck() deck[card] -= 2 // must be a pair deck[0] = 50 var egs [10]float64 // overall results
// now play a single hand score := card eleven := 0 if card == 1 { // an ace score = 11 eleven = 1 } for uc := 1; uc < 11; uc++ { // collect results for each dealer up-card if deck[uc] == 0 { continue // card no longer present in deck } deck2 := deck deck2[uc]-- deck2[0]-- ix := uc - 2 if ix == -1 { ix = 9 // in tables ace comes last } var peg float64 // player expected gain for this up-card // get second player card for c := 1; c < 11; c++ { if deck2[c] == 0 { continue // card no longer present in deck } prob := float64(deck2[c]) / float64(deck2[0]) deck3 := deck2 deck3[c]-- deck3[0]-- score2 := score + c eleven2 := eleven if c == 1 { // score all aces initially as 11 score2 += 10 eleven2++ } if score2 == 21 { // player has blackjack & we know dealer hasn't peg += 1.5 * prob continue } if score2 > 21 && eleven2 > 0 { score2 -= 10 // bust but can demote an ace eleven2-- } var action string if eleven2 > 0 { action = sTable2[score2-12][ix] // use soft strategy table, no doubling } else { // including pairs as no re-splitting action = hTable2[score2-4][ix] // use hard strategy table, no doubling } var peg2 float64 if action == "S" { dp := dealerProbs(uc, deck3) peg2 = calcGain(score2, dp) } else { peg2 = playerGain2(card, c, uc, deck3) } peg += peg2 * prob } if uc > 1 { egs[uc-2] = peg * 2 // allow for both hands in overall results } else { egs[9] = peg * 2 // ditto } } return egs
}
// Returns the action with the highest expected gain. func bestAction(ags []ActionGain) string {
max := ags[0].gain maxi := 0 for i := 1; i < len(ags); i++ { if ags[i].gain > max { max = ags[i].gain maxi = i } } return ags[maxi].action
}
// Prints title and header for a given chart. func printHeader(title string) {
fmt.Println(title) fmt.Println("P/D 2 3 4 5 6 7 8 9 T A") fmt.Println("--------------------------------------------------------------------------")
}
// Prints header for a pair of cards. func printPair(c int) {
if c == 1 { fmt.Print("AA ") } else if c == 10 { fmt.Print("TT ") } else { fmt.Printf("%d%d ", c, c) }
}
// Computed strategy tables. var (
hTable = [15][10]string{} // hard strategy table (round 1) sTable = [8][10]string{} // soft strategy table (round 1) pTable = [10][10]string{} // pairs strategy table (round 1) hTable2 = [18][10]string{} // hard strategy table (round >= 2, no doubling) sTable2 = [10][10]string{} // soft strategy table (round >= 2, no doubling)
)
// Simulates 'perDay' blackjack games for 'days' days. func simulate(perDay, days int) {
winDays, loseDays, evenDays := 0, 0, 0 bigWin, bigLoss := 0.0, 0.0 totalGain, totalStake := 0.0, 0.0 for d := 1; d <= days; d++ { dailyGain, dailyStake := 0.0, 0.0 for p := 1; p <= perDay; p++ { gain, stake := playerPlay() dailyGain += gain dailyStake += stake } if dailyGain > 0 { winDays++ } else if dailyGain < 0 { loseDays++ } else { evenDays++ } if dailyGain > bigWin { bigWin = dailyGain } else if -dailyGain > bigLoss { bigLoss = -dailyGain } totalGain += dailyGain totalStake += dailyStake } fmt.Printf("\nAfter playing %d times a day for %d days:\n", perDay, days) fmt.Println("Winning days :", winDays) fmt.Println("Losing days :", loseDays) fmt.Println("Breakeven days :", evenDays) fmt.Println("Biggest win :", bigWin) fmt.Println("Biggest loss :", bigLoss) if totalGain < 0 { fmt.Println("Total loss :", -totalGain) fmt.Println("Total staked :", totalStake) fmt.Printf("Loss %% staked : %0.3f\n", -totalGain/totalStake*100) } else { fmt.Println("Total win :", totalGain) fmt.Println("Total staked :", totalStake) fmt.Printf("Win %% staked : %0.3f\n", totalGain/totalStake*100) }
}
// Simulates a dealer's play for a given player's hand and state of deck. // Returns the player's gain (positive or negative) per unit staked. func dealerPlay(pscore int, next *int, cards, d []int) float64 {
dscore := d[0] + d[1] aces := 0 if d[0] == 1 || d[1] == 1 { // dealer has an ace dscore += 10 aces++ } for { if dscore > 21 && aces > 0 { dscore -= 10 // bust but we can demote an ace aces-- } if dscore > 21 { return 1 // dealer is bust and player gains stake } if dscore >= 17 { // dealer must stick on 17 or above, hard or not if dscore > pscore { return -1 // dealer wins and player loses stake } else if dscore == pscore { break // player breaks even } else { return 1 // dealer loses and player gains stake } } nc := cards[*next] // get new card from pack *next++ dscore += nc if nc == 1 { // count aces initially as 11 dscore += 10 aces++ } } return 0
}
// Simulates the playing of a random player's hand according to the strategy tables. // Returns both the gain (positive or negative) and the stake (1 or 2). func playerPlay() (float64, float64) {
perm := rand.Perm(52) // randomizes integers from 0 to 51 inclusive cards := make([]int, 52) for i, r := range perm { card := r/4 + 1 if card > 10 { card = 10 } cards[i] = card } var p, d []int // player and dealer hands // initial deal for i, card := range cards[0:4] { if i < 2 { p = append(p, card) } else { d = append(d, card) } } next := 4 // index of next card to be dealt
// check if dealer and/or player have blackjack dbj := (d[0] == 1 && d[1] == 10) || (d[0] == 10 && d[1] == 1) pbj := (p[0] == 1 && p[1] == 10) || (p[0] == 10 && p[1] == 1) if dbj { if pbj { return 0.0, 1.0 // player neither wins nor loses } return -1.0, 1.0 // player loses stake } if pbj { return 1.5, 1.0 // player wins 1.5 x stake }
uc := d[0] // dealer's up-card for accessing tables if uc == 0 { uc = 9 // move ace to last place } else { uc-- // move others down 1 } stake := 1.0 // player's initial stake var fscores [2]int // final player scores (one or, after split, two hands) var action string var score, aces int
h := func(hand int) { // processes a 'hit' for { nc := cards[next] // get new card from pack next++ score += nc if nc == 1 { // count aces initially as 11 score += 10 aces++ } if score > 21 && aces > 0 { score -= 10 // bust but we can demote an ace aces-- } if score > 21 { fscores[hand] = 22 // player is bust and loses stake return } if action == "D" { fscores[hand] = score return } // get further strategy and act accordingly if aces == 0 { action = hTable2[score-4][uc] } else { action = sTable2[score-12][uc] } if action == "S" { // stand fscores[hand] = score return } } }
score = p[0] + p[1] // get kind of player hand: hard, soft, pair var kind string if p[0] == p[1] { kind = "pair" } else if p[0] == 1 || p[1] == 1 { kind = "soft" } else { kind = "hard" } switch kind { case "hard": action = hTable[score-5][uc] case "soft": // includes one ace otherCard := p[0] if otherCard == 1 { otherCard = p[1] } score += 10 aces = 1 action = sTable[otherCard-2][uc] case "pair": if p[0] == 1 { // pair of aces score += 10 aces = 2 } action = pTable[p[0]-1][uc] } switch action { case "S": // stand fscores[0] = score case "H": // hit h(0) case "D": // double h(0) stake = 2 case "P": // split for hand := 0; hand < 2; hand++ { score = p[0] aces = 0 if score == 1 { // count aces initially as 11 score = 11 aces++ } h(hand) } } sum := 0.0 if fscores[0] < 22 { sum += dealerPlay(fscores[0], &next, cards, d) * stake } else { sum -= 1 * stake // this hand is bust } if fscores[1] > 0 { // pair if fscores[1] < 22 { sum += dealerPlay(fscores[1], &next, cards, d) } else { sum -= 1 // this hand is bust } stake = 2 } return sum, stake
}
func main() {
// print dealer probabilities chart dealerChart()
// for hard scores (i.e. different cards, no aces) tuples := [][2]int{ {2, 3}, {2, 4}, {2, 5}, {3, 4}, {2, 6}, {3, 5}, {2, 7}, {3, 6}, {4, 5}, {2, 8}, {3, 7}, {4, 6}, {2, 9}, {3, 8}, {4, 7}, {5, 6}, {2, 10}, {3, 9}, {4, 8}, {5, 7}, {3, 10}, {4, 9}, {5, 8}, {6, 7}, {4, 10}, {5, 9}, {6, 8}, {5, 10}, {6, 9}, {7, 8}, {6, 10}, {7, 9}, {7, 10}, {8, 9}, {8, 10}, {9, 10}, } // number of tuples for each player score from 5 to 19 counts := [15]float64{1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1} // expected gains for each player score & for each dealer up-card segs := [15][10]float64{} // if stands hegs := [15][10]float64{} // if hits degs := [15][10]float64{} // if doubles for _, tuple := range tuples { i := tuple[0] + tuple[1] sg := stand(tuple[0], tuple[1]) hg := hit(tuple[0], tuple[1], false) dg := double(tuple[0], tuple[1]) for j := 0; j < 10; j++ { segs[i-5][j] += sg[j] hegs[i-5][j] += hg[j] degs[i-5][j] += dg[j] } } // calculate the average per tuple for each score for i := 0; i < 15; i++ { for j := 0; j < 10; j++ { segs[i][j] /= counts[i] hegs[i][j] /= counts[i] degs[i][j] /= counts[i] } }
printHeader("\nHard Chart - Player Expected Gains per unit (Stand)") for i := 5; i < 20; i++ { fmt.Printf("%2d ", i) for j := 0; j < 10; j++ { fmt.Printf("% 0.3f ", segs[i-5][j]) } fmt.Println() }
printHeader("\nHard Chart - Player Expected Gains per unit (Hit)") for i := 5; i < 20; i++ { fmt.Printf("%2d ", i) for j := 0; j < 10; j++ { fmt.Printf("% 0.3f ", hegs[i-5][j]) } fmt.Println() }
printHeader("\nHard Chart - Player Expected Gains per original unit (Double)") for i := 5; i < 20; i++ { fmt.Printf("%2d ", i) for j := 0; j < 10; j++ { fmt.Printf("% 0.3f ", degs[i-5][j]) } fmt.Println() }
printHeader("\nHard Chart - Player Strategy (Round 1)") for i := 5; i < 20; i++ { fmt.Printf("%2d ", i) for j := 0; j < 10; j++ { ags := []ActionGain{{"S", segs[i-5][j]}, {"H", hegs[i-5][j]}, {"D", degs[i-5][j]}} action := bestAction(ags) hTable[i-5][j] = action fmt.Printf("%4s ", action) } fmt.Println() }
// for hard scores (no aces) - after round 1 (no doubling or splitting) // based on hard table figures (round 1) with scores of 4, 20, and 21 added segs2 := [18][10]float64{} // expected gains if stands hegs2 := [18][10]float64{} // expected gains if hits for i := 5; i < 20; i++ { segs2[i-4] = segs[i-5] hegs2[i-4] = hegs[i-5] } sg4, hg4 := stand(2, 2), hit(2, 2, false) sg20, hg20 := stand(10, 10), hit(10, 10, false) sg21, hg21 := stand(1, 10), hit(1, 10, false) for j := 0; j < 10; j++ { segs2[0][j] += sg4[j] hegs2[0][j] += hg4[j] segs2[16][j] += sg20[j] hegs2[16][j] += hg20[j] segs2[17][j] += sg21[j] hegs2[17][j] += hg21[j] }
printHeader("\nHard Chart - Player Strategy (Round >= 2, No Doubling)") for i := 4; i < 22; i++ { fmt.Printf("%2d ", i) for j := 0; j < 10; j++ { action := "S" if hegs2[i-4][j] > segs2[i-4][j] { action = "H" } hTable2[i-4][j] = action fmt.Printf("%4s ", action) } fmt.Println() }
// for soft scores (i.e. including exactly one ace)
// expected gains for each player second card (2 to 9) & for each dealer up-card segs3 := [8][10]float64{} // if stands hegs3 := [8][10]float64{} // if hits degs3 := [8][10]float64{} // if doubles for c := 2; c < 10; c++ { sg := stand(1, c) hg := hit(1, c, false) dg := double(1, c) for j := 0; j < 10; j++ { segs3[c-2][j] += sg[j] hegs3[c-2][j] += hg[j] degs3[c-2][j] += dg[j] } }
printHeader("\nSoft Chart - Player Expected Gains per unit (Stand)") for c := 2; c < 10; c++ { fmt.Printf("A%d ", c) for j := 0; j < 10; j++ { fmt.Printf("% 0.3f ", segs3[c-2][j]) } fmt.Println() }
printHeader("\nSoft Chart - Player Expected Gains per unit (Hit)") for c := 2; c < 10; c++ { fmt.Printf("A%d ", c) for j := 0; j < 10; j++ { fmt.Printf("% 0.3f ", hegs3[c-2][j]) } fmt.Println() }
printHeader("\nSoft Chart - Player Expected Gains per original unit (Double)") for c := 2; c < 10; c++ { fmt.Printf("A%d ", c) for j := 0; j < 10; j++ { fmt.Printf("% 0.3f ", degs3[c-2][j]) } fmt.Println() }
printHeader("\nSoft Chart - Player Strategy (Round 1)") for c := 2; c < 10; c++ { fmt.Printf("A%d ", c) for j := 0; j < 10; j++ { ags := []ActionGain{{"S", segs3[c-2][j]}, {"H", hegs3[c-2][j]}, {"D", degs3[c-2][j]}} action := bestAction(ags) sTable[c-2][j] = action fmt.Printf("%4s ", action) } fmt.Println() }
// for soft scores (at least one ace) - after round 1 (no doubling or splitting) // based on soft table figures (round 1) with scores of 12 and 21 added // assumes one ace counted as 11 segs4 := [10][10]float64{} // expected gains if stands hegs4 := [10][10]float64{} // expected gains if hits for i := 1; i < 9; i++ { segs4[i] = segs3[i-1] hegs4[i] = hegs3[i-1] } sg12, hg12 := stand(1, 1), hit(1, 1, false) for j := 0; j < 10; j++ { segs4[0][j] += sg12[j] hegs4[0][j] += hg12[j] segs4[9][j] += sg21[j] hegs4[9][j] += hg21[j] }
printHeader("\nSoft Chart - Player Strategy (Round >= 2, No Doubling)") for i := 12; i < 22; i++ { fmt.Printf("%2d ", i) for j := 0; j < 10; j++ { action := "S" if hegs4[i-12][j] > segs4[i-12][j] { action = "H" } sTable2[i-12][j] = action fmt.Printf("%4s ", action) } fmt.Println() }
// for pairs
// expected gains for each pair (A to 10) & for each dealer up-card segs5 := [10][10]float64{} // if stands hegs5 := [10][10]float64{} // if hits degs5 := [10][10]float64{} // if doubles pegs5 := [10][10]float64{} // if splits for c := 1; c < 11; c++ { sg := stand(c, c) hg := hit(c, c, false) dg := double(c, c) pg := split(c) for j := 0; j < 10; j++ { segs5[c-1][j] += sg[j] hegs5[c-1][j] += hg[j] degs5[c-1][j] += dg[j] pegs5[c-1][j] += pg[j] } }
printHeader("\nPairs Chart - Player Expected Gains per unit (Stand)") for c := 1; c < 11; c++ { printPair(c) for j := 0; j < 10; j++ { fmt.Printf("% 0.3f ", segs5[c-1][j]) } fmt.Println() }
printHeader("\nPairs Chart - Player Expected Gains per unit (Hit)") for c := 1; c < 11; c++ { printPair(c) for j := 0; j < 10; j++ { fmt.Printf("% 0.3f ", hegs5[c-1][j]) } fmt.Println() }
printHeader("\nPairs Chart - Player Expected Gains per original unit (Double)") for c := 1; c < 11; c++ { printPair(c) for j := 0; j < 10; j++ { fmt.Printf("% 0.3f ", degs5[c-1][j]) } fmt.Println() }
printHeader("\nPairs Chart - Player Expected Gains per original unit (Split)") for c := 1; c < 11; c++ { printPair(c) for j := 0; j < 10; j++ { fmt.Printf("% 0.3f ", pegs5[c-1][j]) } fmt.Println() }
printHeader("\nPairs Chart - Player Strategy (Round 1)") for c := 1; c < 11; c++ { printPair(c) for j := 0; j < 10; j++ { ags := []ActionGain{{"S", segs5[c-1][j]}, {"H", hegs5[c-1][j]}, {"D", degs5[c-1][j]}, {"P", pegs5[c-1][j]}} action := bestAction(ags) pTable[c-1][j] = action fmt.Printf("%4s ", action) } fmt.Println() } rand.Seed(time.Now().UnixNano()) // do 10 years of simulations for i := 1; i <= 10; i++ { fmt.Printf("\nSimulation for Year %d:\n", i) simulate(50, 365) }
}</lang>
- Output:
Dealer Probabilities, Stands on Soft 17, 1 Deck, U.S Rules Up Card 17 18 19 20 21 Bust ------------------------------------------------------------------- Ace 0.183786 0.190890 0.188680 0.191692 0.075137 0.169815 2 0.138976 0.131762 0.131815 0.123948 0.120526 0.352973 3 0.130313 0.130946 0.123761 0.123345 0.116047 0.375588 4 0.130973 0.114163 0.120679 0.116286 0.115096 0.402803 5 0.119687 0.123483 0.116909 0.104694 0.106321 0.428905 6 0.166948 0.106454 0.107192 0.100705 0.097878 0.420823 7 0.372345 0.138583 0.077334 0.078897 0.072987 0.259854 8 0.130857 0.362989 0.129445 0.068290 0.069791 0.238627 9 0.121886 0.103921 0.357391 0.122250 0.061109 0.233442 10 0.124156 0.122486 0.124421 0.356869 0.039570 0.232499 Hard Chart - Player Expected Gains per unit (Stand) P/D 2 3 4 5 6 7 8 9 T A -------------------------------------------------------------------------- 5 -0.293 -0.248 -0.176 -0.104 -0.122 -0.469 -0.513 -0.533 -0.546 -0.659 6 -0.291 -0.232 -0.172 -0.101 -0.119 -0.467 -0.522 -0.533 -0.547 -0.659 7 -0.283 -0.229 -0.163 -0.098 -0.117 -0.471 -0.521 -0.537 -0.547 -0.658 8 -0.276 -0.229 -0.162 -0.100 -0.130 -0.478 -0.523 -0.539 -0.549 -0.648 9 -0.277 -0.224 -0.160 -0.108 -0.134 -0.480 -0.528 -0.543 -0.542 -0.646 10 -0.279 -0.227 -0.172 -0.120 -0.146 -0.484 -0.531 -0.539 -0.537 -0.644 11 -0.277 -0.231 -0.175 -0.123 -0.147 -0.488 -0.529 -0.537 -0.537 -0.646 12 -0.286 -0.241 -0.185 -0.134 -0.151 -0.485 -0.526 -0.535 -0.533 -0.655 13 -0.282 -0.236 -0.181 -0.133 -0.156 -0.488 -0.529 -0.537 -0.534 -0.649 14 -0.282 -0.238 -0.188 -0.134 -0.159 -0.489 -0.529 -0.533 -0.536 -0.651 15 -0.280 -0.239 -0.190 -0.144 -0.169 -0.494 -0.531 -0.536 -0.531 -0.648 16 -0.287 -0.250 -0.194 -0.152 -0.179 -0.495 -0.526 -0.540 -0.530 -0.648 17 -0.147 -0.120 -0.074 -0.044 -0.011 -0.122 -0.405 -0.414 -0.402 -0.459 18 0.119 0.144 0.164 0.202 0.268 0.389 0.096 -0.196 -0.155 -0.082 19 0.385 0.384 0.404 0.448 0.484 0.610 0.577 0.264 0.103 0.308 Hard Chart - Player Expected Gains per unit (Hit) P/D 2 3 4 5 6 7 8 9 T A -------------------------------------------------------------------------- 5 -0.131 -0.098 -0.041 0.022 0.019 -0.119 -0.181 -0.262 -0.309 -0.417 6 -0.151 -0.107 -0.055 0.009 0.014 -0.164 -0.234 -0.305 -0.349 -0.443 7 -0.111 -0.072 -0.013 0.053 0.064 -0.069 -0.223 -0.295 -0.332 -0.401 8 -0.015 0.021 0.084 0.136 0.148 0.092 -0.056 -0.213 -0.253 -0.275 9 0.090 0.137 0.181 0.226 0.235 0.194 0.111 -0.052 -0.148 -0.128 10 0.215 0.246 0.277 0.314 0.319 0.277 0.211 0.119 0.030 0.030 11 0.272 0.296 0.327 0.361 0.362 0.293 0.222 0.146 0.107 0.113 12 -0.256 -0.232 -0.206 -0.181 -0.179 -0.241 -0.308 -0.380 -0.378 -0.413 13 -0.315 -0.293 -0.270 -0.252 -0.251 -0.301 -0.362 -0.389 -0.423 -0.440 14 -0.363 -0.353 -0.337 -0.315 -0.313 -0.346 -0.366 -0.426 -0.455 -0.460 15 -0.419 -0.414 -0.406 -0.392 -0.383 -0.351 -0.406 -0.466 -0.496 -0.487 16 -0.461 -0.460 -0.454 -0.448 -0.397 -0.376 -0.426 -0.481 -0.510 -0.497 17 -0.534 -0.536 -0.538 -0.493 -0.484 -0.450 -0.475 -0.529 -0.558 -0.546 18 -0.633 -0.634 -0.597 -0.591 -0.586 -0.567 -0.565 -0.593 -0.624 -0.630 19 -0.750 -0.713 -0.712 -0.709 -0.707 -0.699 -0.697 -0.698 -0.712 -0.740 Hard Chart - Player Expected Gains per original unit (Double) P/D 2 3 4 5 6 7 8 9 T A -------------------------------------------------------------------------- 5 -0.587 -0.497 -0.352 -0.209 -0.244 -0.938 -1.025 -1.066 -1.093 -1.318 6 -0.560 -0.446 -0.324 -0.186 -0.215 -0.870 -1.023 -1.045 -1.074 -1.295 7 -0.415 -0.317 -0.186 -0.066 -0.059 -0.555 -0.851 -0.936 -0.956 -1.127 8 -0.165 -0.081 0.032 0.143 0.157 -0.140 -0.433 -0.697 -0.743 -0.802 9 0.114 0.193 0.286 0.380 0.393 0.175 0.007 -0.281 -0.442 -0.409 10 0.428 0.492 0.554 0.628 0.638 0.446 0.313 0.164 0.007 0.025 11 0.542 0.592 0.654 0.722 0.724 0.479 0.341 0.223 0.164 0.198 12 -0.511 -0.463 -0.413 -0.362 -0.358 -0.556 -0.690 -0.811 -0.789 -0.827 13 -0.630 -0.587 -0.541 -0.503 -0.503 -0.651 -0.775 -0.807 -0.862 -0.880 14 -0.727 -0.706 -0.673 -0.630 -0.627 -0.723 -0.759 -0.862 -0.915 -0.921 15 -0.838 -0.829 -0.812 -0.783 -0.767 -0.716 -0.826 -0.937 -0.992 -0.973 16 -0.921 -0.920 -0.908 -0.896 -0.793 -0.751 -0.853 -0.961 -1.019 -0.995 17 -1.069 -1.072 -1.076 -0.985 -0.967 -0.901 -0.949 -1.058 -1.116 -1.092 18 -1.265 -1.267 -1.195 -1.182 -1.172 -1.135 -1.130 -1.186 -1.248 -1.260 19 -1.499 -1.425 -1.423 -1.417 -1.414 -1.397 -1.395 -1.396 -1.425 -1.481 Hard Chart - Player Strategy (Round 1) P/D 2 3 4 5 6 7 8 9 T A -------------------------------------------------------------------------- 5 H H H H H H H H H H 6 H H H H H H H H H H 7 H H H H H H H H H H 8 H H H D D H H H H H 9 D D D D D H H H H H 10 D D D D D D D D H H 11 D D D D D D D D D D 12 H H S S S H H H H H 13 S S S S S H H H H H 14 S S S S S H H H H H 15 S S S S S H H H H H 16 S S S S S H H H H H 17 S S S S S S S S S S 18 S S S S S S S S S S 19 S S S S S S S S S S Hard Chart - Player Strategy (Round >= 2, No Doubling) P/D 2 3 4 5 6 7 8 9 T A -------------------------------------------------------------------------- 4 H H H H H H H H H H 5 H H H H H H H H H H 6 H H H H H H H H H H 7 H H H H H H H H H H 8 H H H H H H H H H H 9 H H H H H H H H H H 10 H H H H H H H H H H 11 H H H H H H H H H H 12 H H S S S H H H H H 13 S S S S S H H H H H 14 S S S S S H H H H H 15 S S S S S H H H H H 16 S S S S S H H H H H 17 S S S S S S S S S S 18 S S S S S S S S S S 19 S S S S S S S S S S 20 S S S S S S S S S S 21 S S S S S S S S S S Soft Chart - Player Expected Gains per unit (Stand) P/D 2 3 4 5 6 7 8 9 T A -------------------------------------------------------------------------- A2 -0.283 -0.241 -0.186 -0.119 -0.114 -0.462 -0.508 -0.517 -0.539 -0.662 A3 -0.284 -0.240 -0.170 -0.116 -0.112 -0.460 -0.505 -0.527 -0.538 -0.661 A4 -0.283 -0.224 -0.166 -0.113 -0.109 -0.458 -0.514 -0.526 -0.538 -0.659 A5 -0.266 -0.221 -0.164 -0.111 -0.108 -0.468 -0.515 -0.525 -0.537 -0.659 A6 -0.132 -0.093 -0.037 0.005 0.010 -0.090 -0.385 -0.407 -0.418 -0.483 A7 0.136 0.167 0.204 0.222 0.262 0.412 0.121 -0.179 -0.186 -0.101 A8 0.402 0.420 0.415 0.461 0.482 0.615 0.608 0.288 0.064 0.290 A9 0.656 0.644 0.654 0.682 0.694 0.773 0.785 0.766 0.555 0.681 Soft Chart - Player Expected Gains per unit (Hit) P/D 2 3 4 5 6 7 8 9 T A -------------------------------------------------------------------------- A2 0.039 0.071 0.110 0.159 0.168 0.107 0.039 -0.014 -0.090 -0.184 A3 0.017 0.044 0.091 0.137 0.147 0.060 0.035 -0.060 -0.124 -0.216 A4 -0.012 0.022 0.061 0.108 0.120 0.034 -0.035 -0.114 -0.172 -0.256 A5 -0.032 -0.003 0.038 0.082 0.116 -0.024 -0.084 -0.167 -0.229 -0.296 A6 0.007 0.036 0.077 0.140 0.133 0.060 -0.065 -0.135 -0.189 -0.242 A7 0.065 0.093 0.156 0.175 0.192 0.175 0.047 -0.087 -0.140 -0.160 A8 0.120 0.173 0.187 0.227 0.241 0.222 0.158 0.005 -0.087 -0.081 A9 0.191 0.196 0.230 0.268 0.280 0.243 0.172 0.096 0.007 -0.008 Soft Chart - Player Expected Gains per original unit (Double) P/D 2 3 4 5 6 7 8 9 T A -------------------------------------------------------------------------- A2 -0.042 0.028 0.115 0.212 0.230 -0.157 -0.312 -0.373 -0.478 -0.586 A3 -0.047 0.011 0.109 0.204 0.222 -0.175 -0.254 -0.394 -0.479 -0.588 A4 -0.070 0.003 0.085 0.175 0.201 -0.141 -0.314 -0.422 -0.495 -0.613 A5 -0.082 -0.019 0.063 0.148 0.217 -0.189 -0.333 -0.452 -0.536 -0.649 A6 0.013 0.074 0.155 0.280 0.266 0.014 -0.230 -0.345 -0.433 -0.522 A7 0.128 0.189 0.313 0.349 0.385 0.240 -0.015 -0.254 -0.322 -0.359 A8 0.237 0.346 0.373 0.453 0.483 0.325 0.190 -0.060 -0.226 -0.200 A9 0.380 0.392 0.459 0.536 0.560 0.351 0.230 0.111 -0.055 -0.055 Soft Chart - Player Strategy (Round 1) P/D 2 3 4 5 6 7 8 9 T A -------------------------------------------------------------------------- A2 H H D D D H H H H H A3 H H D D D H H H H H A4 H H D D D H H H H H A5 H H D D D H H H H H A6 D D D D D H H H H H A7 S D D D D S S H H S A8 S S S S D S S S S S A9 S S S S S S S S S S Soft Chart - Player Strategy (Round >= 2, No Doubling) P/D 2 3 4 5 6 7 8 9 T A -------------------------------------------------------------------------- 12 H H H H H H H H H H 13 H H H H H H H H H H 14 H H H H H H H H H H 15 H H H H H H H H H H 16 H H H H H H H H H H 17 H H H H H H H H H H 18 S S S S S S S H H S 19 S S S S S S S S S S 20 S S S S S S S S S S 21 S S S S S S S S S S Pairs Chart - Player Expected Gains per unit (Stand) P/D 2 3 4 5 6 7 8 9 T A -------------------------------------------------------------------------- AA -0.274 -0.232 -0.178 -0.130 -0.104 -0.452 -0.500 -0.511 -0.531 -0.663 22 -0.291 -0.251 -0.192 -0.107 -0.125 -0.471 -0.515 -0.523 -0.547 -0.660 33 -0.295 -0.246 -0.160 -0.101 -0.119 -0.467 -0.510 -0.542 -0.546 -0.660 44 -0.290 -0.214 -0.152 -0.095 -0.114 -0.463 -0.529 -0.543 -0.547 -0.656 55 -0.256 -0.206 -0.146 -0.090 -0.112 -0.484 -0.531 -0.541 -0.545 -0.653 66 -0.262 -0.211 -0.152 -0.102 -0.165 -0.493 -0.536 -0.549 -0.552 -0.617 77 -0.268 -0.219 -0.164 -0.156 -0.174 -0.502 -0.539 -0.555 -0.510 -0.631 88 -0.275 -0.228 -0.215 -0.165 -0.178 -0.503 -0.551 -0.516 -0.518 -0.644 99 0.137 0.123 0.167 0.203 0.265 0.401 0.065 -0.196 -0.133 -0.055 TT 0.627 0.636 0.645 0.674 0.697 0.765 0.783 0.744 0.583 0.650 Pairs Chart - Player Expected Gains per unit (Hit) P/D 2 3 4 5 6 7 8 9 T A -------------------------------------------------------------------------- AA 0.095 0.120 0.142 0.182 0.200 0.158 0.093 -0.003 -0.048 -0.075 22 -0.113 -0.082 -0.035 0.036 0.032 -0.092 -0.141 -0.222 -0.277 -0.395 33 -0.153 -0.118 -0.047 0.008 0.014 -0.164 -0.231 -0.310 -0.346 -0.444 44 -0.013 0.028 0.098 0.154 0.175 0.111 -0.055 -0.206 -0.246 -0.268 55 0.224 0.254 0.295 0.347 0.362 0.279 0.207 0.119 0.032 0.042 66 -0.253 -0.222 -0.190 -0.162 -0.194 -0.265 -0.322 -0.386 -0.386 -0.411 77 -0.406 -0.388 -0.369 -0.370 -0.367 -0.389 -0.408 -0.475 -0.516 -0.510 88 -0.454 -0.450 -0.461 -0.453 -0.397 -0.374 -0.426 -0.487 -0.512 -0.490 99 -0.627 -0.638 -0.597 -0.590 -0.587 -0.566 -0.566 -0.595 -0.626 -0.621 TT -0.847 -0.846 -0.846 -0.846 -0.845 -0.843 -0.843 -0.842 -0.840 -0.882 Pairs Chart - Player Expected Gains per original unit (Double) P/D 2 3 4 5 6 7 8 9 T A -------------------------------------------------------------------------- AA -0.019 0.055 0.137 0.216 0.248 -0.137 -0.296 -0.421 -0.468 -0.591 22 -0.582 -0.501 -0.384 -0.214 -0.249 -0.942 -1.030 -1.047 -1.094 -1.320 33 -0.567 -0.472 -0.302 -0.184 -0.215 -0.871 -1.000 -1.065 -1.072 -1.298 44 -0.185 -0.082 0.044 0.162 0.193 -0.108 -0.447 -0.701 -0.741 -0.802 55 0.446 0.510 0.590 0.695 0.724 0.466 0.323 0.175 0.014 0.042 66 -0.505 -0.444 -0.380 -0.325 -0.387 -0.599 -0.711 -0.817 -0.803 -0.823 77 -0.813 -0.777 -0.738 -0.741 -0.734 -0.823 -0.858 -0.978 -1.035 -1.019 88 -0.908 -0.900 -0.922 -0.906 -0.793 -0.747 -0.853 -0.974 -1.024 -0.980 99 -1.255 -1.277 -1.194 -1.181 -1.173 -1.132 -1.133 -1.189 -1.252 -1.242 TT -1.693 -1.693 -1.693 -1.691 -1.690 -1.686 -1.685 -1.684 -1.681 -1.764 Pairs Chart - Player Expected Gains per original unit (Split) P/D 2 3 4 5 6 7 8 9 T A -------------------------------------------------------------------------- AA 1.192 1.223 1.265 1.321 1.344 1.308 1.201 1.039 0.860 0.921 22 -0.128 -0.070 -0.007 0.128 0.126 -0.054 -0.213 -0.383 -0.463 -0.566 33 -0.202 -0.128 0.009 0.117 0.112 -0.115 -0.265 -0.418 -0.509 -0.579 44 -0.236 -0.127 -0.013 0.095 0.083 -0.223 -0.343 -0.493 -0.580 -0.623 55 -0.232 -0.150 -0.038 0.068 0.056 -0.299 -0.448 -0.608 -0.685 -0.695 66 -0.219 -0.135 -0.028 0.068 -0.011 -0.270 -0.413 -0.570 -0.652 -0.660 77 -0.163 -0.084 0.016 0.039 0.053 -0.123 -0.423 -0.564 -0.634 -0.635 88 0.017 0.077 0.106 0.188 0.234 0.202 -0.100 -0.430 -0.464 -0.378 99 0.170 0.170 0.253 0.339 0.359 0.341 0.179 -0.112 -0.268 -0.109 TT 0.412 0.465 0.518 0.596 0.619 0.576 0.447 0.276 0.146 0.140 Pairs Chart - Player Strategy (Round 1) P/D 2 3 4 5 6 7 8 9 T A -------------------------------------------------------------------------- AA P P P P P P P P P P 22 H P P P P P H H H H 33 H H P P P P H H H H 44 H H H D D H H H H H 55 D D D D D D D D H D 66 P P P P P H H H H H 77 P P P P P P H H S H 88 P P P P P P P P P P 99 P P P P P S P P S S TT S S S S S S S S S S Simulation for Year 1: After playing 50 times a day for 365 days: Winning days : 170 Losing days : 185 Breakeven days : 10 Biggest win : 20 Biggest loss : 20.5 Total loss : 263 Total staked : 20498 Loss % staked : 1.283 Simulation for Year 2: After playing 50 times a day for 365 days: Winning days : 171 Losing days : 184 Breakeven days : 10 Biggest win : 18.5 Biggest loss : 22.5 Total loss : 332.5 Total staked : 20515 Loss % staked : 1.621 Simulation for Year 3: After playing 50 times a day for 365 days: Winning days : 154 Losing days : 204 Breakeven days : 7 Biggest win : 28 Biggest loss : 24 Total loss : 339.5 Total staked : 20461 Loss % staked : 1.659 Simulation for Year 4: After playing 50 times a day for 365 days: Winning days : 164 Losing days : 191 Breakeven days : 10 Biggest win : 26.5 Biggest loss : 26.5 Total loss : 211.5 Total staked : 20587 Loss % staked : 1.027 Simulation for Year 5: After playing 50 times a day for 365 days: Winning days : 175 Losing days : 186 Breakeven days : 4 Biggest win : 18 Biggest loss : 21.5 Total loss : 162 Total staked : 20493 Loss % staked : 0.791 Simulation for Year 6: After playing 50 times a day for 365 days: Winning days : 179 Losing days : 177 Breakeven days : 9 Biggest win : 25.5 Biggest loss : 26 Total win : 55.5 Total staked : 20495 Win % staked : 0.271 Simulation for Year 7: After playing 50 times a day for 365 days: Winning days : 162 Losing days : 190 Breakeven days : 13 Biggest win : 26.5 Biggest loss : 27 Total loss : 274 Total staked : 20545 Loss % staked : 1.334 Simulation for Year 8: After playing 50 times a day for 365 days: Winning days : 165 Losing days : 192 Breakeven days : 8 Biggest win : 21 Biggest loss : 25.5 Total loss : 329 Total staked : 20536 Loss % staked : 1.602 Simulation for Year 9: After playing 50 times a day for 365 days: Winning days : 169 Losing days : 186 Breakeven days : 10 Biggest win : 18.5 Biggest loss : 26.5 Total loss : 241 Total staked : 20549 Loss % staked : 1.173 Simulation for Year 10: After playing 50 times a day for 365 days: Winning days : 173 Losing days : 183 Breakeven days : 9 Biggest win : 23 Biggest loss : 19 Total loss : 370 Total staked : 20541 Loss % staked : 1.801
Nim
<lang Nim>import random, sequtils, strformat, strutils
type
Card = 1..10
Deck = object size: int cards: array[Card, int]
HandKind {.pure.} = enum Hard, Soft, Pair
Action {.pure.} = enum Stand, Hit, Double, Split
ActionGain = tuple[action: Action; gain: float]
var
# Computed strategy tables. hTable: array[15, array[10, Action]] # Hard strategy table (round 1). sTable: array[8, array[10, Action]] # Soft strategy table (round 1). pTable: array[10, array[10, Action]] # Pairs strategy table (round 1). hTable2: array[18, array[10, Action]] # Hard strategy table (round >= 2, no doubling). sTable2: array[10, array[10, Action]] # Soft strategy table (round >= 2, no doubling).
func initDeck(): Deck =
Deck(size: 52, cards: [4, 4, 4, 4, 4, 4, 4, 4, 4, 16])
func `<`(ag1, ag2: ActionGain): bool = ag1.gain < ag2.gain
func dealerProbs(upcard: Card; startDeck: Deck): array[7, float] =
## Returns probabilities of dealer eventually getting: ## 0: 17, 1: 18, 2: 19, 3: 20, 4: 21 (non-blackjack), 5: blackjack (nil), 6: bust. ## It is assumed that the dealer has already checked for blackjack, that one deck is used ## and that the dealer stands on 'soft' 17.
var res: array[7, float] # Results. decks: array[9, Deck] # Decks for each level. scores: array[9, int] # Scores for each level. elevens: array[9, int] # Number of aces for each level scored as 11. probs: array[9, float] # Probabilities for each level.
decks[0] = startDeck scores[0] = upCard if upcard == 1: # An ace. scores[0] = 11 elevens[0] = 1
probs[0] = 1.0
proc drawCard(lev: Natural) = ## Recursive closure. for c in Card.low..Card.high: if decks[lev].cards[c] == 0: continue # Card no longer present in deck.
# Temporary variables for current level. var deck = decks[lev] score = scores[lev] eleven = elevens[lev] prob = probs[lev]
inc score, c # Add card to score. if c == 1: # Score all aces initially as 11. inc score, 10 inc eleven
prob *= deck.cards[c] / deck.size if score > 21 and eleven > 0: dec score, 10 # Bust but can demote an ace. dec eleven
if lev == 0 and (upCard == 1 and c == 10 or upCard == 10 and c == 1): res[5] += prob # Blackjack, allow for now. elif score in 17..21: res[score-17] += prob # 17 to (non-blackjack) 21. elif score > 21 and eleven == 0: res[6] += prob # Bust. else: dec deck.cards[c] # Remove card from deck. dec deck.size let lev = lev + 1 decks[lev] = deck scores[lev] = score elevens[lev] = eleven probs[lev] = prob drawCard(lev)
drawCard(0) # But can't have blackjack, so adjust probabilities accordingly. let pnbj = 1 - res[5] for i in 0..6: res[i] /= pnbj
res[5] = 0 result = res
proc dealerChart =
## Print chart of dealer probabilities (as a check against an external source). echo "Dealer Probabilities, Stands on Soft 17, 1 Deck, U.S Rules" echo "Up Card 17 18 19 20 21 Bust" echo "———————————————————————————————————————————————————————————————————" var deck = initDeck() deck.size = 51 for uc in Card.low..Card.high: var deck2 = deck dec deck2.cards[uc] let dp = dealerProbs(uc, deck2) stdout.write if uc > 1: &"{uc:3} " else: "Ace " for i in [0, 1, 2, 3, 4, 6]: stdout.write &" {dp[i]:.6f}" echo()
func calcGain(pscore: int; dp: openArray[float]): float =
## Calculates gain per unit staked for a given scenario (helper function). case pscore of 17: result += dp[6] # Dealer is bust. result -= dp[1] + dp[2] + dp[3] + dp[4] # Dealer has 18 to 21. of 18: result += dp[0] + dp[6] # Dealer has 17 or is bust. result -= dp[2] + dp[3] + dp[4] # Dealer has 19 to 21. of 19: result += dp[0] + dp[1] + dp[6] # Dealer has 17, 18 or is bust. result -= dp[3] + dp[4] # Dealer has 20 or 21. of 20: result += dp[0] + dp[1] + dp[2] + dp[6] # Dealer has 17 to 19 or is bust. result -= dp[4] # Dealer has (non-blackjack) 21. of 21: result += dp[0] + dp[1] + dp[2] + dp[3] + dp[6] # Dealer has 17 to 20 or is bust. of 22: # Notional. result += 1.5 # Player blackjack. of 23: # Notional. result -= 1 # Player bust, loses stake irrespective of what dealer has. else: # Player has less than 17 result += dp[6] # Dealer is bust. result -= 1 - dp[6] # Dealer isn't bust.
func playerGain(card1, card2, uc: Card; startDeck: Deck): float =
## Returns player's expected gain per unit staked after hitting once and then standing. let deck = startDeck var score = card1 + card2 eleven = false if card1 == 1 or card2 == 1: inc score, 10 eleven = true
for c in Card.low..Card.high: # Get another card. if deck.cards[c] == 0: continue # Card no longer present in deck. # Temporary variables for current card. var deck2 = deck score2 = score eleven2 = eleven
inc score2, c # Add card to score. if c == 1: # Score all aces initially as 11. inc score2, 10 eleven2 = true
let prob = deck2.cards[c] / deck2.size dec deck2.cards[c] dec deck2.size if score2 > 21 and eleven2: dec score2, 10 # Bust but can demote an ace. if score2 <= 21: let dp = dealerProbs(uc, deck2) result += calcGain(score2, dp) * prob else: # Bust. result -= prob
func playerGain2(card1, card2, uc: Card; startDeck: Deck): float =
## Return player's expected gain per unit staked after hitting once ## and then continuing in accordance with the tables for rounds >= 2.
var eg = 0.0 # Result. decks: array[9, Deck] # Decks for each level. scores: array[9, int] # Scores for each level. elevens: array[9, int] # Number of aces for each level scored as 11. probs: array[9, float] # Probabilities for each level. decks[0] = startDeck scores[0] = card1 + card2 if card1 == 1 or card2 == 1: inc scores[0], 10 elevens[0] = 1
probs[0] = 1.0
proc drawCard(lev: Natural) = ## Recursive closure. for c in Card.low..Card.high: if decks[lev].cards[c] == 0: continue # Card no longer present in deck.
# Temporary variables for current level. var deck = decks[lev] score = scores[lev] eleven = elevens[lev] prob = probs[lev]
inc score, c # Add card to score. if c == 1: # Score all aces initially as 11. inc score, 10 inc eleven
prob *= deck.cards[c] / deck.size if score > 21 and eleven > 0: dec score, 10 # Bust but can demote an ace. dec eleven
dec deck.cards[c] # Remove card from deck. dec deck.size
if (eleven == 0 and (score >= 17 or score >= 13 and uc < 7) or eleven == 0 and score == 12 and uc >= 4 and uc <= 6 or eleven > 0 and score == 18 and uc != 9 and uc != 10 or eleven > 0 and score >= 19) and score <= 21: let dp = dealerProbs(uc, deck) eg += calcGain(score, dp) * prob elif score > 21 and eleven == 0: # Bust. eg -= prob else: let lev = lev + 1 decks[lev] = deck scores[lev] = score elevens[lev] = eleven probs[lev] = prob drawCard(lev)
drawCard(0) result = eg
func stand(card1, card2: Card): array[10, float] =
## Return player's expected gains per unit staked, for each dealer up-card, after standing.
var deck = initDeck() dec deck.cards[card1] dec deck.cards[card2] deck.size = 50 var pscore = card1 + card2 # Player score. if card1 == 1 or card2 == 1: inc pscore, 10
for uc in Card.low..Card.high: # Dealer's up-card. var deck2 = deck dec deck2.cards[uc] dec deck2.size let dp = dealerProbs(uc, deck2) let eg = calcGain(pscore, dp) # Expected gain for this up-card. if uc > 1: result[uc-2] = eg else: # Dealer has Ace. result[9] = eg # Ace comes last in tables.
func hit(card1, card2: Card; once: bool): array[10, float] =
## Return player's expected gains per unit staked, for each dealer ## up-card, after hitting once and then either standing (once == true) ## or continuing as per the round >= 2 tables (once == false). var deck = initDeck() dec deck.cards[card1] dec deck.cards[card2] deck.size = 50 for uc in Card.low..Card.high: # Dealer's up-card. var deck2 = deck dec deck2.cards[uc] deck2.size = 49 # Player's expected gain for this up-card. let peg = if once: playerGain(card1, card2, uc, deck2) else: playerGain2(card1, card2, uc, deck2) if uc > 1: result[uc-2] = peg else: # Dealer has Ace. result[9] = peg
func double(card1, card2: Card): array[10, float] =
## Return player's expected gains per unit originally staked, ## for each dealer up-card, after doubling i.e. hitting once ## and then standing with a doubled stake. result = hit(card1, card2, true) # Hit once and then stand. for item in result.mitems: item *= 2
proc split(card: Card): array[10, float] =
## Return player's expected gains per unit originally staked, for each dealer up-card, after ## splitting a pair and doubling the stake, getting a second card for each hand and then ## continuing in accordance with the rounds >= 2 tables. It is assumed that a player cannot ## double or re-split following a split. It is also assumed (in the interests of simplicity) ## that the expected gains for each split hand (after calculating the gains for the first hand ## as though the second hand is not completed) are exactly the same. var deck = initDeck() dec deck.cards[card], 2 # Must be a pair. deck.size = 50
# Now play a single hand. var score: int = card var eleven = 0 if card == 1: score = 11 eleven = 1
for uc in Card.low..Card.high: # Collect results for each dealer up-card. if deck.cards[uc] == 0: continue # Card no longer present in deck.
var deck2 = deck dec deck2.cards[uc] dec deck2.size var ix = uc - 2 if ix == -1: ix = 9 # In tables ace comes last. var peg: float # Player expected gain for this up-card. # Get second player card. for c in Card.low..Card.high: if deck2.cards[c] == 0: continue # Card no longer present in deck.
let prob = deck2.cards[c] / deck2.size var deck3 = deck2 dec deck3.cards[c] dec deck3.size var score2 = score + c var eleven2 = eleven if c == 1: # Score all aces initially as 11. inc score2, 10 inc eleven2
if score2 == 21: # Player has blackjack & we know dealer hasn't. peg += 1.5 * prob continue
if score2 > 21 and eleven2 > 0: dec score2, 10 # Bust but can demote an ace. dec eleven2
let action = if eleven2 > 0: sTable2[score2-12][ix] # Use soft strategy table, no doubling. else: hTable2[score2-4][ix] # Use hard strategy table, no doubling let peg2 = if action == Stand: calcGain(score2, dealerProbs(uc, deck3)) else: playerGain2(card, c, uc, deck3) peg += peg2 * prob
if uc > 1: result[uc-2] = peg * 2 # Allow for both hands in overall results. else: result[9] = peg * 2 # Ditto.
func bestAction(ags: openArray[ActionGain]): Action =
## Return the action with the highest expected gain. ags[ags.maxIndex()].action
proc printHeader(title: string) =
## Print title and header for a given chart. echo title echo "P/D 2 3 4 5 6 7 8 9 T A" echo "——————————————————————————————————————————————————————————————————————————"
proc printPair(c: Card) =
## Print header for a pair of cards. stdout.write if c == 1: "AA " elif c == 10: "TT " else: &"{c}{c} "
func dealerPlay(pscore: int, next: var int; cards, d: openArray[Card]): float =
## Simulate a dealer's play for a given player's hand and state of deck. ## Return the player's gain (positive or negative) per unit staked. var dscore = d[0] + d[1] var aces = 0 if d[0] == 1 or d[1] == 1: # Dealer has an ace. inc dscore, 10 inc aces
while true: if dscore > 21 and aces > 0: dec dscore, 10 # Bust but we can demote an ace. dec aces if dscore > 21: return 1 # Dealer is bust and player gains stake.
if dscore >= 17: # Dealer must stick on 17 or above, hard or not. if dscore > pscore: return -1 # Dealer wins and player loses stake. if dscore == pscore: break # Player breaks even. return 1 # Dealer loses and player gains stake.
let nc = cards[next] # Get new card from pack. inc next inc dscore, nc if nc == 1: # Count aces initially as 11. inc dscore, 10 inc aces
proc playerPlay(): (float, float) =
## Simulate the playing of a random player's hand according to the strategy tables. ## Return both the gain (positive or negative) and the stake (1 or 2).
var perm = toSeq(0..51) perm.shuffle() var cards: array[52, Card] for i, r in perm: var card = r div 4 + 1 if card > 10: card = 10 cards[i] = card
var p, d: seq[Card] # Player and dealer hands. # Initial deal. p.add cards[0..1] d.add cards[2..3] var next = 4 # Index of next card to be dealt.
# Check if dealer and/or player have blackjack. let dbj = d[0] == 1 and d[1] == 10 or d[0] == 10 and d[1] == 1 let pbj = p[0] == 1 and p[1] == 10 or p[0] == 10 and p[1] == 1 if dbj: if pbj: return (0.0, 1.0) # Player neither wins nor loses. else: return (-1.0, 1.0) # Player loses stake. if pbj: return (1.5, 1.0) # Player wins 1.5 x stake.
var uc = d[0] - 2 # Dealer's up-card as index to access tables. if uc < 0: uc = 9 # Ace is at last place in tables.
var stake = 1.0 # Player's initial stake. var fscores: array[2, int] # Final player scores (one or, after split, two hands). var action: Action var score, aces: int
proc h(hand: int) = ## Process a hit. while true: let nc = cards[next] # Get new card from pack. inc next inc score, nc if nc == 1: # Count aces initially as 11. inc score, 10 inc aces if score > 21 and aces > 0: dec score, 10 # Bust but we can demote an ace. dec aces if score > 21: fscores[hand] = 22 # Player is bust and loses stake. break if action == Double: fscores[hand] = score break # Get further strategy and act accordingly. action = if aces == 0: hTable2[score-4][uc] else: sTable2[score-12][uc] if action == Stand: fscores[hand] = score break
score = p[0] + p[1] # Get kind of player hand: hard, soft, pair. let kind = if p[0] == p[1]: Pair elif p[0] == 1 or p[1] == 1: Soft else: Hard
case kind of Hard: action = hTable[score-5][uc] of Soft: # includes one ace. let othercard = if p[0] == 1: p[1] else: p[0] inc score, 10 aces = 1 action = sTable[otherCard-2][uc] of Pair: if p[0] == 1: # Pair of aces. inc score, 10 aces = 2 action = pTable[p[0]-1][uc]
case action of Stand: fscores[0] = score of Hit: h(0) of Double: h(0) stake = 2 of Split: for hand in 0..1: score = p[0] aces = 0 if score == 1: # Count aces initially as 11. score = 11 inc aces h(hand)
var sum = 0.0 if fscores[0] < 22: sum += dealerPlay(fscores[0], next, cards, d) * stake else: sum -= 1 * stake # This hand is bust. if fscores[1] > 0: # Pair. if fscores[1] < 22: sum += dealerPlay(fscores[1], next, cards, d) else: sum -= 1 stake = 2
result = (sum, stake)
proc simulate(perDay, days: int) =
## Simulate "perDay" blackjack games for "days" days.
var winDays, loseDays, evenDays = 0 bigWin, bigLoss = 0.0 totalGain, totalStake = 0.0
for d in 1..days: var dailyGain, dailyStake = 0.0 for p in 1..perDay: let (gain, stake) = playerPlay() dailyGain += gain dailyStake += stake
if dailyGain > 0: inc winDays elif dailyGain < 0: inc loseDays else: inc evenDays
if dailyGain > bigWin: bigWin = dailyGain elif -dailyGain > bigLoss: bigLoss = -dailyGain
totalGain += dailyGain totalStake += dailyStake
echo &"\nAfter playing {perDay} times a day for {days} days:" echo "Winning days: ", winDays echo "Losing days: ", loseDays echo "Breakeven days: ", evenDays echo "Biggest win: ", bigWin echo "Biggest loss: ", bigLoss if totalGain < 0: echo "Total loss: ", -totalGain echo "Total staked: ", totalStake echo &"Loss % staked: {-totalGain/totalStake*100:0.3f}\n" else: echo "Total win: ", totalGain echo "Total staked: ", totalStake echo &"Win % staked: {totalGain/totalStake*100:0.3f}\n"
proc main() =
# Print dealer probabilities chart. dealerChart()
# For hard scores (i.e. different cards, no aces). const Tuples = [(2, 3), (2, 4), (2, 5), (3, 4), (2, 6), (3, 5), (2, 7), (3, 6), (4, 5), (2, 8), (3, 7), (4, 6), (2, 9), (3, 8), (4, 7), (5, 6), (2, 10), (3, 9), (4, 8), (5, 7), (3, 10), (4, 9), (5, 8), (6, 7), (4, 10), (5, 9), (6, 8), (5, 10), (6, 9), (7, 8), (6, 10), (7, 9), (7, 10), (8, 9), (8, 10), (9, 10)]
# Number of tuples for each player score from 5 to 19. const Counts = [float 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1]
# Expected gains for each player score & for each dealer up-card. var segs: array[15, array[10, float]] # if stands. hegs: array[15, array[10, float]] # if hits. degs: array[15, array[10, float]] # if doubles.
for t in Tuples: let i = t[0] + t[1] let sg = stand(t[0], t[1]) let hg = hit(t[0], t[1], false) let dg = double(t[0], t[1]) for j in 0..9: segs[i-5][j] += sg[j] hegs[i-5][j] += hg[j] degs[i-5][j] += dg[j]
# Calculate the average per tuple for each score. for i in 0..14: for j in 0..9: segs[i][j] /= Counts[i] hegs[i][j] /= Counts[i] degs[i][j] /= Counts[i]
printHeader("\nHard Chart - Player Expected Gains per unit (Stand)") for i in 5..19: stdout.write &"{i:2} " for j in 0..9: stdout.write &"{segs[i-5][j]: 0.3f} " echo()
printHeader("\nHard Chart - Player Expected Gains per unit (Hit)") for i in 5..19: stdout.write &"{i:2} " for j in 0..9: stdout.write &"{hegs[i-5][j]: 0.3f} " echo()
printHeader("\nHard Chart - Player Expected Gains per unit (Double)") for i in 5..19: stdout.write &"{i:2} " for j in 0..9: stdout.write &"{degs[i-5][j]: 0.3f} " echo()
printHeader("\nHard Chart - Player Strategy (Round 1)") for i in 5..19: stdout.write &"{i:2} " for j in 0..9: let ags = [(Stand, segs[i-5][j]), (Hit, hegs[i-5][j]), (Double, degs[i-5][j])] let action = bestAction(ags) hTable[i-5][j] = action stdout.write &"{action:^6} " echo()
# For hard scores (no aces) - after round 1 (no doubling or splitting). # Based on hard table figures (round 1) with scores of 4, 20, and 21 added. var segs2: array[18, array[10, float]] # Expected gains if stands. hegs2: array[18, array[10, float]] # Expected gains if hits. for i in 5..19: segs2[i-4] = segs[i-5] hegs2[i-4] = hegs[i-5]
let sg4 = stand(2, 2) let hg4 = hit(2, 2, false) let sg20 = stand(10, 10) let hg20 = hit(10, 10, false) let sg21 = stand(1, 10) let hg21 = hit(1, 10, false) for j in 0..9: segs2[0][j] += sg4[j] hegs2[0][j] += hg4[j] segs2[16][j] += sg20[j] hegs2[16][j] += hg20[j] segs2[17][j] += sg21[j] hegs2[17][j] += hg21[j]
printHeader("\nHard Chart - Player Strategy (Round >= 2, No Doubling)") for i in 4..21: stdout.write &"{i:2} " for j in 0..9: var action = Stand if hegs2[i-4][j] > segs2[i-4][j]: action = Hit hTable2[i-4][j] = action stdout.write &"{action:^6} " echo()
# For soft scores (i.e. including exactly one ace).
# Expected gains for each player second card (2 to 9) & for each dealer up-card. var segs3: array[8, array[10, float]] # if stands. hegs3: array[8, array[10, float]] # if hits. degs3: array[8, array[10, float]] # if doubles. for c in 2..9: let sg = stand(1, c) let hg = hit(1, c, false) let dg = double(1, c) for j in 0..9: segs3[c-2][j] += sg[j] hegs3[c-2][j] += hg[j] degs3[c-2][j] += dg[j]
printHeader("\nSoft Chart - Player Expected Gains per unit (Stand)") for c in 2..9: stdout.write &"A{c} " for j in 0..9: stdout.write &"{segs3[c-2][j]: 0.3f} " echo()
printHeader("\nSoft Chart - Player Expected Gains per unit (Hit)") for c in 2..9: stdout.write &"A{c} " for j in 0..9: stdout.write &"{hegs3[c-2][j]: 0.3f} " echo()
printHeader("\nSoft Chart - Player Expected Gains per unit (Double)") for c in 2..9: stdout.write &"A{c} " for j in 0..9: stdout.write &"{degs3[c-2][j]: 0.3f} " echo()
printHeader("\nSoft Chart - Player Strategy (Round 1)") for c in 2..9: stdout.write &"A{c} " for j in 0..9: let ags = [(Stand, segs3[c-2][j]), (Hit, hegs3[c-2][j]), (Double, degs3[c-2][j])] let action = bestAction(ags) sTable[c-2][j] = action stdout.write &"{action:^6} " echo()
# For soft scores (at least one ace) - after round 1 (no doubling or splitting). # Based on soft table figures (round 1) with scores of 12 and 21 added. # Assumes one ace counted as 11. var segs4: array[10, array[10, float]] # Expected gains if stands. hegs4: array[10, array[10, float]] # Expected gains if hits. for i in 1..8: segs4[i] = segs3[i-1] hegs4[i] = hegs3[i-1]
let sg12 = stand(1, 1) let hg12 = hit(1, 1, false) for j in 0..9: segs4[0][j] += sg12[j] hegs4[0][j] += hg12[j] segs4[9][j] += sg21[j] hegs4[9][j] += hg21[j]
printHeader("\nSoft Chart - Player Strategy (Round >= 2, No Doubling)") for i in 12..21: stdout.write &"{i:2} " for j in 0..9: var action = Stand if hegs4[i-12][j] > segs4[i-12][j]: action = Hit sTable2[i-12][j] = action stdout.write &"{action:^6} " echo()
# For pairs.
# Expected gains for each pair (A to 10) & for each dealer up-card. var segs5: array[10, array[10, float]] # if stands. hegs5: array[10, array[10, float]] # if hits. degs5: array[10, array[10, float]] # if double. pegs5: array[10, array[10, float]] # if split. for c in 1..10: let sg = stand(c, c) hg = hit(c, c, false) dg = double(c, c) pg = split(c) for j in 0..9: segs5[c-1][j] += sg[j] hegs5[c-1][j] += hg[j] degs5[c-1][j] += dg[j] pegs5[c-1][j] += pg[j]
printHeader("\nPairs Chart - Player Expected Gains per unit (Stand)") for c in 1..10: printPair(c) for j in 0..9: stdout.write &"{segs5[c-1][j]: 0.3f} " echo()
printHeader("\nPairs Chart - Player Expected Gains per unit (Hit)") for c in 1..10: printPair(c) for j in 0..9: stdout.write &"{hegs5[c-1][j]: 0.3f} " echo()
printHeader("\nPairs Chart - Player Expected Gains per unit (Double)") for c in 1..10: printPair(c) for j in 0..9: stdout.write &"{degs5[c-1][j]: 0.3f} " echo()
printHeader("\nPairs Chart - Player Expected Gains per unit (Split)") for c in 1..10: printPair(c) for j in 0..9: stdout.write &"{pegs5[c-1][j]: 0.3f} " echo()
printHeader("\nPairs Chart - Player Strategy (Round 1)") for c in 1..10: printPair(c) for j in 0..9: let ags = [(Stand, segs5[c-1][j]), (Hit, hegs5[c-1][j]), (Double, degs5[c-1][j]), (Split, pegs5[c-1][j])] let action = bestAction(ags) pTable[c-1][j] = action stdout.write &"{action:^6} " echo()
randomize()
# Do 10 years of simulation. for i in 1..10: echo &"Simulation for year {i}:" simulate(50, 365)
main()</lang>
- Output:
Dealer Probabilities, Stands on Soft 17, 1 Deck, U.S Rules Up Card 17 18 19 20 21 Bust ——————————————————————————————————————————————————————————————————— Ace 0.183786 0.190890 0.188680 0.191692 0.075137 0.169815 2 0.138976 0.131762 0.131815 0.123948 0.120526 0.352973 3 0.130313 0.130946 0.123761 0.123345 0.116047 0.375588 4 0.130973 0.114163 0.120679 0.116286 0.115096 0.402803 5 0.119687 0.123483 0.116909 0.104694 0.106321 0.428905 6 0.166948 0.106454 0.107192 0.100705 0.097878 0.420823 7 0.372345 0.138583 0.077334 0.078897 0.072987 0.259854 8 0.130857 0.362989 0.129445 0.068290 0.069791 0.238627 9 0.121886 0.103921 0.357391 0.122250 0.061109 0.233442 10 0.124156 0.122486 0.124421 0.356869 0.039570 0.232499 Hard Chart - Player Expected Gains per unit (Stand) P/D 2 3 4 5 6 7 8 9 T A —————————————————————————————————————————————————————————————————————————— 5 -0.293 -0.248 -0.176 -0.104 -0.122 -0.469 -0.513 -0.533 -0.546 -0.659 6 -0.291 -0.232 -0.172 -0.101 -0.119 -0.467 -0.522 -0.533 -0.547 -0.659 7 -0.283 -0.229 -0.163 -0.098 -0.117 -0.471 -0.521 -0.537 -0.547 -0.658 8 -0.276 -0.229 -0.162 -0.100 -0.130 -0.478 -0.523 -0.539 -0.549 -0.648 9 -0.277 -0.224 -0.160 -0.108 -0.134 -0.480 -0.528 -0.543 -0.542 -0.646 10 -0.279 -0.227 -0.172 -0.120 -0.146 -0.484 -0.531 -0.539 -0.537 -0.644 11 -0.277 -0.231 -0.175 -0.123 -0.147 -0.488 -0.529 -0.537 -0.537 -0.646 12 -0.286 -0.241 -0.185 -0.134 -0.151 -0.485 -0.526 -0.535 -0.533 -0.655 13 -0.282 -0.236 -0.181 -0.133 -0.156 -0.488 -0.529 -0.537 -0.534 -0.649 14 -0.282 -0.238 -0.188 -0.134 -0.159 -0.489 -0.529 -0.533 -0.536 -0.651 15 -0.280 -0.239 -0.190 -0.144 -0.169 -0.494 -0.531 -0.536 -0.531 -0.648 16 -0.287 -0.250 -0.194 -0.152 -0.179 -0.495 -0.526 -0.540 -0.530 -0.648 17 -0.147 -0.120 -0.074 -0.044 -0.011 -0.122 -0.405 -0.414 -0.402 -0.459 18 0.119 0.144 0.164 0.202 0.268 0.389 0.096 -0.196 -0.155 -0.082 19 0.385 0.384 0.404 0.448 0.484 0.610 0.577 0.264 0.103 0.308 Hard Chart - Player Expected Gains per unit (Hit) P/D 2 3 4 5 6 7 8 9 T A —————————————————————————————————————————————————————————————————————————— 5 -0.131 -0.098 -0.041 0.022 0.019 -0.119 -0.181 -0.262 -0.309 -0.417 6 -0.151 -0.107 -0.055 0.009 0.014 -0.164 -0.234 -0.305 -0.349 -0.443 7 -0.111 -0.072 -0.013 0.053 0.064 -0.069 -0.223 -0.295 -0.332 -0.401 8 -0.015 0.021 0.084 0.136 0.148 0.092 -0.056 -0.213 -0.253 -0.275 9 0.090 0.137 0.181 0.226 0.235 0.194 0.111 -0.052 -0.148 -0.128 10 0.215 0.246 0.277 0.314 0.319 0.277 0.211 0.119 0.030 0.030 11 0.272 0.296 0.327 0.361 0.362 0.293 0.222 0.146 0.107 0.113 12 -0.256 -0.232 -0.206 -0.181 -0.179 -0.241 -0.308 -0.380 -0.378 -0.413 13 -0.315 -0.293 -0.270 -0.252 -0.251 -0.301 -0.362 -0.389 -0.423 -0.440 14 -0.363 -0.353 -0.337 -0.315 -0.313 -0.346 -0.366 -0.426 -0.455 -0.460 15 -0.419 -0.414 -0.406 -0.392 -0.383 -0.351 -0.406 -0.466 -0.496 -0.487 16 -0.461 -0.460 -0.454 -0.448 -0.397 -0.376 -0.426 -0.481 -0.510 -0.497 17 -0.534 -0.536 -0.538 -0.493 -0.484 -0.450 -0.475 -0.529 -0.558 -0.546 18 -0.633 -0.634 -0.597 -0.591 -0.586 -0.567 -0.565 -0.593 -0.624 -0.630 19 -0.750 -0.713 -0.712 -0.709 -0.707 -0.699 -0.697 -0.698 -0.712 -0.740 Hard Chart - Player Expected Gains per unit (Double) P/D 2 3 4 5 6 7 8 9 T A —————————————————————————————————————————————————————————————————————————— 5 -0.587 -0.497 -0.352 -0.209 -0.244 -0.938 -1.025 -1.066 -1.093 -1.318 6 -0.560 -0.446 -0.324 -0.186 -0.215 -0.870 -1.023 -1.045 -1.074 -1.295 7 -0.415 -0.317 -0.186 -0.066 -0.059 -0.555 -0.851 -0.936 -0.956 -1.127 8 -0.165 -0.081 0.032 0.143 0.157 -0.140 -0.433 -0.697 -0.743 -0.802 9 0.114 0.193 0.286 0.380 0.393 0.175 0.007 -0.281 -0.442 -0.409 10 0.428 0.492 0.554 0.628 0.638 0.446 0.313 0.164 0.007 0.025 11 0.542 0.592 0.654 0.722 0.724 0.479 0.341 0.223 0.164 0.198 12 -0.511 -0.463 -0.413 -0.362 -0.358 -0.556 -0.690 -0.811 -0.789 -0.827 13 -0.630 -0.587 -0.541 -0.503 -0.503 -0.651 -0.775 -0.807 -0.862 -0.880 14 -0.727 -0.706 -0.673 -0.630 -0.627 -0.723 -0.759 -0.862 -0.915 -0.921 15 -0.838 -0.829 -0.812 -0.783 -0.767 -0.716 -0.826 -0.937 -0.992 -0.973 16 -0.921 -0.920 -0.908 -0.896 -0.793 -0.751 -0.853 -0.961 -1.019 -0.995 17 -1.069 -1.072 -1.076 -0.985 -0.967 -0.901 -0.949 -1.058 -1.116 -1.092 18 -1.265 -1.267 -1.195 -1.182 -1.172 -1.135 -1.130 -1.186 -1.248 -1.260 19 -1.499 -1.425 -1.423 -1.417 -1.414 -1.397 -1.395 -1.396 -1.425 -1.481 Hard Chart - Player Strategy (Round 1) P/D 2 3 4 5 6 7 8 9 T A —————————————————————————————————————————————————————————————————————————— 5 Hit Hit Hit Hit Hit Hit Hit Hit Hit Hit 6 Hit Hit Hit Hit Hit Hit Hit Hit Hit Hit 7 Hit Hit Hit Hit Hit Hit Hit Hit Hit Hit 8 Hit Hit Hit Double Double Hit Hit Hit Hit Hit 9 Double Double Double Double Double Hit Hit Hit Hit Hit 10 Double Double Double Double Double Double Double Double Hit Hit 11 Double Double Double Double Double Double Double Double Double Double 12 Hit Hit Stand Stand Stand Hit Hit Hit Hit Hit 13 Stand Stand Stand Stand Stand Hit Hit Hit Hit Hit 14 Stand Stand Stand Stand Stand Hit Hit Hit Hit Hit 15 Stand Stand Stand Stand Stand Hit Hit Hit Hit Hit 16 Stand Stand Stand Stand Stand Hit Hit Hit Hit Hit 17 Stand Stand Stand Stand Stand Stand Stand Stand Stand Stand 18 Stand Stand Stand Stand Stand Stand Stand Stand Stand Stand 19 Stand Stand Stand Stand Stand Stand Stand Stand Stand Stand Hard Chart - Player Strategy (Round >= 2, No Doubling) P/D 2 3 4 5 6 7 8 9 T A —————————————————————————————————————————————————————————————————————————— 4 Hit Hit Hit Hit Hit Hit Hit Hit Hit Hit 5 Hit Hit Hit Hit Hit Hit Hit Hit Hit Hit 6 Hit Hit Hit Hit Hit Hit Hit Hit Hit Hit 7 Hit Hit Hit Hit Hit Hit Hit Hit Hit Hit 8 Hit Hit Hit Hit Hit Hit Hit Hit Hit Hit 9 Hit Hit Hit Hit Hit Hit Hit Hit Hit Hit 10 Hit Hit Hit Hit Hit Hit Hit Hit Hit Hit 11 Hit Hit Hit Hit Hit Hit Hit Hit Hit Hit 12 Hit Hit Stand Stand Stand Hit Hit Hit Hit Hit 13 Stand Stand Stand Stand Stand Hit Hit Hit Hit Hit 14 Stand Stand Stand Stand Stand Hit Hit Hit Hit Hit 15 Stand Stand Stand Stand Stand Hit Hit Hit Hit Hit 16 Stand Stand Stand Stand Stand Hit Hit Hit Hit Hit 17 Stand Stand Stand Stand Stand Stand Stand Stand Stand Stand 18 Stand Stand Stand Stand Stand Stand Stand Stand Stand Stand 19 Stand Stand Stand Stand Stand Stand Stand Stand Stand Stand 20 Stand Stand Stand Stand Stand Stand Stand Stand Stand Stand 21 Stand Stand Stand Stand Stand Stand Stand Stand Stand Stand Soft Chart - Player Expected Gains per unit (Stand) P/D 2 3 4 5 6 7 8 9 T A —————————————————————————————————————————————————————————————————————————— A2 -0.283 -0.241 -0.186 -0.119 -0.114 -0.462 -0.508 -0.517 -0.539 -0.662 A3 -0.284 -0.240 -0.170 -0.116 -0.112 -0.460 -0.505 -0.527 -0.538 -0.661 A4 -0.283 -0.224 -0.166 -0.113 -0.109 -0.458 -0.514 -0.526 -0.538 -0.659 A5 -0.266 -0.221 -0.164 -0.111 -0.108 -0.468 -0.515 -0.525 -0.537 -0.659 A6 -0.132 -0.093 -0.037 0.005 0.010 -0.090 -0.385 -0.407 -0.418 -0.483 A7 0.136 0.167 0.204 0.222 0.262 0.412 0.121 -0.179 -0.186 -0.101 A8 0.402 0.420 0.415 0.461 0.482 0.615 0.608 0.288 0.064 0.290 A9 0.656 0.644 0.654 0.682 0.694 0.773 0.785 0.766 0.555 0.681 Soft Chart - Player Expected Gains per unit (Hit) P/D 2 3 4 5 6 7 8 9 T A —————————————————————————————————————————————————————————————————————————— A2 0.039 0.071 0.110 0.159 0.168 0.107 0.039 -0.014 -0.090 -0.184 A3 0.017 0.044 0.091 0.137 0.147 0.060 0.035 -0.060 -0.124 -0.216 A4 -0.012 0.022 0.061 0.108 0.120 0.034 -0.035 -0.114 -0.172 -0.256 A5 -0.032 -0.003 0.038 0.082 0.116 -0.024 -0.084 -0.167 -0.229 -0.296 A6 0.007 0.036 0.077 0.140 0.133 0.060 -0.065 -0.135 -0.189 -0.242 A7 0.065 0.093 0.156 0.175 0.192 0.175 0.047 -0.087 -0.140 -0.160 A8 0.120 0.173 0.187 0.227 0.241 0.222 0.158 0.005 -0.087 -0.081 A9 0.191 0.196 0.230 0.268 0.280 0.243 0.172 0.096 0.007 -0.008 Soft Chart - Player Expected Gains per unit (Double) P/D 2 3 4 5 6 7 8 9 T A —————————————————————————————————————————————————————————————————————————— A2 -0.042 0.028 0.115 0.212 0.230 -0.157 -0.312 -0.373 -0.478 -0.586 A3 -0.047 0.011 0.109 0.204 0.222 -0.175 -0.254 -0.394 -0.479 -0.588 A4 -0.070 0.003 0.085 0.175 0.201 -0.141 -0.314 -0.422 -0.495 -0.613 A5 -0.082 -0.019 0.063 0.148 0.217 -0.189 -0.333 -0.452 -0.536 -0.649 A6 0.013 0.074 0.155 0.280 0.266 0.014 -0.230 -0.345 -0.433 -0.522 A7 0.128 0.189 0.313 0.349 0.385 0.240 -0.015 -0.254 -0.322 -0.359 A8 0.237 0.346 0.373 0.453 0.483 0.325 0.190 -0.060 -0.226 -0.200 A9 0.380 0.392 0.459 0.536 0.560 0.351 0.230 0.111 -0.055 -0.055 Soft Chart - Player Strategy (Round 1) P/D 2 3 4 5 6 7 8 9 T A —————————————————————————————————————————————————————————————————————————— A2 Hit Hit Double Double Double Hit Hit Hit Hit Hit A3 Hit Hit Double Double Double Hit Hit Hit Hit Hit A4 Hit Hit Double Double Double Hit Hit Hit Hit Hit A5 Hit Hit Double Double Double Hit Hit Hit Hit Hit A6 Double Double Double Double Double Hit Hit Hit Hit Hit A7 Stand Double Double Double Double Stand Stand Hit Hit Stand A8 Stand Stand Stand Stand Double Stand Stand Stand Stand Stand A9 Stand Stand Stand Stand Stand Stand Stand Stand Stand Stand Soft Chart - Player Strategy (Round >= 2, No Doubling) P/D 2 3 4 5 6 7 8 9 T A —————————————————————————————————————————————————————————————————————————— 12 Hit Hit Hit Hit Hit Hit Hit Hit Hit Hit 13 Hit Hit Hit Hit Hit Hit Hit Hit Hit Hit 14 Hit Hit Hit Hit Hit Hit Hit Hit Hit Hit 15 Hit Hit Hit Hit Hit Hit Hit Hit Hit Hit 16 Hit Hit Hit Hit Hit Hit Hit Hit Hit Hit 17 Hit Hit Hit Hit Hit Hit Hit Hit Hit Hit 18 Stand Stand Stand Stand Stand Stand Stand Hit Hit Stand 19 Stand Stand Stand Stand Stand Stand Stand Stand Stand Stand 20 Stand Stand Stand Stand Stand Stand Stand Stand Stand Stand 21 Stand Stand Stand Stand Stand Stand Stand Stand Stand Stand Pairs Chart - Player Expected Gains per unit (Stand) P/D 2 3 4 5 6 7 8 9 T A —————————————————————————————————————————————————————————————————————————— AA -0.274 -0.232 -0.178 -0.130 -0.104 -0.452 -0.500 -0.511 -0.531 -0.663 22 -0.291 -0.251 -0.192 -0.107 -0.125 -0.471 -0.515 -0.523 -0.547 -0.660 33 -0.295 -0.246 -0.160 -0.101 -0.119 -0.467 -0.510 -0.542 -0.546 -0.660 44 -0.290 -0.214 -0.152 -0.095 -0.114 -0.463 -0.529 -0.543 -0.547 -0.656 55 -0.256 -0.206 -0.146 -0.090 -0.112 -0.484 -0.531 -0.541 -0.545 -0.653 66 -0.262 -0.211 -0.152 -0.102 -0.165 -0.493 -0.536 -0.549 -0.552 -0.617 77 -0.268 -0.219 -0.164 -0.156 -0.174 -0.502 -0.539 -0.555 -0.510 -0.631 88 -0.275 -0.228 -0.215 -0.165 -0.178 -0.503 -0.551 -0.516 -0.518 -0.644 99 0.137 0.123 0.167 0.203 0.265 0.401 0.065 -0.196 -0.133 -0.055 TT 0.627 0.636 0.645 0.674 0.697 0.765 0.783 0.744 0.583 0.650 Pairs Chart - Player Expected Gains per unit (Hit) P/D 2 3 4 5 6 7 8 9 T A —————————————————————————————————————————————————————————————————————————— AA 0.095 0.120 0.142 0.182 0.200 0.158 0.093 -0.003 -0.048 -0.075 22 -0.113 -0.082 -0.035 0.036 0.032 -0.092 -0.141 -0.222 -0.277 -0.395 33 -0.153 -0.118 -0.047 0.008 0.014 -0.164 -0.231 -0.310 -0.346 -0.444 44 -0.013 0.028 0.098 0.154 0.175 0.111 -0.055 -0.206 -0.246 -0.268 55 0.224 0.254 0.295 0.347 0.362 0.279 0.207 0.119 0.032 0.042 66 -0.253 -0.222 -0.190 -0.162 -0.194 -0.265 -0.322 -0.386 -0.386 -0.411 77 -0.406 -0.388 -0.369 -0.370 -0.367 -0.389 -0.408 -0.475 -0.516 -0.510 88 -0.454 -0.450 -0.461 -0.453 -0.397 -0.374 -0.426 -0.487 -0.512 -0.490 99 -0.627 -0.638 -0.597 -0.590 -0.587 -0.566 -0.566 -0.595 -0.626 -0.621 TT -0.847 -0.846 -0.846 -0.846 -0.845 -0.843 -0.843 -0.842 -0.840 -0.882 Pairs Chart - Player Expected Gains per unit (Double) P/D 2 3 4 5 6 7 8 9 T A —————————————————————————————————————————————————————————————————————————— AA -0.019 0.055 0.137 0.216 0.248 -0.137 -0.296 -0.421 -0.468 -0.591 22 -0.582 -0.501 -0.384 -0.214 -0.249 -0.942 -1.030 -1.047 -1.094 -1.320 33 -0.567 -0.472 -0.302 -0.184 -0.215 -0.871 -1.000 -1.065 -1.072 -1.298 44 -0.185 -0.082 0.044 0.162 0.193 -0.108 -0.447 -0.701 -0.741 -0.802 55 0.446 0.510 0.590 0.695 0.724 0.466 0.323 0.175 0.014 0.042 66 -0.505 -0.444 -0.380 -0.325 -0.387 -0.599 -0.711 -0.817 -0.803 -0.823 77 -0.813 -0.777 -0.738 -0.741 -0.734 -0.823 -0.858 -0.978 -1.035 -1.019 88 -0.908 -0.900 -0.922 -0.906 -0.793 -0.747 -0.853 -0.974 -1.024 -0.980 99 -1.255 -1.277 -1.194 -1.181 -1.173 -1.132 -1.133 -1.189 -1.252 -1.242 TT -1.693 -1.693 -1.693 -1.691 -1.690 -1.686 -1.685 -1.684 -1.681 -1.764 Pairs Chart - Player Expected Gains per unit (Split) P/D 2 3 4 5 6 7 8 9 T A —————————————————————————————————————————————————————————————————————————— AA 1.192 1.223 1.265 1.321 1.344 1.308 1.201 1.039 0.860 0.921 22 -0.128 -0.070 -0.007 0.128 0.126 -0.054 -0.213 -0.383 -0.463 -0.566 33 -0.202 -0.128 0.009 0.117 0.112 -0.115 -0.265 -0.418 -0.509 -0.579 44 -0.236 -0.127 -0.013 0.095 0.083 -0.223 -0.343 -0.493 -0.580 -0.623 55 -0.232 -0.150 -0.038 0.068 0.056 -0.299 -0.448 -0.608 -0.685 -0.695 66 -0.219 -0.135 -0.028 0.068 -0.011 -0.270 -0.413 -0.570 -0.652 -0.660 77 -0.163 -0.084 0.016 0.039 0.053 -0.123 -0.423 -0.564 -0.634 -0.635 88 0.017 0.077 0.106 0.188 0.234 0.202 -0.100 -0.430 -0.464 -0.378 99 0.170 0.170 0.253 0.339 0.359 0.341 0.179 -0.112 -0.268 -0.109 TT 0.412 0.465 0.518 0.596 0.619 0.576 0.447 0.276 0.146 0.140 Pairs Chart - Player Strategy (Round 1) P/D 2 3 4 5 6 7 8 9 T A —————————————————————————————————————————————————————————————————————————— AA Split Split Split Split Split Split Split Split Split Split 22 Hit Split Split Split Split Split Hit Hit Hit Hit 33 Hit Hit Split Split Split Split Hit Hit Hit Hit 44 Hit Hit Hit Double Double Hit Hit Hit Hit Hit 55 Double Double Double Double Double Double Double Double Hit Double 66 Split Split Split Split Split Hit Hit Hit Hit Hit 77 Split Split Split Split Split Split Hit Hit Stand Hit 88 Split Split Split Split Split Split Split Split Split Split 99 Split Split Split Split Split Stand Split Split Stand Stand TT Stand Stand Stand Stand Stand Stand Stand Stand Stand Stand Simulation for year 1: After playing 50 times a day for 365 days: Winning days: 179 Losing days: 180 Breakeven days: 6 Biggest win: 33.0 Biggest loss: 22.5 Total win: 144.0 Total staked: 20623.0 Win % staked: 0.698 Simulation for year 2: After playing 50 times a day for 365 days: Winning days: 168 Losing days: 184 Breakeven days: 13 Biggest win: 25.5 Biggest loss: 25.0 Total loss: 233.0 Total staked: 20553.0 Loss % staked: 1.134 Simulation for year 3: After playing 50 times a day for 365 days: Winning days: 177 Losing days: 176 Breakeven days: 12 Biggest win: 25.5 Biggest loss: 22.0 Total loss: 18.5 Total staked: 20646.0 Loss % staked: 0.090 Simulation for year 4: After playing 50 times a day for 365 days: Winning days: 193 Losing days: 164 Breakeven days: 8 Biggest win: 26.5 Biggest loss: 22.0 Total win: 210.5 Total staked: 20635.0 Win % staked: 1.020 Simulation for year 5: After playing 50 times a day for 365 days: Winning days: 161 Losing days: 191 Breakeven days: 13 Biggest win: 25.0 Biggest loss: 31.0 Total loss: 270.0 Total staked: 20502.0 Loss % staked: 1.317 Simulation for year 6: After playing 50 times a day for 365 days: Winning days: 182 Losing days: 175 Breakeven days: 8 Biggest win: 23.0 Biggest loss: 26.0 Total win: 104.0 Total staked: 20610.0 Win % staked: 0.505 Simulation for year 7: After playing 50 times a day for 365 days: Winning days: 178 Losing days: 181 Breakeven days: 6 Biggest win: 19.0 Biggest loss: 21.5 Total loss: 86.0 Total staked: 20546.0 Loss % staked: 0.419 Simulation for year 8: After playing 50 times a day for 365 days: Winning days: 178 Losing days: 177 Breakeven days: 10 Biggest win: 26.5 Biggest loss: 21.0 Total loss: 127.5 Total staked: 20522.0 Loss % staked: 0.621 Simulation for year 9: After playing 50 times a day for 365 days: Winning days: 173 Losing days: 184 Breakeven days: 8 Biggest win: 25.0 Biggest loss: 24.0 Total loss: 14.5 Total staked: 20551.0 Loss % staked: 0.071 Simulation for year 10: After playing 50 times a day for 365 days: Winning days: 170 Losing days: 178 Breakeven days: 17 Biggest win: 20.0 Biggest loss: 21.5 Total loss: 93.0 Total staked: 20516.0 Loss % staked: 0.453
Phix
-- demo/rosetta/blackjack.exw with javascript_semantics function NewDeck() return {4, 4, 4, 4, 4, 4, 4, 4, 4, 16, 52} end function function dealerProbs(integer upCard, sequence startDeck) -- -- Returns probabilities of dealer eventually getting: -- 1: 17, 2: 18, 3: 19, 4: 20, 5: 21 (non-blackjack), 6: blackjack (nil), 7: bust. -- It is assumed that the dealer has already checked for blackjack, that one deck is used -- and that the dealer stands on 'soft' 17. -- sequence res := repeat(0.0, 7) // results sequence decks := repeat({}, 9) // decks for each level sequence scores := repeat(0, 9) // scores for each level sequence elevens := repeat(0, 9) // number of aces for each level scored as 11 sequence probs := repeat(0.0, 9) // probs for each level decks[1] = startDeck scores[1] = upCard if upCard == 1 then // an ace scores[1] = 11 elevens[1] = 1 end if probs[1] = 1.0 integer lev = 1 sequence cs = repeat(0,9) while lev do cs[lev] += 1 integer c = cs[lev] if c>10 then lev -= 1 elsif decks[lev][c]!=0 then -- card still present in deck // temporary variables for current level sequence deck := deep_copy(decks[lev]) integer score := scores[lev] integer eleven := elevens[lev] atom prob := probs[lev] score += c // add card to score if c == 1 then // score all aces initially as 11 score += 10 eleven += 1 end if prob *= deck[c]/deck[11] if score > 21 and eleven > 0 then score -= 10 // bust but can demote an ace eleven -= 1 end if if lev == 1 and ((upCard == 1 and c == 10) or (upCard == 10 and c == 1)) then res[6] += prob // blackjack, allow for now elsif score >= 17 and score <= 21 then res[score-16] += prob // 17 to (non-blackjack) 21 elsif score > 21 and eleven == 0 then res[7] += prob // bust else deck[c] -= 1 // remove card from deck deck[11] -= 1 // decrement deck size lev += 1 decks[lev] = deck scores[lev] = score elevens[lev] = eleven probs[lev] = prob -- get another card: cs[lev] = 0 end if end if end while // but can't have blackjack, so adjust probabilities accordingly atom pnbj := 1 - res[6] for i=1 to 7 do res[i] /= pnbj end for res[6] = 0 return res end function procedure dealerChart() // Prints chart of dealer probabilities (as a check against an external source). printf(1,"Dealer Probabilities, Stands on Soft 17, 1 Deck, U.S Rules\n") printf(1,"Up Card 17 18 19 20 21 Bust\n") printf(1,"-------------------------------------------------------------------\n") sequence deck := NewDeck() deck[11] = 51 for uc=1 to 10 do sequence deck2 := deep_copy(deck) deck2[uc] -= 1 sequence dp := dealerProbs(uc, deck2) if uc > 1 then printf(1,"%3d ", uc) else printf(1,"Ace ") end if dp[6] = dp[7] printf(1,"%f %f %f %f %f %f\n", dp) end for end procedure // Calculates gain per unit staked for a given scenario (helper function). function calcGain(integer pscore, sequence dp) atom eg := 0.0 switch pscore do case 17: eg += dp[7] // dealer is bust eg -= dp[2] + dp[3] + dp[4] + dp[5] // dealer has 18 to 21 case 18: eg += dp[1] + dp[7] // dealer has 17 or is bust eg -= dp[3] + dp[4] + dp[5] // dealer has 19 to 21 case 19: eg += dp[1] + dp[2] + dp[7] // dealer has 17, 18 or is bust eg -= dp[4] + dp[5] // dealer has 20 or 21 case 20: eg += dp[1] + dp[2] + dp[3] + dp[7] // dealer has 17 to 19 or is bust eg -= dp[5] // dealer has (non-blackjack) 21 case 21: eg += dp[1] + dp[2] + dp[3] + dp[4] + dp[7] // dealer has 17 to 20 or is bust case 22: // notional eg += 1.5 // player blackjack case 23: // notional eg -= 1 // player bust, loses stake irrespective of what dealer has default: // player has less than 17 eg += dp[7] // dealer is bust eg -= (1 - dp[7]) // dealer isn't bust end switch return eg end function function playerGain(integer card1, card2, uc, sequence startDeck) // Returns player's expected gain per unit staked after hitting once and then standing. atom eg := 0.0 sequence deck := startDeck integer score := card1 + card2 bool eleven := false if card1 == 1 or card2 == 1 then // an ace score += 10 eleven = true end if for c=1 to 10 do // get another card if deck[c] != 0 then // card still present in deck // temporary variables for current card sequence deck2 := deep_copy(deck) integer score2 := score --DEV shouldn't this be false? bool eleven2 := eleven score2 += c // add card to score if c == 1 then // score all aces initially as 11 score2 += 10 eleven2 = true end if atom prob := deck2[c] / deck2[11] deck2[c] -= 1 // remove card from deck deck2[11] -= 1 // decrement deck size if score2 > 21 and eleven2 then score2 -= 10 // bust but can demote an ace end if if score2 <= 21 then sequence dp := dealerProbs(uc, deck2) eg += calcGain(score2, dp) * prob else // bust eg -= prob end if end if end for return eg end function function playerGain2(integer card1, card2, uc, sequence startDeck) // Returns player's expected gain per unit staked after hitting once and then continuing in accordance // with the tables for rounds >= 2. atom eg := 0.0 // result sequence decks := repeat({}, 9) // decks for each level sequence scores := repeat(0, 9) // scores for each level sequence elevens := repeat(0, 9) // number of aces for each level scored as 11 sequence probs := repeat(0.0, 9) // probs for each level decks[1] = startDeck scores[1] = card1 + card2 if card1 == 1 or card2 == 1 then // an ace scores[1] += 10 elevens[1] = 1 end if probs[1] = 1.0 integer lev = 1 sequence cs = repeat(0,9) while lev do cs[lev] += 1 integer c = cs[lev] if c>10 then lev -= 1 elsif decks[lev][c]!=0 then -- card still present in deck // temporary variables for current level sequence deck := deep_copy(decks[lev]) integer score := scores[lev] integer eleven := elevens[lev] atom prob := probs[lev] score += c // add card to score if c == 1 then // score all aces initially as 11 score += 10 eleven += 1 end if prob *= deck[c] / deck[11] if score > 21 and eleven > 0 then score -= 10 // bust but can demote an ace eleven -= 1 end if deck[c] -= 1 // remove card from deck deck[11] -= 1 // decrement deck size if ((eleven == 0 and (score >= 17 or (score >= 13 and uc < 7))) or (eleven == 0 and score == 12 and uc >= 4 and uc <= 6) or (eleven > 0 and score == 18 and uc != 9 and uc != 10) or (eleven > 0 and score >= 19)) and score <= 21 then sequence dp := dealerProbs(uc, deck) eg += calcGain(score, dp) * prob elsif score > 21 and eleven == 0 then // bust eg -= prob else lev += 1 decks[lev] = deck scores[lev] = score elevens[lev] = eleven probs[lev] = prob // get another card: cs[lev] = 0 end if end if end while return eg end function function stand(integer card1, card2) // Returns player's expected gains per unit staked, for each dealer up-card, after standing. sequence deck := NewDeck() deck[card1] -= 1 deck[card2] -= 1 deck[11] = 50 integer pscore := card1 + card2 // player score if card1 == 1 or card2 == 1 then pscore += 10 end if sequence egs = repeat(0,10) // results for uc=1 to 10 do // dealer's up-card sequence deck2 := deep_copy(deck) deck2[uc] -= 1 deck2[11] -= 1 sequence dp := dealerProbs(uc, deck2) atom eg := calcGain(pscore, dp) // expected gain for this up-card if uc > 1 then egs[uc-1] = eg else // dealer has Ace egs[10] = eg // ace comes last in tables end if end for return egs end function function hit(integer card1, card2, bool once) // Returns player's expected gains per unit staked, for each dealer up-card, after hitting once and // then either standing (once == true) or continuing as per the round >= 2 tables (once == false). sequence deck := NewDeck() deck[card1] -= 1 deck[card2] -= 1 deck[11] = 50 sequence egs =repeat(0,10) // results for uc=1 to 10 do // dealer's up-card sequence deck2 := deep_copy(deck) deck2[uc] -= 1 deck2[11] = 49 atom peg // player's expected gain for this up-card if once then peg = playerGain(card1, card2, uc, deck2) else peg = playerGain2(card1, card2, uc, deck2) end if if uc > 1 then egs[uc-1] = peg else // dealer has Ace egs[10] = peg end if end for return egs end function function double(integer card1, card2) // Returns player's expected gains per unit oiginally staked, for each dealer up-card, after // doubling i.e. hitting once and then standing with a doubled stake. sequence egs := hit(card1, card2, true) // hit once and then stand for i=1 to 10 do egs[i] *= 2 end for return egs end function // Computed strategy tables. sequence hTable = repeat(repeat("",10),15) // hard strategy table (round 1) sequence sTable = repeat(repeat("",10),8) // soft strategy table (round 1) sequence pTable = repeat(repeat("",10),10) // pairs strategy table (round 1) sequence hTable2 = repeat(repeat("",10),18) // hard strategy table (round >= 2, no doubling) sequence sTable2 = repeat(repeat("",10),10) // soft strategy table (round >= 2, no doubling) function splitt(integer card) // Returns player's expected gains per unit originally staked, for each dealer up-card, after // splitting a pair and doubling the stake, getting a second card for each hand and then continuing // in accordace with the rounds >= 2 tables. It is assumed that a player cannot double or re-split // following a split. It is also assumed (in the interests of simplicity) that the expected gains // for each split hand (after calculating the gains for the first hand as though the second hand // is not completed) are exactly the same. sequence deck := NewDeck() deck[card] -= 2 // must be a pair deck[11] = 50 sequence egs = repeat(0,10) // overall results // now play a single hand integer score := card, eleven := 0 if card == 1 then // an ace score = 11 eleven = 1 end if for uc=1 to 10 do // collect results for each dealer up-card if deck[uc] != 0 then // card still present in deck sequence deck2 := deep_copy(deck) deck2[uc] -= 1 deck2[11] -= 1 integer ix := uc - 1 if ix == 0 then ix = 10 // in tables ace comes last end if atom peg = 0 // player expected gain for this up-card // get second player card for c=1 to 10 do if deck2[c] != 0 then // card still present in deck atom prob := deck2[c] / deck2[11] sequence deck3 := deep_copy(deck2) deck3[c] -= 1 deck3[11] -= 1 integer score2 := score + c, eleven2 := eleven if c == 1 then // score all aces initially as 11 score2 += 10 eleven2 += 1 end if if score2 == 21 then // player has blackjack & we know dealer hasn't peg += 1.5 * prob else if score2 > 21 and eleven2 > 0 then score2 -= 10 // bust but can demote an ace eleven2 -= 1 end if string action if eleven2 > 0 then action = sTable2[score2-11][ix] // use soft strategy table, no doubling else // including pairs as no re-splitting action = hTable2[score2-3][ix] // use hard strategy table, no doubling end if atom peg2 if action == "S" then sequence dp := dealerProbs(uc, deck3) peg2 = calcGain(score2, dp) else peg2 = playerGain2(card, c, uc, deck3) end if peg += peg2 * prob end if end if end for if uc > 1 then egs[uc-1] = peg * 2 // allow for both hands in overall results else egs[10] = peg * 2 // ditto end if end if end for return egs end function function bestAction(sequence ags) // Returns the action with the highest expected gain. atom best := ags[1][2] integer besti := 1 for i=2 to length(ags) do if ags[i][2] > best then best = ags[i][2] besti = i end if end for return ags[besti][1] end function procedure printHeader(string title) // Prints title and header for a given chart. printf(1,"%s\n",title) printf(1,"P/D 2 3 4 5 6 7 8 9 T A\n") printf(1,"--------------------------------------------------------------------------\n") end procedure procedure printPair(integer c) // Prints header for a pair of cards. c = "A23456789T"[c] printf(1,"%c%c ",c) end procedure sequence cards, fscores integer next, score, aces, uc string action function dealerPlay(integer pscore, sequence d) // Simulates a dealer's play for a given player's hand and state of deck. // Returns the player's gain (positive or negative) per unit staked. integer dscore := d[1] + d[2], aces := 0 if d[1] == 1 or d[2] == 1 then // dealer has an ace dscore += 10 aces += 1 end if while true do if dscore > 21 and aces > 0 then dscore -= 10 // bust but we can demote an ace aces -= 1 end if if dscore > 21 then return 1 // dealer is bust and player gains stake end if if dscore >= 17 then // dealer must stick on 17 or above, hard or not if dscore > pscore then return -1 // dealer wins and player loses stake elsif dscore == pscore then exit // player breaks even else return 1 // dealer loses and player gains stake end if end if integer nc := cards[next] // get new card from pack next += 1 dscore += nc if nc == 1 then // count aces initially as 11 dscore += 10 aces += 1 end if end while return 0 end function procedure h(integer hand) // simulate a 'hit' while true do integer nc := cards[next] // get new card from pack next += 1 score += nc if nc == 1 then // count aces initially as 11 score += 10 aces += 1 end if if score > 21 and aces > 0 then score -= 10 // bust but we can demote an ace aces -= 1 end if if score > 21 then fscores[hand] = 22 // player is bust and loses stake return end if if action == "D" then fscores[hand] = score return end if // get further strategy and act accordingly if aces == 0 then action = hTable2[score-3][uc] else action = sTable2[score-11][uc] end if if action == "S" then // stand fscores[hand] = score return end if end while end procedure function playerPlay() // Simulates the playing of a random player's hand according to the strategy tables. // Returns both the gain (positive or negative) and the stake (1 or 2). sequence perm = shuffle(tagset(52)) // randomizes integers from 1 to 52 inclusive cards := repeat(0,52) for i=1 to length(perm) do integer r = perm[i] integer card := floor((r-1)/4) + 1 if card > 10 then card = 10 end if cards[i] = card end for sequence p = {}, d = {} // player and dealer hands // initial deal for i=1 to 4 do if i <= 2 then p = append(p, cards[i]) else d = append(d, cards[i]) end if end for next := 5 // index of next card to be dealt // check if dealer and/or player have blackjack bool dbj := (d[1] == 1 and d[2] == 10) or (d[1] == 10 and d[2] == 1), pbj := (p[1] == 1 and p[2] == 10) or (p[1] == 10 and p[2] == 1) if dbj then if pbj then return {0.0, 1.0} // player neither wins nor loses end if return {-1.0, 1.0} // player loses stake end if if pbj then return {1.5, 1.0} // player wins 1.5 x stake end if uc := d[1] // dealer's up-card for accessing tables if uc == 1 then uc = 10 // move ace to last place else uc -= 1 // move others down 1 end if atom stake := 1.0 // player's initial stake fscores = {0,0} // final player scores (one or, after split, two hands) score = p[1] + p[2] aces = 0 // get kind of player hand: hard, soft, pair string kind if p[1] == p[2] then kind = "pair" elsif p[1] == 1 or p[2] == 1 then kind = "soft" else kind = "hard" end if switch kind do case "hard": action = hTable[score-4][uc] case "soft": // includes one ace integer otherCard := p[1] if otherCard == 1 then otherCard = p[2] end if score += 10 aces = 1 action = sTable[otherCard-1][uc] case "pair": if p[1] == 1 then // pair of aces score += 10 aces = 2 end if action = pTable[p[1]][uc] end switch switch action do case "S": // stand fscores[1] = score case "H": // hit h(1) case "D": // double h(1) stake = 2 case "P": // split for hand=1 to 2 do score = p[1] aces = 0 if score == 1 then // count aces initially as 11 score = 11 aces += 1 end if h(hand) end for end switch atom tot := 0.0 if fscores[1] < 22 then tot += dealerPlay(fscores[1], d) * stake else tot -= 1 * stake // this hand is bust end if if fscores[2] > 0 then // pair if fscores[1] < 22 then tot += dealerPlay(fscores[2], d) else tot -= 1 // this hand is bust end if stake = 2 end if return {tot, stake} end function procedure simulate(integer perDay, days) // Simulates 'perDay' blackjack games for 'days' days. integer winDays = 0, loseDays = 0, evenDays = 0 atom bigWin = 0.0, bigLoss = 0.0, totalGain = 0.0, totalStake = 0.0 for d=1 to days do atom dailyGain = 0.0, dailyStake = 0.0 for p=1 to perDay do atom {gain, stake} := playerPlay() dailyGain += gain dailyStake += stake end for if dailyGain > 0 then winDays += 1 elsif dailyGain < 0 then loseDays += 1 else evenDays += 1 end if if dailyGain > bigWin then bigWin = dailyGain elsif -dailyGain > bigLoss then bigLoss = -dailyGain end if totalGain += dailyGain totalStake += dailyStake end for printf(1,"\nAfter playing %d times a day for %d days:\n", {perDay, days}) printf(1,"Winning days :%d\n", winDays) printf(1,"Losing days :%d\n", loseDays) printf(1,"Breakeven days :%d\n", evenDays) printf(1,"Biggest win :%.2f\n", bigWin) printf(1,"Biggest loss :%.2f\n", bigLoss) if totalGain < 0 then printf(1,"Total loss :%.2f\n", -totalGain) printf(1,"Total staked :%.2f\n", totalStake) printf(1,"Loss %% staked : %0.3f\n", -totalGain/totalStake*100) else printf(1,"Total win :%.2f\n", totalGain) printf(1,"Total staked :%.2f\n", totalStake) printf(1,"Win %% staked : %0.3f\n", totalGain/totalStake*100) end if end procedure procedure main() // print dealer probabilities chart dealerChart() // for hard scores (i.e. different cards, no aces) sequence tuples := { {2, 3}, {2, 4}, {2, 5}, {3, 4}, {2, 6}, {3, 5}, {2, 7}, {3, 6}, {4, 5}, {2, 8}, {3, 7}, {4, 6}, {2, 9}, {3, 8}, {4, 7}, {5, 6}, {2, 10}, {3, 9}, {4, 8}, {5, 7}, {3, 10}, {4, 9}, {5, 8}, {6, 7}, {4, 10}, {5, 9}, {6, 8}, {5, 10}, {6, 9}, {7, 8}, {6, 10}, {7, 9}, {7, 10}, {8, 9}, {8, 10}, {9, 10}, } // number of tuples for each player score from 5 to 19 sequence counts := {1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1} // expected gains for each player score & for each dealer up-card sequence segs := repeat(repeat(0,10),15) // if stands sequence hegs := repeat(repeat(0,10),15) // if hits sequence degs := repeat(repeat(0,10),15) // if doubles for t = 1 to length(tuples) do if platform()!=JS then progress("%d/%d\r",{t,length(tuples)}) end if sequence tuple := tuples[t] integer i := tuple[1] + tuple[2] sequence sg := stand(tuple[1], tuple[2]) sequence hg := hit(tuple[1], tuple[2], false) sequence dg := double(tuple[1], tuple[2]) for j=1 to 10 do segs[i-4][j] += sg[j] hegs[i-4][j] += hg[j] degs[i-4][j] += dg[j] end for end for if platform()!=JS then progress("") end if // calculate the average per tuple for each score for i=1 to 15 do for j=1 to 10 do segs[i][j] /= counts[i] hegs[i][j] /= counts[i] degs[i][j] /= counts[i] end for end for printHeader("\nHard Chart - Player Expected Gains per unit (Stand)") for i=5 to 19 do printf(1,"%2d ", i) for j=1 to 10 do printf(1,"%6.3f ", segs[i-4][j]) end for printf(1,"\n") end for printHeader("\nHard Chart - Player Expected Gains per unit (Hit)") for i=5 to 19 do printf(1,"%2d ", i) for j=1 to 10 do printf(1,"%6.3f ", hegs[i-4][j]) end for printf(1,"\n") end for printHeader("\nHard Chart - Player Expected Gains per original unit (Double)") for i=5 to 19 do printf(1,"%2d ", i) for j=1 to 10 do printf(1,"%6.3f ", degs[i-4][j]) end for printf(1,"\n") end for printHeader("\nHard Chart - Player Strategy (Round 1)") for i=5 to 19 do printf(1,"%2d ", i) for j=1 to 10 do sequence ags := {{"S", segs[i-4][j]}, {"H", hegs[i-4][j]}, {"D", degs[i-4][j]}} string action := bestAction(ags) hTable[i-4][j] = action printf(1,"%4s ", action) end for printf(1,"\n") end for // for hard scores (no aces) - after round 1 (no doubling or splitting) // based on hard table figures (round 1) with scores of 4, 20, and 21 added sequence segs2 := repeat(repeat(0,10),18) // expected gains if stands sequence hegs2 := repeat(repeat(0,10),18) // expected gains if hits for i=5 to 19 do segs2[i-3] = segs[i-4] hegs2[i-3] = hegs[i-4] end for sequence sg4 = stand(2, 2), hg4 = hit(2, 2, false), sg20 = stand(10, 10), hg20 = hit(10, 10, false), sg21 = stand(1, 10), hg21 = hit(1, 10, false) for j=1 to 10 do segs2[1][j] += sg4[j] hegs2[1][j] += hg4[j] segs2[17][j] += sg20[j] hegs2[17][j] += hg20[j] segs2[18][j] += sg21[j] hegs2[18][j] += hg21[j] end for printHeader("\nHard Chart - Player Strategy (Round >= 2, No Doubling)") for i=4 to 21 do printf(1,"%2d ", i) for j=1 to 10 do string action := "S" if hegs2[i-3][j] > segs2[i-3][j] then action = "H" end if hTable2[i-3][j] = action printf(1,"%4s ", action) end for printf(1,"\n") end for // for soft scores (i.e. including exactly one ace) // expected gains for each player second card (2 to 9) & for each dealer up-card sequence segs3 = repeat(repeat(0,10),8), // if stands hegs3 = repeat(repeat(0,10),8), // if hits degs3 = repeat(repeat(0,10),8) // if doubles for c=2 to 9 do sequence sg := stand(1, c), hg := hit(1, c, false), dg := double(1, c) for j=1 to 10 do segs3[c-1][j] += sg[j] hegs3[c-1][j] += hg[j] degs3[c-1][j] += dg[j] end for end for printHeader("\nSoft Chart - Player Expected Gains per unit (Stand)") for c=2 to 9 do printf(1,"A%d ", c) for j=1 to 10 do printf(1,"%6.3f ", segs3[c-1][j]) end for printf(1,"\n") end for printHeader("\nSoft Chart - Player Expected Gains per unit (Hit)") for c=2 to 9 do printf(1,"A%d ", c) for j=1 to 10 do printf(1,"%6.3f ", hegs3[c-1][j]) end for printf(1,"\n") end for printHeader("\nSoft Chart - Player Expected Gains per original unit (Double)") for c=2 to 9 do printf(1,"A%d ", c) for j=1 to 10 do printf(1,"%6.3f ", degs3[c-1][j]) end for printf(1,"\n") end for printHeader("\nSoft Chart - Player Strategy (Round 1)") for c=2 to 9 do printf(1,"A%d ", c) for j=1 to 10 do sequence ags := {{"S", segs3[c-1][j]}, {"H", hegs3[c-1][j]}, {"D", degs3[c-1][j]}} string action := bestAction(ags) sTable[c-1][j] = action printf(1,"%4s ", action) end for printf(1,"\n") end for // for soft scores (at least one ace) - after round 1 (no doubling or splitting) // based on soft table figures (round 1) with scores of 12 and 21 added // assumes one ace counted as 11 sequence segs4 := repeat(repeat(0,10),10), // expected gains if stands hegs4 := repeat(repeat(0,10),10) // expected gains if hits for i=2 to 9 do segs4[i] = segs3[i-1] hegs4[i] = hegs3[i-1] end for sequence sg12 = stand(1, 1), hg12 = hit(1, 1, false) for j=1 to 10 do segs4[1][j] += sg12[j] hegs4[1][j] += hg12[j] segs4[10][j] += sg21[j] hegs4[10][j] += hg21[j] end for printHeader("\nSoft Chart - Player Strategy (Round >= 2, No Doubling)") for i=12 to 21 do printf(1,"%2d ", i) for j=1 to 10 do string action := "S" if hegs4[i-11][j] > segs4[i-11][j] then action = "H" end if sTable2[i-11][j] = action printf(1,"%4s ", action) end for printf(1,"\n") end for // for pairs // expected gains for each pair (A to 10) & for each dealer up-card sequence segs5 := repeat(repeat(0,10),10), // if stands hegs5 := repeat(repeat(0,10),10), // if hits degs5 := repeat(repeat(0,10),10), // if doubles pegs5 := repeat(repeat(0,10),10) // if splits for c=1 to 10 do if platform()!=JS then progress("%d/10",{c}) end if sequence sg := stand(c, c), hg := hit(c, c, false), dg := double(c, c), pg := splitt(c) for j=1 to 10 do segs5[c][j] += sg[j] hegs5[c][j] += hg[j] degs5[c][j] += dg[j] pegs5[c][j] += pg[j] end for end for if platform()!=JS then progress("") end if printHeader("\nPairs Chart - Player Expected Gains per unit (Stand)") for c=1 to 10 do printPair(c) for j=1 to 10 do printf(1,"%6.3f ", segs5[c][j]) end for printf(1,"\n") end for printHeader("\nPairs Chart - Player Expected Gains per unit (Hit)") for c=1 to 10 do printPair(c) for j=1 to 10 do printf(1,"%6.3f ", hegs5[c][j]) end for printf(1,"\n") end for printHeader("\nPairs Chart - Player Expected Gains per original unit (Double)") for c=1 to 10 do printPair(c) for j=1 to 10 do printf(1,"%6.3f ", degs5[c][j]) end for printf(1,"\n") end for printHeader("\nPairs Chart - Player Expected Gains per original unit (Split)") for c=1 to 10 do printPair(c) for j=1 to 10 do printf(1,"%6.3f ", pegs5[c][j]) end for printf(1,"\n") end for printHeader("\nPairs Chart - Player Strategy (Round 1)") for c=1 to 10 do printPair(c) for j=1 to 10 do sequence ags := {{"S", segs5[c][j]}, {"H", hegs5[c][j]}, {"D", degs5[c][j]}, {"P", pegs5[c][j]}} string action := bestAction(ags) pTable[c][j] = action printf(1,"%4s ", action) end for printf(1,"\n") end for // do 10 years of simulations for i=1 to 10 do printf(1,"\nSimulation for Year %d:\n", i) simulate(50, 365) end for end procedure main() ?"done" {} = wait_key()
- Output:
Dealer Probabilities, Stands on Soft 17, 1 Deck, U.S Rules Up Card 17 18 19 20 21 Bust ------------------------------------------------------------------- Ace 0.183786 0.190890 0.188680 0.191692 0.075137 0.169815 2 0.138976 0.131762 0.131815 0.123948 0.120526 0.352973 3 0.130313 0.130946 0.123761 0.123345 0.116047 0.375588 4 0.130973 0.114163 0.120679 0.116286 0.115096 0.402803 5 0.119687 0.123483 0.116909 0.104694 0.106321 0.428905 6 0.166948 0.106454 0.107192 0.100705 0.097878 0.420823 7 0.372345 0.138583 0.077334 0.078897 0.072987 0.259854 8 0.130857 0.362989 0.129445 0.068290 0.069791 0.238627 9 0.121886 0.103921 0.357391 0.122250 0.061109 0.233442 10 0.124156 0.122486 0.124421 0.356869 0.039570 0.232499 Hard Chart - Player Expected Gains per unit (Stand) P/D 2 3 4 5 6 7 8 9 T A -------------------------------------------------------------------------- 5 -0.293 -0.248 -0.176 -0.104 -0.122 -0.469 -0.513 -0.533 -0.546 -0.659 6 -0.291 -0.232 -0.172 -0.101 -0.119 -0.467 -0.522 -0.533 -0.547 -0.659 7 -0.283 -0.229 -0.163 -0.098 -0.117 -0.471 -0.521 -0.537 -0.547 -0.658 8 -0.276 -0.229 -0.162 -0.100 -0.130 -0.478 -0.523 -0.539 -0.549 -0.648 9 -0.277 -0.224 -0.160 -0.108 -0.134 -0.480 -0.528 -0.543 -0.542 -0.646 10 -0.279 -0.227 -0.172 -0.120 -0.146 -0.484 -0.531 -0.539 -0.537 -0.644 11 -0.277 -0.231 -0.175 -0.123 -0.147 -0.488 -0.529 -0.537 -0.537 -0.646 12 -0.286 -0.241 -0.185 -0.134 -0.151 -0.485 -0.526 -0.535 -0.533 -0.655 13 -0.282 -0.236 -0.181 -0.133 -0.156 -0.488 -0.529 -0.537 -0.534 -0.649 14 -0.282 -0.238 -0.188 -0.134 -0.159 -0.489 -0.529 -0.533 -0.536 -0.651 15 -0.280 -0.239 -0.190 -0.144 -0.169 -0.494 -0.531 -0.536 -0.531 -0.648 16 -0.287 -0.250 -0.194 -0.152 -0.179 -0.495 -0.526 -0.540 -0.530 -0.648 17 -0.147 -0.120 -0.074 -0.044 -0.011 -0.122 -0.405 -0.414 -0.402 -0.459 18 0.119 0.144 0.164 0.202 0.268 0.389 0.096 -0.196 -0.155 -0.082 19 0.385 0.384 0.404 0.448 0.484 0.610 0.577 0.264 0.103 0.308 Hard Chart - Player Expected Gains per unit (Hit) P/D 2 3 4 5 6 7 8 9 T A -------------------------------------------------------------------------- 5 -0.131 -0.098 -0.041 0.022 0.019 -0.119 -0.181 -0.262 -0.309 -0.417 6 -0.151 -0.107 -0.055 0.009 0.014 -0.164 -0.234 -0.305 -0.349 -0.443 7 -0.111 -0.072 -0.013 0.053 0.064 -0.069 -0.223 -0.295 -0.332 -0.401 8 -0.015 0.021 0.084 0.136 0.148 0.092 -0.056 -0.213 -0.253 -0.275 9 0.090 0.137 0.181 0.226 0.235 0.194 0.111 -0.052 -0.148 -0.128 10 0.215 0.246 0.277 0.314 0.319 0.277 0.211 0.119 0.030 0.030 11 0.272 0.296 0.327 0.361 0.362 0.293 0.222 0.146 0.107 0.113 12 -0.256 -0.232 -0.206 -0.181 -0.179 -0.241 -0.308 -0.380 -0.378 -0.413 13 -0.315 -0.293 -0.270 -0.252 -0.251 -0.301 -0.362 -0.389 -0.423 -0.440 14 -0.363 -0.353 -0.337 -0.315 -0.313 -0.346 -0.366 -0.426 -0.455 -0.460 15 -0.419 -0.414 -0.406 -0.392 -0.383 -0.351 -0.406 -0.466 -0.496 -0.487 16 -0.461 -0.460 -0.454 -0.448 -0.397 -0.376 -0.426 -0.481 -0.510 -0.497 17 -0.534 -0.536 -0.538 -0.493 -0.484 -0.450 -0.475 -0.529 -0.558 -0.546 18 -0.633 -0.634 -0.597 -0.591 -0.586 -0.567 -0.565 -0.593 -0.624 -0.630 19 -0.750 -0.713 -0.712 -0.709 -0.707 -0.699 -0.697 -0.698 -0.712 -0.740 Hard Chart - Player Expected Gains per original unit (Double) P/D 2 3 4 5 6 7 8 9 T A -------------------------------------------------------------------------- 5 -0.587 -0.497 -0.352 -0.209 -0.244 -0.938 -1.025 -1.066 -1.093 -1.318 6 -0.560 -0.446 -0.324 -0.186 -0.215 -0.870 -1.023 -1.045 -1.074 -1.295 7 -0.415 -0.317 -0.186 -0.066 -0.059 -0.555 -0.851 -0.936 -0.956 -1.127 8 -0.165 -0.081 0.032 0.143 0.157 -0.140 -0.433 -0.697 -0.743 -0.802 9 0.114 0.193 0.286 0.380 0.393 0.175 0.007 -0.281 -0.442 -0.409 10 0.428 0.492 0.554 0.628 0.638 0.446 0.313 0.164 0.007 0.025 11 0.542 0.592 0.654 0.722 0.724 0.479 0.341 0.223 0.164 0.198 12 -0.511 -0.463 -0.413 -0.362 -0.358 -0.556 -0.690 -0.811 -0.789 -0.827 13 -0.630 -0.587 -0.541 -0.503 -0.503 -0.651 -0.775 -0.807 -0.862 -0.880 14 -0.727 -0.706 -0.673 -0.630 -0.627 -0.723 -0.759 -0.862 -0.915 -0.921 15 -0.838 -0.829 -0.812 -0.783 -0.767 -0.716 -0.826 -0.937 -0.992 -0.973 16 -0.921 -0.920 -0.908 -0.896 -0.793 -0.751 -0.853 -0.961 -1.019 -0.995 17 -1.069 -1.072 -1.076 -0.985 -0.967 -0.901 -0.949 -1.058 -1.116 -1.092 18 -1.265 -1.267 -1.195 -1.182 -1.172 -1.135 -1.130 -1.186 -1.248 -1.260 19 -1.499 -1.425 -1.423 -1.417 -1.414 -1.397 -1.395 -1.396 -1.425 -1.481 Hard Chart - Player Strategy (Round 1) P/D 2 3 4 5 6 7 8 9 T A -------------------------------------------------------------------------- 5 H H H H H H H H H H 6 H H H H H H H H H H 7 H H H H H H H H H H 8 H H H D D H H H H H 9 D D D D D H H H H H 10 D D D D D D D D H H 11 D D D D D D D D D D 12 H H S S S H H H H H 13 S S S S S H H H H H 14 S S S S S H H H H H 15 S S S S S H H H H H 16 S S S S S H H H H H 17 S S S S S S S S S S 18 S S S S S S S S S S 19 S S S S S S S S S S Hard Chart - Player Strategy (Round >= 2, No Doubling) P/D 2 3 4 5 6 7 8 9 T A -------------------------------------------------------------------------- 4 H H H H H H H H H H 5 H H H H H H H H H H 6 H H H H H H H H H H 7 H H H H H H H H H H 8 H H H H H H H H H H 9 H H H H H H H H H H 10 H H H H H H H H H H 11 H H H H H H H H H H 12 H H S S S H H H H H 13 S S S S S H H H H H 14 S S S S S H H H H H 15 S S S S S H H H H H 16 S S S S S H H H H H 17 S S S S S S S S S S 18 S S S S S S S S S S 19 S S S S S S S S S S 20 S S S S S S S S S S 21 S S S S S S S S S S Soft Chart - Player Expected Gains per unit (Stand) P/D 2 3 4 5 6 7 8 9 T A -------------------------------------------------------------------------- A2 -0.283 -0.241 -0.186 -0.119 -0.114 -0.462 -0.508 -0.517 -0.539 -0.662 A3 -0.284 -0.240 -0.170 -0.116 -0.112 -0.460 -0.505 -0.527 -0.538 -0.661 A4 -0.283 -0.224 -0.166 -0.113 -0.109 -0.458 -0.514 -0.526 -0.538 -0.659 A5 -0.266 -0.221 -0.164 -0.111 -0.108 -0.468 -0.515 -0.525 -0.537 -0.659 A6 -0.132 -0.093 -0.037 0.005 0.010 -0.090 -0.385 -0.407 -0.418 -0.483 A7 0.136 0.167 0.204 0.222 0.262 0.412 0.121 -0.179 -0.186 -0.101 A8 0.402 0.420 0.415 0.461 0.482 0.615 0.608 0.288 0.064 0.290 A9 0.656 0.644 0.654 0.682 0.694 0.773 0.785 0.766 0.555 0.681 Soft Chart - Player Expected Gains per unit (Hit) P/D 2 3 4 5 6 7 8 9 T A -------------------------------------------------------------------------- A2 0.039 0.071 0.110 0.159 0.168 0.107 0.039 -0.014 -0.090 -0.184 A3 0.017 0.044 0.091 0.137 0.147 0.060 0.035 -0.060 -0.124 -0.216 A4 -0.012 0.022 0.061 0.108 0.120 0.034 -0.035 -0.114 -0.172 -0.256 A5 -0.032 -0.003 0.038 0.082 0.116 -0.024 -0.084 -0.167 -0.229 -0.296 A6 0.007 0.036 0.077 0.140 0.133 0.060 -0.065 -0.135 -0.189 -0.242 A7 0.065 0.093 0.156 0.175 0.192 0.175 0.047 -0.087 -0.140 -0.160 A8 0.120 0.173 0.187 0.227 0.241 0.222 0.158 0.005 -0.087 -0.081 A9 0.191 0.196 0.230 0.268 0.280 0.243 0.172 0.096 0.007 -0.008 Soft Chart - Player Expected Gains per original unit (Double) P/D 2 3 4 5 6 7 8 9 T A -------------------------------------------------------------------------- A2 -0.042 0.028 0.115 0.212 0.230 -0.157 -0.312 -0.373 -0.478 -0.586 A3 -0.047 0.011 0.109 0.204 0.222 -0.175 -0.254 -0.394 -0.479 -0.588 A4 -0.070 0.003 0.085 0.175 0.201 -0.141 -0.314 -0.422 -0.495 -0.613 A5 -0.082 -0.019 0.063 0.148 0.217 -0.189 -0.333 -0.452 -0.536 -0.649 A6 0.013 0.074 0.155 0.280 0.266 0.014 -0.230 -0.345 -0.433 -0.522 A7 0.128 0.189 0.313 0.349 0.385 0.240 -0.015 -0.254 -0.322 -0.359 A8 0.237 0.346 0.373 0.453 0.483 0.325 0.190 -0.060 -0.226 -0.200 A9 0.380 0.392 0.459 0.536 0.560 0.351 0.230 0.111 -0.055 -0.055 Soft Chart - Player Strategy (Round 1) P/D 2 3 4 5 6 7 8 9 T A -------------------------------------------------------------------------- A2 H H D D D H H H H H A3 H H D D D H H H H H A4 H H D D D H H H H H A5 H H D D D H H H H H A6 D D D D D H H H H H A7 S D D D D S S H H S A8 S S S S D S S S S S A9 S S S S S S S S S S Soft Chart - Player Strategy (Round >= 2, No Doubling) P/D 2 3 4 5 6 7 8 9 T A -------------------------------------------------------------------------- 12 H H H H H H H H H H 13 H H H H H H H H H H 14 H H H H H H H H H H 15 H H H H H H H H H H 16 H H H H H H H H H H 17 H H H H H H H H H H 18 S S S S S S S H H S 19 S S S S S S S S S S 20 S S S S S S S S S S 21 S S S S S S S S S S Pairs Chart - Player Expected Gains per unit (Stand) P/D 2 3 4 5 6 7 8 9 T A -------------------------------------------------------------------------- AA -0.274 -0.232 -0.178 -0.130 -0.104 -0.452 -0.500 -0.511 -0.531 -0.663 22 -0.291 -0.251 -0.192 -0.107 -0.125 -0.471 -0.515 -0.523 -0.547 -0.660 33 -0.295 -0.246 -0.160 -0.101 -0.119 -0.467 -0.510 -0.542 -0.546 -0.660 44 -0.290 -0.214 -0.152 -0.095 -0.114 -0.463 -0.529 -0.543 -0.547 -0.656 55 -0.256 -0.206 -0.146 -0.090 -0.112 -0.484 -0.531 -0.541 -0.545 -0.653 66 -0.262 -0.211 -0.152 -0.102 -0.165 -0.493 -0.536 -0.549 -0.552 -0.617 77 -0.268 -0.219 -0.164 -0.156 -0.174 -0.502 -0.539 -0.555 -0.510 -0.631 88 -0.275 -0.228 -0.215 -0.165 -0.178 -0.503 -0.551 -0.516 -0.518 -0.644 99 0.137 0.123 0.167 0.203 0.265 0.401 0.065 -0.196 -0.133 -0.055 TT 0.627 0.636 0.645 0.674 0.697 0.765 0.783 0.744 0.583 0.650 Pairs Chart - Player Expected Gains per unit (Hit) P/D 2 3 4 5 6 7 8 9 T A -------------------------------------------------------------------------- AA 0.095 0.120 0.142 0.182 0.200 0.158 0.093 -0.003 -0.048 -0.075 22 -0.113 -0.082 -0.035 0.036 0.032 -0.092 -0.141 -0.222 -0.277 -0.395 33 -0.153 -0.118 -0.047 0.008 0.014 -0.164 -0.231 -0.310 -0.346 -0.444 44 -0.013 0.028 0.098 0.154 0.175 0.111 -0.055 -0.206 -0.246 -0.268 55 0.224 0.254 0.295 0.347 0.362 0.279 0.207 0.119 0.032 0.042 66 -0.253 -0.222 -0.190 -0.162 -0.194 -0.265 -0.322 -0.386 -0.386 -0.411 77 -0.406 -0.388 -0.369 -0.370 -0.367 -0.389 -0.408 -0.475 -0.516 -0.510 88 -0.454 -0.450 -0.461 -0.453 -0.397 -0.374 -0.426 -0.487 -0.512 -0.490 99 -0.627 -0.638 -0.597 -0.590 -0.587 -0.566 -0.566 -0.595 -0.626 -0.621 TT -0.847 -0.846 -0.846 -0.846 -0.845 -0.843 -0.843 -0.842 -0.840 -0.882 Pairs Chart - Player Expected Gains per original unit (Double) P/D 2 3 4 5 6 7 8 9 T A -------------------------------------------------------------------------- AA -0.019 0.055 0.137 0.216 0.248 -0.137 -0.296 -0.421 -0.468 -0.591 22 -0.582 -0.501 -0.384 -0.214 -0.249 -0.942 -1.030 -1.047 -1.094 -1.320 33 -0.567 -0.472 -0.302 -0.184 -0.215 -0.871 -1.000 -1.065 -1.072 -1.298 44 -0.185 -0.082 0.044 0.162 0.193 -0.108 -0.447 -0.701 -0.741 -0.802 55 0.446 0.510 0.590 0.695 0.724 0.466 0.323 0.175 0.014 0.042 66 -0.505 -0.444 -0.380 -0.325 -0.387 -0.599 -0.711 -0.817 -0.803 -0.823 77 -0.813 -0.777 -0.738 -0.741 -0.734 -0.823 -0.858 -0.978 -1.035 -1.019 88 -0.908 -0.900 -0.922 -0.906 -0.793 -0.747 -0.853 -0.974 -1.024 -0.980 99 -1.255 -1.277 -1.194 -1.181 -1.173 -1.132 -1.133 -1.189 -1.252 -1.242 TT -1.693 -1.693 -1.693 -1.691 -1.690 -1.686 -1.685 -1.684 -1.681 -1.764 Pairs Chart - Player Expected Gains per original unit (Split) P/D 2 3 4 5 6 7 8 9 T A -------------------------------------------------------------------------- AA 1.192 1.223 1.265 1.321 1.344 1.308 1.201 1.039 0.860 0.921 22 -0.128 -0.070 -0.007 0.128 0.126 -0.054 -0.213 -0.383 -0.463 -0.566 33 -0.202 -0.128 0.009 0.117 0.112 -0.115 -0.265 -0.418 -0.509 -0.579 44 -0.236 -0.127 -0.013 0.095 0.083 -0.223 -0.343 -0.493 -0.580 -0.623 55 -0.232 -0.150 -0.038 0.068 0.056 -0.299 -0.448 -0.608 -0.685 -0.695 66 -0.219 -0.135 -0.028 0.068 -0.011 -0.270 -0.413 -0.570 -0.652 -0.660 77 -0.163 -0.084 0.016 0.039 0.053 -0.123 -0.423 -0.564 -0.634 -0.635 88 0.017 0.077 0.106 0.188 0.234 0.202 -0.100 -0.430 -0.464 -0.378 99 0.170 0.170 0.253 0.339 0.359 0.341 0.179 -0.112 -0.268 -0.109 TT 0.412 0.465 0.518 0.596 0.619 0.576 0.447 0.276 0.146 0.140 Pairs Chart - Player Strategy (Round 1) P/D 2 3 4 5 6 7 8 9 T A -------------------------------------------------------------------------- AA P P P P P P P P P P 22 H P P P P P H H H H 33 H H P P P P H H H H 44 H H H D D H H H H H 55 D D D D D D D D H D 66 P P P P P H H H H H 77 P P P P P P H H S H 88 P P P P P P P P P P 99 P P P P P S P P S S TT S S S S S S S S S S Simulation for Year 1: After playing 50 times a day for 365 days: Winning days :186 Losing days :171 Breakeven days :8 Biggest win :25.00 Biggest loss :26.00 Total win :211.00 Total staked :20546.00 Win % staked : 1.027 Simulation for Year 2: After playing 50 times a day for 365 days: Winning days :174 Losing days :181 Breakeven days :10 Biggest win :25.50 Biggest loss :22.50 Total loss :213.00 Total staked :20536.00 Loss % staked : 1.037 Simulation for Year 3: After playing 50 times a day for 365 days: Winning days :174 Losing days :182 Breakeven days :9 Biggest win :23.00 Biggest loss :26.50 Total loss :204.50 Total staked :20542.00 Loss % staked : 0.996 Simulation for Year 4: After playing 50 times a day for 365 days: Winning days :181 Losing days :176 Breakeven days :8 Biggest win :25.50 Biggest loss :22.00 Total win :122.50 Total staked :20555.00 Win % staked : 0.596 Simulation for Year 5: After playing 50 times a day for 365 days: Winning days :164 Losing days :187 Breakeven days :14 Biggest win :25.50 Biggest loss :21.50 Total loss :87.00 Total staked :20566.00 Loss % staked : 0.423 Simulation for Year 6: After playing 50 times a day for 365 days: Winning days :180 Losing days :175 Breakeven days :10 Biggest win :22.50 Biggest loss :26.00 Total win :23.50 Total staked :20524.00 Win % staked : 0.115 Simulation for Year 7: After playing 50 times a day for 365 days: Winning days :175 Losing days :181 Breakeven days :9 Biggest win :21.50 Biggest loss :19.00 Total win :15.00 Total staked :20552.00 Win % staked : 0.073 Simulation for Year 8: After playing 50 times a day for 365 days: Winning days :185 Losing days :173 Breakeven days :7 Biggest win :20.00 Biggest loss :22.00 Total win :80.00 Total staked :20623.00 Win % staked : 0.388 Simulation for Year 9: After playing 50 times a day for 365 days: Winning days :172 Losing days :180 Breakeven days :13 Biggest win :20.50 Biggest loss :19.00 Total loss :1.50 Total staked :20589.00 Loss % staked : 0.007 Simulation for Year 10: After playing 50 times a day for 365 days: Winning days :172 Losing days :186 Breakeven days :7 Biggest win :24.50 Biggest loss :25.50 Total loss :15.00 Total staked :20436.00 Loss % staked : 0.073
Wren
This runs OK but takes a long time to do so - 6 minutes 21 seconds compared to less than 3 seconds for the Go example. As I also wrote the latter I'm confident it's a faithful translation.
The bottleneck seems to be the dealerProbs method which is called around 300,000 times during the course of the program and contains several arrays (heap allocated in Wren) plus a recursive closure and so is not cheap to run. Making the arrays global and resetting them each time the method is called doesn't help and memoization is out of the question as there are just too many parameter combinations.
So interpreted languages such as Wren are probably stuck with slow times for this task. <lang ecmascript>import "random" for Random import "./array" for Array, ArrayType import "./dynamic" for Struct import "./fmt" for Fmt import "./trait" for Indexed
var Rand = Random.new()
// 0: deck size, 1 to 10: number of cards of that denomination var Deck = ArrayType.create("Deck", 11, 0)
var ActionGain = Struct.create("ActionGain", ["action", "gain"])
var NewDeck = Fn.new { Deck.fit([52, 4, 4, 4, 4, 4, 4, 4, 4, 4, 16]) }
// Computed strategy tables var HTable = List.filled(15, null) // hard strategy table (round 1) var STable = List.filled( 8, null) // soft strategy table (round 1) var PTable = List.filled(10, null) // pairs strategy table (round 1) var HTable2 = List.filled(18, null) // hard strategy table (round >= 2, no doubling) var STable2 = List.filled(10, null) // soft strategy table (round >= 2, no doubling)
for (i in 0..14) HTable[i] = List.filled(10, null) for (i in 0..7) STable[i] = List.filled(10, null) for (i in 0..9) PTable[i] = List.filled(10, null) for (i in 0..17) HTable2[i] = List.filled(10, null) for (i in 0..9) STable2[i] = List.filled(10, null)
class Blackjack {
// Returns probabilities of dealer eventually getting: // 0: 17, 1: 18, 2: 19, 3: 20, 4: 21 (non-blackjack), 5: blackjack (nil), 6: bust. // It is assumed that the dealer has already checked for blackjack, that one deck is used // and that the dealer stands on 'soft' 17. static dealerProbs(upCard, startDeck) { var res = List.filled(7, 0) // results var decks = List.filled(9, null) // decks for each level var scores = List.filled(9, 0) // scores for each level var elevens = List.filled(9, 0) // number of aces for each level scored as 11 var probs = List.filled(9, 0) // probs for each level decks[0] = startDeck.toList scores[0] = upCard if (upCard == 1) { // an ace scores[0] = 11 elevens[0] = 1 } probs[0] = 1 var f // recursive closure f = Fn.new { |lev| for (c in 1..10) { if (decks[lev][c] == 0) continue // card no longer present in deck // temporary variables for current level var deck = decks[lev].toList var score = scores[lev] var eleven = elevens[lev] var prob = probs[lev] score = score + c // add card to score if (c == 1) { // score all aces initially as 11 score = score + 10 eleven = eleven + 1 } prob = prob * deck[c] / deck[0] if (score > 21 && eleven > 0) { score = score - 10 // bust but can demote an ace eleven = eleven - 1 } if (lev == 0 && ((upCard == 1 && c == 10) || (upCard == 10 && c == 1))) { res[5] = res[5] + prob // blackjack, allow for now } else if (score >= 17 && score <= 21) { res[score-17] = res[score-17] + prob // 17 to (non-blackjack) 21 } else if (score > 21 && eleven == 0) { res[6] = res[6] + prob // bust } else { deck[c] = deck[c] - 1 // remove card from deck deck[0] = deck[0] - 1 // decrement deck size var lev2 = lev + 1 decks[lev2] = deck scores[lev2] = score elevens[lev2] = eleven probs[lev2] = prob f.call(lev2) // get another card } } } f.call(0) // but can't have blackjack, so adjust probabilities accordingly var pnbj = 1 - res[5] for (i in 0..6) res[i] = res[i] / pnbj res[5] = 0 return res }
// Prints chart of dealer probabilities (as a check against an external source). static dealerChart() { System.print("Dealer Probabilities, Stands on Soft 17, 1 Deck, U.S Rules") System.print("Up Card 17 18 19 20 21 Bust") System.print("-------------------------------------------------------------------") var deck = NewDeck.call() deck[0] = 51 for (uc in 1..10) { var deck2 = deck.toList deck2[uc] = deck2[uc] - 1 var dp = dealerProbs(uc, deck2) if (uc > 1) { Fmt.write("$3d ", uc) } else { System.write("Ace ") } Fmt.lprint("$f $f $f $f $f $f", [dp[0], dp[1], dp[2], dp[3], dp[4], dp[6]]) } }
// Returns player's expected gain per unit staked after hitting once and then standing. static playerGain(card1, card2, uc, startDeck) { var eg = 0 var deck = startDeck.toList var score = card1 + card2 var eleven = false if (card1 == 1 || card2 == 1) { // an ace score = score + 10 eleven = true } for (c in 1..10) { // get another card if (deck[c] == 0) continue // card no longer present in deck // temporary variables for current card var deck2 = deck.toList var score2 = score var eleven2 = eleven score2 = score2 + c // add card to score if (c == 1) { // score all aces initially as 11 score2 = score2 + 10 eleven2 = true } var prob = deck2[c] / deck2[0] deck2[c] = deck2[c] - 1 // remove card from deck deck2[0] = deck2[0] - 1 // decrement deck size if (score2 > 21 && eleven2) { score2 = score2 - 10 // bust but can demote an ace } if (score2 <= 21) { var dp = dealerProbs(uc, deck2) eg = eg + calcGain(score2, dp) * prob } else { // bust eg = eg - prob } } return eg }
// Returns player's expected gain per unit staked after hitting once and then continuing // in accordance with the tables for rounds >= 2. static playerGain2(card1, card2, uc, startDeck) { var eg = 0 // result var decks = List.filled(9, null) // decks for each level var scores = List.filled(9, 0) // scores for each level var elevens = List.filled(9, 0) // number of aces for each level scored as 11 var probs = List.filled(9, 0) // probs for each level decks[0] = startDeck.toList scores[0] = card1 + card2 if (card1 == 1 || card2 == 1) { // an ace scores[0] = scores[0] + 10 elevens[0] = 1 } probs[0] = 1 var f // recursive closure f = Fn.new { |lev| for (c in 1..10) { if (decks[lev][c] == 0) continue // card no longer present in deck // temporary variables for current level var deck = decks[lev].toList var score = scores[lev] var eleven = elevens[lev] var prob = probs[lev] score = score + c // add card to score if (c == 1) { // score all aces initially as 11 score = score + 10 eleven = eleven + 1 } prob = prob * deck[c] / deck[0] if (score > 21 && eleven > 0) { score = score - 10 // bust but can demote an ace eleven = eleven - 1 } deck[c] = deck[c] - 1 // remove card from deck deck[0] = deck[0] - 1 // decrement deck size if ((eleven == 0 && (score >= 17 || (score >= 13 && uc < 7)) || (eleven == 0 && score == 12 && uc >= 4 && uc <= 6) || (eleven > 0 && score == 18 && uc != 9 && uc != 10) || (eleven > 0 && score >= 19)) && score <= 21) { var dp = dealerProbs(uc, deck) eg = eg + calcGain(score, dp) * prob } else if (score > 21 && eleven == 0) { // bust eg = eg - prob } else { var lev2 = lev + 1 decks[lev2] = deck scores[lev2] = score elevens[lev2] = eleven probs[lev2] = prob f.call(lev2) // get another card } } } f.call(0) return eg }
// Calculates gain per unit staked for a given scenario (helper function). static calcGain(pscore, dp) { var eg = 0 if (pscore == 17) { eg = eg + dp[6] // dealer is bust eg = eg - dp[1] - dp[2] - dp[3] - dp[4] // dealer has 18 to 21 } else if (pscore == 18) { eg = eg + dp[0] + dp[6] // dealer has 17 or is bust eg = eg - dp[2] - dp[3] - dp[4] // dealer has 19 to 21 } else if (pscore == 19) { eg = eg + dp[0] + dp[1] + dp[6] // dealer has 17, 18 or is bust eg = eg - dp[3] - dp[4] // dealer has 20 or 21 } else if (pscore == 20) { eg = eg + dp[0] + dp[1] + dp[2] + dp[6] // dealer has 17 to 19 or is bust eg = eg - dp[4] // dealer has (non-blackjack) 21 } else if (pscore == 21) { eg = eg + dp[0] + dp[1] + dp[2] + dp[3] + dp[6] // dealer has 17 to 20 or is bust } else if (pscore == 22) { // notional eg = eg + 1.5 // player blackjack } else if (pscore == 23) { // notional eg = eg - 1 // player bust, loses stake irrespective of what dealer has } else { // player has less than 17 eg = eg + dp[6] // dealer is bust eg = eg - (1 - dp[6]) // dealer isn't bust } return eg }
// Returns player's expected gains per unit staked, for each dealer up-card, after standing. static stand(card1, card2) { var deck = NewDeck.call() deck[card1] = deck[card1] - 1 deck[card2] = deck[card2] - 1 deck[0] = 50 var pscore = card1 + card2 // player score if (card1 == 1 || card2 == 1) pscore = pscore + 10 var egs = List.filled(10, 0) // results for (uc in 1..10) { // dealer's up-card var deck2 = deck.toList deck2[uc] = deck2[uc] - 1 deck2[0] = deck2[0] - 1 var dp = dealerProbs(uc, deck2) var eg = calcGain(pscore, dp) // expected gain for this up-card if (uc > 1) { egs[uc-2] = eg } else { // dealer has Ace egs[9] = eg // ace comes last in tables } } return egs }
// Returns player's expected gains per unit staked, for each dealer up-card, after // hitting once and then either standing (once == true) or continuing // as per the round >= 2 tables (once == false). static hit(card1, card2, once) { var deck = NewDeck.call() deck[card1] = deck[card1] - 1 deck[card2] = deck[card2] - 1 deck[0] = 50 var egs = List.filled(10, 0) // results for (uc in 1..10) { // dealer's up-card var deck2 = deck.toList deck2[uc] = deck2[uc] - 1 deck2[0] = 49 var peg = once ? playerGain (card1, card2, uc, deck2) : playerGain2(card1, card2, uc, deck2) if (uc > 1) { egs[uc-2] = peg } else { // dealer has Ace egs[9] = peg } } return egs }
// Returns player's expected gains per unit oiginally staked, for each dealer up-card, // after doubling i.e. hitting once and then standing with a doubled stake. static double(card1, card2) { var egs = hit(card1, card2, true) // hit once and then stand for (i in 0..9) egs[i] = egs[i] * 2 return egs }
// Returns player's expected gains per unit originally staked, for each dealer up-card, // after splitting a pair and doubling the stake, getting a second card for each hand // and then continuing in accordance with the rounds >= 2 tables. It is assumed that a // player cannot double or re-split following a split. It is also assumed // (in the interests of simplicity) that the expected gains for each split hand (after // calculating the gains for the first hand as though the second hand is not completed) // are exactly the same. static split(card) { var deck = NewDeck.call() deck[card] = deck[card] - 2 // must be a pair deck[0] = 50 var egs = List.filled(10, 0) // overall results
// now play a single hand var score = card var eleven = 0 if (card == 1) { // an ace score = 11 eleven = 1 } for (uc in 1..10) { // collect results for each dealer up-card if (deck[uc] == 0) continue // card no longer present in deck var deck2 = deck.toList deck2[uc] = deck2[uc] - 1 deck2[0] = deck2[0] - 1 var ix = uc - 2 if (ix == -1) ix = 9 // in tables ace comes last var peg = 0 // player expected gain for this up-card // get second player card for (c in 1..10) { if (deck2[c] == 0) continue // card no longer present in deck var prob = deck2[c] / deck2[0] var deck3 = deck2.toList deck3[c] = deck3[c] - 1 deck3[0] = deck3[0] - 1 var score2 = score + c var eleven2 = eleven if (c == 1) { // score all aces initially as 11 score2 = score2 + 10 eleven2 = eleven2 + 1 } if (score2 == 21) { // player has blackjack & we know dealer hasn't peg = peg + 1.5 * prob continue } if (score2 > 21 && eleven2 > 0) { score2 = score2 - 10 // bust but can demote an ace eleven2 = eleven2 - 1 } var action if (eleven2 > 0) { action = STable2[score2-12][ix] // use soft strategy table, no doubling } else { // including pairs as no re-splitting action = HTable2[score2-4][ix] // use hard strategy table, no doubling } var peg2 if (action == "S") { var dp = dealerProbs(uc, deck3) peg2 = calcGain(score2, dp) } else { peg2 = playerGain2(card, c, uc, deck3) } peg = peg + peg2 * prob } if (uc > 1) { egs[uc-2] = peg * 2 // allow for both hands in overall results } else { egs[9] = peg * 2 // ditto } } return egs }
// Returns the action with the highest expected gain. static bestAction(ags) { var max = ags[0].gain var maxi = 0 for (i in 1...ags.count) { if (ags[i].gain > max) { max = ags[i].gain maxi = i } } return ags[maxi].action }
// Prints title and header for a given chart. static printHeader(title) { System.print(title) System.print("P/D 2 3 4 5 6 7 8 9 T A") System.print("--------------------------------------------------------------------------") }
// Prints header for a pair of cards. static printPair(c) { if (c == 1) { System.write("AA ") } else if (c == 10) { System.write("TT ") } else { Fmt.write("$d$d ", c, c) } }
// Simulates 'perDay' blackjack games for 'days' days. static simulate(perDay, days) { var winDays = 0 var loseDays = 0 var evenDays = 0 var bigWin = 0 var bigLoss = 0 var totGain = 0 var totStake = 0 for (d in 1..days) { var dailyGain = 0 var dailyStake = 0 for (p in 1..perDay) { var gs = playerPlay() dailyGain = dailyGain + gs[0] dailyStake = dailyStake + gs[1] } if (dailyGain > 0) { winDays = winDays + 1 } else if (dailyGain < 0) { loseDays = loseDays + 1 } else { evenDays = evenDays + 1 } if (dailyGain > bigWin) { bigWin = dailyGain } else if (-dailyGain > bigLoss) { bigLoss = -dailyGain } totGain = totGain + dailyGain totStake = totStake + dailyStake } Fmt.print("\nAfter playing $d times a day for $d days:", perDay, days) Fmt.print("Winning days : $d", winDays) Fmt.print("Losing days : $d", loseDays) Fmt.print("Breakeven days : $d", evenDays) Fmt.print("Biggest win : $h", bigWin) Fmt.print("Biggest loss : $h", bigLoss) if (totGain < 0) { Fmt.print("Total loss : $h", -totGain) Fmt.print("Total staked : $h", totStake) Fmt.print("Loss \% staked : $0.3f", -totGain/totStake*100) } else { Fmt.print("Total win : $h", totGain) Fmt.print("Total staked : $h", totStake) Fmt.print("Win \% staked : $0.3f", totGain/totStake*100) } }
// Simulates a dealer's play for a given player's hand and state of deck. // Returns the player's gain (positive or negative) per unit staked. static dealerPlay(pscore, next, cards, d) { var dscore = d[0] + d[1] var aces = 0 if (d[0] == 1 || d[1] == 1) { // dealer has an ace dscore = dscore + 10 aces = aces + 1 } while (true) { if (dscore > 21 && aces > 0) { dscore = dscore - 10 // bust but we can demote an ace aces = aces - 1 } if (dscore > 21) { return [1, next] // dealer is bust and player gains stake } if (dscore >= 17) { // dealer must stick on 17 or above, hard or not if (dscore > pscore) { return [-1, next] // dealer wins and player loses stake } else if (dscore == pscore) { break // player breaks even } else { return [1, next] // dealer loses and player gains stake } } var nc = cards[next] // get new card from pack next = next + 1 dscore = dscore + nc if (nc == 1) { // count aces initially as 11 dscore = dscore + 10 aces = aces + 1 } } return [0, next] }
// Simulates the playing of a random player's hand according to the strategy tables. // Returns both the gain (positive or negative) and the stake (1 or 2). static playerPlay() { var perm = (0..51).toList Rand.shuffle(perm) // randomizes integers from 0 to 51 inclusive var cards = List.filled(52, 0) for (se in Indexed.new(perm)) { var card = (se.value/4).floor + 1 if (card > 10) card = 10 cards[se.index] = card } var p = [] // player hand var d = [] // dealer hand // initial deal for (se in Indexed.new(cards[0..3])) { if (se.index < 2) { p.add(se.value) } else { d.add(se.value) } } var next = 4 // index of next card to be dealt
// check if dealer and/or player have blackjack var dbj = (d[0] == 1 && d[1] == 10) || (d[0] == 10 && d[1] == 1) var pbj = (p[0] == 1 && p[1] == 10) || (p[0] == 10 && p[1] == 1) if (dbj) { if (pbj) return [0, 1] // player neither wins nor loses return [-1, 1] // player loses stake } if (pbj) return [1.5, 1] // player wins 1.5 x stake var uc = d[0] // dealer's up-card for accessing tables if (uc == 0) { uc = 9 // move ace to last place } else { uc = uc - 1 // move others down 1 } var stake = 1 // player's initial stake var fscores = List.filled(2, 0) // final player scores (one or, after split, two hands) var action = "" var score = 0 var aces = 0
var h = Fn.new { |hand| // processes a 'hit' while (true) { var nc = cards[next] // get new card from pack next = next + 1 score = score + nc if (nc == 1) { // count aces initially as 11 score = score + 10 aces= aces + 1 } if (score > 21 && aces > 0) { score = score - 10 // bust but we can demote an ace aces = aces - 1 } if (score > 21) { fscores[hand] = 22 // player is bust and loses stake return } if (action == "D") { fscores[hand] = score return } // get further strategy and act accordingly if (aces == 0) { action = HTable2[score-4][uc] } else { action = STable2[score-12][uc] } if (action == "S") { // stand fscores[hand] = score return } } }
score = p[0] + p[1] // get kind of player hand: hard, soft, pair var kind if (p[0] == p[1]) { kind = "pair" } else if (p[0] == 1 || p[1] == 1) { kind = "soft" } else { kind = "hard" } if (kind == "hard") { action = HTable[score-5][uc] } else if (kind == "soft") { // includes one ace var otherCard = p[0] if (otherCard == 1) otherCard = p[1] score = score + 10 aces = 1 action = STable[otherCard-2][uc] } else if (kind == "pair") { if (p[0] == 1) { // pair of aces score = score + 10 aces = 2 } action = PTable[p[0]-1][uc] } if (action == "S") { // stand fscores[0] = score } else if (action == "H") { // hit h.call(0) } else if (action == "D") { // double h.call(0) stake = 2 } else if (action == "P") { // split for (hand in 0..1) { score = p[0] aces = 0 if (score == 1) { // count aces initially as 11 score = 11 aces = aces + 1 } h.call(hand) } } var sum = 0 if (fscores[0] < 22) { var res = dealerPlay(fscores[0], next, cards, d) sum = sum + res[0] * stake next = res[1] } else { sum = sum - stake // this hand is bust } if (fscores[1] > 0) { // pair if (fscores[1] < 22) { var res = dealerPlay(fscores[1], next, cards, d) sum = sum + res[0] next = res[1] } else { sum = sum - 1 // this hand is bust } stake = 2 } return [sum, stake] }
static main() { // print dealer probabilities chart dealerChart()
// for hard scores (i.e. different cards, no aces) var tuples = [ [2, 3], [2, 4], [2, 5], [3, 4], [2, 6], [3, 5], [2, 7], [3, 6], [4, 5], [2, 8], [3, 7], [4, 6], [2, 9], [3, 8], [4, 7], [5, 6], [2, 10], [3, 9], [4, 8], [5, 7], [3, 10], [4, 9], [5, 8], [6, 7], [4, 10], [5, 9], [6, 8], [5, 10], [6, 9], [7, 8], [6, 10], [7, 9], [7, 10], [8, 9], [8, 10], [9, 10] ]
// number of tuples for each player score from 5 to 19 var counts = Array.from([1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1]) // expected gains for each player score & for each dealer up-card var segs = List.filled(15, null) // if stands var hegs = List.filled(15, null) // if hits var degs = List.filled(15, null) // if doubles for (i in 0..14) { segs[i] = List.filled(10, 0) hegs[i] = List.filled(10, 0) degs[i] = List.filled(10, 0) } for (tuple in tuples) { var i = tuple[0] + tuple[1] var sg = stand(tuple[0], tuple[1]) var hg = hit(tuple[0], tuple[1], false) var dg = double(tuple[0], tuple[1]) for (j in 0..9) { segs[i-5][j] = segs[i-5][j] + sg[j] hegs[i-5][j] = hegs[i-5][j] + hg[j] degs[i-5][j] = degs[i-5][j] + dg[j] } }
// calculate the average per tuple for each score for (i in 0..14) { for (j in 0..9) { segs[i][j] = segs[i][j] / counts[i] hegs[i][j] = hegs[i][j] / counts[i] degs[i][j] = degs[i][j] / counts[i] } }
printHeader("\nHard Chart - Player Expected Gains per unit (Stand)") for (i in 5..19) { Fmt.write("$2d ", i) for (j in 0..9) Fmt.write("$6.3f ", segs[i-5][j]) System.print() }
printHeader("\nHard Chart - Player Expected Gains per unit (Hit)") for (i in 5..19) { Fmt.write("$2d ", i) for (j in 0..9) Fmt.write("$6.3f ", hegs[i-5][j]) System.print() }
printHeader("\nHard Chart - Player Expected Gains per original unit (Double)") for (i in 5..19) { Fmt.write("$2d ", i) for (j in 0..9) Fmt.write("$6.3f ", degs[i-5][j]) System.print() }
printHeader("\nHard Chart - Player Strategy (Round 1)") for (i in 5..19) { Fmt.write("$2d ", i) for (j in 0..9) { var ags = [ ActionGain.new("S", segs[i-5][j]), ActionGain.new("H", hegs[i-5][j]), ActionGain.new("D", degs[i-5][j]) ] var action = bestAction(ags) HTable[i-5][j] = action Fmt.write("$4s ", action) } System.print() }
// for hard scores (no aces) - after round 1 (no doubling or splitting) // based on hard table figures (round 1) with scores of 4, 20, and 21 added var segs2 = List.filled(18, null) // expected gains if stands var hegs2 = List.filled(18, null) // expected gains if hits for (i in 0..17) { segs2[i] = List.filled(10, 0) hegs2[i] = List.filled(10, 0) } for (i in 5..19) { segs2[i-4] = segs[i-5] hegs2[i-4] = hegs[i-5] } var sg4 = stand(2, 2) var hg4 = hit(2, 2, false) var sg20 = stand(10, 10) var hg20 = hit(10, 10, false) var sg21 = stand(1, 10) var hg21 = hit(1, 10, false) for (j in 0..9) { segs2[0][j] = segs2[0][j] + sg4[j] hegs2[0][j] = hegs2[0][j] + hg4[j] segs2[16][j] = segs2[16][j] + sg20[j] hegs2[16][j] = hegs2[16][j] + hg20[j] segs2[17][j] = segs2[17][j] + sg21[j] hegs2[17][j] = hegs2[17][j] + hg21[j] }
printHeader("\nHard Chart - Player Strategy (Round >= 2, No Doubling)") for (i in 4..21) { Fmt.write("$2d ", i) for (j in 0..9) { var action = "S" if (hegs2[i-4][j] > segs2[i-4][j]) action = "H" HTable2[i-4][j] = action Fmt.write("$4s ", action) } System.print() }
/* for soft scores (i.e. including exactly one ace) */
// expected gains for each player second card (2 to 9) & for each dealer up-card var segs3 = List.filled(8, null) // if stands var hegs3 = List.filled(8, null) // if hits var degs3 = List.filled(8, null) // if doubles for (i in 0..7) { segs3[i] = List.filled(10, 0) hegs3[i] = List.filled(10, 0) degs3[i] = List.filled(10, 0) } for (c in 2..9) { var sg = stand(1, c) var hg = hit(1, c, false) var dg = double(1, c) for (j in 0..9) { segs3[c-2][j] = segs3[c-2][j] + sg[j] hegs3[c-2][j] = hegs3[c-2][j] + hg[j] degs3[c-2][j] = degs3[c-2][j] + dg[j] } }
printHeader("\nSoft Chart - Player Expected Gains per unit (Stand)") for (c in 2..9) { Fmt.write("A$d ", c) for (j in 0..9) Fmt.write("$6.3f ", segs3[c-2][j]) System.print() }
printHeader("\nSoft Chart - Player Expected Gains per unit (Hit)") for (c in 2..9) { Fmt.write("A$d ", c) for (j in 0..9) Fmt.write("$6.3f ", hegs3[c-2][j]) System.print() }
printHeader("\nSoft Chart - Player Expected Gains per unit (Double)") for (c in 2..9) { Fmt.write("A$d ", c) for (j in 0..9) Fmt.write("$6.3f ", degs3[c-2][j]) System.print() }
printHeader("\nSoft Chart - Player Strategy (Round 1)") for (c in 2..9) { Fmt.write("A$d ", c) for (j in 0..9) { var ags = [ ActionGain.new("S", segs3[c-2][j]), ActionGain.new("H", hegs3[c-2][j]), ActionGain.new("D", degs3[c-2][j]) ] var action = bestAction(ags) STable[c-2][j] = action Fmt.write("$4s ", action) } System.print() }
// for soft scores (at least one ace) - after round 1 (no doubling or splitting) // based on soft table figures (round 1) with scores of 12 and 21 added // assumes one ace counted as 11 var segs4 = List.filled(10, null) // expected gains if stands var hegs4 = List.filled(10, null) // expected gains if hits for (i in 0..9) { segs4[i] = List.filled(10, 0) hegs4[i] = List.filled(10, 0) } for (i in 1..8) { segs4[i] = segs3[i-1] hegs4[i] = hegs3[i-1] } var sg12 = stand(1, 1) var hg12 = hit(1, 1, false) for (j in 0..9) { segs4[0][j] = segs4[0][j] + sg12[j] hegs4[0][j] = hegs4[0][j] + hg12[j] segs4[9][j] = segs4[9][j] + sg21[j] hegs4[9][j] = hegs4[9][j] + hg21[j] }
printHeader("\nSoft Chart - Player Strategy (Round >= 2, No Doubling)") for (i in 12..21) { Fmt.write("$2d ", i) for (j in 0..9) { var action = "S" if (hegs4[i-12][j] > segs4[i-12][j]) action = "H" STable2[i-12][j] = action Fmt.write("$4s ", action) } System.print() }
/* for pairs */
// expected gains for each pair (A to 10) & for each dealer up-card var segs5 = List.filled(10, null) // if stands var hegs5 = List.filled(10, null) // if hits var degs5 = List.filled(10, null) // if doubles var pegs5 = List.filled(10, null) // if splits for (i in 0..9) { segs5[i] = List.filled(10, 0) hegs5[i] = List.filled(10, 0) degs5[i] = List.filled(10, 0) pegs5[i] = List.filled(10, 0) } for (c in 1..10) { var sg = stand(c, c) var hg = hit(c, c, false) var dg = double(c, c) var pg = split(c) for (j in 0..9) { segs5[c-1][j] = segs5[c-1][j] + sg[j] hegs5[c-1][j] = hegs5[c-1][j] + hg[j] degs5[c-1][j] = degs5[c-1][j] + dg[j] pegs5[c-1][j] = pegs5[c-1][j] + pg[j] } }
printHeader("\nPairs Chart - Player Expected Gains per unit (Stand)") for (c in 1..10) { printPair(c) for (j in 0..9) Fmt.write("$6.3f ", segs5[c-1][j]) System.print() }
printHeader("\nPairs Chart - Player Expected Gains per unit (Hit)") for (c in 1..10) { printPair(c) for (j in 0..9) Fmt.write("$6.3f ", hegs5[c-1][j]) System.print() }
printHeader("\nPairs Chart - Player Expected Gains per unit (Double)") for (c in 1..10) { printPair(c) for (j in 0..9) Fmt.write("$6.3f ", degs5[c-1][j]) System.print() }
printHeader("\nPairs Chart - Player Expected Gains per unit (Split)") for (c in 1..10) { printPair(c) for (j in 0..9) Fmt.write("$6.3f ", pegs5[c-1][j]) System.print() }
printHeader("\nPairs Chart - Player Strategy (Round 1)") for (c in 1..10) { printPair(c) for (j in 0..9) { var ags = [ ActionGain.new("S", segs5[c-1][j]), ActionGain.new("H", hegs5[c-1][j]), ActionGain.new("D", degs5[c-1][j]), ActionGain.new("P", pegs5[c-1][j]), ] var action = bestAction(ags) PTable[c-1][j] = action Fmt.write("$4s ", action) } System.print() }
// do 10 years of simulations for (i in 1..10) { Fmt.print("\nSimulation for Year $d:", i) simulate(50, 365) } }
}
Blackjack.main()</lang>
- Output:
Dealer Probabilities, Stands on Soft 17, 1 Deck, U.S Rules Up Card 17 18 19 20 21 Bust ------------------------------------------------------------------- Ace 0.183786 0.190890 0.188680 0.191692 0.075137 0.169815 2 0.138976 0.131762 0.131815 0.123948 0.120526 0.352973 3 0.130313 0.130946 0.123761 0.123345 0.116047 0.375588 4 0.130973 0.114163 0.120679 0.116286 0.115096 0.402803 5 0.119687 0.123483 0.116909 0.104694 0.106321 0.428905 6 0.166948 0.106454 0.107192 0.100705 0.097878 0.420823 7 0.372345 0.138583 0.077334 0.078897 0.072987 0.259854 8 0.130857 0.362989 0.129445 0.068290 0.069791 0.238627 9 0.121886 0.103921 0.357391 0.122250 0.061109 0.233442 10 0.124156 0.122486 0.124421 0.356869 0.039570 0.232499 Hard Chart - Player Expected Gains per unit (Stand) P/D 2 3 4 5 6 7 8 9 T A -------------------------------------------------------------------------- 5 -0.293 -0.248 -0.176 -0.104 -0.122 -0.469 -0.513 -0.533 -0.546 -0.659 6 -0.291 -0.232 -0.172 -0.101 -0.119 -0.467 -0.522 -0.533 -0.547 -0.659 7 -0.283 -0.229 -0.163 -0.098 -0.117 -0.471 -0.521 -0.537 -0.547 -0.658 8 -0.276 -0.229 -0.162 -0.100 -0.130 -0.478 -0.523 -0.539 -0.549 -0.648 9 -0.277 -0.224 -0.160 -0.108 -0.134 -0.480 -0.528 -0.543 -0.542 -0.646 10 -0.279 -0.227 -0.172 -0.120 -0.146 -0.484 -0.531 -0.539 -0.537 -0.644 11 -0.277 -0.231 -0.175 -0.123 -0.147 -0.488 -0.529 -0.537 -0.537 -0.646 12 -0.286 -0.241 -0.185 -0.134 -0.151 -0.485 -0.526 -0.535 -0.533 -0.655 13 -0.282 -0.236 -0.181 -0.133 -0.156 -0.488 -0.529 -0.537 -0.534 -0.649 14 -0.282 -0.238 -0.188 -0.134 -0.159 -0.489 -0.529 -0.533 -0.536 -0.651 15 -0.280 -0.239 -0.190 -0.144 -0.169 -0.494 -0.531 -0.536 -0.531 -0.648 16 -0.287 -0.250 -0.194 -0.152 -0.179 -0.495 -0.526 -0.540 -0.530 -0.648 17 -0.147 -0.120 -0.074 -0.044 -0.011 -0.122 -0.405 -0.414 -0.402 -0.459 18 0.119 0.144 0.164 0.202 0.268 0.389 0.096 -0.196 -0.155 -0.082 19 0.385 0.384 0.404 0.448 0.484 0.610 0.577 0.264 0.103 0.308 Hard Chart - Player Expected Gains per unit (Hit) P/D 2 3 4 5 6 7 8 9 T A -------------------------------------------------------------------------- 5 -0.131 -0.098 -0.041 0.022 0.019 -0.119 -0.181 -0.262 -0.309 -0.417 6 -0.151 -0.107 -0.055 0.009 0.014 -0.164 -0.234 -0.305 -0.349 -0.443 7 -0.111 -0.072 -0.013 0.053 0.064 -0.069 -0.223 -0.295 -0.332 -0.401 8 -0.015 0.021 0.084 0.136 0.148 0.092 -0.056 -0.213 -0.253 -0.275 9 0.090 0.137 0.181 0.226 0.235 0.194 0.111 -0.052 -0.148 -0.128 10 0.215 0.246 0.277 0.314 0.319 0.277 0.211 0.119 0.030 0.030 11 0.272 0.296 0.327 0.361 0.362 0.293 0.222 0.146 0.107 0.113 12 -0.256 -0.232 -0.206 -0.181 -0.179 -0.241 -0.308 -0.380 -0.378 -0.413 13 -0.315 -0.293 -0.270 -0.252 -0.251 -0.301 -0.362 -0.389 -0.423 -0.440 14 -0.363 -0.353 -0.337 -0.315 -0.313 -0.346 -0.366 -0.426 -0.455 -0.460 15 -0.419 -0.414 -0.406 -0.392 -0.383 -0.351 -0.406 -0.466 -0.496 -0.487 16 -0.461 -0.460 -0.454 -0.448 -0.397 -0.376 -0.426 -0.481 -0.510 -0.497 17 -0.534 -0.536 -0.538 -0.493 -0.484 -0.450 -0.475 -0.529 -0.558 -0.546 18 -0.633 -0.634 -0.597 -0.591 -0.586 -0.567 -0.565 -0.593 -0.624 -0.630 19 -0.750 -0.713 -0.712 -0.709 -0.707 -0.699 -0.697 -0.698 -0.712 -0.740 Hard Chart - Player Expected Gains per original unit (Double) P/D 2 3 4 5 6 7 8 9 T A -------------------------------------------------------------------------- 5 -0.587 -0.497 -0.352 -0.209 -0.244 -0.938 -1.025 -1.066 -1.093 -1.318 6 -0.560 -0.446 -0.324 -0.186 -0.215 -0.870 -1.023 -1.045 -1.074 -1.295 7 -0.415 -0.317 -0.186 -0.066 -0.059 -0.555 -0.851 -0.936 -0.956 -1.127 8 -0.165 -0.081 0.032 0.143 0.157 -0.140 -0.433 -0.697 -0.743 -0.802 9 0.114 0.193 0.286 0.380 0.393 0.175 0.007 -0.281 -0.442 -0.409 10 0.428 0.492 0.554 0.628 0.638 0.446 0.313 0.164 0.007 0.025 11 0.542 0.592 0.654 0.722 0.724 0.479 0.341 0.223 0.164 0.198 12 -0.511 -0.463 -0.413 -0.362 -0.358 -0.556 -0.690 -0.811 -0.789 -0.827 13 -0.630 -0.587 -0.541 -0.503 -0.503 -0.651 -0.775 -0.807 -0.862 -0.880 14 -0.727 -0.706 -0.673 -0.630 -0.627 -0.723 -0.759 -0.862 -0.915 -0.921 15 -0.838 -0.829 -0.812 -0.783 -0.767 -0.716 -0.826 -0.937 -0.992 -0.973 16 -0.921 -0.920 -0.908 -0.896 -0.793 -0.751 -0.853 -0.961 -1.019 -0.995 17 -1.069 -1.072 -1.076 -0.985 -0.967 -0.901 -0.949 -1.058 -1.116 -1.092 18 -1.265 -1.267 -1.195 -1.182 -1.172 -1.135 -1.130 -1.186 -1.248 -1.260 19 -1.499 -1.425 -1.423 -1.417 -1.414 -1.397 -1.395 -1.396 -1.425 -1.481 Hard Chart - Player Strategy (Round 1) P/D 2 3 4 5 6 7 8 9 T A -------------------------------------------------------------------------- 5 H H H H H H H H H H 6 H H H H H H H H H H 7 H H H H H H H H H H 8 H H H D D H H H H H 9 D D D D D H H H H H 10 D D D D D D D D H H 11 D D D D D D D D D D 12 H H S S S H H H H H 13 S S S S S H H H H H 14 S S S S S H H H H H 15 S S S S S H H H H H 16 S S S S S H H H H H 17 S S S S S S S S S S 18 S S S S S S S S S S 19 S S S S S S S S S S Hard Chart - Player Strategy (Round >= 2, No Doubling) P/D 2 3 4 5 6 7 8 9 T A -------------------------------------------------------------------------- 4 H H H H H H H H H H 5 H H H H H H H H H H 6 H H H H H H H H H H 7 H H H H H H H H H H 8 H H H H H H H H H H 9 H H H H H H H H H H 10 H H H H H H H H H H 11 H H H H H H H H H H 12 H H S S S H H H H H 13 S S S S S H H H H H 14 S S S S S H H H H H 15 S S S S S H H H H H 16 S S S S S H H H H H 17 S S S S S S S S S S 18 S S S S S S S S S S 19 S S S S S S S S S S 20 S S S S S S S S S S 21 S S S S S S S S S S Soft Chart - Player Expected Gains per unit (Stand) P/D 2 3 4 5 6 7 8 9 T A -------------------------------------------------------------------------- A2 -0.283 -0.241 -0.186 -0.119 -0.114 -0.462 -0.508 -0.517 -0.539 -0.662 A3 -0.284 -0.240 -0.170 -0.116 -0.112 -0.460 -0.505 -0.527 -0.538 -0.661 A4 -0.283 -0.224 -0.166 -0.113 -0.109 -0.458 -0.514 -0.526 -0.538 -0.659 A5 -0.266 -0.221 -0.164 -0.111 -0.108 -0.468 -0.515 -0.525 -0.537 -0.659 A6 -0.132 -0.093 -0.037 0.005 0.010 -0.090 -0.385 -0.407 -0.418 -0.483 A7 0.136 0.167 0.204 0.222 0.262 0.412 0.121 -0.179 -0.186 -0.101 A8 0.402 0.420 0.415 0.461 0.482 0.615 0.608 0.288 0.064 0.290 A9 0.656 0.644 0.654 0.682 0.694 0.773 0.785 0.766 0.555 0.681 Soft Chart - Player Expected Gains per unit (Hit) P/D 2 3 4 5 6 7 8 9 T A -------------------------------------------------------------------------- A2 0.039 0.071 0.110 0.159 0.168 0.107 0.039 -0.014 -0.090 -0.184 A3 0.017 0.044 0.091 0.137 0.147 0.060 0.035 -0.060 -0.124 -0.216 A4 -0.012 0.022 0.061 0.108 0.120 0.034 -0.035 -0.114 -0.172 -0.256 A5 -0.032 -0.003 0.038 0.082 0.116 -0.024 -0.084 -0.167 -0.229 -0.296 A6 0.007 0.036 0.077 0.140 0.133 0.060 -0.065 -0.135 -0.189 -0.242 A7 0.065 0.093 0.156 0.175 0.192 0.175 0.047 -0.087 -0.140 -0.160 A8 0.120 0.173 0.187 0.227 0.241 0.222 0.158 0.005 -0.087 -0.081 A9 0.191 0.196 0.230 0.268 0.280 0.243 0.172 0.096 0.007 -0.008 Soft Chart - Player Expected Gains per unit (Double) P/D 2 3 4 5 6 7 8 9 T A -------------------------------------------------------------------------- A2 -0.042 0.028 0.115 0.212 0.230 -0.157 -0.312 -0.373 -0.478 -0.586 A3 -0.047 0.011 0.109 0.204 0.222 -0.175 -0.254 -0.394 -0.479 -0.588 A4 -0.070 0.003 0.085 0.175 0.201 -0.141 -0.314 -0.422 -0.495 -0.613 A5 -0.082 -0.019 0.063 0.148 0.217 -0.189 -0.333 -0.452 -0.536 -0.649 A6 0.013 0.074 0.155 0.280 0.266 0.014 -0.230 -0.345 -0.433 -0.522 A7 0.128 0.189 0.313 0.349 0.385 0.240 -0.015 -0.254 -0.322 -0.359 A8 0.237 0.346 0.373 0.453 0.483 0.325 0.190 -0.060 -0.226 -0.200 A9 0.380 0.392 0.459 0.536 0.560 0.351 0.230 0.111 -0.055 -0.055 Soft Chart - Player Strategy (Round 1) P/D 2 3 4 5 6 7 8 9 T A -------------------------------------------------------------------------- A2 H H D D D H H H H H A3 H H D D D H H H H H A4 H H D D D H H H H H A5 H H D D D H H H H H A6 D D D D D H H H H H A7 S D D D D S S H H S A8 S S S S D S S S S S A9 S S S S S S S S S S Soft Chart - Player Strategy (Round >= 2, No Doubling) P/D 2 3 4 5 6 7 8 9 T A -------------------------------------------------------------------------- 12 H H H H H H H H H H 13 H H H H H H H H H H 14 H H H H H H H H H H 15 H H H H H H H H H H 16 H H H H H H H H H H 17 H H H H H H H H H H 18 S S S S S S S H H S 19 S S S S S S S S S S 20 S S S S S S S S S S 21 S S S S S S S S S S Pairs Chart - Player Expected Gains per unit (Stand) P/D 2 3 4 5 6 7 8 9 T A -------------------------------------------------------------------------- AA -0.274 -0.232 -0.178 -0.130 -0.104 -0.452 -0.500 -0.511 -0.531 -0.663 22 -0.291 -0.251 -0.192 -0.107 -0.125 -0.471 -0.515 -0.523 -0.547 -0.660 33 -0.295 -0.246 -0.160 -0.101 -0.119 -0.467 -0.510 -0.542 -0.546 -0.660 44 -0.290 -0.214 -0.152 -0.095 -0.114 -0.463 -0.529 -0.543 -0.547 -0.656 55 -0.256 -0.206 -0.146 -0.090 -0.112 -0.484 -0.531 -0.541 -0.545 -0.653 66 -0.262 -0.211 -0.152 -0.102 -0.165 -0.493 -0.536 -0.549 -0.552 -0.617 77 -0.268 -0.219 -0.164 -0.156 -0.174 -0.502 -0.539 -0.555 -0.510 -0.631 88 -0.275 -0.228 -0.215 -0.165 -0.178 -0.503 -0.551 -0.516 -0.518 -0.644 99 0.137 0.123 0.167 0.203 0.265 0.401 0.065 -0.196 -0.133 -0.055 TT 0.627 0.636 0.645 0.674 0.697 0.765 0.783 0.744 0.583 0.650 Pairs Chart - Player Expected Gains per unit (Hit) P/D 2 3 4 5 6 7 8 9 T A -------------------------------------------------------------------------- AA 0.095 0.120 0.142 0.182 0.200 0.158 0.093 -0.003 -0.048 -0.075 22 -0.113 -0.082 -0.035 0.036 0.032 -0.092 -0.141 -0.222 -0.277 -0.395 33 -0.153 -0.118 -0.047 0.008 0.014 -0.164 -0.231 -0.310 -0.346 -0.444 44 -0.013 0.028 0.098 0.154 0.175 0.111 -0.055 -0.206 -0.246 -0.268 55 0.224 0.254 0.295 0.347 0.362 0.279 0.207 0.119 0.032 0.042 66 -0.253 -0.222 -0.190 -0.162 -0.194 -0.265 -0.322 -0.386 -0.386 -0.411 77 -0.406 -0.388 -0.369 -0.370 -0.367 -0.389 -0.408 -0.475 -0.516 -0.510 88 -0.454 -0.450 -0.461 -0.453 -0.397 -0.374 -0.426 -0.487 -0.512 -0.490 99 -0.627 -0.638 -0.597 -0.590 -0.587 -0.566 -0.566 -0.595 -0.626 -0.621 TT -0.847 -0.846 -0.846 -0.846 -0.845 -0.843 -0.843 -0.842 -0.840 -0.882 Pairs Chart - Player Expected Gains per unit (Double) P/D 2 3 4 5 6 7 8 9 T A -------------------------------------------------------------------------- AA -0.019 0.055 0.137 0.216 0.248 -0.137 -0.296 -0.421 -0.468 -0.591 22 -0.582 -0.501 -0.384 -0.214 -0.249 -0.942 -1.030 -1.047 -1.094 -1.320 33 -0.567 -0.472 -0.302 -0.184 -0.215 -0.871 -1.000 -1.065 -1.072 -1.298 44 -0.185 -0.082 0.044 0.162 0.193 -0.108 -0.447 -0.701 -0.741 -0.802 55 0.446 0.510 0.590 0.695 0.724 0.466 0.323 0.175 0.014 0.042 66 -0.505 -0.444 -0.380 -0.325 -0.387 -0.599 -0.711 -0.817 -0.803 -0.823 77 -0.813 -0.777 -0.738 -0.741 -0.734 -0.823 -0.858 -0.978 -1.035 -1.019 88 -0.908 -0.900 -0.922 -0.906 -0.793 -0.747 -0.853 -0.974 -1.024 -0.980 99 -1.255 -1.277 -1.194 -1.181 -1.173 -1.132 -1.133 -1.189 -1.252 -1.242 TT -1.693 -1.693 -1.693 -1.691 -1.690 -1.686 -1.685 -1.684 -1.681 -1.764 Pairs Chart - Player Expected Gains per unit (Split) P/D 2 3 4 5 6 7 8 9 T A -------------------------------------------------------------------------- AA 1.192 1.223 1.265 1.321 1.344 1.308 1.201 1.039 0.860 0.921 22 -0.128 -0.070 -0.007 0.128 0.126 -0.054 -0.213 -0.383 -0.463 -0.566 33 -0.202 -0.128 0.009 0.117 0.112 -0.115 -0.265 -0.418 -0.509 -0.579 44 -0.236 -0.127 -0.013 0.095 0.083 -0.223 -0.343 -0.493 -0.580 -0.623 55 -0.232 -0.150 -0.038 0.068 0.056 -0.299 -0.448 -0.608 -0.685 -0.695 66 -0.219 -0.135 -0.028 0.068 -0.011 -0.270 -0.413 -0.570 -0.652 -0.660 77 -0.163 -0.084 0.016 0.039 0.053 -0.123 -0.423 -0.564 -0.634 -0.635 88 0.017 0.077 0.106 0.188 0.234 0.202 -0.100 -0.430 -0.464 -0.378 99 0.170 0.170 0.253 0.339 0.359 0.341 0.179 -0.112 -0.268 -0.109 TT 0.412 0.465 0.518 0.596 0.619 0.576 0.447 0.276 0.146 0.140 Pairs Chart - Player Strategy (Round 1) P/D 2 3 4 5 6 7 8 9 T A -------------------------------------------------------------------------- AA P P P P P P P P P P 22 H P P P P P H H H H 33 H H P P P P H H H H 44 H H H D D H H H H H 55 D D D D D D D D H D 66 P P P P P H H H H H 77 P P P P P P H H S H 88 P P P P P P P P P P 99 P P P P P S P P S S TT S S S S S S S S S S Simulation for Year 1: After playing 50 times a day for 365 days: Winning days : 180 Losing days : 176 Breakeven days : 9 Biggest win : 23.5 Biggest loss : 24.5 Total loss : 263 Total staked : 20482 Loss % staked : 1.284 Simulation for Year 2: After playing 50 times a day for 365 days: Winning days : 176 Losing days : 174 Breakeven days : 15 Biggest win : 20.5 Biggest loss : 20 Total loss : 60.5 Total staked : 20514 Loss % staked : 0.295 Simulation for Year 3: After playing 50 times a day for 365 days: Winning days : 163 Losing days : 195 Breakeven days : 7 Biggest win : 20.5 Biggest loss : 26 Total loss : 297 Total staked : 20501 Loss % staked : 1.449 Simulation for Year 4: After playing 50 times a day for 365 days: Winning days : 163 Losing days : 190 Breakeven days : 12 Biggest win : 21.5 Biggest loss : 20.5 Total loss : 144.5 Total staked : 20507 Loss % staked : 0.705 Simulation for Year 5: After playing 50 times a day for 365 days: Winning days : 153 Losing days : 203 Breakeven days : 9 Biggest win : 19 Biggest loss : 24.5 Total loss : 485.5 Total staked : 20501 Loss % staked : 2.368 Simulation for Year 6: After playing 50 times a day for 365 days: Winning days : 176 Losing days : 177 Breakeven days : 12 Biggest win : 21 Biggest loss : 29.5 Total loss : 4 Total staked : 20536 Loss % staked : 0.019 Simulation for Year 7: After playing 50 times a day for 365 days: Winning days : 161 Losing days : 195 Breakeven days : 9 Biggest win : 25.5 Biggest loss : 24.5 Total loss : 316.5 Total staked : 20568 Loss % staked : 1.539 Simulation for Year 8: After playing 50 times a day for 365 days: Winning days : 168 Losing days : 189 Breakeven days : 8 Biggest win : 21 Biggest loss : 28 Total loss : 240 Total staked : 20524 Loss % staked : 1.169 Simulation for Year 9: After playing 50 times a day for 365 days: Winning days : 181 Losing days : 174 Breakeven days : 10 Biggest win : 22 Biggest loss : 26 Total loss : 17 Total staked : 20494 Loss % staked : 0.083 Simulation for Year 10: After playing 50 times a day for 365 days: Winning days : 156 Losing days : 198 Breakeven days : 11 Biggest win : 22 Biggest loss : 21 Total loss : 346.5 Total staked : 20438 Loss % staked : 1.695