Blackjack strategy: Difference between revisions

From Rosetta Code
Content added Content deleted
("will I" --> "can I expect to" / link to archive.org / Category:Games)
(Added Go)
Line 25: Line 25:
* What can I expect to be my biggest loss?
* What can I expect to be my biggest loss?
* What can I expect to win/lose over the year?
* What can I expect to win/lose over the year?

=={{header|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>

{{out}}
<pre>
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
</pre>

Revision as of 16:50, 27 February 2020

Blackjack strategy is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

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