Poker hand analyser: Difference between revisions
Walterpachl (talk | contribs) (added REXX) |
m (Perl 6 formatting tweaks) |
||
Line 40: | Line 40: | ||
=={{header|Perl 6}}== |
=={{header|Perl 6}}== |
||
This solution is written |
This solution is written as a Perl 6 grammar. |
||
<lang perl6> |
<lang perl6>use v6; |
||
use v6; |
|||
grammar PokerHand { |
grammar PokerHand { |
||
Line 70: | Line 69: | ||
token card {<face><suit> <?{ |
token card {<face><suit> <?{ |
||
my $card = ~$/; |
my $card = ~$/; |
||
! %*PLAYED{$card.lc}++; |
! %*PLAYED{$card.lc}++; |
||
}> |
}> |
||
} |
} |
Revision as of 19:46, 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 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
Goes a little further in also giving the ordered tie-breaker information from the wikipedia page. <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']
REXX
<lang rexx>/* REXX ---------------------------------------------------------------
- 10.12.2013 Walter Pachl
- --------------------------------------------------------------------*/
d.1='2h 2d 2s ks qd'; x.1='three-of-a-kind' d.2='2h 5h 7d 8s 9d'; x.2='high-card' d.3='ah 2d 3s 4s 5s'; x.3='straight' d.4='2h 3h 2d 3s 3d'; x.4='full-house' d.5='2h 7h 2d 3s 3d'; x.5='two-pair' d.6='2h 7h 7d 7s 7c'; x.6='four-of-a-kind' d.7='th jh qh kh ah'; x.7='straight-flush' d.8='4h 4c kc 5d tc'; x.8='one-pair' d.9='qc tc 7c 6c 4c'; x.9='flush' d.10='ah 2h 3h 4h' d.11='ah 2h 3h 4h 5h 6h' d.12='2h 2h 3h 4h 5h' d.13='xh 2h 3h 4h 5h' d.14='2x 2h 3h 4h 5h' Do ci=1 To 14
Call poker d.ci,x.ci end
Exit
poker: Parse Arg deck,expected have.=0 f.=0; fmax=0 s.=0; smax=0 cnt.=0 If words(deck)<5 Then Return err('less than 5 cards') If words(deck)>5 Then Return err('more than 5 cards') Do i=1 To 5
c=word(deck,i) Parse Var c f +1 s If have.f.s=1 Then Return err('duplicate card:' c) have.f.s=1 m=pos(f,'a23456789tjqk') If m=0 Then Return err('invalid face' f 'in' c) cnt.m=cnt.m+1 n=pos(s,'hdcs') If n=0 Then Return err('invalid suit' s 'in' c) f.m=f.m+1; fmax=max(fmax,f.m) s.n=s.n+1; smax=max(smax,s.n) End
cntl= cnt.14=cnt.1 Do i=1 To 14
cntl=cntl||cnt.i End
Select
When fmax=4 Then res='four-of-a-kind' When fmax=3 Then Do If x_pair() Then res='full-house' Else res='three-of-a-kind' End When fmax=2 Then Do If x_2pair() Then res='two-pair' Else res='one-pair' End When smax=5 Then Do If x_street() Then res='straight-flush' Else res='flush' End When x_street() Then res='straight' Otherwise res='high-card' End
Say deck res If res<>expected Then
Say copies(' ',14) expected
Return
x_pair:
Do p=1 To 13 If f.p=2 Then return 1 End Return 0
x_2pair:
pp=0 Do p=1 To 13 If f.p=2 Then pp=pp+1 End Return pp=2
x_street:
Return pos('11111',cntl)>0
err:
Say deck 'Error:' arg(1) Return 0</lang>
- Output:
2h 2d 2s ks qd three-of-a-kind 2h 5h 7d 8s 9d high-card ah 2d 3s 4s 5s straight 2h 3h 2d 3s 3d full-house 2h 7h 2d 3s 3d two-pair 2h 7h 7d 7s 7c four-of-a-kind th jh qh kh ah straight-flush 4h 4c kc 5d tc one-pair qc tc 7c 6c 4c flush ah 2h 3h 4h Error: less than 5 cards ah 2h 3h 4h 5h 6h Error: more than 5 cards 2h 2h 3h 4h 5h Error: duplicate card: 2h xh 2h 3h 4h 5h Error: invalid face x in xh 2x 2h 3h 4h 5h Error: invalid suit x in 2x