Bridge Hand Probability Analysis

Introduction

Bridge is a wonderful card game. It is sufficiently complex and interesting that some extremely talented people spend a good proportion of their lives playing, analysing, commenting, writing and teaching it. It also lends itself to computer analysis but not in the way chess or similar “complete knowledge” games do. In Bridge the exactly position (card holdings) is usually not completely known. So it is a game about probability and inference.

This is the first in (hopefully) a series of articles on computer analysis of the game of Bridge. It looks at the probability of holding various types of hand after the initial deal and before any other information is available. Several standard tables are generated that represent some key aspects of the game. A general algorithm is developed that allows a broad range of hand probabilities to be calculated.

The code examples are in Clojure but should translate readily to other languages. Some general understanding of both probability theory and Bridge is assumed.

Basic Terminology

Bridge deals are made up of 52 unique cards equally distributed between four hands – North (N), South (S), East (E) and West (W) – with 13 cards in each hand. The cards are shuffled (mixed up) before being dealt (distributed) so that the cards in each hand are randomly selected. The cards are grouped into four suits – Spades (♠︎), Hearts (♥︎), Diamonds (♦︎) and Clubs (♣︎). Spades and Hearts are called the major suits, Clubs and Diamonds are called the minor suits.

Each suit has 13 cards: Ace (A), King (K), Queen (Q), Jack (J), 10, 9, …, 2 (in rank order highest to lowest). The A, K, Q and J of each suit are collectively known as honours (honors US).

The distribution of a hand is the number of cards in each suit. These need to sum to 13, so there are only a certain number of distribution “patterns”. Some examples are 4333, 4432, 5332, etc. These are “generic” distributions in the sense that a 4333 distribution could have the 4-card suit as S, H, D or C (i.e. 4-3-3-3, 3-4-3-3, 3-3-4-3 or 3-3-3-4). The normal convention, as above, is to place dashes (-) between suit counts to denote specific suit distributions, and to exclude them when representing generic distributions. In the generic case the suit counts are always listed in order of most cards to fewest cards.

A common method of assessing the strength of a hand is to use a High Card Points (HCP) count. It assigns points to the four honour cards in each suit as follows: A=4, K=3, Q=2 and J=1. This means there a 40 HCP in any deal, distributed between the four hands.

An example hand:

♠︎ KJ3
♥︎ A10952
♦︎ QJ4
♣︎ A8

This hand has:

  • Generic distribution: 5332
  • Specific distribution: 3-5-3-2
  • HCP: 15

Combinations

We need some utility functions for our analysis. The most useful is the combination function which calculates the number of ways you can select “k” objects from a collection of “n” objects where order is not important. It will be denoted C(n,k) here and can be calculated using the following equation:

C(n,k) = n!/k!(n-k)!
where n! = n factorial = n(n-1)(n-2)…1
Note C(n,0) = C(n,n) = 1.

For example, given A,B,C and D. There are C(4,1) = 4!/1!3! = 4 ways to select one letter (A, B, C or D), and C(4,2) = 4!/2!2! = 6 ways to select two letters (AB, AC, AD, BC, BD or CD).

The Clojure code to calculate combinations is straightforward. However, note the use of *’ instead of just * for the multiplier function. The former supports arbitrary precious arithmetic which is important as factorials become very large, very quickly.

(defn fact
  "Return the factorial of integer n >= 0"
  [n]
  (apply *' (range 1 (inc n))))

(defn comb
  "Return the combination - ways of selecting k from n (k<=n)"
  [n k]
  (/ (fact n) (*' (fact k) (fact (- n k)))))

Initial Analysis

Let’s start by answering some basic questions.

How many deals are there?

One way to look at this is to imagine laying out the 52 cards in every possible order and then taking the first 13 as the North (say) hand, the next 13 as the South hand, etc. This isn’t quite correct as it includes the card order within each hand. We don’t care about this order. To remove it, we can divide by the number of ways 13 cards can be ordered within a hand. The number of ways 52 cards can be ordered is 52!. The number of ways 13 cards can be ordered is 13!. This leads to the result of:

52!/13!4 = 53,644,737,765,488,792,839,237,440,000

Another, perhaps more obvious, approach is to consider each hand in turn. The first, North (say), selects 13 cards from 52, the second, selects 13 cards from the remaining 39 cards (after North’s selection). The third hand selects 13 cards from the remaining 26 cards. The final 13 cards go to the last hand. This leads to:

C(52,13)*C(39,13)*C(26,13)*C(13,13) = 53,644,737,765,488,792,839,237,440,000

The same result. Note the last term is 1 but is included for completeness.

Either way it is a seriously big number and pretty much guarantees you will never play the same deal no matter how often you play!

How many hands are there?

This is easy using the analysis above. It is just the number of ways 13 cards can be selected from 52:

C(52,13) = 635,013,559,600

This is significantly smaller than the number of deals. Using the birthday problem approximation you would need to play around 938,000 hands before there was a fifty percent chance of holding the same hand. This equates to playing 50 hands a day, each day of the year, for 51 years.

Suit Distribution

The obvious first question is:

What is the probability of each distribution?

Let’s consider a specific case, say, 4-3-2-4 (i.e. 4♠︎, 3♥︎, 2♦︎ and 4♣︎). To make up this hand we need to select 4 Spades from the 13 on offer, 3 Hearts from 13, 2 Diamonds from 13 and 4 Clubs. This means there are C(13,4)*C(13,3)*C(13,2)*C(13,4) ways to construct this distribution:

C(13,4)*C(13,3)*C(13,2)*C(13,4) = 11,404,407,300

As there are C(52,13) possible ways to construct all hands, the probability of this exact distribution is:

C(13,4)*C(13,3)*C(13,2)*C(13,4) / C(52,13) = 0.017959313 (or 1.796%)

This is the probability of the specific 4-3-2-4 distribution. If we want to determine the probability of the generic 4432 distribution we need to consider all the 12 various 4432 specific distributions:

4-4-3-2, 4-4-2-3, 4-3-4-2, 4-2-4-3, 4-3-2-4, 4-2-3-4,
3-4-4-2, 2-4-4-3, 3-4-2-4, 2-4-3-4, 3-2-4-4, 2-3-4-4

This leads to a result for the generic 4432 distribution of:

12*C(13,4)*C(13,3)*C(13,2)*C(13,4) / C(52,13) = 0.21551175 (or 21.55%)

So the generic 4432 distribution occurs about 21.55% of the time.

We can generalise this result for all distributions. The following code calculates the probability of each specific distribution. It generates all the possible suit length (0-13) combinations (144 = 38,416) and selects those that add to 13. For these, the probability is calculated as above.

(def distr-specific
  (let [c5213 (comb 52 13)]
    (for [s (range 14)
          h (range 14)
          d (range 14)
          c (range 14)
          :when (= 13 (+ s h d c))]
      (let [cs (* (comb 13 s) (comb 13 h) (comb 13 d) (comb 13 c))
            pp (/ (* 100.0 cs) c5213)]
        [[s h d c] pp]))))

;=> ([[0 0 0 13] 1.5747695224491076E-10]
;    [[0 0 1 12] 2.661360492938992E-8]
;    [[0 0 2 11] 9.580897774580372E-7]
;    ...
;    [[3 4 3 3] 2.6340325788532972]
;    [[3 4 4 2] 1.7959313037636118]
;    ...
;    [[12 1 0 0] 2.661360492938992E-8]
;    [[13 0 0 0] 1.5747695224491076E-10])

(apply + (map second distr-specific))
;=> 100.00000000000003

(count distr-specific)
;=> 560

The result shows there are 560 specific distributions and, encouragingly, their probabilities sum to a total of 100%. Throughout this article, when probability tables are generated, some summation code is usually included to validate the result.

The specific distributions can be grouped into generic distributions using the following code, which also counts the number of specific distributions associated with each generic distribution.

(def distr-generic
  (let [ds-fn (fn [acc [d p]]
                (let [dg-key (vec (sort > d))
                      acc (update-in acc [dg-key :prob] (fnil + 0.0) p)
                      acc (update-in acc [dg-key :cnt] (fnil inc 0))]
                  acc))]
    (reduce ds-fn (sorted-map) distr-specific)))

;=> {[4 3 3 3] {:prob 10.536130315413189, :cnt 4},
;    [4 4 3 2] {:prob 21.551175645163344, :cnt 12},
;    [4 4 4 1] {:prob 2.9932188396060195, :cnt 4},
;    [5 3 3 2] {:prob 15.516846464517606, :cnt 12},
;    [5 4 2 2] {:prob 10.579668043989278, :cnt 12},
;    [5 4 3 1] {:prob 12.930705387098, :cnt 24},
;    ...
;    [13 0 0 0] {:prob 6.299078089796431E-10, :cnt 4}}

(apply + (map :prob (vals distr-generic)))
;=> 99.99999999999996

(count distr-generic)
;=> 39

Full results are shown in Table 1 below and are downloadable here as a csv file. The “Specific count” column is the number for specific distributions that make up each generic distribution. So the columns are related as follows:

“Specific count” * “Specific Prob (%)” = “Generic Prob (%)”
Generic Distribution Specific Count Specific Prob (%) Generic Prob (%)
4333 4 2.6340 10.5361
4432 12 1.7959 21.5512
4441 4 0.7483 2.9932
5332 12 1.2931 15.5168
5422 12 0.8816 10.5797
5431 24 0.5388 12.9307
5440 12 0.1036 1.2433
5521 12 0.2645 3.1739
5530 12 0.0746 0.8952
6322 12 0.4702 5.6425
6331 12 0.2873 3.4482
6421 24 0.1959 4.7021
6430 24 0.0553 1.3262
6511 12 0.0588 0.7053
6520 24 0.0271 0.6511
6610 12 0.0060 0.0723
7222 4 0.1282 0.5130
7321 24 0.0784 1.8808
7330 12 0.0221 0.2652
7411 12 0.0327 0.3918
7420 24 0.0151 0.3617
7510 24 0.0045 0.1085
7600 12 0.0005 0.0056
8221 12 0.0160 0.1924
8311 12 0.0098 0.1176
8320 24 0.0045 0.1085
8410 24 0.0019 0.0452
8500 12 0.0003 0.0031
9211 12 0.0015 0.0178
9220 12 0.0007 0.0082
9310 24 0.0004 0.0100
9400 12 0.0001 0.0010
10-111 4 0.0001 0.0004
10-210 24 0.0000 0.0011
10-300 12 0.0000 0.0002
11-110 12 0.0000 0.0000
11-200 12 0.0000 0.0000
12-100 12 0.0000 0.0000
13-000 4 0.0000 0.0000
Total 560 100.00

Table 1 Percentage probability of all distributions

Although 4333 is the most balanced and likely specific distribution, it is much less common than 4432 because there are only 4 ‘variants’ compared with 12 variants for 4432. Similarly 4441 is comparatively rare compared with other semi-balanced hands.

It is also interesting to consider the table in probability order with an aggregated total. Part of this table is shown in Table 2 below.

Generic Distribution Generic Prob (%) Aggregate Prob (%)
4432 21.55 21.55
5332 15.52 37.07
5431 12.93 50.00
5422 10.58 60.58
4333 10.54 71.11
6322 5.64 76.76
6421 4.70 81.46
6331 3.45 84.91
5521 3.17 88.08
4441 2.99 91.07
7321 1.88 92.96
6430 1.33 94.28
5440 1.24 95.52
5530 0.90 96.42
6511 0.71 97.13
6520 0.65 97.78
7222 0.51 98.29
7411 0.39 98.68
7420 0.36 99.04

Table 2 Aggregate percentage probability of most common distributions

As we can see, only three distributions (4432, 5332 and 5431) represent half of all hands, the top six cover 75% and the top half (there are 39 in total) cover over 99%.

Armed with these results, we can answer distribution based questions such as:

What is the probability of holding a suit of a given length?

The code below uses the generic distribution table above to find all distributions that contain a suit of the specified length (the first :let line). It then retrieves the probabilities of these distributions as a list (the second :let line), and sums them to produce a total for each length.

(for [n (range 14)
      :let [ks (filter #(contains? (set %) n) (keys distr-generic))
            ps (for [k ks] (:prob (get distr-generic k)))]]
  [n (apply + ps)])

;=> [0 5.106552086923343]
;   [1 30.79141395392653]
;   [2 64.90069876863775]
;   ...

The full results are shown in Table 3 below.

Suit length Probability (%)
0 5.10655
1 30.79141
2 64.90070
3 74.22930
4 66.66225
5 45.80767
6 16.55325
7 3.52664
8 0.46676
9 0.03704
10 0.00165
11 0.00004
12 0.00000
13 0.00000

Table 3 Percentage probability of holding a suit of a given length

Holding a void (zero cards) is more common than holding a 7-card suit. Over a third of hands have a singleton or void, perhaps explaining the prevalence of systems with splinter bids. 9-card suits are very rare, 10+ card suits are practically non-existent.

What is the probability of holding two suits of given lengths?

We can use the same general approach by generating all the two suit length combinations as (a,b) pairs and summing the probabilities of the generic distributes that contain them. However, there are a few subtleties. First, as the probability of (a,b) is going to be the same as the probability of (b,a), we only need to consider one of these cases, say ‘a’ less than or equal to ‘b’.

Secondly, for each pair of lengths, we need to find those distributions that contain both. This is a little tricky when a=b. For example, with a length pair of (2,2) and a distribution of 5332. Both ‘a’ (2) and ‘b’ (2) are contained in 5332, if we treat them separately, but not as a pair (i.e. two instances). Whereas (3,3) is contained in 5332.

To account for this the code uses the frequencies function to create a map of the number of instances of each length in the distribution. These counts are then checked based on whether ‘a’ equals ‘b’. If so, a count of two or more is required, otherwise one each is sufficient.

(for [a (range 14)
      b (range a (- 14 a))
      :let [k-fn (fn [d]
                   (let [df (frequencies d)]
                     (if (= a b)
                       (>= (get df a 0) 2)
                       (and (>= (get df a 0) 1)
                            (>= (get df b 0) 1)))))
            ks (filter k-fn (keys distr-generic))
            ps (for [k ks] (:prob (get distr-generic k)))]]
  [[a b] (apply + ps)])

;=> ([[0 0] 0.009827127477294894]
;    [[1 0] 0.2372297355270522]
;    [[1 1] 1.2329342241025114]
;    [[2 0] 1.1305919257098023]
;    [[2 1] 9.968069518369386]
;    [[2 2] 16.935689280673436]
;    [[3 0] 2.605385268689623]
;    [[3 1] 18.387322400099496]
;    [[3 2] 44.699851023464674]
;    [[3 3] 29.76641012186663]
;    ...
0 1 2 3 4 5 6
0 0.00983
1 0.23723 1.23293
2 1.13059 9.96807 16.93569
3 2.60539 18.38732 44.69985 29.76641
4 2.97744 21.06305 37.19462 46.34424 25.78773
5 2.90124 16.91843 29.92147 29.34275 24.75371 4.06910
6 2.05519 8.92791 10.99562 10.41690 6.02830 1.35637 0.07234
7 0.74102 2.38118 2.75548 2.14608 0.75354 0.10851 0.00556
8 0.15685 0.35512 0.30087 0.22606 0.04521 0.00313
9 0.01923 0.02786 0.02603 0.01005 0.00097
10 0.00125 0.00149 0.00110 0.00015
11 0.00004 0.00002 0.00001
12 0.00000 0.00000
13 0.00000

Table 4 Percentage probability of holding two suits of a given length

Two suited hands 5-4 or better occur about 37% of the time. Suggesting that two-suited bids have some merit. 6-4 is more common than 5-5.

Hand distributions are only part of the story. What about hand strength?

HCP Distribution

As with suit distribution, the initial question is:

What is the probability of holding a given number of HCP?

Let’s again consider a specific example. What is the probability of a hand containing exactly 4 HCP? Using the HCP scale (A=4, K=3, Q=2, J=1) it is clear that the 4 HCP could be made up in any of the following ways:

1 x A
1 x K, 1 x J
2 x Q
1 x Q, 2 x J
4 x J

By dividing the cards into five groups, 4xA, 4xK, 4xQ, 4xJ and 36xOther, we can apply the same combination selection approach. So the number of hands that contain exactly one Ace and 12 Other cards (i.e. non-honour cards) is:

C(4,1)*C(4,0)*C(4,0)*C(4,0)*C(36,12) = 5,006,710,800

Similarly, the number of hands containing exactly one King and one Jack and 11 Other cards is:

C(4,0)*C(4,1)*C(4,0)*C(4,1)*C(36,11) = 9,612,884,736

In general, the number of hands containing ‘a’ aces, ‘k’ kings, ‘q’ queens and ‘j’ jacks is:

C(4,a)*C(4,k)*C(4,q)*C(4,j)*C(36,(13 – (a+k+q+j)))

So we could calculate the number of hands for each of the HCP combinations above, sum them, and divide by C(52,13) to get the probability of holding exactly 4 HCPs, but it is easier to work the other way around. By considering the 5 x 5 x 5 x 5 combinations of honour cards (A, K, Q and J) and their corresponding HCP score, we can accumulate the total number of hands for each HCP value as shown in the following code.

(defn hcp-prob
  "Return a sorted map of HCP probabilities"
  []
  (let [c5213 (comb 52 13)
        plst (for [a (range 5)
                   k (range 5)
                   q (range 5)
                   j (range 5)
                   :let [x (- 13 (+ a k q j))]
                   :when (>= x 0)]
               (let [hcp (+ (* 4 a) (* 3 k) (* 2 q) (* 1 j))
                     cs (* (comb 4 a)
                           (comb 4 k)
                           (comb 4 q)
                           (comb 4 j)
                           (comb 36 x))
                     prob (/ cs c5213)]
                 [hcp prob]))
        h-fn (fn [acc [h p]] (update acc h (fnil + 0.0) (* 100.0 p)))]
    (reduce h-fn (sorted-map) plst)))

;=> {0 0.3638961034872365,
;    1 0.788441557555679,
;    2 1.356119478995768,
;    ...

(apply + (map val (hcp-prob)))
;=> 99.99999999999997

The full results are shown in Table 5 below and can be download as a csv file here.

HCP Exactly N Prob (%) N-or-less Prob (%) N-or-more Prob (%)
0 0.3639 0.3639 100.0000
1 0.7884 1.1523 99.6361
2 1.3561 2.5085 98.8477
3 2.4624 4.9708 97.4915
4 3.8454 8.8163 95.0292
5 5.1862 14.0025 91.1837
6 6.5541 20.5565 85.9975
7 8.0281 28.5846 79.4435
8 8.8922 37.4768 71.4154
9 9.3562 46.8331 62.5232
10 9.4051 56.2382 53.1669
11 8.9447 65.1828 43.7618
12 8.0269 73.2097 34.8172
13 6.9143 80.1240 26.7903
14 5.6933 85.8174 19.8760
15 4.4237 90.2410 14.1826
16 3.3109 93.5520 9.7590
17 2.3617 95.9137 6.4480
18 1.6051 97.5187 4.0863
19 1.0362 98.5549 2.4813
20 0.6435 99.1985 1.4451
21 0.3779 99.5763 0.8015
22 0.2100 99.7864 0.4237
23 0.1119 99.8983 0.2136
24 0.0559 99.9542 0.1017
25 0.0264 99.9806 0.0458
26 0.0117 99.9923 0.0194
27 0.0049 99.9972 0.0077
28 0.0019 99.9990 0.0028
29 0.0007 99.9997 0.0010
30 0.0002 99.9999 0.0003
31 0.0001 100.0000 0.0001
32 0.0000 100.0000 0.0000
33 0.0000 100.0000 0.0000
34 0.0000 100.0000 0.0000
35 0.0000 100.0000 0.0000
36 0.0000 100.0000 0.0000
37 0.0000 100.0000 0.0000

Table 5 Percentage Probability of holding a specific number of HCP

Our original question on the probability of holding a hand with exactly 4 HCP can now be read directly from the above table as 3.8454%. We can also see, for example, that if you open hands with 12+ HCP, you would open about 34.8% of the time (excluding weak distributed openings). Strong club systems using 16+ HCP would open 1C just under 10% of the time.

The next challenge is to combine distribution and HCP so we can answer questions like what is the probability of a ‘balanced’ hand with 14-16 HCP. Unfortunately, because distribution and HCP are not mutually exclusive we can’t simply used: prob(A and B) = prob(A) x prob(B).

Combined Suit and HCP Distribution

Again let’s consider the following question first:

What is the probability of holding a given distribution and number of HCP?

We can extend the approach above only this time we need to divide the cards into a lot more groups. We need one for each honour card as we are now interested in both its HCP value and its suit. The remaining non-honour cards need to be broken into four groups – one for each suit.

As an example, consider the probability of this hand:

♠︎ Kxx
♥︎ Axxxx
♦︎ QJx
♣︎ xx

The non-honour cards have been replaced with ‘x’ as their rank (or face value) is irrelevant. The number of ways this hand can be selected is:

C(1,0)*C(1,1)*C(1,0)*C(1,0)*C(9,2)* (Spades)
C(1,1)*C(1,0)*C(1,0)*C(1,0)*C(9,4)* (Hearts)
C(1,0)*C(1,0)*C(1,1)*C(1,1)*C(9,1)* (Diamonds)
C(1,0)*C(1,0)*C(1,0)*C(1,0)*C(9,2) (Clubs)

Note that as C(1,0) = C(1,1) = 1, the honour cards don’t directly contribute to the probability result but they do determine the number of remaining cards (Xs) and the HCP value. This hand would be counted in the 5332 distribution and 10 HCP group. Its probability would be added to all the other hands that have the same distribution and HCP count.

The idea is to iterate over all the honours and Xs, and for each valid hand, determine its distribution, HCP and probability, and aggregate it with all the others with the same distribution and HCP. At the end, the probability of each distribution and HCP combination is determined.

The main problem with this approach is there are 216 x 104 (655,360,000) iterations in 20 nested loops! Don’t try this on an abacus. Fortunately modern computers are insanely quick. The following “heroic” code, illustrative but not optimised, runs in just over 3 minutes on my Macbook Pro.

(defn distr-hcp-prob
  "Return a map of distribution and HCP probabilities (not optimised)"
  []
  (let [c5213 (comb 52 13)
        z [0 1]
        plst (for [sa z, sk z, sq z, sj z, sx (range 10)
                   ha z, hk z, hq z, hj z, hx (range 10)
                   da z, dk z, dq z, dj z, dx (range 10)
                   ca z, ck z, cq z, cj z, cx (range 10)
                   :let [ss (+ sa sk sq sj sx)
                         hs (+ ha hk hq hj hx)
                         ds (+ da dk dq dj dx)
                         cs (+ ca ck cq cj cx)]
                   :when (= (+ ss hs ds cs) 13)]
               (let [hcp (+ (* 4 (+ sa ha da ca))
                            (* 3 (+ sk hk dk ck))
                            (* 2 (+ sq hq dq cq))
                            (* 1 (+ sj hj dj cj)))
                     distr (vec (sort > [ss hs ds cs]))
                     cs (* (comb 9 sx)
                           (comb 9 hx)
                           (comb 9 dx)
                           (comb 9 cx))]
                 [[distr hcp] (* 100.0 (/ cs c5213))]))
        p-fn (fn [acc [k p]] (update acc k (fnil + 0.0) p))]
    (reduce p-fn (sorted-map) plst)))

;=> {[[4 3 3 3] 0] 0.04704195862969728,
;    [[4 3 3 3] 1] 0.0918438239913137,
;    [[4 3 3 3] 2] 0.15808658206170367,
;    [[4 3 3 3] 3] 0.27387426011745303,
;    ...

(apply + (map val (distr-hcp-prob)))
;=> 99.99999999999093

This code can be significantly improved by reorganising it by:

  • moving the Xs loops and the probability calculation to the top
  • testing the card count earlier to eliminate pointless lower iterations
  • making use of various pre-calculated results
  • using bit-based honour indexes for fast vector lookups and less nesting

These changes result in a significant improvement reducing the runtime to around 25 seconds (code and more details in the Generalised Algorithm for Table Generation section below).

It is also noteworthy that the Clojure for loop is “lazy” and only returns the next, or next few, items in the sequence when requested by the reduce function. This means the memory requirement of the calculation is quite low. Without laziness the entire sequence would need to be “realised” and held in memory which would be somewhat problematic.

Finally note there are alternative ways to create “cartesian products” of sequences using the Clojure Combinatorics library.

A subset of the result is shown below in Table 6 because of space restrictions. The full table in csv format is available here.

10 11 12 13 14 15 16 17 18
4333 0.9597 0.9167 0.8246 0.7126 0.5925 0.4687 0.3551 0.2585 0.1804
4432 1.9802 1.8903 1.6996 1.4669 1.2166 0.9581 0.7234 0.5234 0.3627
4441 0.2797 0.2670 0.2400 0.2066 0.1706 0.1332 0.0997 0.0712 0.0485
5332 1.4389 1.3719 1.2313 1.0626 0.8779 0.6880 0.5176 0.3727 0.2563
5422 0.9903 0.9431 0.8466 0.7291 0.6012 0.4685 0.3515 0.2511 0.1715
5431 1.2204 1.1627 1.0441 0.8980 0.7391 0.5735 0.4282 0.3038 0.2055
5440 0.1202 0.1148 0.1032 0.0885 0.0724 0.0555 0.0408 0.0283 0.0185
5521 0.3057 0.2901 0.2607 0.2232 0.1828 0.1400 0.1040 0.0724 0.0483
5530 0.0875 0.0833 0.0749 0.0641 0.0523 0.0398 0.0292 0.0200 0.0130
6322 0.5392 0.5110 0.4564 0.3937 0.3217 0.2478 0.1844 0.1308 0.0877
6331 0.3324 0.3151 0.2817 0.2425 0.1978 0.1516 0.1123 0.0790 0.0524
6421 0.4580 0.4333 0.3879 0.3328 0.2711 0.2062 0.1524 0.1060 0.0698
6430 0.1312 0.1245 0.1116 0.0956 0.0776 0.0586 0.0427 0.0292 0.0187
6511 0.0709 0.0667 0.0600 0.0510 0.0413 0.0308 0.0225 0.0151 0.0096
6520 0.0659 0.0622 0.0559 0.0476 0.0384 0.0285 0.0207 0.0138 0.0086
6610 0.0076 0.0072 0.0065 0.0055 0.0043 0.0031 0.0022 0.0014 0.0008
7222 0.0515 0.0479 0.0424 0.0367 0.0295 0.0220 0.0161 0.0113 0.0073
7321 0.1906 0.1773 0.1574 0.1356 0.1090 0.0807 0.0588 0.0406 0.0258
7330 0.0273 0.0255 0.0227 0.0195 0.0156 0.0115 0.0082 0.0056 0.0034
7411 0.0405 0.0377 0.0336 0.0287 0.0230 0.0168 0.0121 0.0082 0.0050
7420 0.0377 0.0351 0.0313 0.0268 0.0214 0.0156 0.0112 0.0074 0.0045
7510 0.0117 0.0109 0.0097 0.0083 0.0065 0.0046 0.0033 0.0021 0.0012
7600 0.0006 0.0006 0.0005 0.0004 0.0003 0.0002 0.0002 0.0001 0.0000
8221 0.0213 0.0187 0.0165 0.0143 0.0113 0.0078 0.0056 0.0038 0.0023
8311 0.0131 0.0116 0.0102 0.0088 0.0070 0.0048 0.0034 0.0023 0.0013
8320 0.0122 0.0108 0.0095 0.0082 0.0065 0.0044 0.0031 0.0021 0.0012
8410 0.0052 0.0046 0.0041 0.0035 0.0027 0.0018 0.0013 0.0008 0.0004
8500 0.0004 0.0003 0.0003 0.0003 0.0002 0.0001 0.0001 0.0000 0.0000
9211 0.0023 0.0018 0.0016 0.0014 0.0011 0.0006 0.0004 0.0003 0.0002
9220 0.0011 0.0008 0.0007 0.0006 0.0005 0.0003 0.0002 0.0001 0.0001
9310 0.0013 0.0011 0.0009 0.0008 0.0006 0.0004 0.0002 0.0002 0.0001
9400 0.0001 0.0001 0.0001 0.0001 0.0001 0.0000 0.0000 0.0000 0.0000

Table 6 Part of the Distribution vs. HCP table

This allows us to answer more complex questions like:

How often do you hold a weak no-trump hand?

Let’s say we define a weak no-trump as 12-14 HCP with either 4333 or 4432 distribution. Summing the six cells above matching this criteria gives 6.51%.

If we also include 5332 distributions with a 5-card minor, we need to add a further 3.17 / 2 (as 5-card minors only occurs half the time), resulting in 8.10%.

How often do you hold a weak-two in a major?

Here we just need to decide what HCP range and distributions constitutes a weak-two opening bid, sum the results and divide by two (to exclude the minors). For 5-10 HCP, with 6322 or 6331 distributions, we get 2.18%.

How often do you hold a strong no-trump hand?

Assuming 16-18 HCP and 4333, 4432 and 5332 (including majors) distributions, we get 3.55%.

Generalised Algorithm for Table Generation

From these results alone, we can’t account for more sophisticated considerations such as suit quality, number of key cards or singleton honours. However, it is relatively easy to modify the code above to cover a broad range of probabilities based on two variables.

The following code takes a single function that returns the “row” and “column” values, as a vector, based on the honour combinations and card counts for each iteration. The honour cards for each suit are represented by the lower four bits of a suit index (is, ih, id and ic) as follows: A = 1000, K = 0100, Q = 0010 and J = 0001. The table below shows all the 16 combinations.

Index Bits Honours Card Count HCP
0 0000 0 0
1 0001 J 1 1
2 0010 Q 1 2
3 0011 QJ 2 3
4 0100 K 1 3
5 0101 KJ 2 4
6 0110 KQ 2 5
7 0111 KQJ 3 6
8 1000 A 1 4
9 1001 AJ 2 5
10 1010 AQ 2 6
11 1011 AQJ 3 7
12 1100 AK 2 7
13 1101 AKJ 3 8
14 1110 AKQ 3 9
15 1111 AKQJ 4 10

Table 7 Honour combinations associated with the index value

These allow for some pre-processing of aggregate values into vectors that can be quickly referenced using just the single index.

The code ignores iterations when either key function returns a non-true (i.e. nil or false) value. This allows filtering and “normalisation” (results inflated to sum to 100%) of subsets of the combinations. This is useful for comparisons with single variable solutions (see below).

(defn hand-map
  "Using the supplied keys function, return a raw and normalised map of
  row and column keys versus probability ignoring non-true keys"
  [keys-fn]
  (let [c5213 (comb 52 13)
        combs [1 9 36 84 126 126 84 36 9 1]     ; pre-calculated comb(9,x)
        hcnt [0 1 1 2 1 2 2 3 1 2 2 3 2 3 3 4]  ; honour count by index
        hcps [0 1 2 3 3 4 5 6 4 5 6 7 7 8 9 10] ; hcp by index
        ;
        plst (apply concat
                    (for [sx (range 10)
                          hx (range 10)
                          dx (range 10)
                          cx (range 10)
                          :when (<= (+ sx hx dx cx) 13)]
                      (let [cs (* (combs sx)
                                  (combs hx)
                                  (combs dx)
                                  (combs cx))
                            prob (* 100.0 (float (/ cs c5213)))]
                        (for [is (range 16)
                              ih (range 16)
                              id (range 16)
                              ic (range 16)
                              :let [ss (+ sx (hcnt is))
                                    hs (+ hx (hcnt ih))
                                    ds (+ dx (hcnt id))
                                    cs (+ cx (hcnt ic))]
                              :when (= (+ ss hs ds cs) 13)]
                          (let [hcp (+ (hcps is)
                                       (hcps ih)
                                       (hcps id)
                                       (hcps ic))
                                rc-key (keys-fn [ss hs ds cs]
                                                [is ih id ic]
                                                hcp)]
                            [[(rc-key 0) (rc-key 1)] prob])))))
        ;
        p-fn (fn [acc [k p]]
               (if (and (k 0) (k 1))
                 (update acc k (fnil + 0.0) p)
                 acc))
        raw (reduce p-fn (sorted-map) plst)
        tot (apply + (map val raw))
        ;
        n-fn (fn [acc [k p]] (assoc acc k (* p (/ 100.0 tot))))
        norm (reduce n-fn (sorted-map) raw)]
    {:raw raw
     :norm norm}))

Using this code the earlier distribution vs. HCP result would be generated using the following code:

"Distribution and HCP probabilities"
(hand-map
  (fn [vs vi hcp] [(vec (sort > vs)) hcp]))

This approach allows for a broad range of zero, one and two variable relationships to be calculated on any aspect of card holdings involving honour combinations and suit distributions. For example:

Probability of holding a singleton King (zero variable)

The following code calculates this single value (zero variables). For each combination it checks each suit for a length of one and a King honour.

"Probability of a holding a singleton King"
(hand-map
  (fn [vs vi hcp] ["Singleton King"
                   (some
                     #(and (= 1 (vs %)) (= 4 (vi %)))
                     (range 4))]))

;=>
; {:raw {["Singleton King" true] 2.45614190016599},
;  :norm {["Singleton King" true] 100.0}}

The result of 2.456% is higher than you might have thought.

Probability of holding a specific number of Aces if you have 16+ HCP (one variable)

This is a one variable example. The variable is the number of Aces. The other fixed values is 16 or more HCP.

"Probability of holding a specific number of aces with 16+ HCP"
(let [aces [0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1]]
  (hand-map
    (fn [vs vi hcp] [(>= hcp 16)
                     (apply + (map aces vi))])))

;=> {:raw {[true 0] 0.06541575839532875,
;          [true 1] 1.5688942600834856,
;          [true 2] 5.052602400387602,
;          [true 3] 2.807934725002544,
;          [true 4] 0.2641056474450006},
;   :norm {[true 0] 0.6703153483184444,
;          [true 1] 16.076461210878005,
;          [true 2] 51.774022361136055,
;          [true 3] 28.772910219442497,
;          [true 4] 2.7062908602249824}}

So if you hold 16 or more HCP the probability of holding the specified number of Aces is:

Aces Probability (%)
0 0.67
1 16.08
2 51.77
3 28.77
4 2.71

Table 8 Probability of holding a specific number of Aces with 16+ HCP

Probability of holding a specific number of Aces for all HCP values (two variables)

The above result can easily be extended to all HCP values (instead of just 16+ HCP) by “freeing” the HCP variable as shown in the code below. Of course now normalisation of any subset has to be performed separately.

"Probability of holding a specific number of aces and HCP"
(let [aces [0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1]]
  (hand-map
    (fn [vs vi hcp] [hcp
                     (apply + (map aces vi))])))

The results are shown below and can be downloaded as a CSV file here.

HCP 0 1 2 3 4
0 0.36390
1 0.78844
2 1.35612
3 2.46236
4 3.05700 0.78844
5 3.67239 1.51381
6 4.07960 2.47449
7 3.71524 4.31284
8 3.35970 4.96481 0.56768
9 2.68226 5.71328 0.96069
10 1.90530 6.00541 1.49440
11 1.31782 5.12856 2.49829
12 0.78724 4.42847 2.65104 0.16011
13 0.43953 3.31575 2.92184 0.23721
14 0.22792 2.21836 2.89547 0.35157
15 0.10151 1.45162 2.30733 0.56322
16 0.04300 0.80700 1.89932 0.54677 0.01483
17 0.01568 0.42424 1.32570 0.57701 0.01906
18 0.00495 0.20486 0.83164 0.53668 0.02695
19 0.00141 0.08428 0.51270 0.39646 0.04132
20 0.00031 0.03330 0.26309 0.31050 0.03634
21 0.00006 0.01102 0.12960 0.20048 0.03671
22 0.00001 0.00319 0.05776 0.11722 0.03187
23 0.00000 0.00082 0.02173 0.06773 0.02163
24 0.00000 0.00015 0.00795 0.03172 0.01608
25 0.00002 0.00234 0.01455 0.00951
26 0.00000 0.00061 0.00591 0.00514
27 0.00000 0.00014 0.00201 0.00276
28 0.00000 0.00002 0.00067 0.00116
29 0.00000 0.00017 0.00049
30 0.00000 0.00004 0.00018
31 0.00000 0.00001 0.00005
32 0.00000 0.00002
33 0.00000 0.00000
34 0.00000 0.00000
35 0.00000
36 0.00000
37 0.00000

Table 9 Percentage probability of holding a specific HCP value and number of Aces

Probability of holding a specific number of Keycards for all HCP values

Keycards are defined as the four Aces and the King of trumps. The Queen of trumps is sometimes included. We will assume that the longest suit, or one of them if there are several, is the trump suit. The following code will produce a similar table to the Aces one above. In this case, we will include the keycard count both with, and without, the Queen of trumps.

"Probability of holding a specific number of keycards and HCP
(assumes one of the longest suits is trumps)"
(let [aces [0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1]
      kings [0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1]
      queens [0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1]]
  (hand-map
    (fn [vs vi hcp]
      (let [inx (->> (map vector vs vi)
                     (sort-by first >)
                     first
                     peek)
            kcds (+ (apply + (map aces vi))
                    (kings inx))]
        [hcp [kcds (queens inx)]]))))

The results are shown below and can be downloaded as a CSV file here.

0-Q 0+Q 1-Q 1+Q 2-Q 2+Q 3-Q 3+Q 4-Q 4+Q 5-Q 5+Q
0 0.36390
1 0.78844
2 1.00808 0.34804
3 1.46033 0.65399 0.34804
4 1.57982 0.82318 1.44243
5 1.41167 1.18930 2.33699 0.24824
6 1.27307 1.20774 3.00980 1.06349
7 0.92933 1.07349 3.77905 1.59223 0.65399
8 0.61917 0.94239 3.66454 2.01087 1.65522
9 0.39269 0.65989 3.15431 2.49294 2.24690 0.40950
10 0.20388 0.43626 2.57033 2.30077 2.88262 1.01126
11 0.10118 0.26570 1.74543 1.95070 3.16818 1.29606 0.41743
12 0.04411 0.13420 1.10128 1.54337 2.80455 1.63106 0.76829
13 0.01579 0.06525 0.63525 1.00744 2.30161 1.74794 0.91154 0.22953
14 0.00541 0.02712 0.30987 0.62410 1.68513 1.48433 1.16258 0.39479
15 0.00136 0.00954 0.14272 0.34541 1.06626 1.19193 1.11213 0.44883 0.10550
16 0.00029 0.00311 0.05650 0.16295 0.62605 0.84402 0.90710 0.56281 0.14810
17 0.00005 0.00075 0.01889 0.07284 0.32524 0.51456 0.70542 0.51582 0.15724 0.05089
18 0.00000 0.00015 0.00579 0.02740 0.14745 0.29427 0.45942 0.40731 0.19947 0.06382
19 0.00000 0.00002 0.00130 0.00889 0.06142 0.14662 0.27137 0.30764 0.16401 0.06611 0.00879
20 0.00000 0.00025 0.00257 0.02171 0.06411 0.14622 0.19237 0.12402 0.08278 0.00951
21 0.00000 0.00003 0.00055 0.00656 0.02575 0.06763 0.11000 0.09101 0.06346 0.00919 0.00370
22 0.00000 0.00010 0.00172 0.00865 0.02835 0.05704 0.05199 0.04719 0.01161 0.00338
23 0.00000 0.00001 0.00033 0.00251 0.01042 0.02529 0.02872 0.03328 0.00806 0.00328
24 0.00000 0.00005 0.00062 0.00324 0.01020 0.01401 0.01800 0.00567 0.00412
25 0.00000 0.00000 0.00011 0.00086 0.00357 0.00568 0.00973 0.00392 0.00255
26 0.00000 0.00002 0.00018 0.00106 0.00220 0.00447 0.00193 0.00182
27 0.00000 0.00003 0.00027 0.00069 0.00174 0.00100 0.00119
28 0.00000 0.00000 0.00005 0.00019 0.00064 0.00043 0.00054
29 0.00000 0.00001 0.00004 0.00019 0.00015 0.00028
30 0.00000 0.00001 0.00005 0.00005 0.00011
31 0.00000 0.00000 0.00001 0.00001 0.00004
32 0.00000 0.00000 0.00000 0.00001
33 0.00000 0.00000 0.00000
34 0.00000 0.00000 0.00000
35 0.00000 0.00000
36 0.00000
37 0.00000

Table 10 Percentage probability of holding a specific HCP value and number of Keycards (with and without trump Q)

One final example:

Probability of holding a weak-two hand

Earlier we calculated the probability of a major weak two hand using only distribution (6322 and 6331) and HCP (5-10) as 2.18%. We can refine the criteria further using this more detailed approach.

Let’s say we define an ‘idealish’ weak-two in a major as:

  • 5-10 HCP
  • 6-card major with two honours
  • no void
  • no second suit (4 or more cards)
  • no singleton honour

The following code can be used to calculate the probability:

"Probability of holding  an 'ideal' 5-10 HCP, 2-honour
weak-two major hand with no singleton honour"
(hand-map
  (fn [vs vi hcp]
    (let [hcnt [0 1 1 2 1 2 2 3 1 2 2 3 2 3 3 4]]
      (if (and
            (>= hcp 5) (<= hcp 10)            ; 5-10 HCP
            (or (and (= 6 (vs 0))             ; 6 Spades ...
                     (>= (hcnt (vi 0)) 2))    ; and 2 honours
                (and (= 6 (vs 1))             ; 6 Hearts ...
                     (>= (hcnt (vi 1)) 2)))   ; and 2 honours
            (> (apply min vs) 0)              ; no voids
            (< (second (sort > vs)) 4)        ; 2nd suit < 4
            (every? #(or                      ; for all suits either:
                       (not= 1 (vs %))        ; not a singleton or
                       (zero? (vi %)))        ; no honours
                    (range 4)))
        ["Major weak-two" ""]
        [false false]))))

;=> {["Major weak-two" ""] 1.162334466504511}

So our final result shows this type of hand occurs about 1.16% of the time.

Hopefully, by using these examples, the reader will be able to craft code to find the probability of any particular hand type that may interest them.

Next Article

This article has focussed on the probability of various attributes of a single hand. The next step is to look at characteristics of the two hands of a partnership.

Leave a Reply

Your email address will not be published. Required fields are marked *