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.