# Cribbage - Expected Average

Posted by Troy Williams on Sun 01 July 2018 Updated on Wed 06 February 2019

# References

• http://www.cribbageforum.com/YourCrib.htm

# Imports

You will have to make a module called cribbage.py from the previous article making the cribbage objects.

import random
import json
from itertools import combinations

from cribbage import Card, Hand, score_hand, display_points, make_deck, make_hand


# Average Hand Value

In this section we'll explore how to calculate the average hand value of any particular 4 card hand based on what the cut card could potentially be. Given the fact that we are dealt 6 cards, what is the best hand, on average, that can be made with 4 cards and the potential cut card.

Given that we are dealt the following hand: 2C 3H 4D 5D 5S JS

Let's analyze the following 4 card hand: 3C, 4H, 5C, 5D, what can we expect in terms of the cut card? That is, based on what could be turned up as the cut card what kind of points can be expected, on average?

We need to remove the cards that we know about from the deck so that we have:

$$52 - 6 = 46$$
Starter Card Frequency Hand Value Total
A 4 10 40
2 3 12 36
3 3 20 60
4 3 16 48
5 2 17 34
6 4 14 56
7 4 12 48
8 4 10 40
9 4 8 32
10 4 12 48
J 3 12 36
Q 4 12 48
K 4 12 48
Total: 574

for the 4 card hand, the average points would be:

$$\frac{574}{46} = 12.48$$

Table columns: - Starter - This column simply lists the potential cut card. - Card Frequency - This is the number of cards in the deck that are left based on the 6 cards you are dealt. For example, if I was dealt 2 aces, that would mean there are only two aces left in the deck. - Hand Value - This is the value of the hand including the cut/starter card - Total - This is the product of the card frequency and hand value. Basically it is a weighting factor.

You sum the value in the total column and divide by the number of cards left in the deck,

$$52 - 6 = 46$$

.

deck = make_deck()

# shuffle the deck
random.shuffle(deck)

# extract the first 6 cards (removing them from the deck)
hand = make_hand(deck, 6)

random.shuffle(hand)
candidate = Hand(hand[:4])
print('Candidate Hand: ',candidate)
print()

total = 0.0
for i, cut in enumerate(sorted(deck), 1):
scores, counts = score_hand(candidate, cut)
value = sum(scores.values())
print('{:>2} Cards = {}, Cut = {} - Points = {}'.format(i, candidate.sorted(), cut, value))
total += value

print()
print('Average hand value = {}'.format(total/len(deck)))


Output:

Candidate Hand:  [6♣, K♠, 8♠, 5♣]

1 Cards = [5♣, 6♣, 8♠, K♠], Cut = A♥ - Points = 4
2 Cards = [5♣, 6♣, 8♠, K♠], Cut = A♣ - Points = 4
3 Cards = [5♣, 6♣, 8♠, K♠], Cut = A♠ - Points = 4
4 Cards = [5♣, 6♣, 8♠, K♠], Cut = A♦ - Points = 4
5 Cards = [5♣, 6♣, 8♠, K♠], Cut = 2♥ - Points = 4
6 Cards = [5♣, 6♣, 8♠, K♠], Cut = 2♠ - Points = 4
7 Cards = [5♣, 6♣, 8♠, K♠], Cut = 2♣ - Points = 4
8 Cards = [5♣, 6♣, 8♠, K♠], Cut = 2♦ - Points = 4
9 Cards = [5♣, 6♣, 8♠, K♠], Cut = 3♥ - Points = 2
10 Cards = [5♣, 6♣, 8♠, K♠], Cut = 3♦ - Points = 2
11 Cards = [5♣, 6♣, 8♠, K♠], Cut = 3♣ - Points = 2
12 Cards = [5♣, 6♣, 8♠, K♠], Cut = 3♠ - Points = 2
13 Cards = [5♣, 6♣, 8♠, K♠], Cut = 4♣ - Points = 7
14 Cards = [5♣, 6♣, 8♠, K♠], Cut = 4♥ - Points = 7
15 Cards = [5♣, 6♣, 8♠, K♠], Cut = 4♦ - Points = 7
16 Cards = [5♣, 6♣, 8♠, K♠], Cut = 5♦ - Points = 6
17 Cards = [5♣, 6♣, 8♠, K♠], Cut = 5♥ - Points = 6
18 Cards = [5♣, 6♣, 8♠, K♠], Cut = 5♠ - Points = 6
19 Cards = [5♣, 6♣, 8♠, K♠], Cut = 6♦ - Points = 4
20 Cards = [5♣, 6♣, 8♠, K♠], Cut = 6♥ - Points = 4
21 Cards = [5♣, 6♣, 8♠, K♠], Cut = 6♠ - Points = 4
22 Cards = [5♣, 6♣, 8♠, K♠], Cut = 7♥ - Points = 8
23 Cards = [5♣, 6♣, 8♠, K♠], Cut = 7♠ - Points = 8
24 Cards = [5♣, 6♣, 8♠, K♠], Cut = 7♣ - Points = 8
25 Cards = [5♣, 6♣, 8♠, K♠], Cut = 7♦ - Points = 8
26 Cards = [5♣, 6♣, 8♠, K♠], Cut = 8♣ - Points = 4
27 Cards = [5♣, 6♣, 8♠, K♠], Cut = 8♦ - Points = 4
28 Cards = [5♣, 6♣, 8♠, K♠], Cut = 8♥ - Points = 4
29 Cards = [5♣, 6♣, 8♠, K♠], Cut = 9♥ - Points = 4
30 Cards = [5♣, 6♣, 8♠, K♠], Cut = 9♦ - Points = 4
31 Cards = [5♣, 6♣, 8♠, K♠], Cut = 9♣ - Points = 4
32 Cards = [5♣, 6♣, 8♠, K♠], Cut = 9♠ - Points = 4
33 Cards = [5♣, 6♣, 8♠, K♠], Cut = T♦ - Points = 4
34 Cards = [5♣, 6♣, 8♠, K♠], Cut = T♥ - Points = 4
35 Cards = [5♣, 6♣, 8♠, K♠], Cut = T♠ - Points = 4
36 Cards = [5♣, 6♣, 8♠, K♠], Cut = T♣ - Points = 4
37 Cards = [5♣, 6♣, 8♠, K♠], Cut = J♥ - Points = 4
38 Cards = [5♣, 6♣, 8♠, K♠], Cut = J♠ - Points = 4
39 Cards = [5♣, 6♣, 8♠, K♠], Cut = J♦ - Points = 4
40 Cards = [5♣, 6♣, 8♠, K♠], Cut = J♣ - Points = 4
41 Cards = [5♣, 6♣, 8♠, K♠], Cut = Q♠ - Points = 4
42 Cards = [5♣, 6♣, 8♠, K♠], Cut = Q♦ - Points = 4
43 Cards = [5♣, 6♣, 8♠, K♠], Cut = Q♣ - Points = 4
44 Cards = [5♣, 6♣, 8♠, K♠], Cut = K♣ - Points = 6
45 Cards = [5♣, 6♣, 8♠, K♠], Cut = K♦ - Points = 6
46 Cards = [5♣, 6♣, 8♠, K♠], Cut = K♥ - Points = 6

Average hand value = 4.630434782608695

hand = Hand([Card('3', 'C'), Card('4', 'D'), Card('5', 'H'), Card('5', 'C')])

deck = make_deck()

for c in hand:
deck.remove(c)

deck.remove(c)

print('Candidate Hand: ',hand)
print()

total = 0.0
for i, cut in enumerate(sorted(deck), 1):
scores, counts = score_hand(hand, cut)
value = sum(scores.values())
print('{:>2} Cards = {}, Cut = {} - Points = {}'.format(i, hand.sorted(), cut, value))
total += value

print()
print('Average hand value = {:.3f}'.format(total/len(deck)))


Output:

Candidate Hand:  [3♣, 4♦, 5♥, 5♣]

1 Cards = [3♣, 4♦, 5♥, 5♣], Cut = A♦ - Points = 10
2 Cards = [3♣, 4♦, 5♥, 5♣], Cut = A♥ - Points = 10
3 Cards = [3♣, 4♦, 5♥, 5♣], Cut = A♣ - Points = 10
4 Cards = [3♣, 4♦, 5♥, 5♣], Cut = A♠ - Points = 10
5 Cards = [3♣, 4♦, 5♥, 5♣], Cut = 2♦ - Points = 12
6 Cards = [3♣, 4♦, 5♥, 5♣], Cut = 2♥ - Points = 12
7 Cards = [3♣, 4♦, 5♥, 5♣], Cut = 2♠ - Points = 12
8 Cards = [3♣, 4♦, 5♥, 5♣], Cut = 3♦ - Points = 20
9 Cards = [3♣, 4♦, 5♥, 5♣], Cut = 3♥ - Points = 20
10 Cards = [3♣, 4♦, 5♥, 5♣], Cut = 3♠ - Points = 20
11 Cards = [3♣, 4♦, 5♥, 5♣], Cut = 4♥ - Points = 16
12 Cards = [3♣, 4♦, 5♥, 5♣], Cut = 4♣ - Points = 16
13 Cards = [3♣, 4♦, 5♥, 5♣], Cut = 4♠ - Points = 16
14 Cards = [3♣, 4♦, 5♥, 5♣], Cut = 5♦ - Points = 17
15 Cards = [3♣, 4♦, 5♥, 5♣], Cut = 5♠ - Points = 17
16 Cards = [3♣, 4♦, 5♥, 5♣], Cut = 6♦ - Points = 14
17 Cards = [3♣, 4♦, 5♥, 5♣], Cut = 6♥ - Points = 14
18 Cards = [3♣, 4♦, 5♥, 5♣], Cut = 6♣ - Points = 14
19 Cards = [3♣, 4♦, 5♥, 5♣], Cut = 6♠ - Points = 14
20 Cards = [3♣, 4♦, 5♥, 5♣], Cut = 7♦ - Points = 12
21 Cards = [3♣, 4♦, 5♥, 5♣], Cut = 7♥ - Points = 12
22 Cards = [3♣, 4♦, 5♥, 5♣], Cut = 7♣ - Points = 12
23 Cards = [3♣, 4♦, 5♥, 5♣], Cut = 7♠ - Points = 12
24 Cards = [3♣, 4♦, 5♥, 5♣], Cut = 8♦ - Points = 10
25 Cards = [3♣, 4♦, 5♥, 5♣], Cut = 8♥ - Points = 10
26 Cards = [3♣, 4♦, 5♥, 5♣], Cut = 8♣ - Points = 10
27 Cards = [3♣, 4♦, 5♥, 5♣], Cut = 8♠ - Points = 10
28 Cards = [3♣, 4♦, 5♥, 5♣], Cut = 9♦ - Points = 8
29 Cards = [3♣, 4♦, 5♥, 5♣], Cut = 9♥ - Points = 8
30 Cards = [3♣, 4♦, 5♥, 5♣], Cut = 9♣ - Points = 8
31 Cards = [3♣, 4♦, 5♥, 5♣], Cut = 9♠ - Points = 8
32 Cards = [3♣, 4♦, 5♥, 5♣], Cut = T♦ - Points = 12
33 Cards = [3♣, 4♦, 5♥, 5♣], Cut = T♥ - Points = 12
34 Cards = [3♣, 4♦, 5♥, 5♣], Cut = T♣ - Points = 12
35 Cards = [3♣, 4♦, 5♥, 5♣], Cut = T♠ - Points = 12
36 Cards = [3♣, 4♦, 5♥, 5♣], Cut = J♦ - Points = 12
37 Cards = [3♣, 4♦, 5♥, 5♣], Cut = J♥ - Points = 12
38 Cards = [3♣, 4♦, 5♥, 5♣], Cut = J♠ - Points = 12
39 Cards = [3♣, 4♦, 5♥, 5♣], Cut = Q♦ - Points = 12
40 Cards = [3♣, 4♦, 5♥, 5♣], Cut = Q♥ - Points = 12
41 Cards = [3♣, 4♦, 5♥, 5♣], Cut = Q♣ - Points = 12
42 Cards = [3♣, 4♦, 5♥, 5♣], Cut = Q♠ - Points = 12
43 Cards = [3♣, 4♦, 5♥, 5♣], Cut = K♦ - Points = 12
44 Cards = [3♣, 4♦, 5♥, 5♣], Cut = K♥ - Points = 12
45 Cards = [3♣, 4♦, 5♥, 5♣], Cut = K♣ - Points = 12
46 Cards = [3♣, 4♦, 5♥, 5♣], Cut = K♠ - Points = 12

Average hand value = 12.478


# Average Hand Method

def average_hand_value(deck, hand, **kwargs):
"""
The player is dealt 6 cards which are removed from
the deck. This method calculates the average hand value,
given the remaining cards in the deck (46) and a hand of 4 cards.

Essentially, it uses the remaining cards in the deck, each one in turn,
as the potential starter card. The hand value + the starter card is
calculated. The average value is returned

Parameters
----------
deck - iterable - a list of cards that are remaining in the deck. There should be 46.
hand - Hand object - a list of cards that the player wants to evaluate. There should be 4 cards.

Returns
-------

The average hand value.

"""

assert len(deck) == 46
assert len(hand) == 4

verbose = False if 'verbose' not in kwargs else kwargs['verbose']

total = 0.0
for i, cut in enumerate(sorted(deck), 1):
scores, counts = score_hand(hand, cut)
value = sum(scores.values())
total += value

if verbose:
print('{:>2} Cards = {}, Cut = {} - Points = {}'.format(i, hand.sorted(), cut, value))


hand = Hand([Card('3', 'C'), Card('4', 'D'), Card('5', 'H'), Card('5', 'C')])

deck = make_deck()

for c in hand:
deck.remove(c)

deck.remove(c)

print('Candidate Hand: ',hand)
print()

average = average_hand_value(deck, hand)

print('Average hand value = {:.3f}'.format(average))


Output:

Candidate Hand:  [3♣, 4♦, 5♥, 5♣]

Average hand value = 12.478


Given 6 cards and the starter card is not turned up, find the best set of 4 cards to keep. That is the 4 cards with the highest point total and the highest hand average

def determine_best_hand(hand, **kwargs):
"""
Takes a hand of 6 cards and determines the best 4 hand combination based on point value.
"""
assert len(hand) == 6

verbose = False if 'verbose' not in kwargs else kwargs['verbose']

max_score = 0.0
max_average = 0.0
best_hand = None

for combo in hand.every_combination(count=4):
new_hand = Hand(combo).sorted()
scores, counts = score_hand(new_hand, None) # we are ignoring the cut card when scoring|
score = sum(scores.values())

deck = make_deck()

for c in hand:
deck.remove(c)

average = average_hand_value(deck, new_hand)

if verbose:
print('Hand = {}, value = {}, average = {:.4f}'.format(new_hand, score, average))

if score >= max_score and average > max_average:
max_score = score
max_average = average
best_hand = new_hand

return max_score, best_hand

deck = make_deck()

# shuffle the deck
random.shuffle(deck)

# extract the first 6 cards (removing them from the deck)
hand = make_hand(deck, 6)
print('Candidate Hand = {} '.format(hand))
print('-------')

score, best_hand = determine_best_hand(hand,verbose=True)
print('---------')
print('Best Hand      = {}'.format(best_hand))

scores, counts = score_hand(best_hand, None)
print('Hand Value     = {}'.format(sum(scores.values())))

# find the average hand value
average = average_hand_value(deck, best_hand)

print()
print('Average hand value = {:.4f}'.format(average))


Output:

Candidate Hand = [K♥, 7♦, 9♦, A♦, 8♣, J♦]
-------
Hand = [A♦, 7♦, 9♦, K♥], value = 0, average = 1.6739
Hand = [7♦, 8♣, 9♦, K♥], value = 5, average = 6.8913
Hand = [7♦, 9♦, J♦, K♥], value = 0, average = 2.0870
Hand = [A♦, 7♦, 8♣, K♥], value = 2, average = 3.8913
Hand = [A♦, 7♦, J♦, K♥], value = 0, average = 1.9348
Hand = [7♦, 8♣, J♦, K♥], value = 2, average = 4.0435
Hand = [A♦, 8♣, 9♦, K♥], value = 0, average = 1.9783
Hand = [A♦, 9♦, J♦, K♥], value = 0, average = 2.2826
Hand = [8♣, 9♦, J♦, K♥], value = 0, average = 2.1739
Hand = [A♦, 8♣, J♦, K♥], value = 0, average = 1.9783
Hand = [A♦, 7♦, 8♣, 9♦], value = 5, average = 7.1957
Hand = [A♦, 7♦, 9♦, J♦], value = 4, average = 6.3261
Hand = [7♦, 8♣, 9♦, J♦], value = 5, average = 7.1739
Hand = [A♦, 7♦, 8♣, J♦], value = 2, average = 4.0870
Hand = [A♦, 8♣, 9♦, J♦], value = 0, average = 2.2609
---------
Best Hand      = [A♦, 7♦, 8♣, 9♦]
Hand Value     = 5

Average hand value = 7.1957


If you read the literature on average hand values, they typically make simplifying assumptions. For example, the exclude flushes and nobs. This method uses the proper cribbage scoring system and doesn't make any simplifications.

• https://cliambrown.com/cribbage/methodology.php
• http://www.cribbageforum.com/YourCrib.htm

From the literature, only discard tables are available. There is really no detail in how the tables were calculated or the assumptions used when creating them.

Following the approach from the previous section we need to: 1. Take the cards to discard and pair them with two other cards from the remaining cards 1. Take a starter from the deck 1. score the crib along with the starter and determine its average value 1. Search for the maximum value and the minimum value

def average_crib_value(deck, discard, **kwargs):
"""
The player is dealt 6 cards which are removed from
the deck. They have picked 2 cards to discard to the crib.
This method calculates the average crib value,
given the remaining cards in the deck (46) and 2 discard cards
intended for the crib.

Essentially, it uses the remaining cards in the deck, each pair in turn,
as the potential crib mates and an extra card for the starter.

Parameters
----------
deck - iterable - a list of cards that are remaining in the deck. There should be 46.
discard - Hand object - a list of cards that will be part of the crib. There should be 2 cards.

Returns
-------

The average crib value.

"""

#     assert len(deck) == 46

verbose = False if 'verbose' not in kwargs else kwargs['verbose']

total = 0.0
count = 0

for i, combo in enumerate(combinations(deck, 3), 1):
c1, c2, cut = combo
crib = Hand([c1, c2])

scores, counts = score_hand(crib, cut, is_crib=True)
value = sum(scores.values())
total += value
count += 1

if verbose:
print('{:>2} Crib = {} Cut = {} Points = {}'.format(i, crib.sorted(), cut, value))

average = total/count

return average

hand = Hand([Card('4', 'C'),  Card('Q', 'C'), Card('A','C'), Card('A', 'D')])
discard = Hand([Card('5', 'D'), Card('Q', 'H')])

deck = make_deck()

for c in hand:
deck.remove(c)

deck.remove(c)

print('Candidate Hand = {} '.format(hand.sorted()))
print('-------')

# find the average hand value

print()
print('Average crib value = {:.4f}'.format(average))
print('-------')
print()


Output:

Candidate Hand = [A♣, A♦, 4♣, Q♣]
-------

Average crib value = 6.9283


On my computer it takes about 6 seconds to compute the average value of the discard to the crib. To speed things up we'll create a lookup dictionary contianing all possible pairings of the cards and the average value. Once the dictionary has been calculated we'll store it as .json so we don't need to do it every time.

I don't think counting flushes will make much of a difference to this process. To save computation time, I'll reduce all duplicate ranks from the deck

deck = make_deck()
candidates = sorted(deck)[0::2]

average_crib_values = {}

# attempt to load the pre-calculated averages
try:

except FileNotFoundError:
# there is no pre-calculated dictionary
pass

for i, combo in enumerate(combinations(candidates, 2), 1):
key = str([c.rank for c in discard])

if key not in average_crib_values:
average_crib_values[key] = average
print('{:<2} Discard = {} Average = {:.4f}'.format(i, key, average_crib_values[key]))

# dump the averages into a json file so we don't have to recalculate it every time
json.dump(average_crib_values, fp, indent=4)

print('Overall average crib points = {:.4f}'.format(sum(average_crib_values.values())/len(average_crib_values)))

Overall average crib points = 4.8762


# Expected Average

For the dealer the expected average is the Average Hand Value + the Average Discard Value

For the pone the expected average is the Average Hand Value - the Average Discard Value

hand = Hand([Card('4', 'C'),  Card('Q', 'C'), Card('A','C'), Card('A', 'D'), Card('5', 'D'), Card('Q', 'H')])
deck = make_deck()

for c in hand:
deck.remove(c)


## As Dealer

As the dealer, we want to maximize the amount of points we have in our hand and in the crib. I think the best approach is to maximize the value of the hand as the crib, on average is only valued at about 4.88 points.

print('Candidate Hand = {} '.format(hand.sorted()))
print('')

score, best_hand = determine_best_hand(hand,verbose=False)
print('---------')
print('Best Hand      = {}'.format(best_hand))
print('Hand Value     = {}'.format(score))

# find the average hand value
hand_average = average_hand_value(deck, best_hand)
print('Average hand value = {:.4f}'.format(hand_average))

print('-------')

# find the average crib value. This takes into account flushes and all suits.
# No simplification is made, the calculations are correct as far as I can tell.

# in the cached dictionary of pre-calculated values suits where basically ignored
# so the values will be slightly different then the other method
# key = str([c.rank for c in discard])
key = str(sorted([c.rank for c in discard]))
average_crib_precalc = average_crib_values[key]

print()
print('Average crib value                  = {:.4f}'.format(average_crib))
print('Average crib value (pre-calculated) = {:.4f}'.format(average_crib_precalc))
print('-------')
print()

print('Expected Average - Dealer = {}'.format(hand_average + average_crib))
print('Expected Average - Dealer = {}'.format(hand_average + average_crib_precalc))

Candidate Hand = [A♣, A♦, 4♣, 5♦, Q♣, Q♥]

---------
Best Hand      = [A♣, A♦, 4♣, Q♣]
Hand Value     = 6
Average hand value = 8.4348
-------

Average crib value                  = 6.9283
Average crib value (pre-calculated) = 6.9634
-------

Expected Average - Dealer = 15.36304347826087
Expected Average - Dealer = 15.398149955634427


## As Pone

The strategy for the pone is different. They want to maximize the average value of their hand while at the same time minimize the average value of the crib. This will take a little different strategy.

print('Candidate Hand = {} '.format(hand.sorted()))
print('')

score, best_hand = determine_best_hand(hand,verbose=False)
print('---------')
print('Best Hand      = {}'.format(best_hand))
print('Hand Value     = {}'.format(score))

# find the average hand value
hand_average = average_hand_value(deck, best_hand)
print('Average hand value = {:.4f}'.format(hand_average))

print('-------')

# find the average crib value. This takes into account flushes and all suits.
# No simplification is made, the calculations are correct as far as I can tell.

# in the cached dictionary of pre-calculated values suits where basically ignored
# so the values will be slightly different then the other method
# key = str([c.rank for c in discard])
key = str(sorted([c.rank for c in discard]))
average_crib_precalc = average_crib_values[key]

print()
print('Average crib value                  = {:.4f}'.format(average_crib))
print('Average crib value (pre-calculated) = {:.4f}'.format(average_crib_precalc))
print('-------')
print()

print('Expected Average - Pone = {}'.format(hand_average - average_crib))
print('Expected Average - Pone = {}'.format(hand_average - average_crib_precalc))

Candidate Hand = [A♣, A♦, 4♣, 5♦, Q♣, Q♥]

---------
Best Hand      = [A♣, A♦, 4♣, Q♣]
Hand Value     = 6
Average hand value = 8.4348
-------

Average crib value                  = 6.9283
Average crib value (pre-calculated) = 6.9634
-------

Expected Average - Pone = 1.5065217391304353
Expected Average - Pone = 1.471415261756877


Maximizing the value of the pone's hand doesn't yield an optimal strategy for the pone overall. In the above case it looks as though the pone can only expect about 1.5 points on average even though the hand value is 8.4! We need a different strategy to minimize the loss in points.

def four_card_hand_scores(hand, **kwargs):
"""
Takes a hand of more than 4 cards and returns a list containing
every 4 card hand combination along with the point value, point value dictionary and
a counts dictionary outlining the number of matches for the point value dictionary.

"""
hands = []

for combo in hand.every_combination(count=4):
new_hand = Hand(combo).sorted()

# we are only dealing with 4 cards, ignoring a cut card
scores, counts = score_hand(new_hand, None)
score = sum(scores.values())

hands.append((new_hand, score, scores, counts))

return hands

def average_hand_values(deck, hands, **kwargs):
"""
Calculates the average value for the hands.

Parameters
----------
deck - the cards left unturned as far as the person (pone or dealer)
that are used to calculate the average hand value.

hands - tuple(Hand, number, dictionary, dictionary) - the list of hands to evaluate
the average values for.

Returns
-------
a list of tuples similar to the input list of tuples that contain the hand,
the value of the hand and the average value.

"""

assert len(deck) == 46

average_hands = []
for hand_tuple in hands:
hand, hand_value, *_ = hand_tuple
average_value = average_hand_value(deck, hand)

average_hands.append((hand, hand_value, average_value))

return average_hands

def average_discard_values(hand, average_hands, **kwargs):

for i, a in enumerate(average_hands):
average_hand, value, average = a
#     average_crib = average_crib_value(deck, discard, verbose=False)

try:

key = str(sorted([c.rank for c in discard]))
average_crib_precalc = average_crib_values[key]

except KeyError:
key = str(sorted([c.rank for c in discard], reverse=True))
average_crib_precalc = average_crib_values[key]

value,
average,
average_crib_precalc))


hands = sorted(four_card_hand_scores(hand), key=lambda x:x, reverse=True)
average_hands = sorted(average_hand_values(deck, hands), key=lambda x:x, reverse=True)

row = '{:<3} {} = {} -> Average = {:.4f} - Discard = {} -> Average = {:.4f} -> EA = {:.4f}'

0   [A♣, 4♣, 5♦, Q♣] = 4 -> Average = 7.0000 - Discard = [A♦, Q♥] -> Average = 3.8951 -> EA = 3.1049
1   [A♦, 4♣, 5♦, Q♣] = 4 -> Average = 7.0000 - Discard = [A♣, Q♥] -> Average = 3.8951 -> EA = 3.1049
2   [A♣, 4♣, 5♦, Q♥] = 4 -> Average = 7.0000 - Discard = [A♦, Q♣] -> Average = 3.8951 -> EA = 3.1049
3   [A♦, 4♣, 5♦, Q♥] = 4 -> Average = 7.0000 - Discard = [A♣, Q♣] -> Average = 3.8951 -> EA = 3.1049
4   [A♣, A♦, 5♦, Q♣] = 4 -> Average = 6.0870 - Discard = [4♣, Q♥] -> Average = 4.0782 -> EA = 2.0087
5   [A♣, A♦, 5♦, Q♥] = 4 -> Average = 6.0870 - Discard = [4♣, Q♣] -> Average = 4.0782 -> EA = 2.0087
6   [A♣, A♦, 4♣, 5♦] = 2 -> Average = 6.0435 - Discard = [Q♥, Q♣] -> Average = 5.2502 -> EA = 0.7933
7   [A♣, 5♦, Q♣, Q♥] = 6 -> Average = 7.6957 - Discard = [4♣, A♦] -> Average = 5.5085 -> EA = 2.1871
8   [A♦, 5♦, Q♣, Q♥] = 6 -> Average = 7.6957 - Discard = [4♣, A♣] -> Average = 5.5085 -> EA = 2.1871
9   [4♣, 5♦, Q♣, Q♥] = 6 -> Average = 8.1739 - Discard = [A♦, A♣] -> Average = 5.5335 -> EA = 2.6404
10  [A♣, 4♣, Q♣, Q♥] = 6 -> Average = 7.6957 - Discard = [A♦, 5♦] -> Average = 5.7722 -> EA = 1.9234
11  [A♦, 4♣, Q♣, Q♥] = 6 -> Average = 7.6957 - Discard = [A♣, 5♦] -> Average = 5.7722 -> EA = 1.9234
12  [A♣, A♦, 4♣, Q♣] = 6 -> Average = 8.4348 - Discard = [Q♥, 5♦] -> Average = 6.9634 -> EA = 1.4714
13  [A♣, A♦, 4♣, Q♥] = 6 -> Average = 8.4348 - Discard = [5♦, Q♣] -> Average = 6.9634 -> EA = 1.4714
14  [A♣, A♦, Q♣, Q♥] = 4 -> Average = 5.4783 - Discard = [4♣, 5♦] -> Average = 7.0352 -> EA = -1.5569


By minimizing the average points discarded to the crib, we maximize our expected average points for the hand.

tags: cribbage, math