Poker hand analyser: Difference between revisions
(→{{header|Python}}: Update highcard) |
(→{{header|Python}}: Update tie-breaker for flush) |
||
Line 206: | Line 206: | ||
allstypes = {s for f, s in hand} |
allstypes = {s for f, s in hand} |
||
if len(allstypes) == 1: |
if len(allstypes) == 1: |
||
allfaces = [f for f,s in hand] |
|||
return 'flush', max(hand, key=lambda card: (face.index(card.face), card.suit)).face |
|||
return 'flush', sorted(allfaces, |
|||
key=lambda f: face.index(f), |
|||
reverse=True) |
|||
return False |
return False |
||
Line 311: | Line 314: | ||
'10♥ j♥ q♥ k♥ a♥' straight-flush 'a' |
'10♥ j♥ q♥ k♥ a♥' straight-flush 'a' |
||
'4♥ 4♠ k♠ 5♦ 10♠' one-pair ['4', 'k', '10', '5'] |
'4♥ 4♠ k♠ 5♦ 10♠' one-pair ['4', 'k', '10', '5'] |
||
'q♣ 10♣ 7♣ 6♣ 4♣' flush 'q'</pre> |
'q♣ 10♣ 7♣ 6♣ 4♣' flush ['q', '10', '7', '6', '4']</pre> |
Revision as of 17:32, 10 December 2013
Create a program to parse a single 5 card poker hand and rank it according to the List of poker hands [1].
A poker hand is specified as a space separated list of 5 playing cards. Each input card has two characters indicating face and suit. For example 2d (two of diamonds).
Faces are: a, 2, 3, 4, 5, 6, 7, 8, 9, 10, j, q, k
Suits are: h (hearts), d (diamonds), c (clubs), and s (spades), or alternatively the unicode card-suit characters: ♥ ♦ ♣ ♠
For extra credit, use the playing card characters introduced with Unicode 6.0 (U+1F0A1 - U+1F0DE).
Duplicate cards are illegal.
The program should analyse a single hand and produce one of the following outputs:
straight-flush four-of-a-kind full-house flush straight three-of-a-kind two-pair one-pair high-card invalid
Examples
2♥ 2♦ 2♣ k♣ q♦: three-of-a-kind 2♥ 5♥ 7♦ 8♣ 9♠: high-card a♥ 2♦ 3♣ 4♣ 5♦: straight 2♥ 3♥ 2♦ 3♣ 3♦: full-house 2♥ 7♥ 2♦ 3♣ 3♦: two-pair 2♥ 7♥ 7♦ 7♣ 7♠: four-of-a-kind 10♥ j♥ q♥ k♥ a♥: straight-flush 4♥ 4♠ k♠ 5♦ 10♠: one-pair q♣ 10♣ 7♣ 6♣ 4♣: flush
The programs output for the above examples should be displayed here on this page.
Perl 6
This solution is written entirely as a Perl 6 grammar. <lang perl6> use v6;
grammar PokerHand {
# Perl6 Grammar to parse and rank 5-card poker hands # E.g. PokerHand.parse("2♥ 3♥ 2♦ 3♣ 3♦");
rule TOP { <hand>
:my ($n, $flush, $straight); { $n = n-of-a-kind($<hand>); $flush = flush($<hand>); $straight = straight($<hand>); } <rank($n, $flush, $straight)> }
proto token suit {*} token suit:sym<♥> {<sym>} token suit:sym<♦> {<sym>} token suit:sym<♠> {<sym>} token suit:sym<♣> {<sym>}
token face {:i <[2..9]> | 10 | j | q | k | a }
token card {<face><suit> <?{ my $card = ~$/; ! %*PLAYED{$card.lc}++; }> }
rule hand { :my %*PLAYED; { %*PLAYED = () }
[ <card> ]**5 }
token rank($n, $flush, $straight) { $<straight-flush> = <?{$straight && $flush}> || $<four-of-a-kind> = <?{$n[0] == 4}> || $<full-house> = <?{$n[0] == 3 && $n[1] == 2}> || $<flush> = <?{$flush}> || $<straight> = <?{$straight}> || $<three-of-a-kind> = <?{$n[0] == 3}> || $<two-pair> = <?{$n[0] == 2 && $n[1] == 2}> || $<one-pair> = <?{$n[0] == 2}> || $<high-card> = <?> }
sub n-of-a-kind($/) { my %n;
for @<card> -> $/ { %n{ ~$<face> }++ } return %n.values.sort: {$^b <=> $^a}; }
sub flush($/) { my %m;
for @<card> -> $/ { %m{ ~$<suit> }++ } return +%m.keys == 1; }
sub straight($/) { # allow both ace-low and ace-high straights my @seq = 'a', 2 .. 10, < j q k a >; my %got;
for @<card> -> $/ { %got{ ~$<face>.lc }++ }
my $run = 0; for @seq { if %got{ $_ } { return True if ++$run >= 5; } else { $run = 0; } } return False; }
}
for ("2♥ 2♦ 2♣ k♣ q♦",
"2♥ 5♥ 7♦ 8♣ 9♠", "a♥ 2♦ 3♣ 4♣ 5♦", "2♥ 3♥ 2♦ 3♣ 3♦", "2♥ 7♥ 2♦ 3♣ 3♦", "2♥ 7♥ 7♦ 7♣ 7♠", "10♥ j♥ q♥ k♥ a♥", ) { PokerHand.parse($_); my $rank = $<rank> ?? $<rank>.caps.lc !! 'invalid'; say "$_: $rank";
} </lang>
Python
<lang python>from collections import namedtuple
class Card(namedtuple('Card', 'face, suit')):
def __repr__(self): return .join(self)
suit = '♥ ♦ ♣ ♠'.split()
- ordered strings of faces
faces = '2 3 4 5 6 7 8 9 10 j q k a' lowaces = 'a 2 3 4 5 6 7 8 9 10 j q k'
- faces as lists
face = faces.split() lowace = lowaces.split()
def straightflush(hand):
f,fs = ( (lowace, lowaces) if any(card.face == '2' for card in hand) else (face, faces) ) ordered = sorted(hand, key=lambda card: (f.index(card.face), card.suit)) first, rest = ordered[0], ordered[1:] if ( all(card.suit == first.suit for card in rest) and ' '.join(card.face for card in ordered) in fs ): return 'straight-flush', ordered[-1].face return False
def fourofakind(hand):
allfaces = [f for f,s in hand] allftypes = set(allfaces) if len(allftypes) != 2: return False for f in allftypes: if allfaces.count(f) == 4: allftypes.remove(f) return 'four-of-a-kind', [f, allftypes.pop()] else: return False
def fullhouse(hand):
allfaces = [f for f,s in hand] allftypes = set(allfaces) if len(allftypes) != 2: return False for f in allftypes: if allfaces.count(f) == 3: allftypes.remove(f) return 'full-house', [f, allftypes.pop()] else: return False
def flush(hand):
allstypes = {s for f, s in hand} if len(allstypes) == 1: allfaces = [f for f,s in hand] return 'flush', sorted(allfaces, key=lambda f: face.index(f), reverse=True) return False
def straight(hand):
f,fs = ( (lowace, lowaces) if any(card.face == '2' for card in hand) else (face, faces) ) ordered = sorted(hand, key=lambda card: (f.index(card.face), card.suit)) first, rest = ordered[0], ordered[1:] if ' '.join(card.face for card in ordered) in fs: return 'straight', ordered[-1].face return False
def threeofakind(hand):
allfaces = [f for f,s in hand] allftypes = set(allfaces) if len(allftypes) <= 2: return False for f in allftypes: if allfaces.count(f) == 3: allftypes.remove(f) return ('three-of-a-kind', [f] + sorted(allftypes, key=lambda f: face.index(f), reverse=True)) else: return False
def twopair(hand):
allfaces = [f for f,s in hand] allftypes = set(allfaces) pairs = [f for f in allftypes if allfaces.count(f) == 2] if len(pairs) != 2: return False p0, p1 = pairs other = [(allftypes - set(pairs)).pop()] return 'two-pair', pairs + other if face.index(p0) > face.index(p1) else pairs[::-1] + other
def onepair(hand):
allfaces = [f for f,s in hand] allftypes = set(allfaces) pairs = [f for f in allftypes if allfaces.count(f) == 2] if len(pairs) != 1: return False allftypes.remove(pairs[0]) return 'one-pair', pairs + sorted(allftypes, key=lambda f: face.index(f), reverse=True)
def highcard(hand):
allfaces = [f for f,s in hand] return 'high-card', sorted(allfaces, key=lambda f: face.index(f), reverse=True)
handrankorder = (straightflush, fourofakind, fullhouse,
flush, straight, threeofakind, twopair, onepair, highcard)
def rank(cards):
hand = handy(cards) for ranker in handrankorder: rank = ranker(hand) if rank: break assert rank, "Invalid: Failed to rank cards: %r" % cards return rank
def handy(cards='2♥ 2♦ 2♣ k♣ q♦'):
hand = [] for card in cards.split(): f, s = card[:-1], card[-1] assert f in face, "Invalid: Don't understand card face %r" % f assert s in suit, "Invalid: Don't understand card suit %r" % s hand.append(Card(f, s)) assert len(hand) == 5, "Invalid: Must be 5 cards in a hand, not %i" % len(hand) return hand
if __name__ == '__main__':
hands = ["2♥ 2♦ 2♣ k♣ q♦", "2♥ 5♥ 7♦ 8♣ 9♠", "a♥ 2♦ 3♣ 4♣ 5♦", "2♥ 3♥ 2♦ 3♣ 3♦", "2♥ 7♥ 2♦ 3♣ 3♦", "2♥ 7♥ 7♦ 7♣ 7♠", "10♥ j♥ q♥ k♥ a♥"] + [ "4♥ 4♠ k♠ 5♦ 10♠", "q♣ 10♣ 7♣ 6♣ 4♣", ] print("%-18s %-15s %s" % ("HAND", "CATEGORY", "TIE-BREAKER")) for cards in hands: r = rank(cards) print("%-18r %-15s %r" % (cards, r[0], r[1]))</lang>
- Output:
HAND CATEGORY TIE-BREAKER '2♥ 2♦ 2♣ k♣ q♦' three-of-a-kind ['2', 'k', 'q'] '2♥ 5♥ 7♦ 8♣ 9♠' high-card ['9', '8', '7', '5', '2'] 'a♥ 2♦ 3♣ 4♣ 5♦' straight '5' '2♥ 3♥ 2♦ 3♣ 3♦' full-house ['3', '2'] '2♥ 7♥ 2♦ 3♣ 3♦' two-pair ['3', '2', '7'] '2♥ 7♥ 7♦ 7♣ 7♠' four-of-a-kind ['7', '2'] '10♥ j♥ q♥ k♥ a♥' straight-flush 'a' '4♥ 4♠ k♠ 5♦ 10♠' one-pair ['4', 'k', '10', '5'] 'q♣ 10♣ 7♣ 6♣ 4♣' flush ['q', '10', '7', '6', '4']