A middling deal
Weekly challenge 291 — 14 October 2024
Week 291: 14 Oct 2024
A draw poker hand consists of 5 cards, drawn from a pack of 52: no jokers, no wild cards. An ace can rank either high or low.
Write a script to determine the following three things:
Well, this is a bit different! I've never been a poker player, so it's all new to me.
I think that there are two ways to approach this challenge. The first way is to look at each rank and work out how many hands will meet its rule. For example, for Straight Flush we need 5 cards of the same suit with descending ranks. The first of these cards could be anything from a King down to a 5, which is 9 possibilities. And then, these 9 possible combinations could be in any of the 4 suits, so that makes 36, added to which there are another 4 possibilities starting with a high ace, so the total is 40.
But the frequency of some of the other rank categories aren't so easy to calculate, though I'm sure it can be done.
My second approach, and the one I've coded, is to generate all the possible combinations of 5 cards, and check them successively against the top 9 rank catgories, with any hands that fail to match all of those being consigned to the 10th, High Card.
The number of different hands is 52! / (47! × 5!)
,
which is 2598960. While it's easy to generate all these combinations using a Perl module, I wondered
whether the execution time to calculate which rank category each of the 2.5 million hands falls into would be
unreasonable.
But I gave it a try, and on my (quite slow) computer it manages about 20k combinations a second, or just over 2 minutes for the 2.5 million possible hands.
Is my answer correct? Fortunately, someone has done this calculation before me, and their answers are here. My answers agree with theirs, which is satisfying.
#!/usr/bin/perl # Blog: http://ccgi.campbellsmiths.force9.co.uk/challenge use v5.26; # The Weekly Challenge - 2024-10-14 use utf8; # Week 291 - task 2 - Poker hand rankings use warnings; # Peter Campbell Smith binmode STDOUT, ':utf8'; use Algorithm::Combinatorics qw(combinations); poker_hand_rankings(); sub poker_hand_rankings { my ($i, $c, @data, $count, $card, @hand, $value, $suit, @suits, @m, @rank, @counts, $j, $combs, $secs, $start, $k, @ranks); say qq[\n1. How many different 5-card hands can be dealt?]; say qq[Answer: 52! / (47! × 5!) = ] . (52 * 51 * 50 * 49 * 48 / (5 * 4 * 3 * 2)); say ''; # initialise @data = reverse(0 .. 51); $i = combinations(\@data, 5); $count = 0; @rank[$_] = 0 for 0 .. 10; $start = time(); # loop over all possible combinations COMB: while ($c = $i->next) { # analyse combination $count ++; say qq[tested ] . ($count / 1000) . qq[k hands in ] . (time() - $start) . qq[ sec] if ($count % 100000 == 0); @hand = (); # split card number (0 .. 51) into suit (0 .. 3) and rank (0 .. 12) for $card (@$c) { $suit = int($card / 13); $value = $card % 13; push @hand, $suit, $value; } # straight flush (5 consecutive in same suit) if ($hand[2] == $hand[0] and $hand[4] == $hand[0] and $hand[6] == $hand[0] and $hand[8] == $hand[0]) { if ($hand[1] >= 4 and $hand[3] == $hand[1] - 1 and $hand[5] == $hand[1] - 2 and $hand[7] == $hand[1] - 3 and $hand[9] == $hand[1] - 4) { $rank[2] ++; next COMB; } if ($hand[9] == 0 # check for ace high and $hand[1] == 12 and $hand[3] == 11 and $hand[5] == 10 and $hand[7] == 9) { $rank[2] ++; next COMB; } } # four of a kind (4 of same rank) @counts[$_] = 0 for 0 .. 12; for $j (0 .. 4) { $counts[$hand[2 * $j + 1]] ++; } $k = 0; for $j (0 .. 12) { if ($counts[$j] == 4) { $rank[3] ++; next COMB; } # full house (3 of one rank + 2 of another) $k |= 1 if $counts[$j] == 3; $k |= 2 if $counts[$j] == 2; if ($k == 3) { $rank[4] ++; next COMB; } } # flush (5 of same suit) @counts[$_] = 0 for 0 .. 3; for $j (0 .. 4) { $counts[$hand[2 * $j]] ++; } for $j (0 .. 3) { if ($counts[$j] == 5) { $rank[5] ++; next COMB; } } # straight (5 with sequential ranks) @ranks = sort { $a <=> $b }($hand[1], $hand[3], $hand[5], $hand[7], $hand[9]); if ($ranks[4] >= 4 and $ranks[3] == $ranks[4] - 1 and $ranks[2] == $ranks[3] - 1 and $ranks[1] == $ranks[2] - 1 and $ranks[0] == $ranks[1] - 1) { $rank[6] ++; next COMB; } if ($ranks[0] == 0 # check for ace high and $ranks[1] == 9 and $ranks[2] == 10 and $ranks[3] == 11 and $ranks[4] == 12) { $rank[6] ++; next COMB; } # three of a kind (3 of same rank) @counts[$_] = 0 for 0 .. 12; for $j (0 .. 4) { $counts[$hand[2 * $j + 1]] ++; } $k = 0; for $j (0 .. 12) { if ($counts[$j] == 3) { $rank[7] ++; next COMB; } } # two pair (2 of one rank, 2 of another, 1 of yet another) @counts[$_] = 0 for 0 .. 12; for $j (0 .. 4) { $counts[$hand[2 * $j + 1]] ++; } $k = 0; for $j (0 .. 12) { $k ++ if $counts[$j] == 2; if ($k == 2) { $rank[8] ++; next COMB; } } # one pair (2 of one rank) @counts[$_] = 0 for 0 .. 12; for $j (0 .. 4) { $counts[$hand[2 * $j + 1]] ++ } $k = 0; for $j (0 .. 12) { $k ++ if $counts[$j] == 2; if ($k == 1) { $rank[9] ++; next COMB; } } # high card (anything else) $rank[10] ++; } # results say qq[\n2. How many different hands of each of the 10 ranks can be dealt?\n]; say qq[Five of a kind: ] . sprintf('%7d', $rank[1]); say qq[Straight flush: ] . sprintf('%7d', $rank[2]); say qq[Four of a kind: ] . sprintf('%7d', $rank[3]); say qq[Full house: ] . sprintf('%7d', $rank[4]); say qq[Flush: ] . sprintf('%7d', $rank[5]); say qq[Straight: ] . sprintf('%7d', $rank[6]); say qq[Three of a kind: ] . sprintf('%7d', $rank[7]); say qq[Two pair: ] . sprintf('%7d', $rank[8]); say qq[One pair: ] . sprintf('%7d', $rank[9]); say qq[High card: ] . sprintf('%7d', $rank[10]); say qq[\n3. Total: $count - matches answer 1 above.\n]; }
1. How many different 5-card hands can be dealt? Answer: 52! / (47! × 5!) = 2598960 tested 100k hands in 4 sec tested 200k hands in 9 sec tested 300k hands in 14 sec tested 400k hands in 19 sec tested 500k hands in 24 sec tested 600k hands in 29 sec tested 700k hands in 33 sec tested 800k hands in 38 sec tested 900k hands in 43 sec tested 1000k hands in 48 sec tested 1100k hands in 53 sec tested 1200k hands in 57 sec tested 1300k hands in 62 sec tested 1400k hands in 67 sec tested 1500k hands in 72 sec tested 1600k hands in 77 sec tested 1700k hands in 81 sec tested 1800k hands in 86 sec tested 1900k hands in 91 sec tested 2000k hands in 96 sec tested 2100k hands in 101 sec tested 2200k hands in 105 sec tested 2300k hands in 110 sec tested 2400k hands in 115 sec tested 2500k hands in 120 sec 2. How many different hands of each of the 10 ranks can be dealt? Five of a kind: 0 Straight flush: 40 Four of a kind: 624 Full house: 3744 Flush: 5108 Straight: 10200 Three of a kind: 54912 Two pair: 123552 One pair: 1098240 High card: 1302540 3. Total: 2598960 - matches answer 1 above.
Any content of this website which has been created by Peter Campbell Smith is in the public domain