Poker hand analyser: Difference between revisions
m (→{{header|Raku}}: more Perl 6 -> Raku) |
(Added FreeBASIC) |
||
(45 intermediate revisions by 13 users not shown) | |||
Line 9:
A poker hand is specified as a space separated list of five playing cards.
Each input card has two characters indicating face and suit.
;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
<br>alternatively, the unicode card-suit characters: <big> ♥ ♦ ♣ ♠ </big>
Duplicate cards are illegal.
Line 29 ⟶ 37:
invalid
;Examples:
2♥
2♥
2♥ 7♥
q♣ 10♣ 7♣ 6♣ q♣: invalid
The programs output for the above examples should be displayed here on this page.
Line 46 ⟶ 55:
# use the playing card characters introduced with Unicode 6.0 (U+1F0A1 - U+1F0DE).
# allow two jokers
::* use the symbol '''joker'''
::* duplicates would be allowed (for jokers only)
::* five-of-a-kind would then be the highest hand
Line 52 ⟶ 61:
;More extra credit examples:
joker 2♦ 2♠ k♠ q♦: three-of-a-kind
joker 5♥ 7♦ 8♠ 9♦: straight
joker 2♦ 3♠ 4♠ 5♠: straight
joker 3♥ 2♦ 3♠ 3♦: four-of-a-kind
joker 7♥ 2♦ 3♠ 3♦: three-of-a-kind
joker 7♥ 7♦ 7♠ 7♣: five-of-a-kind
joker j♥ q♥ k♥ A♥: straight-flush
joker 4♣ k♣ 5♦ 10♠: one-pair
joker k♣ 7♣ 6♣ 4♣: flush
joker 2♦ joker 4♠ 5♠: straight
joker Q♦ joker A♠ 10♠: straight
joker Q♦ joker A♦ 10♦: straight-flush
joker 2♦ 2♠ joker q♦: four-of-a-kind
;Related tasks:
* [[Playing cards]]
* [[Card shuffles]]
* [[Deal cards_for_FreeCell]]
* [[War Card_Game]]
* [[Go Fish]]
<br><br>
=={{header|11l}}==
{{trans|D}}
<syntaxhighlight lang="11l">F analyzeHandHelper(faceCount, suitCount)
V
p1 = 0B
p2 = 0B
t = 0B
f = 0B
fl = 0B
st = 0B
L(fc) faceCount
S fc
2 {I p1 {p2 = 1B} E p1 = 1B}
3 {t = 1B}
4 {f = 1B}
L(sc) suitCount
I sc == 5
fl = 1B
I !p1 & !p2 & !t & !f
V s = 0
L(fc) faceCount
I fc != 0
s++
E
s = 0
I s == 5
L.break
st = (s == 5) | (s == 4 & faceCount[0] != 0 & faceCount[1] == 0)
I st & fl {R ‘straight-flush’}
E I f {R ‘four-of-a-kind’}
E I p1 & t {R ‘full-house’}
E I fl {R ‘flush’}
E I st {R ‘straight’}
E I t {R ‘three-of-a-kind’}
E I p1 & p2 {R ‘two-pair’}
E I p1 {R ‘one-pair’}
E {R ‘high-card’}
F analyzeHand(inHand)
V handLen = 5
V face = ‘A23456789TJQK’
V suit = ‘SHCD’
V errorMessage = ‘invalid hand.’
V hand = sorted(inHand.split(‘ ’))
I hand.len != handLen
R errorMessage
I Set(hand).len != handLen
R errorMessage‘ Duplicated cards.’
V faceCount = [0] * face.len
V suitCount = [0] * suit.len
L(card) hand
I card.len != 2
R errorMessage
V? n = face.find(card[0])
V? l = suit.find(card[1])
I n == N | l == N
R errorMessage
faceCount[n]++
suitCount[l]++
R analyzeHandHelper(faceCount, suitCount)
L(hand) [‘2H 2D 2S KS QD’,
‘2H 5H 7D 8S 9D’,
‘AH 2D 3S 4S 5S’,
‘2H 3H 2D 3S 3D’,
‘2H 7H 2D 3S 3D’,
‘2H 7H 7D 7S 7C’,
‘TH JH QH KH AH’,
‘4H 4C KC 5D TC’,
‘QC TC 7C 6C 4C’]
print(hand‘: ’analyzeHand(hand))</syntaxhighlight>
{{out}}
<pre>
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
</pre>
=={{header|AutoHotkey}}==
<
StringUpper, hand, hand
Sort, hand, FCardSort D%A_Space%
Line 91 ⟶ 202:
b := (b = "T") ? 10 : (b = "J") ? 11 : (b = "Q") ? 12 : (b = "K") ? 13 : b
return a > b ? 1 : a < b ? -1 : 0
}</
Examples:<
(join`r`n
2♥ 2♦ 2♣ k♣ q♦
Line 109 ⟶ 220:
res .= PokerHand(A_LoopField) "`n"
MsgBox, 262144, , % res
return</
Outputs:<pre>2♦ 2♣ 2♥ Q♦ K♣ Three of a Kind
2♥ 5♥ 7♦ 8♣ 9♠ High Card
Line 124 ⟶ 235:
=={{header|C}}==
{{trans|Kotlin}}
<
#include <ctype.h>
#include <string.h>
Line 252 ⟶ 363:
}
return 0;
}</
{{output}}
Line 270 ⟶ 381:
=={{header|C sharp}}==
{{works with|C sharp|8}}
<
using System.Collections.Generic;
using static System.Linq.Enumerable;
Line 431 ⟶ 542:
}
}</
{{out}}
<pre>
Line 459 ⟶ 570:
=={{header|C++}}==
<syntaxhighlight lang="cpp">
#include <iostream>
#include <sstream>
Line 534 ⟶ 645:
cout << p.analyze( "qc tc 7c 6c 4c" ) << endl << endl; return system( "pause" );
}
</syntaxhighlight>
{{out}}
<pre>
Line 549 ⟶ 660:
=={{header|Clojure}}==
<
(let [[fst _] card]
(if (Character/isDigit fst)
Line 629 ⟶ 740:
["QC" "TC" "7C" "6C" "4C"]])
(run! println (map #(str % " : " (check-hand %)) hands))
</syntaxhighlight>
{{out}}
Line 648 ⟶ 759:
No bonus for this simple version.
{{trans|C++}}
<
string analyzeHand(in string inHand) pure /*nothrow @safe*/ {
Line 733 ⟶ 844:
"QC TC 7C 6C 4C"])
writeln(hand, ": ", hand.analyzeHand);
}</
{{out}}
<pre>2H 2D 2S KS QD: three-of-a-kind
Line 748 ⟶ 859:
{{trans|Ruby}}
{{works with|Elixir|1.2}}
<
@faces ~w(2 3 4 5 6 7 8 9 10 j q k a)
@suits ~w(♥ ♦ ♣ ♠) # ~w(h d c s)
Line 874 ⟶ 985:
best = Enum.map(cards_list, &Hand.new &1) |> Enum.max
IO.puts "#{hand}:\t#{elem(best,3)}"
end)</
{{out}}
Line 911 ⟶ 1,022:
=={{header|F Sharp|F#}}==
<
type Card = int * int
Line 1,011 ⟶ 1,122:
]
|> List.iter showHandRank
</syntaxhighlight>
{{out}}
Line 1,041 ⟶ 1,152:
=={{header|Factor}}==
Factor comes with a poker hand evaluator.
<
{
"2H 2D 2C KC QD"
Line 1,052 ⟶ 1,163:
"4H 4S KS 5D TS"
"QC TC 7C 6C 4C"
} [ dup string>hand-name "%s: %s\n" printf ] each</
{{out}}
<pre>
Line 1,065 ⟶ 1,176:
QC TC 7C 6C 4C: Flush
</pre>
=={{header|FreeBASIC}}==
{{trans|C}}
<syntaxhighlight lang="vbnet">Const As String FACES = "23456789tjqka"
Const As String SUITS = "shdc"
Type card
face As Integer ' FACES map to 0..12 respectively
suit As String
End Type
Dim Shared As card cards(4)
Sub insertionSort(arr() As card, Byval n As Integer)
Dim As Integer i, key, j
For i = 1 To n-1
key = arr(i).face
j = i-1
While j >= 0 And arr(j).face > key
arr(j+1) = arr(j)
j = j-1
Wend
arr(j+1).face = key
Next i
End Sub
Function compareCard(Byval a As card, Byval b As card) As Integer
Return a.face - b.face
End Function
Function equalsCard(Byval c1 As card, Byval c2 As card) As Integer
If c1.face = c2.face And c1.suit = c2.suit Then Return True
Return False
End Function
Function areDistinct() As Integer
Dim As Integer i, j
For i = 0 To 3
For j = i + 1 To 4
If equalsCard(cards(i), cards(j)) = True Then Return False
Next j
Next i
Return True
End Function
Function isStraight() As Boolean
insertionSort(cards(), 5)
If cards(0).face + 4 = cards(4).face Then Return True
If cards(4).face = 12 And cards(0).face = 0 And cards(3).face = 3 Then Return True
Return False
End Function
Function isFlush() As Boolean
Dim As String suit = cards(0).suit
For i As Integer = 1 To 4
If cards(i).suit <> suit Then Return False
Next i
Return True
End Function
Function analyzeHand(Byval hand As String) As String
Dim As Integer i, j, cp, gs = 0
Dim As String suit
Dim As Integer found, flush, straight
Dim As Integer groups(12)
If Len(hand) <> 14 Then Return "invalid"
For i = 0 To 13 Step 3
cp = Instr(FACES, Lcase(Mid(hand, i + 1, 1)))
If cp = 0 Then Return "invalid"
j = i \ 3
cards(j).face = cp - 1
suit = Lcase(Mid(hand, i + 2, 1))
cp = Instr(SUITS, suit)
If cp = 0 Then Return "invalid"
cards(j).suit = suit
Next i
If areDistinct() = False Then Return "invalid"
For i = 0 To 12
groups(i) = 0
Next i
For i = 0 To 4
groups(cards(i).face) += 1
Next i
For i = 0 To 12
If groups(i) > 0 Then gs += 1
Next i
Select Case gs
Case 2
found = False
For i = 0 To 12
If groups(i) = 4 Then
found = True
Exit For
End If
Next i
If found = True Then Return "four-of-a-kind"
Return "full-house"
Case 3
found = False
For i = 0 To 12
If groups(i) = 3 Then
found = True
Exit For
End If
Next i
If found = True Then Return "three-of-a-kind"
Return "two-pairs"
Case 4
Return "one-pair"
Case Else
flush = isFlush()
straight = isStraight()
If flush = True And straight = True Then
Return "straight-flush"
Elseif flush = True Then
Return "flush"
Elseif straight = True Then
Return "straight"
Else
Return "high-card"
End If
End Select
End Function
Dim As String tipo
Dim As String hands(9) = { _
"2h 2d 2c kc qd", _
"2h 5h 7d 8c 9s", _
"ah 2d 3c 4c 5d", _
"2h 3h 2d 3c 3d", _
"2h 7h 2d 3c 3d", _
"2h 7h 7d 7c 7s", _
"th jh qh kh ah", _
"4h 4s ks 5d ts", _
"qc tc 7c 6c 4c", _
"ah ah 7c 6c 4c" }
For i As Integer = 0 To 9
tipo = analyzeHand(hands(i))
Print hands(i); ": "; tipo
Next i
Sleep</syntaxhighlight>
{{out}}
<pre>Same as C entry.</pre>
=={{header|Go}}==
{{trans|Kotlin}}
===Basic Version===
<
import (
Line 1,194 ⟶ 1,451:
fmt.Printf("%s: %s\n", hand, analyzeHand(hand))
}
}</
{{out}}
Line 1,211 ⟶ 1,468:
===Extra Credit Version===
<
import (
Line 1,426 ⟶ 1,683:
fmt.Printf("%s: %s\n", hand, analyzeHand(hand))
}
}</
{{out}}
Line 1,446 ⟶ 1,703:
🃂 🃞 🃍 🃁 🃊 : high-card
</pre>
=={{header|Haskell}}==
===Basic Version===
<syntaxhighlight lang="haskell">{-# LANGUAGE TupleSections #-}
import Data.Function (on)
import Data.List (group, nub, any, sort, sortBy)
import Data.Maybe (mapMaybe)
import Text.Read (readMaybe)
data Suit = Club | Diamond | Spade | Heart deriving (Show, Eq)
data Rank = Ace | Two | Three | Four | Five | Six | Seven
| Eight | Nine | Ten | Jack | Queen | King
deriving (Show, Eq, Enum, Ord, Bounded)
data Card = Card { suit :: Suit, rank :: Rank } deriving (Show, Eq)
type Hand = [Card]
consumed = pure . (, "")
instance Read Suit where
readsPrec d s = case s of "♥" -> consumed Heart
"♦" -> consumed Diamond
"♣" -> consumed Spade
"♠" -> consumed Club
"h" -> consumed Heart
_ -> []
instance Read Rank where
readsPrec d s = case s of "a" -> consumed Ace
"2" -> consumed Two
"3" -> consumed Three
"4" -> consumed Four
"5" -> consumed Five
"6" -> consumed Six
"7" -> consumed Seven
"8" -> consumed Eight
"9" -> consumed Nine
"10" -> consumed Ten
"j" -> consumed Jack
"q" -> consumed Queen
"k" -> consumed King
_ -> []
instance Read Card where
readsPrec d = fmap (, "") . mapMaybe card . lex
where
card (r, s) = Card <$> (readMaybe s :: Maybe Suit)
<*> (readMaybe r :: Maybe Rank)
-- Special hand
acesHigh :: [Rank]
acesHigh = [Ace, Ten, Jack, Queen, King]
isSucc :: (Enum a, Eq a, Bounded a) => [a] -> Bool
isSucc [] = True
isSucc [x] = True
isSucc (x:y:zs) = (x /= maxBound && y == succ x) && isSucc (y:zs)
nameHand :: Hand -> String
nameHand [] = "Invalid Input"
nameHand cards | invalidHand = "Invalid hand"
| straight && flush = "Straight flush"
| ofKind 4 = "Four of a kind"
| ofKind 3 && ofKind 2 = "Full house"
| flush = "Flush"
| straight = "Straight"
| ofKind 3 = "Three of a kind"
| uniqRanks == 3 = "Two pair"
| uniqRanks == 4 = "One pair"
| otherwise = "High card"
where
sortedRank = sort $ rank <$> cards
rankCounts = sortBy (compare `on` snd) $ (,) <$> head <*> length <$> group sortedRank
uniqRanks = length rankCounts
ofKind n = any ((==n) . snd) rankCounts
straight = isSucc sortedRank || sortedRank == acesHigh
flush = length (nub $ suit <$> cards) == 1
invalidHand = length (nub cards) /= 5
testHands :: [(String, Hand)]
testHands = (,) <$> id <*> mapMaybe readMaybe . words <$>
[ "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♣"
, "q♣ 10♣ 7♣ 6♣ 7♣" -- duplicate cards
, "Bad input" ]
main :: IO ()
main = mapM_ (putStrLn . (fst <> const ": " <> nameHand . snd)) testHands</syntaxhighlight>
{{out}}
<pre>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
q♣ 10♣ 7♣ 6♣ 7♣: Invalid hand
Bad input: Invalid Input</pre>
=={{header|J}}==
<
Suits=: <"> 7 u: '♥♦♣♦' NB. or Suits=: 'hdcs'
Faces=: <;._1 ' 2 3 4 5 6 7 8 9 10 j q k a'
Line 1,484 ⟶ 1,851:
('straight-flush' IF (straight * flush)) Or
('five-of-a-kind' IF five)
)
Hands=: <@deb;._2 {{)n
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♣
}}</syntaxhighlight>
Note that * acts as "logical and" on logical values (if you need to deal with boolean values in the original sense - which were not constrained to logical values - you should use *. instead of * to achieve boolean multiplication, but that's not needed here).
Output for
</pre>
Output for extra-credit examples
Line 1,519 ⟶ 1,899:
{{works with|Java|7}}
This code does not qualify for extra credit. Although it supports wildcards, it does not allow for duplicates.
<
import java.util.Collections;
import java.util.HashSet;
Line 1,698 ⟶ 2,078:
}
}
}</
{{out}}
Line 1,748 ⟶ 2,128:
=={{header|JavaScript}}==
{{works with|JavaScript|ECMAScript 6}}
<
const SUITS = ['♥', '♦', '♣', '♠'];
Line 1,778 ⟶ 2,158:
else if (groups[0] === 2) return 'one-pair'
else return 'high-card';
}</
Demonstrating:
<
"2♥ 2♦ 2♣ k♣ q♦",
"2♥ 5♥ 7♦ 8♣ 9♠",
Line 1,799 ⟶ 2,179:
];
for(hand of testHands) console.log(hand + ": " + analyzeHand(hand));</
{{out}}
<pre>
Line 1,821 ⟶ 2,201:
=={{header|Julia}}==
<
cardlessthan(card1, card2) = indexin(x, sorteddeck)[1] < indexin(y, sorteddeck)[1]
Line 1,941 ⟶ 2,321:
println("Hand $hand is a ", scorehand(hand), " hand.")
end
</
Hand ["2♥", "2♦", "2♣", "K♣", "Q♦"] is a three-of-a-kind hand.
Hand ["2♥", "5♥", "7♦", "8♣", "9♠"] is a high-card hand.
Line 1,952 ⟶ 2,332:
Hand ["Q♣", "T♣", "7♣", "6♣", "4♣"] is a flush hand.
</pre>
=={{header|Kotlin}}==
===Basic Version===
<
class Card(val face: Int, val suit: Char)
Line 2,030 ⟶ 2,413:
println("$hand: ${analyzeHand(hand)}")
}
}</
{{out}}
Line 2,047 ⟶ 2,430:
===Extra Credit Version===
<
class Card(val face: Int, val suit: Char)
Line 2,174 ⟶ 2,557:
println("$hand : ${analyzeHand(hand)}")
}
}</
{{out}}
Line 2,196 ⟶ 2,579:
=={{header|Lua}}==
<
function valid (t)
if #t ~= 5 then return false end
Line 2,318 ⟶ 2,701:
"qc 10c 7c 6c 4c" -- flush
}
for _, case in pairs(testCases) do print(case, ": " .. rank(case)) end</
{{out}}
<pre>2h 2d 2c kc qd : three-of-a-kind
Line 2,329 ⟶ 2,712:
4h 4s ks 5d 10s : one-pair
qc 10c 7c 6c 4c : flush</pre>
=={{header|Nim}}==
<syntaxhighlight lang="nim">import algorithm, sequtils, strutils, tables, unicode
type
Suit* = enum ♠, ♥, ♦, ♣
Face* {.pure.} = enum
Ace = (1, "a")
Two = (2, "2")
Three = (3, "3")
Four = (4, "4")
Five = (5, "5")
Six = (6, "6")
Seven = (7, "7")
Eight = (8, "8")
Nine = (9, "9")
Ten = (10, "10")
Jack = (11, "j")
Queen = (12, "q")
King = (13, "k")
Card* = tuple[face: Face; suit: Suit]
Hand* = array[5, Card]
HandValue {.pure.} = enum
Invalid = "invalid"
StraightFlush = "straight-flush"
FourOfAKind = "four-of-a-kind"
FullHouse = "full-house"
Flush = "flush"
Straight = "straight"
ThreeOfAKind = "three-of-a-kind"
TwoPair = "two-pair"
OnePair = "one-pair"
HighCard = "high-card"
CardError = object of ValueError
proc toCard(cardStr: string): Card =
## Convert a card string to a Card.
var runes = cardStr.toRunes
let suitStr = $(runes.pop()) # Extract the suit.
let faceStr = $runes # Take what’s left as the face.
try:
result.face = parseEnum[Face](faceStr)
except ValueError:
raise newException(CardError, "wrong face: " & faceStr)
try:
result.suit = parseEnum[Suit](suitStr)
except ValueError:
raise newException(CardError, "wrong suit: " & suitStr)
proc value(hand: openArray[Card]): HandValue =
## Return the value of a hand.
doAssert hand.len == 5, "Hand must have five cards."
var
cards: seq[Card] # The cards.
faces: CountTable[Face] # Count faces.
suits: CountTable[Suit] # Count suits.
for card in hand:
if card in cards: return Invalid # Duplicate card.
cards.add card
faces.inc card.face
suits.inc card.suit
faces.sort() # Greatest counts first.
suits.sort() # Greatest counts first.
cards.sort() # Smallest faces first.
# Check faces.
for face, count in faces:
case count
of 4:
return FourOfAKind
of 3:
result = ThreeOfAKind
of 2:
if result == ThreeOfAKind: return FullHouse
if result == OnePair: return TwoPair
result = OnePair
else:
if result != Invalid: return
# Search straight.
result = Straight
let start = if cards[0].face == Ace and cards[4].face == King: 2 else: 1
for n in start..4:
if cards[n].face != succ(cards[n - 1].face):
result = HighCard # No straight.
break
# Check suits.
if suits.len == 1: # A single suit.
result = if result == Straight: StraightFlush else: Flush
proc `$`(card: Card): string =
## Return the representation of a card.
var val = 0x1F0A0 + ord(card.suit) * 0x10 + ord(card.face)
if card.face >= Queen: inc val # Skip Knight.
result = $Rune(val)
when isMainModule:
const HandStrings = ["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♣",
"4♥ 4♣ 4♥ 4♠ 4♦"]
for handString in HandStrings:
let hand = handString.split(' ').map(toCard)
echo hand.map(`$`).join(" "), " → ", hand.value</syntaxhighlight>
{{out}}
<pre>🂲 🃂 🃒 🃞 🃍 → three-of-a-kind
🂲 🂵 🃇 🃘 🂩 → high-card
🂱 🃂 🃓 🃔 🃅 → straight
🂲 🂳 🃂 🃓 🃃 → full-house
🂲 🂷 🃂 🃓 🃃 → two-pair
🂲 🂷 🃇 🃗 🂧 → four-of-a-kind
🂺 🂻 🂽 🂾 🂱 → straight-flush
🂴 🂤 🂮 🃅 🂪 → one-pair
🃝 🃚 🃗 🃖 🃔 → flush
🂴 🃔 🂴 🂤 🃄 → invalid</pre>
=={{header|Perl}}==
I dont like jokers. Instead I decided to give hands proper names. For example, "Kings full of Tens" rather than just "full-house".
<
use strict;
use warnings;
Line 2,476 ⟶ 2,997:
say Hand::describe($_) for @cards;
</syntaxhighlight>
{{out}}
<pre>
Line 2,502 ⟶ 3,023:
Note: I have left a copy of this in demo\HelloUTF8.exw to prove it works, but non-ascii on a Windows
console is not Phix's forte.<br>
For an(other) example of using the unicode card characters see [[Playing_cards#Phix]]<br>
{{libheader|Phix/online}}
You can run this online [http://phix.x10.mx/p2js/poker_hand.htm here].
<!--<syntaxhighlight lang="phix">(phixonline)-->
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">poker</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">hand</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">hand</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">substitute</span><span style="color: #0000FF;">(</span><span style="color: #000000;">hand</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"10"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"t"</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">cards</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">split</span><span style="color: #0000FF;">(</span><span style="color: #000000;">hand</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">if</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cards</span><span style="color: #0000FF;">)!=</span><span style="color: #000000;">5</span> <span style="color: #008080;">then</span> <span style="color: #008080;">return</span> <span style="color: #008000;">"invalid hand"</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">ranks</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #000000;">13</span><span style="color: #0000FF;">),</span>
<span style="color: #000000;">suits</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #000000;">4</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">jokers</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cards</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">ci</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">utf8_to_utf32</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cards</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">])</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">ci</span><span style="color: #0000FF;">=</span><span style="color: #008000;">"joker"</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">jokers</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">1</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">jokers</span><span style="color: #0000FF;">></span><span style="color: #000000;">2</span> <span style="color: #008080;">then</span> <span style="color: #008080;">return</span> <span style="color: #008000;">"invalid hand"</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">else</span>
<span style="color: #008080;">if</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">ci</span><span style="color: #0000FF;">)!=</span><span style="color: #000000;">2</span> <span style="color: #008080;">then</span> <span style="color: #008080;">return</span> <span style="color: #008000;">"invalid hand"</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">rank</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">lower</span><span style="color: #0000FF;">(</span><span style="color: #000000;">ci</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]),</span><span style="color: #008000;">"23456789tjqka"</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">suit</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #000000;">ci</span><span style="color: #0000FF;">[</span><span style="color: #000000;">2</span><span style="color: #0000FF;">],</span><span style="color: #7060A8;">utf8_to_utf32</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"♥♣♦♠"</span><span style="color: #0000FF;">))</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">rank</span><span style="color: #0000FF;">=</span><span style="color: #000000;">0</span> <span style="color: #008080;">or</span> <span style="color: #000000;">suit</span><span style="color: #0000FF;">=</span><span style="color: #000000;">0</span> <span style="color: #008080;">then</span> <span style="color: #008080;">return</span> <span style="color: #008000;">"invalid hand"</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000000;">ranks</span><span style="color: #0000FF;">[</span><span style="color: #000000;">rank</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">1</span>
<span style="color: #000000;">suits</span><span style="color: #0000FF;">[</span><span style="color: #000000;">suit</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">1</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">straight</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">match</span><span style="color: #0000FF;">({</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1</span><span style="color: #0000FF;">},</span><span style="color: #000000;">ranks</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">if</span> <span style="color: #008080;">not</span> <span style="color: #000000;">straight</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">straight</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">sort</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">deep_copy</span><span style="color: #0000FF;">(</span><span style="color: #000000;">ranks</span><span style="color: #0000FF;">))[$]=</span><span style="color: #000000;">1</span> <span style="color: #008080;">and</span> <span style="color: #7060A8;">match</span><span style="color: #0000FF;">({</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0</span><span style="color: #0000FF;">},</span><span style="color: #000000;">ranks</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">_flush</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">(</span><span style="color: #7060A8;">max</span><span style="color: #0000FF;">(</span><span style="color: #000000;">suits</span><span style="color: #0000FF;">)+</span><span style="color: #000000;">jokers</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">5</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">_pairs</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">max</span><span style="color: #0000FF;">(</span><span style="color: #000000;">ranks</span><span style="color: #0000FF;">)+</span><span style="color: #000000;">jokers</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">pair</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #000000;">2</span><span style="color: #0000FF;">,</span><span style="color: #000000;">ranks</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">full_house</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">_pairs</span><span style="color: #0000FF;">=</span><span style="color: #000000;">3</span> <span style="color: #008080;">and</span> <span style="color: #000000;">pair</span> <span style="color: #008080;">and</span> <span style="color: #0000FF;">(</span><span style="color: #000000;">jokers</span><span style="color: #0000FF;">=</span><span style="color: #000000;">0</span> <span style="color: #008080;">or</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #000000;">2</span><span style="color: #0000FF;">,</span><span style="color: #000000;">ranks</span><span style="color: #0000FF;">,</span><span style="color: #000000;">pair</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">))</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">two_pair</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #000000;">2</span><span style="color: #0000FF;">,</span><span style="color: #000000;">ranks</span><span style="color: #0000FF;">,</span><span style="color: #000000;">pair</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">high_card</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">rfind</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">sq_ne</span><span style="color: #0000FF;">(</span><span style="color: #000000;">ranks</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0</span><span style="color: #0000FF;">))+</span><span style="color: #000000;">1</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">jokers</span> <span style="color: #008080;">and</span> <span style="color: #000000;">_pairs</span><span style="color: #0000FF;">=</span><span style="color: #000000;">jokers</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">straight</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">1</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">k</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">ranks</span><span style="color: #0000FF;">),</span> <span style="color: #000000;">j</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">jokers</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">l</span><span style="color: #0000FF;">=</span><span style="color: #000000;">k</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">min</span><span style="color: #0000FF;">(</span><span style="color: #000000;">k</span><span style="color: #0000FF;">+</span><span style="color: #000000;">5</span><span style="color: #0000FF;">-</span><span style="color: #000000;">j</span><span style="color: #0000FF;">,</span><span style="color: #000000;">13</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">ranks</span><span style="color: #0000FF;">[</span><span style="color: #000000;">l</span><span style="color: #0000FF;">]=</span><span style="color: #000000;">0</span> <span style="color: #008080;">then</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">j</span><span style="color: #0000FF;">=</span><span style="color: #000000;">0</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">straight</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0</span>
<span style="color: #008080;">exit</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000000;">j</span> <span style="color: #0000FF;">-=</span> <span style="color: #000000;">1</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">straight</span> <span style="color: #008080;">and</span> <span style="color: #000000;">j</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">high_card</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">min</span><span style="color: #0000FF;">(</span><span style="color: #000000;">high_card</span><span style="color: #0000FF;">+</span><span style="color: #000000;">j</span><span style="color: #0000FF;">,</span><span style="color: #000000;">14</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">elsif</span> <span style="color: #000000;">straight</span> <span style="color: #008080;">and</span> <span style="color: #000000;">ranks</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]!=</span><span style="color: #000000;">0</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">high_card</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #000000;">ranks</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">_pairs</span><span style="color: #0000FF;">=</span><span style="color: #000000;">5</span> <span style="color: #008080;">then</span> <span style="color: #008080;">return</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">10</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"five of a kind"</span><span style="color: #0000FF;">,</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #000000;">5</span><span style="color: #0000FF;">-</span><span style="color: #000000;">jokers</span><span style="color: #0000FF;">,</span><span style="color: #000000;">ranks</span><span style="color: #0000FF;">)+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">}</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">straight</span> <span style="color: #008080;">and</span> <span style="color: #000000;">_flush</span> <span style="color: #008080;">then</span> <span style="color: #008080;">return</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">9</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"straight flush"</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">high_card</span><span style="color: #0000FF;">}</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">_pairs</span><span style="color: #0000FF;">=</span><span style="color: #000000;">4</span> <span style="color: #008080;">then</span> <span style="color: #008080;">return</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">8</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"four of a kind"</span><span style="color: #0000FF;">,</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #000000;">4</span><span style="color: #0000FF;">-</span><span style="color: #000000;">jokers</span><span style="color: #0000FF;">,</span><span style="color: #000000;">ranks</span><span style="color: #0000FF;">)+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">}</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">full_house</span> <span style="color: #008080;">then</span> <span style="color: #008080;">return</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">7</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"full house"</span><span style="color: #0000FF;">,</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #000000;">3</span><span style="color: #0000FF;">-</span><span style="color: #000000;">jokers</span><span style="color: #0000FF;">,</span><span style="color: #000000;">ranks</span><span style="color: #0000FF;">)+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">}</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">_flush</span> <span style="color: #008080;">then</span> <span style="color: #008080;">return</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">6</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"flush"</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">high_card</span><span style="color: #0000FF;">}</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">straight</span> <span style="color: #008080;">then</span> <span style="color: #008080;">return</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">5</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"straight"</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">high_card</span><span style="color: #0000FF;">}</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">_pairs</span><span style="color: #0000FF;">=</span><span style="color: #000000;">3</span> <span style="color: #008080;">then</span> <span style="color: #008080;">return</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">4</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"three of a kind"</span><span style="color: #0000FF;">,</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #000000;">3</span><span style="color: #0000FF;">-</span><span style="color: #000000;">jokers</span><span style="color: #0000FF;">,</span><span style="color: #000000;">ranks</span><span style="color: #0000FF;">)+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">}</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">pair</span> <span style="color: #008080;">and</span> <span style="color: #000000;">two_pair</span> <span style="color: #008080;">then</span> <span style="color: #008080;">return</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">3</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"two pair"</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">two_pair</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">}</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">pair</span> <span style="color: #008080;">then</span> <span style="color: #008080;">return</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">2</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"one pair"</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">pair</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">}</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">jokers</span> <span style="color: #008080;">then</span> <span style="color: #008080;">return</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">2</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"one pair"</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">high_card</span><span style="color: #0000FF;">}</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">return</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"high card"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">high_card</span><span style="color: #0000FF;">}</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">hands</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{{</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"2♥ 2♦ 2♣ k♣ q♦"</span><span style="color: #0000FF;">},</span>
<span style="color: #0000FF;">{</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"2♥ 5♥ 7♦ 8♣ 9♠"</span><span style="color: #0000FF;">},</span>
<span style="color: #0000FF;">{</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"a♥ 2♦ 3♣ 4♣ 5♦"</span><span style="color: #0000FF;">},</span>
<span style="color: #0000FF;">{</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"2♥ 3♥ 2♦ 3♣ 3♦"</span><span style="color: #0000FF;">},</span>
<span style="color: #0000FF;">{</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"2♥ 7♥ 2♦ 3♣ 3♦"</span><span style="color: #0000FF;">},</span>
<span style="color: #0000FF;">{</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"2♥ 7♥ 7♦ 7♣ 7♠"</span><span style="color: #0000FF;">},</span>
<span style="color: #0000FF;">{</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"10♥ j♥ q♥ k♥ a♥"</span><span style="color: #0000FF;">},</span>
<span style="color: #0000FF;">{</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"4♥ 4♠ k♠ 5♦ 10♠"</span><span style="color: #0000FF;">},</span>
<span style="color: #0000FF;">{</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"q♣ 10♣ 7♣ 6♣ 4♣"</span><span style="color: #0000FF;">},</span>
<span style="color: #0000FF;">{</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"joker 2♦ 2♠ k♠ q♦"</span><span style="color: #0000FF;">},</span>
<span style="color: #0000FF;">{</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"joker 5♥ 7♦ 8♠ 9♦"</span><span style="color: #0000FF;">},</span>
<span style="color: #0000FF;">{</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"joker 2♦ 3♠ 4♠ 5♠"</span><span style="color: #0000FF;">},</span>
<span style="color: #0000FF;">{</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"joker 3♥ 2♦ 3♠ 3♦"</span><span style="color: #0000FF;">},</span>
<span style="color: #0000FF;">{</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"joker 7♥ 2♦ 3♠ 3♦"</span><span style="color: #0000FF;">},</span>
<span style="color: #0000FF;">{</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"joker 7♥ 7♦ 7♠ 7♣"</span><span style="color: #0000FF;">},</span>
<span style="color: #0000FF;">{</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"joker j♥ q♥ k♥ A♥"</span><span style="color: #0000FF;">},</span>
<span style="color: #0000FF;">{</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"joker 4♣ k♣ 5♦ 10♠"</span><span style="color: #0000FF;">},</span>
<span style="color: #0000FF;">{</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"joker k♣ 7♣ 6♣ 4♣"</span><span style="color: #0000FF;">},</span>
<span style="color: #0000FF;">{</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"joker 2♦ joker 4♠ 5♠"</span><span style="color: #0000FF;">},</span>
<span style="color: #0000FF;">{</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"joker Q♦ joker A♠ 10♠"</span><span style="color: #0000FF;">},</span>
<span style="color: #0000FF;">{</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"joker Q♦ joker A♦ 10♦"</span><span style="color: #0000FF;">},</span>
<span style="color: #0000FF;">{</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"joker 2♦ 2♠ joker q♦"</span><span style="color: #0000FF;">}}</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">hands</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #000000;">hands</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">][</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">poker</span><span style="color: #0000FF;">(</span><span style="color: #000000;">hands</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">][</span><span style="color: #000000;">2</span><span style="color: #0000FF;">])</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #000000;">hands</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">reverse</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">sort</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">deep_copy</span><span style="color: #0000FF;">(</span><span style="color: #000000;">hands</span><span style="color: #0000FF;">)))</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">rank</span>
<span style="color: #004080;">string</span> <span style="color: #000000;">hand</span><span style="color: #0000FF;">,</span><span style="color: #000000;">desc</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">hands</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #0000FF;">{{</span><span style="color: #000000;">rank</span><span style="color: #0000FF;">,</span><span style="color: #000000;">desc</span><span style="color: #0000FF;">},</span><span style="color: #000000;">hand</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">hands</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span>
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"%d. %s %s\n"</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">11</span><span style="color: #0000FF;">-</span><span style="color: #000000;">rank</span><span style="color: #0000FF;">,</span><span style="color: #000000;">hand</span><span style="color: #0000FF;">,</span><span style="color: #000000;">desc</span><span style="color: #0000FF;">})</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<!--</syntaxhighlight>-->
{{out}}
<pre>
1. joker 7♥ 7♦ 7♠ 7♣ five of a kind
2. joker j♥ q♥ k♥ A♥ straight flush
2. joker Q♦ joker A♦ 10♦ straight flush
2. 10♥ j♥ q♥ k♥ a♥ straight flush
3. 2♥ 7♥ 7♦ 7♣ 7♠ four of a kind
3. joker 3♥ 2♦ 3♠ 3♦ four of a kind
3. joker 2♦ 2♠ joker q♦ four of a kind
4. 2♥ 3♥ 2♦ 3♣ 3♦ full house
5. joker k♣ 7♣ 6♣ 4♣ flush
5. q♣ 10♣ 7♣ 6♣ 4♣ flush
6. joker Q♦ joker A♠ 10♠ straight
6. joker 5♥ 7♦ 8♠ 9♦ straight
6. joker 2♦ joker 4♠ 5♠ straight
6. joker 2♦ 3♠ 4♠ 5♠ straight
6. a♥ 2♦ 3♣ 4♣ 5♦ straight
7. joker 7♥ 2♦ 3♠ 3♦ three of a kind
7. joker 2♦ 2♠ k♠ q♦ three of a kind
7. 2♥ 2♦ 2♣ k♣ q♦ three of a kind
8. 2♥ 7♥ 2♦ 3♣ 3♦ two pair
9. joker 4♣ k♣ 5♦ 10♠ one pair
9. 4♥ 4♠ k♠ 5♦ 10♠ one pair
10. 2♥ 5♥ 7♦ 8♣ 9♠ high card
</pre>
=={{header|Picat}}==
<syntaxhighlight lang="picat">go =>
Hands = [
[[2,h], [7,h], [2,d], [3,c], [3,d]], % two-pair
[[2,h], [5,h], [7,d], [8,c], [9,s]], % high-card
[[a,h], [2,d], [3,c], [4,c], [5,d]], % straight
[[2,h], [3,h], [2,d], [3,c], [3,d]], % full-house
[[2,h], [7,h], [2,d], [3,c], [3,d]], % two-pair
[[2,h], [7,h], [7,d], [7,c], [7,s]], % four-of-a-kind
[[10,h],[j,h], [q,h], [k,h], [a,h]], % straight-flush
[[4,h], [4,s], [k,s], [5,d], [10,s]], % one-pair
[[q,c], [10,c],[7,c], [6,c], [4,c]], % flush
[[q,c], [q,d], [q,s], [6,c], [4,c]], % three-of-a-kind
[[q,c], [10,c], [7,c], [7,c], [4,c]], % invalid (duplicates)
[[q,c], [10,c], [7,c], [7,d]] % invalid (too short)
],
foreach(Hand in Hands)
print_hand(Hand),
analyse(Hand, H),
println(hand=H),
nl
end,
nl.
% Print the hand
print_hand(Hand) =>
println([ F.to_string() ++ S.to_string() : [F,S] in Hand]).
% Faces and suites
faces(Faces) => Faces = [a, 2, 3, 4, 5, 6, 7, 8, 9, 10, j, q, k].
faces_order1(Order1) =>
Order1 = new_map([a=1, 2=2, 3=3, 4=4, 5=5, 6=6, 7=7, 8=8, 9=9, 10=10, j=11, q=12, k=13]).
faces_order2(Order2) =>
Order2 = new_map([2=2, 3=3, 4=4, 5=5, 6=6, 7=7, 8=8, 9=9, 10=10, j=11, q=12, k=13, a=14]).
suites(Suites) => Suites = [h,d,c,s].
% Order of the hand
hand_order(HandOrder) =>
HandOrder = [straight_flush,
four_of_a_kind,
full_house,
flush,
straight,
three_of_a_kind,
two_pair,
one_pair,
high_card,
invalid].
% for the straight
in_order(List) =>
foreach(I in 2..List.length)
List[I] = List[I-1] + 1
end.
% Some validity tests first
analyse(Hand,Value) ?=>
(
Hand.remove_dups.length == 5,
faces(Faces),
foreach([F,_] in Hand)
member(F,Faces)
end,
suites(Suites),
foreach([_,S] in Hand)
member(S,Suites)
end,
analyse1(Hand,Value)
;
Value = invalid
).
% Identify the specific hands
% straight flush
analyse1(Hand,Value) ?=>
permutation(Hand,Hand1),
Hand1 = [ [_F1,S], [_F2,S], [_F3,S], [_F4,S], [_F5,S] ],
(
faces_order1(Order1),
in_order([Order1.get(F) : [F,S1] in Hand1])
;
faces_order2(Order2),
in_order([Order2.get(F) : [F,S1] in Hand1]),
println("Royal Straight Flush!")
),
Value=straight_flush.
% four of a kind
analyse1(Hand,Value) ?=>
faces(Faces),
member(A,Faces),
[1 : [F,_S] in Hand, F = A].length == 4,
Value=four_of_a_kind.
% full house
analyse1(Hand,Value) ?=>
permutation(Hand,Hand1),
Hand1 = [ [F1,_S1], [F1,_S2], [F2,_S3], [F2,_S4], [F2,_S5] ],
Value = full_house.
% flush
analyse1(Hand,Value) ?=>
permutation(Hand,Hand1),
Hand1 = [ [_,S], [_,S], [_,S], [_,S], [_,S] ],
Value = flush.
% straight
analyse1(Hand,Value) ?=>
permutation(Hand,Hand1),
(
faces_order1(Order1),
in_order([Order1.get(F) : [F,_S] in Hand1])
;
faces_order2(Order2),
in_order([Order2.get(F) : [F,_S] in Hand1])
),
Value = straight.
% three of a kind
analyse1(Hand,Value) ?=>
faces(Faces),
member(A,Faces),
[1 : [F,_S] in Hand, F = A].length == 3,
Value = three_of_a_kind.
% two pair
analyse1(Hand,Value) ?=>
permutation(Hand,Hand1),
Hand1 = [ [F1,_S1], [F1,_S2], [F2,_S3], [F2,_S4], [_F3,_S5] ],
Value = two_pair.
% one pair
analyse1(Hand,Value) ?=>
faces(Faces),
member(A,Faces),
[1 : [F,_S] in Hand, F = A].length == 2,
Value = one_pair.
% high card
analyse1(_Hand,Value) =>
Value = high_card.</syntaxhighlight>
{{out}}
<pre>[2h,7h,2d,3c,3d]
hand = two_pair
[2h,5h,7d,8c,9s]
hand = high_card
[ah,2d,3c,4c,5d]
hand = straight
[2h,3h,2d,3c,3d]
hand = full_house
[2h,7h,2d,3c,3d]
hand = two_pair
[2h,7h,7d,7c,7s]
hand = four_of_a_kind
[10h,jh,qh,kh,ah]
Royal Straight Flush!
hand = straight_flush
[4h,4s,ks,5d,10s]
hand = one_pair
[qc,10c,7c,6c,4c]
hand = flush
[qc,qd,qs,6c,4c]
hand = three_of_a_kind
[qc,10c,7c,7c,4c]
hand = invalid
[qc,10c,7c,7d]
hand = invalid</pre>
For generating and checking random hands:
<syntaxhighlight lang="picat">go2 =>
_ = random2(),
Hand = random_hand(5),
print_hand(Hand),
analyse(Hand, H),
println(hand=H),
nl.
% Get one element of list L
oneof(L) = L[random(1,L.len)].
% Get a random hand
random_hand(N) = Hand =>
faces(Faces),
suites(Suites),
M = new_map(),
while (M.keys().length < N)
M.put([oneof(Faces),oneof(Suites)],1)
end,
Hand = [C : C=_ in M].sort().</syntaxhighlight>
{{out}}
<pre>[3d,5h,10c,10h,qs]
hand = one_pair</pre>
=={{header|PicoLisp}}==
(rassoc) function in picolisp after 3.1.9.10.
<
'(("2" . 0) ("3" . 1) ("4" . 2)
("5" . 3) ("6" . 4) ("7" . 5)
Line 2,656 ⟶ 3,403:
'two-pair )
((rassoc 2 R) 'pair)
(T 'high-card) ) ) )</
=={{header|Prolog}}==
{{works with|GNU Prolog|1.4.4}}
Not very efficient version.
<
Line 2,731 ⟶ 3,478:
, write(Cards), write('\t'), write(H), nl
.
main :- findall(_, run_tests, _), halt.</
{{out}}
<pre>[c(2,♥),c(2,♦),c(2,♣),c(k,♣),c(q,♦)] three-of-a-kind(2)
Line 2,745 ⟶ 3,492:
=={{header|Python}}==
Goes a little further in also giving the ordered tie-breaker information from the wikipedia page.
<
class Card(namedtuple('Card', 'face, suit')):
Line 2,894 ⟶ 3,641:
for cards in hands:
r = rank(cards)
print("%-18r %-15s %r" % (cards, r[0], r[1]))</
{{out}}
<pre>HAND CATEGORY TIE-BREAKER
Line 2,908 ⟶ 3,655:
=={{header|Racket}}==
<
(require (only-in srfi/1 car+cdr))
Line 3,072 ⟶ 3,819:
(five-of-a-kind 7) (straight-flush 14) (one-pair 13 10 5 4) (flush 14 13 7 6 4) (straight 6)
(straight 14) (straight-flush 14) (four-of-a-kind 2)))
(for ((h e.g.-hands) (r expected-results)) (check-equal? (analyse-hand/string h) r)))</
{{out}}
Line 3,102 ⟶ 3,849:
(formerly Perl 6)
This solution handles jokers. It has been written to use a Raku grammar.
<syntaxhighlight lang="raku"
grammar PokerHand {
Line 3,137 ⟶ 3,884:
method TOP($/) {
my UInt @n = n-of-a-kind($/);
my $flush = 'flush' if flush($/);
my $straight = 'straight' if straight($/);
make rank(@n[0], @n[1], $flush, $straight);
}
multi sub rank(5,
multi sub rank($,$,
multi sub rank(4,
multi sub rank($,$,
multi sub rank($,$,$,
multi sub rank(3,2,
multi sub rank(3,
multi sub rank(2,2,
multi sub rank(2,
multi sub rank(
sub n-of-a-kind($/) {
Line 3,167 ⟶ 3,914:
# allow both ace-low and ace-high straights
constant @Faces = [ "a 2 3 4 5 6 7 8 9 10 j q k a".split: ' ' ];
constant @Possible-Straights = [ (
my $faces = set @<face-card>.map: -> $/ {~$<face>.lc};
Line 3,211 ⟶ 3,958:
say "$_: $rank";
}
</syntaxhighlight>
{{out}}
<pre>2♥ 2♦ 2♣ k♣ q♦: three-of-a-kind
Line 3,239 ⟶ 3,986:
=={{header|REXX}}==
===version 1===
<
* 10.12.2013 Walter Pachl
*--------------------------------------------------------------------*/
Line 3,336 ⟶ 4,083:
err:
Say deck 'Error:' arg(1)
Return 0</
{{out}}
<pre>2h 2d 2s ks qd three-of-a-kind
Line 3,362 ⟶ 4,109:
::* the dealt hands can be in a file (blank lines are ignored)
::* dealt hands in the file can have comments after a semicolon (''';''')
<
parse arg iFID .; if iFID=='' | iFID=="," then iFID= 'POKERHAN.DAT'
/* [↓] read the poker hands dealt. */
Line 3,401 ⟶ 4,148:
if kinds==2 & pairs==2 then return 'two-pair'
if kinds==2 then return 'one-pair'
return 'high-card'</
Programming note: some older REXXes don't have the '''countstr''' BIF, so that REXX statement (above, line '''48''') can be replaced with:
<
{{out|input|text= file:}}
Line 3,440 ⟶ 4,187:
::* supports up to two ''jokers''
::* the ''joker'' card may be abbreviated (and can be in upper/lower/mixed case)
<
/*──────────────────────────────────────────── poker hands may contain up to two jokers.*/
parse arg iFID .; if iFID=='' | iFID=="," then iFID= 'POKERHAJ.DAT'
Line 3,498 ⟶ 4,245:
if kinds==2 & pairs==2 then return 'two-pair'
if kinds==2 then return 'one-pair'
return 'high-card'</
Programming note: the method used for analyzing hands that contain jokers are limited to a maximum of two jokers.
Line 3,552 ⟶ 4,299:
q♣ t♣ 7♣ 6♣ jok ◄─── flush
J♥ Q♦ K♠ A♠ jok ◄─── straight
</pre>
=={{header|RPL}}==
{{works with|HP|48G}}
≪ → hand
≪ { }
1 15 '''FOR''' j
"A23456789TJQK" hand j DUP SUB POS 1 -
"CDHS" hand j 1 + DUP SUB POS 13 * +
+ 3 '''STEP'''
≫ ≫ '<span style="color:blue>HANDCODE</span>' STO
≪ → diffs
≪ { } 1
1 4 '''FOR''' j
'''IF''' diffs j GET '''THEN''' + 1 '''ELSE''' 1 + '''END NEXT'''
+ SORT REVLIST 1 2 SUB
≫ ≫ '<span style="color:blue>GROUPS</span>' STO
≪ DUP ΠLIST 1 ==
SWAP { 9 1 1 1 } 1 == OR
≫ '<span style="color:blue>STRAIGHT?</span>' STO
≪ <span style="color:blue>HANDCODE</span>
DUP 13 / IP ≪ == ≫ DOSUBS ΠLIST
SWAP 13 MOD SORT ΔLIST
DUP <span style="color:blue>GROUPS</span> SWAP <span style="color:blue>STRAIGHT?</span>
→ flush groups straight
≪ '''CASE'''
straight '''THEN''' flush "Straight flush" "Straight" IFTE '''END'''
groups { 4 1 } == '''THEN''' "Four of a kind" '''END'''
groups { 3 2 } == '''THEN''' "Full house" '''END'''
groups { 3 1 } == '''THEN''' "Three of a kind" '''END'''
groups { 2 2 } == '''THEN''' "Two pairs" '''END'''
groups { 2 1 } == '''THEN''' "One pair" '''END'''
flush "Flush" "High card" IFTE '''END'''
≫ ≫ '<span style="color:blue>→HAND</span>' STO
{ "2H 2D 2S KS QD" "2H 5H 7D 8S 9D" "AH 2D 3S 4S 5S" "2H 3H 2D 3S 3D" "2H 7H 2D 3S 3D" "2H 7H 7D 7S 7C" "TH JH QH KH AH" "4H 4C KC 5D TC" "QC TC 7C 6C 4C" }
1 ≪ <span style="color:blue>→HAND</span> ≫ DOLIST
{{out}}
<pre>
1: { "Three of a kind" "High card" "Straight" "Full house" "Two pairs" "Four of a kind" "Straight flush" "One pair" "Flush" }
</pre>
=={{header|Ruby}}==
Joker-less hands are sorted high to low.
<
include Comparable
attr_accessor :ordinal
Line 3,699 ⟶ 4,489:
best = all_tries.max
puts "#{line.strip}: #{best.rank}"
end</
{{out}}
Line 3,732 ⟶ 4,522:
joker Q♦ joker A♦ 10♦: straight-flush
joker 2♦ 2♠ joker q♦: four-of-a-kind
</pre>
=={{header|Rust}}==
Unicode version with jokers. Also checks for Royal Flush (AKQJ10 suited).
<syntaxhighlight lang="rust">
fn main() {
let hands = vec![
"🂡 🂮 🂭 🂫 🂪",
"🃏 🃂 🂢 🂮 🃍",
"🃏 🂵 🃇 🂨 🃉",
"🃏 🃂 🂣 🂤 🂥",
"🃏 🂳 🃂 🂣 🃃",
"🃏 🂷 🃂 🂣 🃃",
"🃏 🂷 🃇 🂧 🃗",
"🃏 🂻 🂽 🂾 🂱",
"🃏 🃔 🃞 🃅 🂪",
"🃏 🃞 🃗 🃖 🃔",
"🃏 🃂 🃟 🂤 🂥",
"🃏 🃍 🃟 🂡 🂪",
"🃏 🃍 🃟 🃁 🃊",
"🃏 🃂 🂢 🃟 🃍",
"🃏 🃂 🂢 🃍 🃍",
"🃂 🃞 🃍 🃁 🃊",
];
for hand in hands{
println!("{} {}", hand, poker_hand(hand));
}
}
fn poker_hand(cards: &str) -> &str {
let mut suits = vec![0u8; 4];
let mut faces = vec![0u8; 15];
let mut hand = vec![];
for card in cards.chars(){
if card == ' ' { continue; }
let values = get_card_value(card);
if values.0 < 14 && hand.contains(&values) {
return "invalid";
}
hand.push(values);
faces[values.0 as usize]+=1;
if values.1 >= 0 {
suits[values.1 as usize]+=1;
}
}
if hand.len()!=5 {
return "invalid";
}
faces[13] = faces[0]; //add ace-high count
let jokers = faces[14];
//count suits
let mut colors = suits.into_iter()
.filter(|&x| x > 0).collect::<Vec<_>>();
colors.sort_unstable();
colors[0] += jokers; // add joker suits to the highest one;
let is_flush = colors[0] == 5;
//straight
let mut is_straight = false;
//pointer to optimise some work
//avoids looking again at cards that were the start of a sequence
//as they cannot be part of another sequence
let mut ptr = 14;
while ptr>3{
let mut jokers_left = jokers;
let mut straight_cards = 0;
for i in (0..ptr).rev(){
if faces[i]==0 {
if jokers_left == 0 {break;}
jokers_left -= 1;
}
else if i==ptr-1 { ptr-=1; }
straight_cards+=1;
}
ptr-=1;
if straight_cards == 5 {
is_straight = true;
break;
}
}
//count values
let mut values = faces.into_iter().enumerate().take(14).filter(|&x| x.1>0).collect::<Vec<_>>();
//sort by quantity, then by value, high to low
values.sort_unstable_by(|a, b| if b.1 == a.1 { (b.0).cmp(&a.0) } else { (b.1).cmp(&a.1)} );
let first_group = values[0].1 + jokers;
let second_group = if values.len()>1 {values[1].1} else {0};
match (is_flush, is_straight, first_group, second_group){
(_,_,5,_) => "five-of-a-kind",
(true, true, _, _) => if ptr == 8 {"royal-flush"} else {"straight-flush"},
(_,_,4,_) => "four-of-a-kind",
(_,_,3,2) => "full-house",
(true,_,_,_) => "flush",
(_,true,_,_) => "straight",
(_,_,3,_) => "three-of-a-kind",
(_,_,2,2) => "two-pair",
(_,_,2,_) => "one-pair",
_ => "high-card"
}
}
fn get_card_value(card: char) -> (i8,i8) {
// transform glyph to face + suit, zero-indexed
let base = card as u32 - 0x1F0A1;
let mut suit = (base / 16) as i8;
let mut face = (base % 16) as i8;
if face > 11 && face < 14 { face-=1; } // Unicode has a Knight that we do not want
if face == 14 { suit = -1; } //jokers do not have a suit
(face, suit)
}
</syntaxhighlight>
{{out}}
<pre>
🂡 🂮 🂭 🂫 🂪 royal-flush
🃏 🃂 🂢 🂮 🃍 three-of-a-kind
🃏 🂵 🃇 🂨 🃉 straight
🃏 🃂 🂣 🂤 🂥 straight
🃏 🂳 🃂 🂣 🃃 four-of-a-kind
🃏 🃂 🂢 🂮 🃍 three-of-a-kind
🃏 🂵 🃇 🂨 🃉 five-of-a-kind
🃏 🃂 🂣 🂤 🂥 straight-flush
🃏 🂳 🃂 🂣 🃃 one-pair
🃏 🃂 🂢 🂮 🃍 flush
🃏 🂵 🃇 🂨 🃉 straight
🃏 🃂 🂣 🂤 🂥 straight
🃏 🂳 🃂 🂣 🃃 straight-flush
🃏 🃂 🂢 🂮 🃍 four-of-a-kind
🃏 🂵 🃇 🂨 🃉 invalid
🃏 🃞 🃍 🃁 🃊 high-card
</pre>
=={{header|Scala}}==
Including jokers, but not special suit characters. Aiming for readability more than performance.
<
val suits = "CHSD"
sealed trait Card
Line 3,809 ⟶ 4,731:
val possibleHands = possibleRealHands(hand)
allHandTypes.find(t => possibleHands.exists(t.check)).map(_.name).getOrElse("high-card")
}</
<
"2H 2D 2S KS QD",
"2H 5H 7D 8S 9D",
Line 3,836 ⟶ 4,758:
for (hand <- testHands) {
println(s"$hand -> ${analyzeHand(parseHand(hand))}")
}</
{{out}}
Line 3,862 ⟶ 4,784:
=={{header|Seed7}}==
<
include "console.s7i";
Line 3,936 ⟶ 4,858:
analyze("4♥ 4♣ k♣ 5♦ t♣");
analyze("q♣ t♣ 7♣ 6♣ 4♣");
end func;</
{{out}}
Line 3,952 ⟶ 4,874:
=={{header|Standard ML}}==
<syntaxhighlight lang="standard ml">
local
val rec ins = fn x : int*'a => fn [] => [x]
Line 4,016 ⟶ 4,938:
end;
</syntaxhighlight>
Example (interpreter):
<syntaxhighlight lang="standard ml">
val rec printio = fn
[] => ()
Line 4,046 ⟶ 4,968:
ac ah ac ad 10h : invalid
val it = (): unit
</syntaxhighlight>
=={{header|Tcl}}==
{{works with|Tcl|8.6}}
<
namespace eval PokerHandAnalyser {
proc analyse {hand} {
Line 4,165 ⟶ 5,087:
return 0
}
}</
Demonstrating:
<
"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♠"
Line 4,173 ⟶ 5,095:
} {
puts "${hand}: [PokerHandAnalyser::analyse $hand]"
}</
{{out}}
<pre>
Line 4,185 ⟶ 5,107:
4♥ 4♠ k♠ 5♦ 10♠: one-pair
q♣ 10♣ 7♣ 6♣ 4♣: flush
</pre>
=={{header|VBScript}}==
<syntaxhighlight lang="vb">
option explicit
class playingcard
dim suit
dim pips
'public property get gsuit():gsuit=suit:end property
'public property get gpips():gpips=pips:end property
public sub print
dim s,p
select case suit
case "S":s=chrW(&h2660)
case "D":s=chrW(&h2666)
case "C":s=chrW(&h2663)
case "H":s=chrW(&h2665)
end select
select case pips
case 1:p="A"
case 11:p="J"
case 12:p="Q"
case 13:p="K"
case else: p=""& pips
end select
wscript.stdout.write right(" "&p & s,3)&" "
end sub
end class
sub printhand(byref h,start)
dim i
for i =start to ubound(h)
h(i).print
next
'wscript.stdout.writeblanklines(1)
end sub
function checkhand(byval arr)
dim ss,i,max,last,uses,toppip,j,straight, flush,used,ace
redim nn(13)
ss=arr(0).suit '?????
straight=true:flush=true
max=0:last=0:used=0:toppip=0:ace=0
for i=0 to ubound(arr)
j=arr(i).pips
if arr(i).suit<>ss then flush=false
if j>toppip then toppip=j
if j=1 then ace=1
nn(j)=nn(j)+1
if nn(j)>max then max= nn(j)
if abs(j-toppip)>=4 then straight=0
next
for i=1 to ubound(nn)
if nn(i) then used=used+1
next
if max=1 then
if nn(1) and nn(10) and nn(11) and nn(12) and nn(13) then straight=1
end if
if flush and straight and max=1 then
checkhand= "straight-flush"
elseif flush then
checkhand= "flush"
elseif straight and max=1 then
checkhand= "straight"
elseif max=4 then
checkhand= "four-of-a-kind"
elseif max=3 then
if used=2 then
checkhand= "full-house"
else
checkhand= "three-of-a-kind"
end if
elseif max=2 then
if used=3 then
checkhand= "two-pair"
else
checkhand= "one-pair"
end if
else
checkhand= "Top "& toppip
End If
end function
function readhand(h)
dim i,b,c,p
redim a(4)
for i=0 to ubound(a)
b=h(i)
set c=new playingcard
p=left(b,1)
select case p
case "j": c.pips=11
case "q": c.pips=12
case "k": c.pips=13
case "t": c.pips=10
case "a": c.pips=1
case else c.pips=cint(p)
end select
c.suit=ucase(right(b,1))
set a(i)=c
next
readhand=a
erase a
end function
dim hands,hh,i
hands = Array(_
Array("2h", "5h", "7d", "8c", "9s"),_
Array("2h", "2d", "2c", "kc", "qd"),_
Array("ah", "2d", "3c", "4c", "5d"),_
Array("2h", "3h", "2d", "3c", "3d"),_
Array("2h", "7h", "2d", "3c", "3d"),_
Array("th", "jh", "qh", "kh", "ah"),_
Array("4h", "4s", "ks", "5d", "ts"),_
Array("qc", "tc", "7c", "6c", "4c"),_
Array("ah", "ah", "7c", "6c", "4c"))
for i=1 to ubound(hands)
hh=readhand(hands(i))
printhand hh,0
wscript.stdout.write vbtab & checkhand(hh)
wscript.stdout.writeblanklines(1)
'exit for
next
</syntaxhighlight>
{{out}}
<pre>
2♥ 2♦ 2♣ K♣ Q♦ three-of-a-kind
A♥ 2♦ 3♣ 4♣ 5♦ straight
2♥ 3♥ 2♦ 3♣ 3♦ full-house
2♥ 7♥ 2♦ 3♣ 3♦ two-pair
10♥ J♥ Q♥ K♥ A♥ straight-flush
4♥ 4♠ K♠ 5♦ 10♠ one-pair
Q♣ 10♣ 7♣ 6♣ 4♣ flush
A♥ A♥ 7♣ 6♣ 4♣ one-pair
</pre>
=={{header|Wren}}==
{{trans|Kotlin}}
{{libheader|Wren-dynamic}}
{{libheader|Wren-sort}}
{{libheader|Wren-str}}
{{libheader|Wren-seq}}
===Basic Version===
<syntaxhighlight lang="wren">import "./dynamic" for Tuple
import "./sort" for Sort
import "./str" for Str
import "./seq" for Lst
var Card = Tuple.create("Card", ["face", "suit"])
var FACES = "23456789tjqka"
var SUITS = "shdc"
var isStraight = Fn.new { |cards|
var cmp = Fn.new { |i, j| (i.face - j.face).sign }
var sorted = Sort.merge(cards, cmp)
if (sorted[0].face + 4 == sorted[4].face) return true
if (sorted[4].face == 14 && sorted[0].face == 2 && sorted[3].face == 5) return true
return false
}
var isFlush = Fn.new { |cards|
var suit = cards[0].suit
if (cards.skip(1).all { |card| card.suit == suit }) return true
return false
}
var analyzeHand = Fn.new { |hand|
var h = Str.lower(hand)
var split = Lst.distinct(h.split(" ").where { |c| c != "" }.toList)
if (split.count != 5) return "invalid"
var cards = []
for (s in split) {
if (s.count != 2) return "invalid"
var fIndex = FACES.indexOf(s[0])
if (fIndex == -1) return "invalid"
var sIndex = SUITS.indexOf(s[1])
if (sIndex == -1) return "invalid"
cards.add(Card.new(fIndex + 2, s[1]))
}
var groups = Lst.groups(cards) { |card| card.face }
if (groups.count == 2) {
if (groups.any { |g| g[1].count == 4 }) return "four-of-a-kind"
return "full-house"
} else if (groups.count == 3) {
if (groups.any { |g| g[1].count == 3 }) return "three-of-a-kind"
return "two-pair"
} else if (groups.count == 4) {
return "one-pair"
} else {
var flush = isFlush.call(cards)
var straight = isStraight.call(cards)
if (flush && straight) return "straight-flush"
if (flush) return "flush"
if (straight) return "straight"
return "high-card"
}
}
var hands = [
"2h 2d 2c kc qd",
"2h 5h 7d 8c 9s",
"ah 2d 3c 4c 5d",
"2h 3h 2d 3c 3d",
"2h 7h 2d 3c 3d",
"2h 7h 7d 7c 7s",
"th jh qh kh ah",
"4h 4s ks 5d ts",
"qc tc 7c 6c 4c",
"ah ah 7c 6c 4c"
]
for (hand in hands) {
System.print("%(hand): %(analyzeHand.call(hand))")
}</syntaxhighlight>
{{out}}
<pre>
2h 2d 2c kc qd: three-of-a-kind
2h 5h 7d 8c 9s: high-card
ah 2d 3c 4c 5d: straight
2h 3h 2d 3c 3d: full-house
2h 7h 2d 3c 3d: two-pair
2h 7h 7d 7c 7s: four-of-a-kind
th jh qh kh ah: straight-flush
4h 4s ks 5d ts: one-pair
qc tc 7c 6c 4c: flush
ah ah 7c 6c 4c: invalid
</pre>
===Extra Credit Version===
<syntaxhighlight lang="wren">import "./dynamic" for Tuple
import "./sort" for Sort
import "./seq" for Lst
var Card = Tuple.create("Card", ["face", "suit"])
var cmp = Fn.new { |i, j| (i.face - j.face).sign }
var isStraight = Fn.new { |cards, jokers|
var sorted = Sort.merge(cards, cmp)
if (jokers == 0) {
if (sorted[0].face + 4 == sorted[4].face) return true
if (sorted[4].face == 14 && sorted[3].face == 5) return true
return false
} else if (jokers == 1) {
if (sorted[0].face + 3 == sorted[3].face) return true
if (sorted[0].face + 4 == sorted[3].face) return true
if (sorted[3].face == 14 && sorted[2].face == 4) return true
if (sorted[3].face == 14 && sorted[2].face == 5) return true
return false
} else {
if (sorted[0].face + 2 == sorted[2].face) return true
if (sorted[0].face + 3 == sorted[2].face) return true
if (sorted[0].face + 4 == sorted[2].face) return true
if (sorted[2].face == 14 && sorted[1].face == 3) return true
if (sorted[2].face == 14 && sorted[1].face == 4) return true
if (sorted[2].face == 14 && sorted[1].face == 5) return true
return false
}
}
var isFlush = Fn.new { |cards|
var sorted = Sort.merge(cards, cmp)
var suit = sorted[0].suit
if (sorted.skip(1).all { |card| card.suit == suit || card.suit == "j" }) return true
return false
}
var analyzeHand = Fn.new { |hand|
var split = Lst.distinct(hand.split(" ").where { |c| c != "" }.toList)
if (split.count != 5) return "invalid"
var cards = []
var jokers = 0
for (s in split) {
if (s.bytes.count != 4) return "invalid"
var cp = s.codePoints[0]
var card =
cp == 0x1f0a1 ? Card.new(14, "s") :
cp == 0x1f0b1 ? Card.new(14, "h") :
cp == 0x1f0c1 ? Card.new(14, "d") :
cp == 0x1f0d1 ? Card.new(14, "c") :
cp == 0x1f0cf ? Card.new(15, "j") : // black joker
cp == 0x1f0df ? Card.new(16, "j") : // white joker
(cp >= 0x1f0a2 && cp <= 0x1f0ab) ? Card.new(cp - 0x1f0a0, "s") :
(cp >= 0x1f0ad && cp <= 0x1f0ae) ? Card.new(cp - 0x1f0a1, "s") :
(cp >= 0x1f0b2 && cp <= 0x1f0bb) ? Card.new(cp - 0x1f0b0, "h") :
(cp >= 0x1f0bd && cp <= 0x1f0be) ? Card.new(cp - 0x1f0b1, "h") :
(cp >= 0x1f0c2 && cp <= 0x1f0cb) ? Card.new(cp - 0x1f0c0, "d") :
(cp >= 0x1f0cd && cp <= 0x1f0ce) ? Card.new(cp - 0x1f0c1, "d") :
(cp >= 0x1f0d2 && cp <= 0x1f0db) ? Card.new(cp - 0x1f0d0, "c") :
(cp >= 0x1f0dd && cp <= 0x1f0de) ? Card.new(cp - 0x1f0d1, "c") :
Card.new(0, "j") // invalid
if (card.face == 0) return "invalid"
if (card.suit == "j") jokers = jokers + 1
cards.add(card)
}
var groups = Lst.groups(cards) { |card| card.face }
if (groups.count == 2) {
if (groups.any { |g| g[1].count == 4 }) {
if (jokers == 0) return "four-of-a-kind"
return "five-of-a-kind"
}
return "full-house"
} else if (groups.count == 3) {
if (groups.any { |g| g[1].count == 3 }) {
if (jokers == 0) return "three-of-a-kind"
if (jokers == 1) return "four-of-a-kind"
return "five-of-a-kind"
}
return (jokers == 0) ? "two-pair" : "full-house"
} else if (groups.count == 4) {
if (jokers == 0) return "one-pair"
if (jokers == 1) return "three-of-a-kind"
return "four-of-a-kind"
} else {
var flush = isFlush.call(cards)
var straight = isStraight.call(cards, jokers)
if (flush && straight) return "straight-flush"
if (flush) return "flush"
if (straight) return "straight"
return (jokers == 0) ? "high-card" : "one-pair"
}
}
var hands = [
"🃏 🃂 🂢 🂮 🃍",
"🃏 🂵 🃇 🂨 🃉",
"🃏 🃂 🂣 🂤 🂥",
"🃏 🂳 🃂 🂣 🃃",
"🃏 🂷 🃂 🂣 🃃",
"🃏 🂷 🃇 🂧 🃗",
"🃏 🂻 🂽 🂾 🂱",
"🃏 🃔 🃞 🃅 🂪",
"🃏 🃞 🃗 🃖 🃔",
"🃏 🃂 🃟 🂤 🂥",
"🃏 🃍 🃟 🂡 🂪",
"🃏 🃍 🃟 🃁 🃊",
"🃏 🃂 🂢 🃟 🃍",
"🃏 🃂 🂢 🃍 🃍",
"🃂 🃞 🃍 🃁 🃊"
]
for (hand in hands) {
System.print("%(hand): %(analyzeHand.call(hand))")
}</syntaxhighlight>
{{out}}
<pre>
🃏 🃂 🂢 🂮 🃍: three-of-a-kind
🃏 🂵 🃇 🂨 🃉: straight
🃏 🃂 🂣 🂤 🂥: straight
🃏 🂳 🃂 🂣 🃃: four-of-a-kind
🃏 🂷 🃂 🂣 🃃: three-of-a-kind
🃏 🂷 🃇 🂧 🃗: five-of-a-kind
🃏 🂻 🂽 🂾 🂱: straight-flush
🃏 🃔 🃞 🃅 🂪: one-pair
🃏 🃞 🃗 🃖 🃔: flush
🃏 🃂 🃟 🂤 🂥: straight
🃏 🃍 🃟 🂡 🂪: straight
🃏 🃍 🃟 🃁 🃊: straight-flush
🃏 🃂 🂢 🃟 🃍: four-of-a-kind
🃏 🃂 🂢 🃍 🃍: invalid
🃂 🃞 🃍 🃁 🃊: high-card
</pre>
|
Revision as of 16:13, 7 March 2024
You are encouraged to solve this task according to the task description, using any language you may know.
- Task
Create a program to parse a single five card poker hand and rank it according to this list of poker hands.
A poker hand is specified as a space separated list of five playing cards.
Each input card has two characters indicating face and suit.
- 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: ♥ ♦ ♣ ♠
Duplicate cards are illegal.
The program should analyze 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♣ q♣: invalid
The programs output for the above examples should be displayed here on this page.
- Extra credit
- use the playing card characters introduced with Unicode 6.0 (U+1F0A1 - U+1F0DE).
- allow two jokers
- use the symbol joker
- duplicates would be allowed (for jokers only)
- five-of-a-kind would then be the highest hand
- More extra credit examples
joker 2♦ 2♠ k♠ q♦: three-of-a-kind joker 5♥ 7♦ 8♠ 9♦: straight joker 2♦ 3♠ 4♠ 5♠: straight joker 3♥ 2♦ 3♠ 3♦: four-of-a-kind joker 7♥ 2♦ 3♠ 3♦: three-of-a-kind joker 7♥ 7♦ 7♠ 7♣: five-of-a-kind joker j♥ q♥ k♥ A♥: straight-flush joker 4♣ k♣ 5♦ 10♠: one-pair joker k♣ 7♣ 6♣ 4♣: flush joker 2♦ joker 4♠ 5♠: straight joker Q♦ joker A♠ 10♠: straight joker Q♦ joker A♦ 10♦: straight-flush joker 2♦ 2♠ joker q♦: four-of-a-kind
- Related tasks
11l
F analyzeHandHelper(faceCount, suitCount)
V
p1 = 0B
p2 = 0B
t = 0B
f = 0B
fl = 0B
st = 0B
L(fc) faceCount
S fc
2 {I p1 {p2 = 1B} E p1 = 1B}
3 {t = 1B}
4 {f = 1B}
L(sc) suitCount
I sc == 5
fl = 1B
I !p1 & !p2 & !t & !f
V s = 0
L(fc) faceCount
I fc != 0
s++
E
s = 0
I s == 5
L.break
st = (s == 5) | (s == 4 & faceCount[0] != 0 & faceCount[1] == 0)
I st & fl {R ‘straight-flush’}
E I f {R ‘four-of-a-kind’}
E I p1 & t {R ‘full-house’}
E I fl {R ‘flush’}
E I st {R ‘straight’}
E I t {R ‘three-of-a-kind’}
E I p1 & p2 {R ‘two-pair’}
E I p1 {R ‘one-pair’}
E {R ‘high-card’}
F analyzeHand(inHand)
V handLen = 5
V face = ‘A23456789TJQK’
V suit = ‘SHCD’
V errorMessage = ‘invalid hand.’
V hand = sorted(inHand.split(‘ ’))
I hand.len != handLen
R errorMessage
I Set(hand).len != handLen
R errorMessage‘ Duplicated cards.’
V faceCount = [0] * face.len
V suitCount = [0] * suit.len
L(card) hand
I card.len != 2
R errorMessage
V? n = face.find(card[0])
V? l = suit.find(card[1])
I n == N | l == N
R errorMessage
faceCount[n]++
suitCount[l]++
R analyzeHandHelper(faceCount, suitCount)
L(hand) [‘2H 2D 2S KS QD’,
‘2H 5H 7D 8S 9D’,
‘AH 2D 3S 4S 5S’,
‘2H 3H 2D 3S 3D’,
‘2H 7H 2D 3S 3D’,
‘2H 7H 7D 7S 7C’,
‘TH JH QH KH AH’,
‘4H 4C KC 5D TC’,
‘QC TC 7C 6C 4C’]
print(hand‘: ’analyzeHand(hand))
- 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
AutoHotkey
PokerHand(hand){
StringUpper, hand, hand
Sort, hand, FCardSort D%A_Space%
cardSeq := RegExReplace(hand, "[^2-9TJQKA]")
Straight:= InStr("23456789TJQKA", cardSeq) || (cardSeq = "2345A") ? true : false
hand := cardSeq = "2345A" ? RegExReplace(hand, "(.*)\h(A.)", "$2 $1") : hand
Royal := InStr(hand, "A") ? "Royal": "Straight"
return (hand ~= "[2-9TJQKA](.)\h.\1\h.\1\h.\1\h.\1") && (Straight) ? hand "`t" Royal " Flush"
: (hand ~= "([2-9TJQKA]).*?\1.*?\1.*?\1") ? hand "`tFour of a Kind"
: (hand ~= "^([2-9TJQKA]).\h\1.\h(?!\1)([2-9TJQKA]).\h\2.\h\2.$") ? hand "`tFull House" ; xxyyy
: (hand ~= "^([2-9TJQKA]).\h\1.\h\1.\h(?!\1)([2-9TJQKA]).\h\2.$") ? hand "`tFull House" ; xxxyy
: (hand ~= "[2-9TJQKA](.)\h.\1\h.\1\h.\1\h.\1") ? hand "`tFlush"
: (Straight) ? hand "`tStraight"
: (hand ~= "([2-9TJQKA]).*?\1.*?\1") ? hand "`tThree of a Kind"
: (hand ~= "([2-9TJQKA]).\h\1.*?([2-9TJQKA]).\h\2") ? hand "`tTwo Pair"
: (hand ~= "([2-9TJQKA]).\h\1") ? hand "`tOne Pair"
: hand "`tHigh Card"
}
CardSort(a, b){
a := SubStr(a, 1, 1), b := SubStr(b, 1, 1)
a := (a = "T") ? 10 : (a = "J") ? 11 : (a = "Q") ? 12 : (a = "K") ? 13 : a
b := (b = "T") ? 10 : (b = "J") ? 11 : (b = "Q") ? 12 : (b = "K") ? 13 : b
return a > b ? 1 : a < b ? -1 : 0
}
Examples:
hands =
(join`r`n
2♥ 2♦ 2♣ k♣ q♦
2♥ 5♥ 7♦ 8♣ 9♠
a♥ 2♦ 3♣ 4♣ 5♦
2♥ 3♥ 2♦ 3♣ 3♦
2♥ 3♥ 2♦ 2♣ 3♦
2♥ 7♥ 2♦ 3♣ 3♦
2♥ 7♥ 7♦ 7♣ 7♠
T♥ j♥ q♥ a♥ K♥
T♥ j♥ q♥ 9♥ K♥
4♥ 4♠ k♠ 5♦ T♠
q♣ T♣ 7♣ 6♣ 4♣
)
loop, parse, hands, `n, `r
res .= PokerHand(A_LoopField) "`n"
MsgBox, 262144, , % res
return
Outputs:
2♦ 2♣ 2♥ Q♦ K♣ Three of a Kind 2♥ 5♥ 7♦ 8♣ 9♠ High Card A♥ 2♦ 3♣ 4♣ 5♦ Straight 2♦ 2♥ 3♣ 3♦ 3♥ Full House 2♣ 2♦ 2♥ 3♦ 3♥ Full House 2♦ 2♥ 3♣ 3♦ 7♥ Two Pair 2♥ 7♦ 7♣ 7♠ 7♥ Four of a Kind T♥ J♥ Q♥ K♥ A♥ Royal Flush 9♥ T♥ J♥ Q♥ K♥ Straight Flush 4♠ 4♥ 5♦ T♠ K♠ One Pair 4♣ 6♣ 7♣ T♣ Q♣ Flush
C
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#define TRUE 1
#define FALSE 0
#define FACES "23456789tjqka"
#define SUITS "shdc"
typedef int bool;
typedef struct {
int face; /* FACES map to 0..12 respectively */
char suit;
} card;
card cards[5];
int compare_card(const void *a, const void *b) {
card c1 = *(card *)a;
card c2 = *(card *)b;
return c1.face - c2.face;
}
bool equals_card(card c1, card c2) {
if (c1.face == c2.face && c1.suit == c2.suit) return TRUE;
return FALSE;
}
bool are_distinct() {
int i, j;
for (i = 0; i < 4; ++i)
for (j = i + 1; j < 5; ++j)
if (equals_card(cards[i], cards[j])) return FALSE;
return TRUE;
}
bool is_straight() {
int i;
qsort(cards, 5, sizeof(card), compare_card);
if (cards[0].face + 4 == cards[4].face) return TRUE;
if (cards[4].face == 12 && cards[0].face == 0 &&
cards[3].face == 3) return TRUE;
return FALSE;
}
bool is_flush() {
int i;
char suit = cards[0].suit;
for (i = 1; i < 5; ++i) if (cards[i].suit != suit) return FALSE;
return TRUE;
}
const char *analyze_hand(const char *hand) {
int i, j, gs = 0;
char suit, *cp;
bool found, flush, straight;
int groups[13];
if (strlen(hand) != 14) return "invalid";
for (i = 0; i < 14; i += 3) {
cp = strchr(FACES, tolower(hand[i]));
if (cp == NULL) return "invalid";
j = i / 3;
cards[j].face = cp - FACES;
suit = tolower(hand[i + 1]);
cp = strchr(SUITS, suit);
if (cp == NULL) return "invalid";
cards[j].suit = suit;
}
if (!are_distinct()) return "invalid";
for (i = 0; i < 13; ++i) groups[i] = 0;
for (i = 0; i < 5; ++i) groups[cards[i].face]++;
for (i = 0; i < 13; ++i) if (groups[i] > 0) gs++;
switch(gs) {
case 2:
found = FALSE;
for (i = 0; i < 13; ++i) if (groups[i] == 4) {
found = TRUE;
break;
}
if (found) return "four-of-a-kind";
return "full-house";
case 3:
found = FALSE;
for (i = 0; i < 13; ++i) if (groups[i] == 3) {
found = TRUE;
break;
}
if (found) return "three-of-a-kind";
return "two-pairs";
case 4:
return "one-pair";
default:
flush = is_flush();
straight = is_straight();
if (flush && straight)
return "straight-flush";
else if (flush)
return "flush";
else if (straight)
return "straight";
else
return "high-card";
}
}
int main(){
int i;
const char *type;
const char *hands[10] = {
"2h 2d 2c kc qd",
"2h 5h 7d 8c 9s",
"ah 2d 3c 4c 5d",
"2h 3h 2d 3c 3d",
"2h 7h 2d 3c 3d",
"2h 7h 7d 7c 7s",
"th jh qh kh ah",
"4h 4s ks 5d ts",
"qc tc 7c 6c 4c",
"ah ah 7c 6c 4c"
};
for (i = 0; i < 10; ++i) {
type = analyze_hand(hands[i]);
printf("%s: %s\n", hands[i], type);
}
return 0;
}
- Output:
2h 2d 2c kc qd: three-of-a-kind 2h 5h 7d 8c 9s: high-card ah 2d 3c 4c 5d: straight 2h 3h 2d 3c 3d: full-house 2h 7h 2d 3c 3d: two-pairs 2h 7h 7d 7c 7s: four-of-a-kind th jh qh kh ah: straight-flush 4h 4s ks 5d ts: one-pair qc tc 7c 6c 4c: flush ah ah 7c 6c 4c: invalid
C#
using System;
using System.Collections.Generic;
using static System.Linq.Enumerable;
public static class PokerHandAnalyzer
{
private enum Hand {
Invalid, High_Card, One_Pair, Two_Pair, Three_Of_A_Kind, Straight,
Flush, Full_House, Four_Of_A_Kind, Straight_Flush, Five_Of_A_Kind
}
private const bool Y = true;
private const char C = '♣', D = '♦', H = '♥', S = '♠';
private const int rankMask = 0b11_1111_1111_1111;
private const int suitMask = 0b1111 << 14;
private static readonly string[] ranks = { "a", "2", "3", "4", "5", "6", "7", "8", "9", "10", "j", "q", "k" };
private static readonly string[] suits = { C + "", D + "", H + "", S + "" };
private static readonly Card[] deck = (from suit in Range(1, 4) from rank in Range(1, 13) select new Card(rank, suit)).ToArray();
public static void Main() {
string[] 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♣",
"4♥ 4♣ 4♥ 4♠ 4♦", //duplicate card
"joker 2♦ 2♠ k♠ q♦",
"joker 5♥ 7♦ 8♠ 9♦",
"joker 2♦ 3♠ 4♠ 5♠",
"joker 3♥ 2♦ 3♠ 3♦",
"joker 7♥ 2♦ 3♠ 3♦",
"joker 7♥ 7♦ 7♠ 7♣",
"joker j♥ q♥ k♥ A♥",
"joker 4♣ k♣ 5♦ 10♠",
"joker k♣ 7♣ 6♣ 4♣",
"joker 2♦ joker 4♠ 5♠",
"joker Q♦ joker A♠ 10♠",
"joker Q♦ joker A♦ 10♦",
"joker 2♦ 2♠ joker q♦"
};
foreach (var h in hands) {
Console.WriteLine($"{h}: {Analyze(h).Name()}");
}
}
static string Name(this Hand hand) => string.Join('-', hand.ToString().Split('_')).ToLower();
static List<T> With<T>(this List<T> list, int index, T item) {
list[index] = item;
return list;
}
struct Card : IEquatable<Card>, IComparable<Card>
{
public static readonly Card Invalid = new Card(-1, -1);
public static readonly Card Joker = new Card(0, 0);
public Card(int rank, int suit) {
(Rank, Suit, Code) = (rank, suit) switch {
(_, -1) => (-1, -1, -1),
(-1, _) => (-1, -1, -1),
(0, _) => (0, 0, 0),
(1, _) => (rank, suit, (1 << (13 + suit)) | ((1 << 13) | 1)),
(_, _) => (rank, suit, (1 << (13 + suit)) | (1 << (rank - 1)))
};
}
public static implicit operator Card((int rank, int suit) tuple) => new Card(tuple.rank, tuple.suit);
public int Rank { get; }
public int Suit { get; }
public int Code { get; }
public override string ToString() => Rank switch {
-1 => "invalid",
0 => "joker",
_ => $"{ranks[Rank-1]}{suits[Suit-1]}"
};
public override int GetHashCode() => Rank << 16 | Suit;
public bool Equals(Card other) => Rank == other.Rank && Suit == other.Suit;
public int CompareTo(Card other) {
int c = Rank.CompareTo(other.Rank);
if (c != 0) return c;
return Suit.CompareTo(other.Suit);
}
}
static Hand Analyze(string hand) {
var cards = ParseHand(hand);
if (cards.Count != 5) return Hand.Invalid; //hand must consist of 5 cards
cards.Sort();
if (cards[0].Equals(Card.Invalid)) return Hand.Invalid;
int jokers = cards.LastIndexOf(Card.Joker) + 1;
if (jokers > 2) return Hand.Invalid; //more than 2 jokers
if (cards.Skip(jokers).Distinct().Count() + jokers != 5) return Hand.Invalid; //duplicate cards
if (jokers == 2) return (from c0 in deck from c1 in deck select Evaluate(cards.With(0, c0).With(1, c1))).Max();
if (jokers == 1) return (from c0 in deck select Evaluate(cards.With(0, c0))).Max();
return Evaluate(cards);
}
static List<Card> ParseHand(string hand) =>
hand.Split(default(char[]), StringSplitOptions.RemoveEmptyEntries)
.Select(card => ParseCard(card.ToLower())).ToList();
static Card ParseCard(string card) => (card.Length, card) switch {
(5, "joker") => Card.Joker,
(3, _) when card[..2] == "10" => (10, ParseSuit(card[2])),
(2, _) => (ParseRank(card[0]), ParseSuit(card[1])),
(_, _) => Card.Invalid
};
static int ParseRank(char rank) => rank switch {
'a' => 1,
'j' => 11,
'q' => 12,
'k' => 13,
_ when rank >= '2' && rank <= '9' => rank - '0',
_ => -1
};
static int ParseSuit(char suit) => suit switch {
C => 1, 'c' => 1,
D => 2, 'd' => 2,
H => 3, 'h' => 3,
S => 4, 's' => 4,
_ => -1
};
static Hand Evaluate(List<Card> hand) {
var frequencies = hand.GroupBy(c => c.Rank).Select(g => g.Count()).OrderByDescending(c => c).ToArray();
(int f0, int f1) = (frequencies[0], frequencies.Length > 1 ? frequencies[1] : 0);
return (IsFlush(), IsStraight(), f0, f1) switch {
(_, _, 5, _) => Hand.Five_Of_A_Kind,
(Y, Y, _, _) => Hand.Straight_Flush,
(_, _, 4, _) => Hand.Four_Of_A_Kind,
(_, _, 3, 2) => Hand.Full_House,
(Y, _, _, _) => Hand.Flush,
(_, Y, _, _) => Hand.Straight,
(_, _, 3, _) => Hand.Three_Of_A_Kind,
(_, _, 2, 2) => Hand.Two_Pair,
(_, _, 2, _) => Hand.One_Pair,
_ => Hand.High_Card
};
bool IsFlush() => hand.Aggregate(suitMask, (r, c) => r & c.Code) > 0;
bool IsStraight() {
int r = hand.Aggregate(0, (r, c) => r | c.Code) & rankMask;
for (int i = 0; i < 4; i++) r &= r << 1;
return r > 0;
}
}
}
- Output:
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 4♥ 4♣ 4♥ 4♠ 4♦: invalid joker 2♦ 2♠ k♠ q♦: three-of-a-kind joker 5♥ 7♦ 8♠ 9♦: straight joker 2♦ 3♠ 4♠ 5♠: straight joker 3♥ 2♦ 3♠ 3♦: four-of-a-kind joker 7♥ 2♦ 3♠ 3♦: three-of-a-kind joker 7♥ 7♦ 7♠ 7♣: five-of-a-kind joker j♥ q♥ k♥ A♥: straight-flush joker 4♣ k♣ 5♦ 10♠: one-pair joker k♣ 7♣ 6♣ 4♣: flush joker 2♦ joker 4♠ 5♠: straight joker Q♦ joker A♠ 10♠: straight joker Q♦ joker A♦ 10♦: straight-flush joker 2♦ 2♠ joker q♦: four-of-a-kind
C++
#include <iostream>
#include <sstream>
#include <algorithm>
#include <vector>
using namespace std;
class poker
{
public:
poker() { face = "A23456789TJQK"; suit = "SHCD"; }
string analyze( string h )
{
memset( faceCnt, 0, 13 ); memset( suitCnt, 0, 4 ); vector<string> hand;
transform( h.begin(), h.end(), h.begin(), toupper ); istringstream i( h );
copy( istream_iterator<string>( i ), istream_iterator<string>(), back_inserter<vector<string> >( hand ) );
if( hand.size() != 5 ) return "invalid hand."; vector<string>::iterator it = hand.begin();
sort( it, hand.end() ); if( hand.end() != adjacent_find( it, hand.end() ) ) return "invalid hand.";
while( it != hand.end() )
{
if( ( *it ).length() != 2 ) return "invalid hand.";
int n = face.find( ( *it ).at( 0 ) ), l = suit.find( ( *it ).at( 1 ) );
if( n < 0 || l < 0 ) return "invalid hand.";
faceCnt[n]++; suitCnt[l]++; it++;
}
cout << h << ": "; return analyzeHand();
}
private:
string analyzeHand()
{
bool p1 = false, p2 = false, t = false, f = false, fl = false, st = false;
for( int x = 0; x < 13; x++ )
switch( faceCnt[x] )
{
case 2: if( p1 ) p2 = true; else p1 = true; break;
case 3: t = true; break;
case 4: f = true;
}
for( int x = 0; x < 4; x++ )if( suitCnt[x] == 5 ){ fl = true; break; }
if( !p1 && !p2 && !t && !f )
{
int s = 0;
for( int x = 0; x < 13; x++ )
{
if( faceCnt[x] ) s++; else s = 0;
if( s == 5 ) break;
}
st = ( s == 5 ) || ( s == 4 && faceCnt[0] && !faceCnt[1] );
}
if( st && fl ) return "straight-flush";
else if( f ) return "four-of-a-kind";
else if( p1 && t ) return "full-house";
else if( fl ) return "flush";
else if( st ) return "straight";
else if( t ) return "three-of-a-kind";
else if( p1 && p2 ) return "two-pair";
else if( p1 ) return "one-pair";
return "high-card";
}
string face, suit;
unsigned char faceCnt[13], suitCnt[4];
};
int main( int argc, char* argv[] )
{
poker p;
cout << p.analyze( "2h 2d 2s ks qd" ) << endl; cout << p.analyze( "2h 5h 7d 8s 9d" ) << endl;
cout << p.analyze( "ah 2d 3s 4s 5s" ) << endl; cout << p.analyze( "2h 3h 2d 3s 3d" ) << endl;
cout << p.analyze( "2h 7h 2d 3s 3d" ) << endl; cout << p.analyze( "2h 7h 7d 7s 7c" ) << endl;
cout << p.analyze( "th jh qh kh ah" ) << endl; cout << p.analyze( "4h 4c kc 5d tc" ) << endl;
cout << p.analyze( "qc tc 7c 6c 4c" ) << endl << endl; return system( "pause" );
}
- 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
Clojure
(defn rank [card]
(let [[fst _] card]
(if (Character/isDigit fst)
(Integer/valueOf (str fst))
({\T 10, \J 11, \Q 12, \K 13, \A 14} fst))))
(defn suit [card]
(let [[_ snd] card]
(str snd)))
(defn n-of-a-kind [hand n]
(not (empty? (filter #(= true %) (map #(>= % n) (vals (frequencies (map rank hand))))))))
(defn ranks-with-ace [hand]
(let [ranks (sort (map rank hand))]
(if (some #(= 14 %) ranks) (cons 1 ranks) ranks)))
(defn pair? [hand]
(n-of-a-kind hand 2))
(defn three-of-a-kind? [hand]
(n-of-a-kind hand 3))
(defn four-of-a-kind? [hand]
(n-of-a-kind hand 4))
(defn flush? [hand]
(not (empty? (filter #(= true %) (map #(>= % 5) (vals (frequencies (map suit hand))))))))
(defn full-house? [hand]
(true? (and
(some #(= 2 %) (vals (frequencies (map rank hand))))
(some #(= 3 %) (vals (frequencies (map rank hand)))))))
(defn two-pairs? [hand]
(or
(full-house? hand)
(four-of-a-kind? hand)
(= 2 (count (filter #(= true %) (map #(>= % 2) (vals (frequencies (map rank hand)))))))))
(defn straight? [hand]
(let [hand-a (ranks-with-ace hand)
fst (first hand-a)
snd (second hand-a)]
(or
(= (take 5 hand-a) (range fst (+ fst 5)))
(= (drop 1 hand-a) (range snd (+ snd 5))))))
(defn straight-flush? [hand]
(and
(straight? hand)
(flush? hand)))
(defn invalid? [hand]
(not= 5 (count (set hand))))
(defn check-hand [hand]
(cond
(invalid? hand) "invalid"
(straight-flush? hand) "straight-flush"
(four-of-a-kind? hand) "four-of-a-kind"
(full-house? hand) "full-house"
(flush? hand) "flush"
(straight? hand) "straight"
(three-of-a-kind? hand) "three-of-a-kind"
(two-pairs? hand) "two-pair"
(pair? hand) "one-pair"
:else "high-card"))
; Test examples
(def hands [["2H" "2D" "2S" "KS" "QD"]
["2H" "5H" "7D" "8S" "9D"]
["AH" "2D" "3S" "4S" "5S"]
["2H" "3H" "2D" "3S" "3D"]
["2H" "7H" "2D" "3S" "3D"]
["2H" "7H" "7D" "7S" "7C"]
["TH" "JH" "QH" "KH" "AH"]
["4H" "4C" "KC" "5D" "TC"]
["QC" "TC" "7C" "6C" "4C"]])
(run! println (map #(str % " : " (check-hand %)) hands))
- 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
D
Basic Version
No bonus for this simple version.
import std.stdio, std.string, std.algorithm, std.range;
string analyzeHand(in string inHand) pure /*nothrow @safe*/ {
enum handLen = 5;
static immutable face = "A23456789TJQK", suit = "SHCD";
static immutable errorMessage = "invalid hand.";
/*immutable*/ const hand = inHand.toUpper.split.sort().release;
if (hand.length != handLen)
return errorMessage;
if (hand.uniq.walkLength != handLen)
return errorMessage ~ " Duplicated cards.";
ubyte[face.length] faceCount;
ubyte[suit.length] suitCount;
foreach (immutable card; hand) {
if (card.length != 2)
return errorMessage;
immutable n = face.countUntil(card[0]);
immutable l = suit.countUntil(card[1]);
if (n < 0 || l < 0)
return errorMessage;
faceCount[n]++;
suitCount[l]++;
}
return analyzeHandHelper(faceCount, suitCount);
}
private string analyzeHandHelper(const ref ubyte[13] faceCount,
const ref ubyte[4] suitCount)
pure nothrow @safe @nogc {
bool p1, p2, t, f, fl, st;
foreach (immutable fc; faceCount)
switch (fc) {
case 2: (p1 ? p2 : p1) = true; break;
case 3: t = true; break;
case 4: f = true; break;
default: // Ignore.
}
foreach (immutable sc; suitCount)
if (sc == 5) {
fl = true;
break;
}
if (!p1 && !p2 && !t && !f) {
uint s = 0;
foreach (immutable fc; faceCount) {
if (fc)
s++;
else
s = 0;
if (s == 5)
break;
}
st = (s == 5) || (s == 4 && faceCount[0] && !faceCount[1]);
}
if (st && fl) return "straight-flush";
else if (f) return "four-of-a-kind";
else if (p1 && t) return "full-house";
else if (fl) return "flush";
else if (st) return "straight";
else if (t) return "three-of-a-kind";
else if (p1 && p2) return "two-pair";
else if (p1) return "one-pair";
else return "high-card";
}
void main() {
// S = Spades, H = Hearts, C = Clubs, D = Diamonds.
foreach (immutable hand; ["2H 2D 2S KS QD",
"2H 5H 7D 8S 9D",
"AH 2D 3S 4S 5S",
"2H 3H 2D 3S 3D",
"2H 7H 2D 3S 3D",
"2H 7H 7D 7S 7C",
"TH JH QH KH AH",
"4H 4C KC 5D TC",
"QC TC 7C 6C 4C"])
writeln(hand, ": ", hand.analyzeHand);
}
- 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
Elixir
defmodule Card do
@faces ~w(2 3 4 5 6 7 8 9 10 j q k a)
@suits ~w(♥ ♦ ♣ ♠) # ~w(h d c s)
@ordinal @faces |> Enum.with_index |> Map.new
defstruct ~w[face suit ordinal]a
def new(str) do
{face, suit} = String.split_at(str, -1)
if face in @faces and suit in @suits do
ordinal = @ordinal[face]
%__MODULE__{face: face, suit: suit, ordinal: ordinal}
else
raise ArgumentError, "invalid card: #{str}"
end
end
def deck do
for face <- @faces, suit <- @suits, do: "#{face}#{suit}"
end
end
defmodule Hand do
@ranks ~w(high-card one-pair two-pair three-of-a-kind straight flush
full-house four-of-a-kind straight-flush five-of-a-kind)a |>
Enum.with_index |> Map.new
@wheel_faces ~w(2 3 4 5 a)
def new(str_of_cards) do
cards = String.downcase(str_of_cards) |>
String.split([" ", ","], trim: true) |>
Enum.map(&Card.new &1)
grouped = Enum.group_by(cards, &(&1.ordinal)) |> Map.values
face_pattern = Enum.map(grouped, &(length &1)) |> Enum.sort
{consecutive, wheel_faces} = consecutive?(cards)
rank = categorize(cards, face_pattern, consecutive)
rank_num = @ranks[rank]
tiebreaker = if wheel_faces do
for ord <- 3..-1, do: {1,ord}
else
Enum.map(grouped, &{length(&1), hd(&1).ordinal}) |>
Enum.sort |> Enum.reverse
end
{rank_num, tiebreaker, str_of_cards, rank}
end
defp one_suit?(cards) do
Enum.map(cards, &(&1.suit)) |> Enum.uniq |> length == 1
end
defp consecutive?(cards) do
sorted = Enum.sort_by(cards, &(&1.ordinal))
if Enum.map(sorted, &(&1.face)) == @wheel_faces do
{true, true}
else
flag = Enum.map(sorted, &(&1.ordinal)) |>
Enum.chunk(2,1) |>
Enum.all?(fn [a,b] -> a+1 == b end)
{flag, false}
end
end
defp categorize(cards, face_pattern, consecutive) do
case {consecutive, one_suit?(cards)} do
{true, true} -> :"straight-flush"
{true, false} -> :straight
{false, true} -> :flush
_ -> case face_pattern do
[1,1,1,1,1] -> :"high-card"
[1,1,1,2] -> :"one-pair"
[1,2,2] -> :"two-pair"
[1,1,3] -> :"three-of-a-kind"
[2,3] -> :"full-house"
[1,4] -> :"four-of-a-kind"
[5] -> :"five-of-a-kind"
end
end
end
end
test_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♥ 6♥ 2♦ 3♣ 3♦
10♥ j♥ q♥ k♥ a♥
4♥ 4♠ k♠ 2♦ 10♠
4♥ 4♠ k♠ 3♦ 10♠
q♣ 10♣ 7♣ 6♣ 4♣
q♣ 10♣ 7♣ 6♣ 3♣
9♥ 10♥ q♥ k♥ j♣
2♥ 3♥ 4♥ 5♥ a♥
2♥ 2♥ 2♦ 3♣ 3♦
"""
hands = String.split(test_hands, "\n", trim: true) |> Enum.map(&Hand.new(&1))
IO.puts "High to low"
Enum.sort(hands) |> Enum.reverse |>
Enum.each(fn hand -> IO.puts "#{elem(hand,2)}: \t#{elem(hand,3)}" end)
# Extra Credit 2. Examples:
IO.puts "\nExtra Credit 2"
extra_hands = """
joker 2♦ 2♠ k♠ q♦
joker 5♥ 7♦ 8♠ 9♦
joker 2♦ 3♠ 4♠ 5♠
joker 3♥ 2♦ 3♠ 3♦
joker 7♥ 2♦ 3♠ 3♦
joker 7♥ 7♦ 7♠ 7♣
joker j♥ q♥ k♥ A♥
joker 4♣ k♣ 5♦ 10♠
joker k♣ 7♣ 6♣ 4♣
joker 2♦ joker 4♠ 5♠
joker Q♦ joker A♠ 10♠
joker Q♦ joker A♦ 10♦
joker 2♦ 2♠ joker q♦
"""
deck = Card.deck
String.split(extra_hands, "\n", trim: true) |>
Enum.each(fn hand ->
[a,b,c,d,e] = String.split(hand) |>
Enum.map(fn c -> if c=="joker", do: deck, else: [c] end)
cards_list = for v<-a, w<-b, x<-c, y<-d, z<-e, do: "#{v} #{w} #{x} #{y} #{z}"
best = Enum.map(cards_list, &Hand.new &1) |> Enum.max
IO.puts "#{hand}:\t#{elem(best,3)}"
end)
- Output:
High to low 10♥ j♥ q♥ k♥ a♥: straight-flush 2♥ 3♥ 4♥ 5♥ a♥: straight-flush 2♥ 3♥ 2♦ 3♣ 3♦: full-house 2♥ 2♥ 2♦ 3♣ 3♦: full-house q♣ 10♣ 7♣ 6♣ 4♣: flush q♣ 10♣ 7♣ 6♣ 3♣: flush 9♥ 10♥ q♥ k♥ j♣: straight a♥ 2♦ 3♣ 4♣ 5♦: straight 2♥ 2♦ 2♣ k♣ q♦: three-of-a-kind 2♥ 7♥ 2♦ 3♣ 3♦: two-pair 2♥ 6♥ 2♦ 3♣ 3♦: two-pair 4♥ 4♠ k♠ 3♦ 10♠: one-pair 4♥ 4♠ k♠ 2♦ 10♠: one-pair 2♥ 5♥ 7♦ 8♣ 9♠: high-card Extra Credit 2 joker 2♦ 2♠ k♠ q♦: three-of-a-kind joker 5♥ 7♦ 8♠ 9♦: straight joker 2♦ 3♠ 4♠ 5♠: straight joker 3♥ 2♦ 3♠ 3♦: four-of-a-kind joker 7♥ 2♦ 3♠ 3♦: three-of-a-kind joker 7♥ 7♦ 7♠ 7♣: five-of-a-kind joker j♥ q♥ k♥ A♥: straight-flush joker 4♣ k♣ 5♦ 10♠: one-pair joker k♣ 7♣ 6♣ 4♣: flush joker 2♦ joker 4♠ 5♠: straight joker Q♦ joker A♠ 10♠: straight joker Q♦ joker A♦ 10♦: straight-flush joker 2♦ 2♠ joker q♦: four-of-a-kind
F#
type Card = int * int
type Cards = Card list
let joker = (69,69)
let rankInvalid = "invalid", 99
let allCards = {0..12} |> Seq.collect (fun x->({0..3} |> Seq.map (fun y->x,y)))
let allSame = function | y::ys -> List.forall ((=) y) ys | _-> false
let straightList (xs:int list) = xs |> List.sort |> List.mapi (fun i n->n - i) |> allSame
let cardList (s:string): Cards =
s.Split() |> Seq.map (fun s->s.ToLower())
|> Seq.map (fun s ->
if s="joker" then joker
else
match (s |> List.ofSeq) with
| '1'::'0'::xs -> (9, xs) | '!'::xs -> (-1, xs) | x::xs-> ("a23456789!jqk".IndexOf(x), xs) | _ as xs-> (-1, xs)
|> function | -1, _ -> (-1, '!') | x, y::[] -> (x, y) | _ -> (-1, '!')
|> function
| x, 'h' | x, '♥' -> (x, 0) | x, 'd' | x, '♦' -> (x, 1) | x, 'c' | x, '♣' -> (x, 2)
| x, 's' | x, '♠' -> (x, 3) | _ -> (-1, -1)
)
|> Seq.filter (fst >> ((<>) -1)) |> List.ofSeq
let rank (cards: Cards) =
if cards.Length<>5 then rankInvalid
else
let cts = cards |> Seq.groupBy fst |> Seq.map (snd >> Seq.length) |> List.ofSeq |> List.sort |> List.rev
if cts.[0]=5 then ("five-of-a-kind", 1)
else
let flush = cards |> List.map snd |> allSame
let straight =
let (ACE, ALT_ACE) = 0, 13
let faces = cards |> List.map fst |> List.sort
(straightList faces) || (if faces.Head<>ACE then false else (straightList (ALT_ACE::(faces.Tail))))
if straight && flush then ("straight-flush", 2)
else
let cts = cards |> Seq.groupBy fst |> Seq.map (snd >> Seq.length) |> List.ofSeq |> List.sort |> List.rev
if cts.[0]=4 then ("four-of-a-kind", 3)
elif cts.[0]=3 && cts.[1]=2 then ("full-house", 4)
elif flush then ("flush", 5)
elif straight then ("straight", 6)
elif cts.[0]=3 then ("three-of-a-kind", 7)
elif cts.[0]=2 && cts.[1]=2 then ("two-pair", 8)
elif cts.[0]=2 then ("one-pair", 9)
else ("high-card", 10)
let pickBest (xs: seq<Cards>) =
let cmp a b = (<) (snd a) (snd b)
let pick currentBest x = if (cmp (snd x) (snd currentBest)) then x else currentBest
xs |> Seq.map (fun x->x, (rank x)) |> Seq.fold pick ([], rankInvalid)
let calcHandRank handStr =
let cards = handStr |> cardList
if cards.Length<>5
then (cards, rankInvalid)
else
cards |> List.partition ((=) joker) |> fun (x,y) -> x.Length, y
|> function
| (0,xs) when (xs |> Seq.distinct |> Seq.length)=5 -> xs, (rank xs)
| (1,xs) -> allCards |> Seq.map (fun x->x::xs) |> pickBest
| (2,xs) -> allCards |> Seq.collect (fun x->allCards |> Seq.map (fun y->y::x::xs)) |> pickBest
| _ -> cards, rankInvalid
let showHandRank handStr =
// handStr |> calcHandRank |> fun (cards, (rankName,_)) -> printfn "%s: %A %s" handStr cards rankName
handStr |> calcHandRank |> (snd >> fst) |> printfn "%s: %s" handStr
[
"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♣"
"joker 2♦ 2♠ k♠ q♦"
"joker 5♥ 7♦ 8♠ 9♦"
"joker 2♦ 3♠ 4♠ 5♠"
"joker 3♥ 2♦ 3♠ 3♦"
"joker 7♥ 2♦ 3♠ 3♦"
"joker 7♥ 7♦ 7♠ 7♣"
"joker j♥ q♥ k♥ A♥"
"joker 4♣ k♣ 5♦ 10♠"
"joker k♣ 7♣ 6♣ 4♣"
"joker 2♦ joker 4♠ 5♠"
"joker Q♦ joker A♠ 10♠"
"joker Q♦ joker A♦ 10♦"
"joker 2♦ 2♠ joker q♦"
]
|> List.iter showHandRank
- Output:
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 joker 2♦ 2♠ k♠ q♦: three-of-a-kind joker 5♥ 7♦ 8♠ 9♦: straight joker 2♦ 3♠ 4♠ 5♠: straight joker 3♥ 2♦ 3♠ 3♦: four-of-a-kind joker 7♥ 2♦ 3♠ 3♦: three-of-a-kind joker 7♥ 7♦ 7♠ 7♣: five-of-a-kind joker j♥ q♥ k♥ A♥: straight-flush joker 4♣ k♣ 5♦ 10♠: one-pair joker k♣ 7♣ 6♣ 4♣: flush joker 2♦ joker 4♠ 5♠: straight joker Q♦ joker A♠ 10♠: straight joker Q♦ joker A♦ 10♦: straight-flush joker 2♦ 2♠ joker q♦: four-of-a-kind
Factor
Factor comes with a poker hand evaluator.
USING: formatting kernel poker sequences ;
{
"2H 2D 2C KC QD"
"2H 5H 7D 8C 9S"
"AH 2D 3C 4C 5D"
"2H 3H 2D 3C 3D"
"2H 7H 2D 3C 3D"
"2H 7H 7D 7C 7S"
"TH JH QH KH AH"
"4H 4S KS 5D TS"
"QC TC 7C 6C 4C"
} [ dup string>hand-name "%s: %s\n" printf ] each
- Output:
2H 2D 2C KC QD: Three of a Kind 2H 5H 7D 8C 9S: High Card AH 2D 3C 4C 5D: Straight 2H 3H 2D 3C 3D: Full House 2H 7H 2D 3C 3D: Two Pair 2H 7H 7D 7C 7S: Four of a Kind TH JH QH KH AH: Straight Flush 4H 4S KS 5D TS: One Pair QC TC 7C 6C 4C: Flush
FreeBASIC
Const As String FACES = "23456789tjqka"
Const As String SUITS = "shdc"
Type card
face As Integer ' FACES map to 0..12 respectively
suit As String
End Type
Dim Shared As card cards(4)
Sub insertionSort(arr() As card, Byval n As Integer)
Dim As Integer i, key, j
For i = 1 To n-1
key = arr(i).face
j = i-1
While j >= 0 And arr(j).face > key
arr(j+1) = arr(j)
j = j-1
Wend
arr(j+1).face = key
Next i
End Sub
Function compareCard(Byval a As card, Byval b As card) As Integer
Return a.face - b.face
End Function
Function equalsCard(Byval c1 As card, Byval c2 As card) As Integer
If c1.face = c2.face And c1.suit = c2.suit Then Return True
Return False
End Function
Function areDistinct() As Integer
Dim As Integer i, j
For i = 0 To 3
For j = i + 1 To 4
If equalsCard(cards(i), cards(j)) = True Then Return False
Next j
Next i
Return True
End Function
Function isStraight() As Boolean
insertionSort(cards(), 5)
If cards(0).face + 4 = cards(4).face Then Return True
If cards(4).face = 12 And cards(0).face = 0 And cards(3).face = 3 Then Return True
Return False
End Function
Function isFlush() As Boolean
Dim As String suit = cards(0).suit
For i As Integer = 1 To 4
If cards(i).suit <> suit Then Return False
Next i
Return True
End Function
Function analyzeHand(Byval hand As String) As String
Dim As Integer i, j, cp, gs = 0
Dim As String suit
Dim As Integer found, flush, straight
Dim As Integer groups(12)
If Len(hand) <> 14 Then Return "invalid"
For i = 0 To 13 Step 3
cp = Instr(FACES, Lcase(Mid(hand, i + 1, 1)))
If cp = 0 Then Return "invalid"
j = i \ 3
cards(j).face = cp - 1
suit = Lcase(Mid(hand, i + 2, 1))
cp = Instr(SUITS, suit)
If cp = 0 Then Return "invalid"
cards(j).suit = suit
Next i
If areDistinct() = False Then Return "invalid"
For i = 0 To 12
groups(i) = 0
Next i
For i = 0 To 4
groups(cards(i).face) += 1
Next i
For i = 0 To 12
If groups(i) > 0 Then gs += 1
Next i
Select Case gs
Case 2
found = False
For i = 0 To 12
If groups(i) = 4 Then
found = True
Exit For
End If
Next i
If found = True Then Return "four-of-a-kind"
Return "full-house"
Case 3
found = False
For i = 0 To 12
If groups(i) = 3 Then
found = True
Exit For
End If
Next i
If found = True Then Return "three-of-a-kind"
Return "two-pairs"
Case 4
Return "one-pair"
Case Else
flush = isFlush()
straight = isStraight()
If flush = True And straight = True Then
Return "straight-flush"
Elseif flush = True Then
Return "flush"
Elseif straight = True Then
Return "straight"
Else
Return "high-card"
End If
End Select
End Function
Dim As String tipo
Dim As String hands(9) = { _
"2h 2d 2c kc qd", _
"2h 5h 7d 8c 9s", _
"ah 2d 3c 4c 5d", _
"2h 3h 2d 3c 3d", _
"2h 7h 2d 3c 3d", _
"2h 7h 7d 7c 7s", _
"th jh qh kh ah", _
"4h 4s ks 5d ts", _
"qc tc 7c 6c 4c", _
"ah ah 7c 6c 4c" }
For i As Integer = 0 To 9
tipo = analyzeHand(hands(i))
Print hands(i); ": "; tipo
Next i
Sleep
- Output:
Same as C entry.
Go
Basic Version
package main
import (
"fmt"
"sort"
"strings"
)
type card struct {
face byte
suit byte
}
const faces = "23456789tjqka"
const suits = "shdc"
func isStraight(cards []card) bool {
sorted := make([]card, 5)
copy(sorted, cards)
sort.Slice(sorted, func(i, j int) bool {
return sorted[i].face < sorted[j].face
})
if sorted[0].face+4 == sorted[4].face {
return true
}
if sorted[4].face == 14 && sorted[0].face == 2 && sorted[3].face == 5 {
return true
}
return false
}
func isFlush(cards []card) bool {
suit := cards[0].suit
for i := 1; i < 5; i++ {
if cards[i].suit != suit {
return false
}
}
return true
}
func analyzeHand(hand string) string {
temp := strings.Fields(strings.ToLower(hand))
splitSet := make(map[string]bool)
var split []string
for _, s := range temp {
if !splitSet[s] {
splitSet[s] = true
split = append(split, s)
}
}
if len(split) != 5 {
return "invalid"
}
var cards []card
for _, s := range split {
if len(s) != 2 {
return "invalid"
}
fIndex := strings.IndexByte(faces, s[0])
if fIndex == -1 {
return "invalid"
}
sIndex := strings.IndexByte(suits, s[1])
if sIndex == -1 {
return "invalid"
}
cards = append(cards, card{byte(fIndex + 2), s[1]})
}
groups := make(map[byte][]card)
for _, c := range cards {
groups[c.face] = append(groups[c.face], c)
}
switch len(groups) {
case 2:
for _, group := range groups {
if len(group) == 4 {
return "four-of-a-kind"
}
}
return "full-house"
case 3:
for _, group := range groups {
if len(group) == 3 {
return "three-of-a-kind"
}
}
return "two-pair"
case 4:
return "one-pair"
default:
flush := isFlush(cards)
straight := isStraight(cards)
switch {
case flush && straight:
return "straight-flush"
case flush:
return "flush"
case straight:
return "straight"
default:
return "high-card"
}
}
}
func main() {
hands := [...]string{
"2h 2d 2c kc qd",
"2h 5h 7d 8c 9s",
"ah 2d 3c 4c 5d",
"2h 3h 2d 3c 3d",
"2h 7h 2d 3c 3d",
"2h 7h 7d 7c 7s",
"th jh qh kh ah",
"4h 4s ks 5d ts",
"qc tc 7c 6c 4c",
"ah ah 7c 6c 4c",
}
for _, hand := range hands {
fmt.Printf("%s: %s\n", hand, analyzeHand(hand))
}
}
- Output:
2h 2d 2c kc qd: three-of-a-kind 2h 5h 7d 8c 9s: high-card ah 2d 3c 4c 5d: straight 2h 3h 2d 3c 3d: full-house 2h 7h 2d 3c 3d: two-pair 2h 7h 7d 7c 7s: four-of-a-kind th jh qh kh ah: straight-flush 4h 4s ks 5d ts: one-pair qc tc 7c 6c 4c: flush ah ah 7c 6c 4c: invalid
Extra Credit Version
package main
import (
"fmt"
"sort"
"strings"
)
type card struct {
face byte
suit byte
}
func isStraight(cards []card, jokers int) bool {
sorted := make([]card, 5)
copy(sorted, cards)
sort.Slice(sorted, func(i, j int) bool {
return sorted[i].face < sorted[j].face
})
switch jokers {
case 0:
switch {
case sorted[0].face+4 == sorted[4].face,
sorted[4].face == 14 && sorted[3].face == 5:
return true
default:
return false
}
case 1:
switch {
case sorted[0].face+3 == sorted[3].face,
sorted[0].face+4 == sorted[3].face,
sorted[3].face == 14 && sorted[2].face == 4,
sorted[3].face == 14 && sorted[2].face == 5:
return true
default:
return false
}
default:
switch {
case sorted[0].face+2 == sorted[2].face,
sorted[0].face+3 == sorted[2].face,
sorted[0].face+4 == sorted[2].face,
sorted[2].face == 14 && sorted[1].face == 3,
sorted[2].face == 14 && sorted[1].face == 4,
sorted[2].face == 14 && sorted[1].face == 5:
return true
default:
return false
}
}
}
func isFlush(cards []card) bool {
sorted := make([]card, 5)
copy(sorted, cards)
sort.Slice(sorted, func(i, j int) bool {
return sorted[i].face < sorted[j].face
})
suit := sorted[0].suit
for i := 1; i < 5; i++ {
if sorted[i].suit != suit && sorted[i].suit != 'j' {
return false
}
}
return true
}
func analyzeHand(hand string) string {
temp := strings.Fields(strings.ToLower(hand))
splitSet := make(map[string]bool)
var split []string
for _, s := range temp {
if !splitSet[s] {
splitSet[s] = true
split = append(split, s)
}
}
if len(split) != 5 {
return "invalid"
}
var cards []card
var jokers = 0
for _, s := range split {
if len(s) != 4 {
return "invalid"
}
cp := []rune(s)[0]
var cd card
switch {
case cp == 0x1f0a1:
cd = card{14, 's'}
case cp == 0x1f0b1:
cd = card{14, 'h'}
case cp == 0x1f0c1:
cd = card{14, 'd'}
case cp == 0x1f0d1:
cd = card{14, 'c'}
case cp == 0x1f0cf:
jokers++
cd = card{15, 'j'} // black joker
case cp == 0x1f0df:
jokers++
cd = card{16, 'j'} // white joker
case cp >= 0x1f0a2 && cp <= 0x1f0ab:
cd = card{byte(cp - 0x1f0a0), 's'}
case cp >= 0x1f0ad && cp <= 0x1f0ae:
cd = card{byte(cp - 0x1f0a1), 's'}
case cp >= 0x1f0b2 && cp <= 0x1f0bb:
cd = card{byte(cp - 0x1f0b0), 'h'}
case cp >= 0x1f0bd && cp <= 0x1f0be:
cd = card{byte(cp - 0x1f0b1), 'h'}
case cp >= 0x1f0c2 && cp <= 0x1f0cb:
cd = card{byte(cp - 0x1f0c0), 'd'}
case cp >= 0x1f0cd && cp <= 0x1f0ce:
cd = card{byte(cp - 0x1f0c1), 'd'}
case cp >= 0x1f0d2 && cp <= 0x1f0db:
cd = card{byte(cp - 0x1f0d0), 'c'}
case cp >= 0x1f0dd && cp <= 0x1f0de:
cd = card{byte(cp - 0x1f0d1), 'c'}
default:
cd = card{0, 'j'} // invalid
}
if cd.face == 0 {
return "invalid"
}
cards = append(cards, cd)
}
groups := make(map[byte][]card)
for _, c := range cards {
groups[c.face] = append(groups[c.face], c)
}
switch len(groups) {
case 2:
for _, group := range groups {
if len(group) == 4 {
switch jokers {
case 0:
return "four-of-a-kind"
default:
return "five-of-a-kind"
}
}
}
return "full-house"
case 3:
for _, group := range groups {
if len(group) == 3 {
switch jokers {
case 0:
return "three-of-a-kind"
case 1:
return "four-of-a-kind"
default:
return "five-of-a-kind"
}
}
}
if jokers == 0 {
return "two-pair"
}
return "full-house"
case 4:
switch jokers {
case 0:
return "one-pair"
case 1:
return "three-of-a-kind"
default:
return "four-of-a-kind"
}
default:
flush := isFlush(cards)
straight := isStraight(cards, jokers)
switch {
case flush && straight:
return "straight-flush"
case flush:
return "flush"
case straight:
return "straight"
default:
if jokers == 0 {
return "high-card"
} else {
return "one-pair"
}
}
}
}
func main() {
hands := [...]string{
"🃏 🃂 🂢 🂮 🃍",
"🃏 🂵 🃇 🂨 🃉",
"🃏 🃂 🂣 🂤 🂥",
"🃏 🂳 🃂 🂣 🃃",
"🃏 🂷 🃂 🂣 🃃",
"🃏 🂷 🃇 🂧 🃗",
"🃏 🂻 🂽 🂾 🂱",
"🃏 🃔 🃞 🃅 🂪",
"🃏 🃞 🃗 🃖 🃔",
"🃏 🃂 🃟 🂤 🂥",
"🃏 🃍 🃟 🂡 🂪",
"🃏 🃍 🃟 🃁 🃊",
"🃏 🃂 🂢 🃟 🃍",
"🃏 🃂 🂢 🃍 🃍",
"🃂 🃞 🃍 🃁 🃊",
}
for _, hand := range hands {
fmt.Printf("%s: %s\n", hand, analyzeHand(hand))
}
}
- Output:
🃏 🃂 🂢 🂮 🃍 : three-of-a-kind 🃏 🂵 🃇 🂨 🃉 : straight 🃏 🃂 🂣 🂤 🂥 : straight 🃏 🂳 🃂 🂣 🃃 : four-of-a-kind 🃏 🂷 🃂 🂣 🃃 : three-of-a-kind 🃏 🂷 🃇 🂧 🃗 : five-of-a-kind 🃏 🂻 🂽 🂾 🂱 : straight-flush 🃏 🃔 🃞 🃅 🂪 : one-pair 🃏 🃞 🃗 🃖 🃔 : flush 🃏 🃂 🃟 🂤 🂥 : straight 🃏 🃍 🃟 🂡 🂪 : straight 🃏 🃍 🃟 🃁 🃊 : straight-flush 🃏 🃂 🂢 🃟 🃍 : four-of-a-kind 🃏 🃂 🂢 🃍 🃍 : invalid 🃂 🃞 🃍 🃁 🃊 : high-card
Haskell
Basic Version
{-# LANGUAGE TupleSections #-}
import Data.Function (on)
import Data.List (group, nub, any, sort, sortBy)
import Data.Maybe (mapMaybe)
import Text.Read (readMaybe)
data Suit = Club | Diamond | Spade | Heart deriving (Show, Eq)
data Rank = Ace | Two | Three | Four | Five | Six | Seven
| Eight | Nine | Ten | Jack | Queen | King
deriving (Show, Eq, Enum, Ord, Bounded)
data Card = Card { suit :: Suit, rank :: Rank } deriving (Show, Eq)
type Hand = [Card]
consumed = pure . (, "")
instance Read Suit where
readsPrec d s = case s of "♥" -> consumed Heart
"♦" -> consumed Diamond
"♣" -> consumed Spade
"♠" -> consumed Club
"h" -> consumed Heart
_ -> []
instance Read Rank where
readsPrec d s = case s of "a" -> consumed Ace
"2" -> consumed Two
"3" -> consumed Three
"4" -> consumed Four
"5" -> consumed Five
"6" -> consumed Six
"7" -> consumed Seven
"8" -> consumed Eight
"9" -> consumed Nine
"10" -> consumed Ten
"j" -> consumed Jack
"q" -> consumed Queen
"k" -> consumed King
_ -> []
instance Read Card where
readsPrec d = fmap (, "") . mapMaybe card . lex
where
card (r, s) = Card <$> (readMaybe s :: Maybe Suit)
<*> (readMaybe r :: Maybe Rank)
-- Special hand
acesHigh :: [Rank]
acesHigh = [Ace, Ten, Jack, Queen, King]
isSucc :: (Enum a, Eq a, Bounded a) => [a] -> Bool
isSucc [] = True
isSucc [x] = True
isSucc (x:y:zs) = (x /= maxBound && y == succ x) && isSucc (y:zs)
nameHand :: Hand -> String
nameHand [] = "Invalid Input"
nameHand cards | invalidHand = "Invalid hand"
| straight && flush = "Straight flush"
| ofKind 4 = "Four of a kind"
| ofKind 3 && ofKind 2 = "Full house"
| flush = "Flush"
| straight = "Straight"
| ofKind 3 = "Three of a kind"
| uniqRanks == 3 = "Two pair"
| uniqRanks == 4 = "One pair"
| otherwise = "High card"
where
sortedRank = sort $ rank <$> cards
rankCounts = sortBy (compare `on` snd) $ (,) <$> head <*> length <$> group sortedRank
uniqRanks = length rankCounts
ofKind n = any ((==n) . snd) rankCounts
straight = isSucc sortedRank || sortedRank == acesHigh
flush = length (nub $ suit <$> cards) == 1
invalidHand = length (nub cards) /= 5
testHands :: [(String, Hand)]
testHands = (,) <$> id <*> mapMaybe readMaybe . words <$>
[ "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♣"
, "q♣ 10♣ 7♣ 6♣ 7♣" -- duplicate cards
, "Bad input" ]
main :: IO ()
main = mapM_ (putStrLn . (fst <> const ": " <> nameHand . snd)) testHands
- Output:
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 q♣ 10♣ 7♣ 6♣ 7♣: Invalid hand Bad input: Invalid Input
J
parseHand=: ' ' cut 7&u: NB. hand must be well formed
Suits=: <"> 7 u: '♥♦♣♦' NB. or Suits=: 'hdcs'
Faces=: <;._1 ' 2 3 4 5 6 7 8 9 10 j q k a'
suits=: {:&.>
faces=: }:&.>
flush=: 1 =&#&~. suits
straight=: 1 = (i.#Faces) +/@E.~ Faces /:~@i. faces
kinds=: #/.~ @:faces
five=: 5 e. kinds NB. jokers or other cheat
four=: 4 e. kinds
three=: 3 e. kinds
two=: 2 e. kinds
twoPair=: 2 = 2 +/ .= kinds
highcard=: 5 = 1 +/ .= kinds
IF=: 2 :'(,&(<m) ^: v)"1'
Or=: 2 :'u ^:(5 e. $) @: v'
Deck=: ,Faces,&.>/Suits
Joker=: <'joker'
joke=: [: ,/^:(#@$ - 2:) (({. ,"1 Deck ,"0 1 }.@}.)^:(5>[)~ i.&Joker)"1^:2@,:
punchLine=: {:@-.&a:@,@|:
rateHand=: [:;:inv [: (, [: punchLine -1 :(0 :0-.LF)@joke) parseHand
('invalid' IF 1:) Or
('high-card' IF highcard) Or
('one-pair' IF two) Or
('two-pair' IF twoPair) Or
('three-of-a-kind' IF three) Or
('straight' IF straight) Or
('flush' IF flush) Or
('full-house' IF (two * three)) Or
('four-of-a-kind' IF four) Or
('straight-flush' IF (straight * flush)) Or
('five-of-a-kind' IF five)
)
Hands=: <@deb;._2 {{)n
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♣
}}
Note that * acts as "logical and" on logical values (if you need to deal with boolean values in the original sense - which were not constrained to logical values - you should use *. instead of * to achieve boolean multiplication, but that's not needed here).
Output for rateHand@> Hands
:
2♥ 2♦ 2♣ k♣ q♦ three-of-a-kind 2♥ 5♥ 7♦ 8♣ 9♠ high-card a♥ 2♦ 3♣ 4♣ 5♦ high-card 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
Output for extra-credit examples
joker 2♦ 2♠ k♠ q♦ three-of-a-kind joker 5♥ 7♦ 8♠ 9♦ straight joker 2♦ 3♠ 4♠ 5♠ straight joker 3♥ 2♦ 3♠ 3♦ four-of-a-kind joker 7♥ 2♦ 3♠ 3♦ three-of-a-kind joker 7♥ 7♦ 7♠ 7♣ five-of-a-kind joker j♥ q♥ k♥ a♥ straight-flush joker 4♣ k♣ 5♦ 10♠ one-pair joker k♣ 7♣ 6♣ 4♣ flush joker 2♦ joker 4♠ 5♠ straight joker q♦ joker a♠ 10♠ straight joker q♦ joker a♦ 10♦ straight-flush joker 2♦ 2♠ joker q♦ four-of-a-kind
Java
This code does not qualify for extra credit. Although it supports wildcards, it does not allow for duplicates.
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
public class PokerHandAnalyzer {
final static String faces = "AKQJT98765432";
final static String suits = "HDSC";
final static String[] deck = buildDeck();
public static void main(String[] args) {
System.out.println("Regular hands:\n");
for (String input : new String[]{"2H 2D 2S KS QD",
"2H 5H 7D 8S 9D",
"AH 2D 3S 4S 5S",
"2H 3H 2D 3S 3D",
"2H 7H 2D 3S 3D",
"2H 7H 7D 7S 7C",
"TH JH QH KH AH",
"4H 4C KC 5D TC",
"QC TC 7C 6C 4C",
"QC TC 7C 7C TD"}) {
System.out.println(analyzeHand(input.split(" ")));
}
System.out.println("\nHands with wildcards:\n");
for (String input : new String[]{"2H 2D 2S KS WW",
"2H 5H 7D 8S WW",
"AH 2D 3S 4S WW",
"2H 3H 2D 3S WW",
"2H 7H 2D 3S WW",
"2H 7H 7D WW WW",
"TH JH QH WW WW",
"4H 4C KC WW WW",
"QC TC 7C WW WW",
"QC TC 7H WW WW"}) {
System.out.println(analyzeHandWithWildcards(input.split(" ")));
}
}
private static Score analyzeHand(final String[] hand) {
if (hand.length != 5)
return new Score("invalid hand: wrong number of cards", -1, hand);
if (new HashSet<>(Arrays.asList(hand)).size() != hand.length)
return new Score("invalid hand: duplicates", -1, hand);
int[] faceCount = new int[faces.length()];
long straight = 0, flush = 0;
for (String card : hand) {
int face = faces.indexOf(card.charAt(0));
if (face == -1)
return new Score("invalid hand: non existing face", -1, hand);
straight |= (1 << face);
faceCount[face]++;
if (suits.indexOf(card.charAt(1)) == -1)
return new Score("invalid hand: non-existing suit", -1, hand);
flush |= (1 << card.charAt(1));
}
// shift the bit pattern to the right as far as possible
while (straight % 2 == 0)
straight >>= 1;
// straight is 00011111; A-2-3-4-5 is 1111000000001
boolean hasStraight = straight == 0b11111 || straight == 0b1111000000001;
// unsets right-most 1-bit, which may be the only one set
boolean hasFlush = (flush & (flush - 1)) == 0;
if (hasStraight && hasFlush)
return new Score("straight-flush", 9, hand);
int total = 0;
for (int count : faceCount) {
if (count == 4)
return new Score("four-of-a-kind", 8, hand);
if (count == 3)
total += 3;
else if (count == 2)
total += 2;
}
if (total == 5)
return new Score("full-house", 7, hand);
if (hasFlush)
return new Score("flush", 6, hand);
if (hasStraight)
return new Score("straight", 5, hand);
if (total == 3)
return new Score("three-of-a-kind", 4, hand);
if (total == 4)
return new Score("two-pair", 3, hand);
if (total == 2)
return new Score("one-pair", 2, hand);
return new Score("high-card", 1, hand);
}
private static WildScore analyzeHandWithWildcards(String[] hand) {
if (Collections.frequency(Arrays.asList(hand), "WW") > 2)
throw new IllegalArgumentException("too many wildcards");
return new WildScore(analyzeHandWithWildcardsR(hand, null), hand.clone());
}
private static Score analyzeHandWithWildcardsR(String[] hand,
Score best) {
for (int i = 0; i < hand.length; i++) {
if (hand[i].equals("WW")) {
for (String card : deck) {
if (!Arrays.asList(hand).contains(card)) {
hand[i] = card;
best = analyzeHandWithWildcardsR(hand, best);
}
}
hand[i] = "WW";
break;
}
}
Score result = analyzeHand(hand);
if (best == null || result.weight > best.weight)
best = result;
return best;
}
private static String[] buildDeck() {
String[] dck = new String[suits.length() * faces.length()];
int i = 0;
for (char s : suits.toCharArray()) {
for (char f : faces.toCharArray()) {
dck[i] = "" + f + s;
i++;
}
}
return dck;
}
private static class Score {
final int weight;
final String name;
final String[] hand;
Score(String n, int w, String[] h) {
weight = w;
name = n;
hand = h != null ? h.clone() : h;
}
@Override
public String toString() {
return Arrays.toString(hand) + " " + name;
}
}
private static class WildScore {
final String[] wild;
final Score score;
WildScore(Score s, String[] w) {
score = s;
wild = w;
}
@Override
public String toString() {
return String.format("%s%n%s%n", Arrays.toString(wild),
score.toString());
}
}
}
- Output:
Regular hands: [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 [QC, TC, 7C, 7C, TD] invalid hand: duplicates Hands with wildcards: [2H, 2D, 2S, KS, WW] [2H, 2D, 2S, KS, 2C] four-of-a-kind [2H, 5H, 7D, 8S, WW] [2H, 5H, 7D, 8S, 8H] one-pair [AH, 2D, 3S, 4S, WW] [AH, 2D, 3S, 4S, 5H] straight [2H, 3H, 2D, 3S, WW] [2H, 3H, 2D, 3S, 3D] full-house [2H, 7H, 2D, 3S, WW] [2H, 7H, 2D, 3S, 2S] three-of-a-kind [2H, 7H, 7D, WW, WW] [2H, 7H, 7D, 7S, 7C] four-of-a-kind [TH, JH, QH, WW, WW] [TH, JH, QH, AH, KH] straight-flush [4H, 4C, KC, WW, WW] [4H, 4C, KC, 4D, 4S] four-of-a-kind [QC, TC, 7C, WW, WW] [QC, TC, 7C, AC, KC] flush [QC, TC, 7H, WW, WW] [QC, TC, 7H, QH, QD] three-of-a-kind
JavaScript
const FACES = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'j', 'q', 'k', 'a'];
const SUITS = ['♥', '♦', '♣', '♠'];
function analyzeHand(hand){
let cards = hand.split(' ').filter(x => x !== 'joker');
let jokers = hand.split(' ').length - cards.length;
let faces = cards.map( card => FACES.indexOf(card.slice(0,-1)) );
let suits = cards.map( card => SUITS.indexOf(card.slice(-1)) );
if( cards.some( (card, i, self) => i !== self.indexOf(card) ) || faces.some(face => face === -1) || suits.some(suit => suit === -1) )
return 'invalid';
let flush = suits.every(suit => suit === suits[0]);
let groups = FACES.map( (face,i) => faces.filter(j => i === j).length).sort( (x, y) => y - x );
let shifted = faces.map(x => (x + 1) % 13);
let distance = Math.min( Math.max(...faces) - Math.min(...faces), Math.max(...shifted) - Math.min(...shifted));
let straight = groups[0] === 1 && distance < 5;
groups[0] += jokers;
if (groups[0] === 5) return 'five-of-a-kind'
else if (straight && flush) return 'straight-flush'
else if (groups[0] === 4) return 'four-of-a-kind'
else if (groups[0] === 3 && groups[1] === 2) return 'full-house'
else if (flush) return 'flush'
else if (straight) return 'straight'
else if (groups[0] === 3) return 'three-of-a-kind'
else if (groups[0] === 2 && groups[1] === 2) return 'two-pair'
else if (groups[0] === 2) return 'one-pair'
else return 'high-card';
}
Demonstrating:
let testHands = [
"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♣",
"joker 4♣ k♣ 5♦ 10♠",
"joker 2♦ 2♠ k♠ q♦",
"joker 3♥ 2♦ 3♠ 3♦",
"joker 7♥ 7♦ 7♠ 7♣",
"joker 2♦ joker 4♠ 5♠",
"joker 2♠ joker a♠ 10♠",
"joker q♦ joker a♦ 10♦"
];
for(hand of testHands) console.log(hand + ": " + analyzeHand(hand));
- Output:
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 joker 4♣ k♣ 5♦ 10♠: one-pair joker 2♦ 2♠ k♠ q♦: three-of-a-kind joker 3♥ 2♦ 3♠ 3♦: four-of-a-kind joker 7♥ 7♦ 7♠ 7♣: five-of-a-kind joker 2♦ joker 4♠ 5♠: straight joker 2♠ joker a♠ 10♠: flush joker q♦ joker a♦ 10♦: straight-flush
Julia
sorteddeck = [string(r) * s for s in "♣♦♥♠", r in "23456789TJQKA"]
cardlessthan(card1, card2) = indexin(x, sorteddeck)[1] < indexin(y, sorteddeck)[1]
decksort(d) = sort(d, lt=cardlessthan)
highestrank(d) = string(highestcard(d)[1])
function hasduplicate(d)
s = sort(d)
for i in 1:length(s)-1
if s[i] == s[i+1]
return true
end
end
false
end
invalid(d) = !all(x -> x in sorteddeck, d) || hasduplicate(d)
function countranks(d)
ranks = Dict()
for c in d
r = string(c[1])
if !haskey(ranks, r)
ranks[r] = 1
else
ranks[r] += 1
end
end
ranks
end
function countsuits(d)
suits = Dict()
for c in d
s = string(c[2])
if !haskey(suits, s)
suits[s] = 1
else
suits[s] += 1
end
end
suits
end
const rankmodifiers = Dict("A" => 130, "K" => 120, "Q" => 110, "J" => 100, "T" => 90,
"9" => 80, "8" => 70, "7" => 60, "6" => 50, "5" => 40,
"4" => 30, "3" => 20, "2" => 10)
rank(card) = rankmodifiers[string(card[1])]
const suitmodifiers = Dict("♠" => 4, "♥" => 3, "♦" => 2, "♣" => 1)
suitrank(card) = suitmodifiers[string(card[2])]
function isstraight(ranksdict)
v = collect(values(ranksdict))
if maximum(v) != 1
return false
else
s = sort(map(x->rankmodifiers[x], collect(keys(ranksdict))))
if s == [10, 20, 30, 40, 130] # aces low straight
return true
else
for i in 1:length(s)-1
if abs(s[i] - s[i+1]) > 10
return false
end
end
end
end
true
end
highestsuit(suitsdict) = sort(collect(keys(suitsdict)), lt=(x,y)->suitsdict[x] < suitsdict[y])[end]
isflush(suitsdict) = length(collect(values(suitsdict))) == 1
isstraightflush(ranks, suits) = isstraight(ranks) && isflush(suits)
isfourofakind(ranksdict) = maximum(values(ranksdict)) == 4 ? true : false
isfullhouse(ranksdict) = sort(collect(values(ranksdict))) == [2, 3]
isthreeofakind(ranksdict) = maximum(values(ranksdict)) == 3 && !isfullhouse(ranksdict) ? true : false
istwopair(ranksdict) = sort(collect(values(ranksdict)))[end-1: end] == [2,2]
isonepair(ranksdict) = sort(collect(values(ranksdict)))[end-1: end] == [1,2]
ishighcard(ranks, suits) = maximum(values(ranks)) == 1 && !isflush(suits) && !isstraight(ranks)
function scorehand(d)
suits = countsuits(d)
ranks = countranks(d)
if invalid(d)
return "invalid"
end
if isstraightflush(ranks, suits) return "straight-flush"
elseif isfourofakind(ranks) return "four-of-a-kind"
elseif isfullhouse(ranks) return "full-house"
elseif isflush(suits) return "flush"
elseif isstraight(ranks) return "straight"
elseif isthreeofakind(ranks) return "three-of-a-kind"
elseif istwopair(ranks) return "two-pair"
elseif isonepair(ranks) return "one-pair"
elseif ishighcard(ranks, suits) return "high-card"
end
end
const 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♠"],
["T♥", "J♥", "Q♥", "K♥", "A♥"], ["4♥", "4♠", "K♠", "5♦", "T♠"],
["Q♣", "T♣", "7♣", "6♣", "4♣"]]
for hand in hands
println("Hand $hand is a ", scorehand(hand), " hand.")
end
- Output:
Hand ["2♥", "2♦", "2♣", "K♣", "Q♦"] is a three-of-a-kind hand. Hand ["2♥", "5♥", "7♦", "8♣", "9♠"] is a high-card hand. Hand ["A♥", "2♦", "3♣", "4♣", "5♦"] is a straight hand. Hand ["2♥", "3♥", "2♦", "3♣", "3♦"] is a full-house hand. Hand ["2♥", "7♥", "2♦", "3♣", "3♦"] is a two-pair hand. Hand ["2♥", "7♥", "7♦", "7♣", "7♠"] is a four-of-a-kind hand. Hand ["T♥", "J♥", "Q♥", "K♥", "A♥"] is a straight-flush hand. Hand ["4♥", "4♠", "K♠", "5♦", "T♠"] is a one-pair hand. Hand ["Q♣", "T♣", "7♣", "6♣", "4♣"] is a flush hand.
Kotlin
Basic Version
// version 1.1.2
class Card(val face: Int, val suit: Char)
const val FACES = "23456789tjqka"
const val SUITS = "shdc"
fun isStraight(cards: List<Card>): Boolean {
val sorted = cards.sortedBy { it.face }
if (sorted[0].face + 4 == sorted[4].face) return true
if (sorted[4].face == 14 && sorted[0].face == 2 && sorted[3].face == 5) return true
return false
}
fun isFlush(cards: List<Card>): Boolean {
val suit = cards[0].suit
if (cards.drop(1).all { it.suit == suit }) return true
return false
}
fun analyzeHand(hand: String): String {
val h = hand.toLowerCase()
val split = h.split(' ').filterNot { it == "" }.distinct()
if (split.size != 5) return "invalid"
val cards = mutableListOf<Card>()
for (s in split) {
if (s.length != 2) return "invalid"
val fIndex = FACES.indexOf(s[0])
if (fIndex == -1) return "invalid"
val sIndex = SUITS.indexOf(s[1])
if (sIndex == -1) return "invalid"
cards.add(Card(fIndex + 2, s[1]))
}
val groups = cards.groupBy { it.face }
when (groups.size) {
2 -> {
if (groups.any { it.value.size == 4 }) return "four-of-a-kind"
return "full-house"
}
3 -> {
if (groups.any { it.value.size == 3 }) return "three-of-a-kind"
return "two-pair"
}
4 -> return "one-pair"
else -> {
val flush = isFlush(cards)
val straight = isStraight(cards)
when {
flush && straight -> return "straight-flush"
flush -> return "flush"
straight -> return "straight"
else -> return "high-card"
}
}
}
}
fun main(args: Array<String>) {
val hands = arrayOf(
"2h 2d 2c kc qd",
"2h 5h 7d 8c 9s",
"ah 2d 3c 4c 5d",
"2h 3h 2d 3c 3d",
"2h 7h 2d 3c 3d",
"2h 7h 7d 7c 7s",
"th jh qh kh ah",
"4h 4s ks 5d ts",
"qc tc 7c 6c 4c",
"ah ah 7c 6c 4c"
)
for (hand in hands) {
println("$hand: ${analyzeHand(hand)}")
}
}
- Output:
2h 2d 2c kc qd: three-of-a-kind 2h 5h 7d 8c 9s: high-card ah 2d 3c 4c 5d: straight 2h 3h 2d 3c 3d: full-house 2h 7h 2d 3c 3d: two-pair 2h 7h 7d 7c 7s: four-of-a-kind th jh qh kh ah: straight-flush 4h 4s ks 5d ts: one-pair qc tc 7c 6c 4c: flush ah ah 7c 6c 4c: invalid
Extra Credit Version
// version 1.1.2
class Card(val face: Int, val suit: Char)
fun isStraight(cards: List<Card>, jokers: Int): Boolean {
val sorted = cards.sortedBy { it.face }
when (jokers) {
0 -> {
if (sorted[0].face + 4 == sorted[4].face) return true
if (sorted[4].face == 14 && sorted[3].face == 5) return true
return false
}
1 -> {
if (sorted[0].face + 3 == sorted[3].face) return true
if (sorted[0].face + 4 == sorted[3].face) return true
if (sorted[3].face == 14 && sorted[2].face == 4) return true
if (sorted[3].face == 14 && sorted[2].face == 5) return true
return false
}
else -> {
if (sorted[0].face + 2 == sorted[2].face) return true
if (sorted[0].face + 3 == sorted[2].face) return true
if (sorted[0].face + 4 == sorted[2].face) return true
if (sorted[2].face == 14 && sorted[1].face == 3) return true
if (sorted[2].face == 14 && sorted[1].face == 4) return true
if (sorted[2].face == 14 && sorted[1].face == 5) return true
return false
}
}
}
fun isFlush(cards: List<Card>): Boolean {
val sorted = cards.sortedBy { it.face }
val suit = sorted[0].suit
if (sorted.drop(1).all { it.suit == suit || it.suit == 'j' }) return true
return false
}
fun analyzeHand(hand: String): String {
val split = hand.split(' ').filterNot { it == "" }.distinct()
if (split.size != 5) return "invalid"
val cards = mutableListOf<Card>()
var jokers = 0
for (s in split) {
if (s.length != 2) return "invalid"
val cp = s.codePointAt(0)
val card = when (cp) {
0x1f0a1 -> Card(14, 's')
0x1f0b1 -> Card(14, 'h')
0x1f0c1 -> Card(14, 'd')
0x1f0d1 -> Card(14, 'c')
0x1f0cf -> { jokers++; Card(15, 'j') } // black joker
0x1f0df -> { jokers++; Card(16, 'j') } // white joker
in 0x1f0a2..0x1f0ab -> Card(cp - 0x1f0a0, 's')
in 0x1f0ad..0x1f0ae -> Card(cp - 0x1f0a1, 's')
in 0x1f0b2..0x1f0bb -> Card(cp - 0x1f0b0, 'h')
in 0x1f0bd..0x1f0be -> Card(cp - 0x1f0b1, 'h')
in 0x1f0c2..0x1f0cb -> Card(cp - 0x1f0c0, 'd')
in 0x1f0cd..0x1f0ce -> Card(cp - 0x1f0c1, 'd')
in 0x1f0d2..0x1f0db -> Card(cp - 0x1f0d0, 'c')
in 0x1f0dd..0x1f0de -> Card(cp - 0x1f0d1, 'c')
else -> Card(0, 'j') // invalid
}
if (card.face == 0) return "invalid"
cards.add(card)
}
val groups = cards.groupBy { it.face }
when (groups.size) {
2 -> {
if (groups.any { it.value.size == 4 }) {
return when (jokers) {
0 -> "four-of-a-kind"
else -> "five-of-a-kind"
}
}
return "full-house"
}
3 -> {
if (groups.any { it.value.size == 3 }) {
return when (jokers) {
0 -> "three-of-a-kind"
1 -> "four-of-a-kind"
else -> "five-of-a-kind"
}
}
return if (jokers == 0) "two-pair" else "full-house"
}
4 -> return when (jokers) {
0 -> "one-pair"
1 -> "three-of-a-kind"
else -> "four-of-a-kind"
}
else -> {
val flush = isFlush(cards)
val straight = isStraight(cards,jokers)
when {
flush && straight -> return "straight-flush"
flush -> return "flush"
straight -> return "straight"
else -> return if (jokers == 0) "high-card" else "one-pair"
}
}
}
}
fun main(args: Array<String>) {
val hands = arrayOf(
"🃏 🃂 🂢 🂮 🃍",
"🃏 🂵 🃇 🂨 🃉",
"🃏 🃂 🂣 🂤 🂥",
"🃏 🂳 🃂 🂣 🃃",
"🃏 🂷 🃂 🂣 🃃",
"🃏 🂷 🃇 🂧 🃗",
"🃏 🂻 🂽 🂾 🂱",
"🃏 🃔 🃞 🃅 🂪",
"🃏 🃞 🃗 🃖 🃔",
"🃏 🃂 🃟 🂤 🂥",
"🃏 🃍 🃟 🂡 🂪",
"🃏 🃍 🃟 🃁 🃊",
"🃏 🃂 🂢 🃟 🃍",
"🃏 🃂 🂢 🃍 🃍",
"🃂 🃞 🃍 🃁 🃊"
)
for (hand in hands) {
println("$hand : ${analyzeHand(hand)}")
}
}
- Output:
🃏 🃂 🂢 🂮 🃍 : three-of-a-kind 🃏 🂵 🃇 🂨 🃉 : straight 🃏 🃂 🂣 🂤 🂥 : straight 🃏 🂳 🃂 🂣 🃃 : four-of-a-kind 🃏 🂷 🃂 🂣 🃃 : three-of-a-kind 🃏 🂷 🃇 🂧 🃗 : five-of-a-kind 🃏 🂻 🂽 🂾 🂱 : straight-flush 🃏 🃔 🃞 🃅 🂪 : one-pair 🃏 🃞 🃗 🃖 🃔 : flush 🃏 🃂 🃟 🂤 🂥 : straight 🃏 🃍 🃟 🂡 🂪 : straight 🃏 🃍 🃟 🃁 🃊 : straight-flush 🃏 🃂 🂢 🃟 🃍 : four-of-a-kind 🃏 🃂 🂢 🃍 🃍 : invalid 🃂 🃞 🃍 🃁 🃊 : high-card
Lua
-- Check whether t is a valid poker hand
function valid (t)
if #t ~= 5 then return false end
for k, v in pairs(t) do
for key, card in pairs(t) do
if v.value == card.value and
v.suit == card.suit and
k ~= key
then
return false
end
end
end
return true
end
-- Return numerical value of a single card
function cardValue (card)
local val = card:sub(1, -2)
local n = tonumber(val)
if n then return n end
if val == "j" then return 11 end
if val == "q" then return 12 end
if val == "k" then return 13 end
if val == "a" then return 1 end
error("Invalid card value: " .. val)
end
-- Detect whether hand t is a straight
function straight (t)
table.sort(t, function (a, b) return a.value < b.value end)
local ace, thisValue, lastValue = false
for i = 2, #t do
thisValue, lastValue = t[i].value, t[i-1].value
if lastValue == 1 then ace = i - 1 end
if thisValue ~= lastValue + 1 then
if ace then
t[ace].value = 14
return straight(t)
else
return false
end
end
end
return true
end
-- Detect whether hand t is a flush
function isFlush (t)
local suit = t[1].suit
for card = 2, #t do
if t[card].suit ~= suit then return false end
end
return true
end
-- Return a table of the count of each card value in hand t
function countValues (t)
local countTab, maxCount = {}, 0
for k, v in pairs(t) do
if countTab[v.value] then
countTab[v.value] = countTab[v.value] + 1
else
countTab[v.value] = 1
end
end
return countTab
end
-- Find the highest value in t
function highestCount (t)
local maxCount = 0
for k, v in pairs(t) do
if v > maxCount then maxCount = v end
end
return maxCount
end
-- Detect full-house and two-pair using the value counts in t
function twoTypes (t)
local threes, twos = 0, 0
for k, v in pairs(t) do
if v == 3 then threes = threes + 1 end
if v == 2 then twos = twos + 1 end
end
return threes, twos
end
-- Return the rank of a poker hand represented as a string
function rank (cards)
local hand = {}
for card in cards:gmatch("%S+") do
table.insert(hand, {value = cardValue(card), suit = card:sub(-1, -1)})
end
if not valid(hand) then return "invalid" end
local st, fl = straight(hand), isFlush(hand)
if st and fl then return "straight-flush" end
local valCount = countValues(hand)
local highCount = highestCount(valCount)
if highCount == 4 then return "four-of-a-kind" end
local n3, n2 = twoTypes(valCount)
if n3 == 1 and n2 == 1 then return "full-house" end
if fl then return "flush" end
if st then return "straight" end
if highCount == 3 then return "three-of-a-kind" end
if n3 == 0 and n2 == 2 then return "two-pair" end
if highCount == 2 then return "one-pair" end
return "high-card"
end
-- Main procedure
local testCases = {
"2h 2d 2c kc qd", -- three-of-a-kind
"2h 5h 7d 8c 9s", -- high-card
"ah 2d 3c 4c 5d", -- straight
"2h 3h 2d 3c 3d", -- full-house
"2h 7h 2d 3c 3d", -- two-pair
"2h 7h 7d 7c 7s", -- four-of-a-kind
"10h jh qh kh ah",-- straight-flush
"4h 4s ks 5d 10s",-- one-pair
"qc 10c 7c 6c 4c" -- flush
}
for _, case in pairs(testCases) do print(case, ": " .. rank(case)) end
- Output:
2h 2d 2c kc qd : three-of-a-kind 2h 5h 7d 8c 9s : high-card ah 2d 3c 4c 5d : straight 2h 3h 2d 3c 3d : full-house 2h 7h 2d 3c 3d : two-pair 2h 7h 7d 7c 7s : four-of-a-kind 10h jh qh kh ah : straight-flush 4h 4s ks 5d 10s : one-pair qc 10c 7c 6c 4c : flush
Nim
import algorithm, sequtils, strutils, tables, unicode
type
Suit* = enum ♠, ♥, ♦, ♣
Face* {.pure.} = enum
Ace = (1, "a")
Two = (2, "2")
Three = (3, "3")
Four = (4, "4")
Five = (5, "5")
Six = (6, "6")
Seven = (7, "7")
Eight = (8, "8")
Nine = (9, "9")
Ten = (10, "10")
Jack = (11, "j")
Queen = (12, "q")
King = (13, "k")
Card* = tuple[face: Face; suit: Suit]
Hand* = array[5, Card]
HandValue {.pure.} = enum
Invalid = "invalid"
StraightFlush = "straight-flush"
FourOfAKind = "four-of-a-kind"
FullHouse = "full-house"
Flush = "flush"
Straight = "straight"
ThreeOfAKind = "three-of-a-kind"
TwoPair = "two-pair"
OnePair = "one-pair"
HighCard = "high-card"
CardError = object of ValueError
proc toCard(cardStr: string): Card =
## Convert a card string to a Card.
var runes = cardStr.toRunes
let suitStr = $(runes.pop()) # Extract the suit.
let faceStr = $runes # Take what’s left as the face.
try:
result.face = parseEnum[Face](faceStr)
except ValueError:
raise newException(CardError, "wrong face: " & faceStr)
try:
result.suit = parseEnum[Suit](suitStr)
except ValueError:
raise newException(CardError, "wrong suit: " & suitStr)
proc value(hand: openArray[Card]): HandValue =
## Return the value of a hand.
doAssert hand.len == 5, "Hand must have five cards."
var
cards: seq[Card] # The cards.
faces: CountTable[Face] # Count faces.
suits: CountTable[Suit] # Count suits.
for card in hand:
if card in cards: return Invalid # Duplicate card.
cards.add card
faces.inc card.face
suits.inc card.suit
faces.sort() # Greatest counts first.
suits.sort() # Greatest counts first.
cards.sort() # Smallest faces first.
# Check faces.
for face, count in faces:
case count
of 4:
return FourOfAKind
of 3:
result = ThreeOfAKind
of 2:
if result == ThreeOfAKind: return FullHouse
if result == OnePair: return TwoPair
result = OnePair
else:
if result != Invalid: return
# Search straight.
result = Straight
let start = if cards[0].face == Ace and cards[4].face == King: 2 else: 1
for n in start..4:
if cards[n].face != succ(cards[n - 1].face):
result = HighCard # No straight.
break
# Check suits.
if suits.len == 1: # A single suit.
result = if result == Straight: StraightFlush else: Flush
proc `$`(card: Card): string =
## Return the representation of a card.
var val = 0x1F0A0 + ord(card.suit) * 0x10 + ord(card.face)
if card.face >= Queen: inc val # Skip Knight.
result = $Rune(val)
when isMainModule:
const HandStrings = ["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♣",
"4♥ 4♣ 4♥ 4♠ 4♦"]
for handString in HandStrings:
let hand = handString.split(' ').map(toCard)
echo hand.map(`$`).join(" "), " → ", hand.value
- Output:
🂲 🃂 🃒 🃞 🃍 → three-of-a-kind 🂲 🂵 🃇 🃘 🂩 → high-card 🂱 🃂 🃓 🃔 🃅 → straight 🂲 🂳 🃂 🃓 🃃 → full-house 🂲 🂷 🃂 🃓 🃃 → two-pair 🂲 🂷 🃇 🃗 🂧 → four-of-a-kind 🂺 🂻 🂽 🂾 🂱 → straight-flush 🂴 🂤 🂮 🃅 🂪 → one-pair 🃝 🃚 🃗 🃖 🃔 → flush 🂴 🃔 🂴 🂤 🃄 → invalid
Perl
I dont like jokers. Instead I decided to give hands proper names. For example, "Kings full of Tens" rather than just "full-house".
use strict;
use warnings;
use utf8;
use feature 'say';
use open qw<:encoding(utf-8) :std>;
package Hand {
sub describe {
my $str = pop;
my $hand = init($str);
return "$str: INVALID" if !$hand;
return analyze($hand);
}
sub init {
(my $str = lc shift) =~ tr/234567891jqka♥♦♣♠//cd;
return if $str !~ m/\A (?: [234567891jqka] [♥♦♣♠] ){5} \z/x;
for (my ($i, $cnt) = (0, 0); $i < 10; $i += 2, $cnt = 0) {
my $try = substr $str, $i, 2;
++$cnt while $str =~ m/$try/g;
return if $cnt > 1;
}
my $suits = $str =~ tr/234567891jqka//dr;
my $ranks = $str =~ tr/♥♦♣♠//dr;
return {
hand => $str,
suits => $suits,
ranks => $ranks,
};
}
sub analyze {
my $hand = shift;
my @ranks = split //, $hand->{ranks};
my %cards;
for (@ranks) {
$_ = 10, next if $_ eq '1';
$_ = 11, next if $_ eq 'j';
$_ = 12, next if $_ eq 'q';
$_ = 13, next if $_ eq 'k';
$_ = 14, next if $_ eq 'a';
} continue {
++$cards{ $_ };
}
my $kicker = 0;
my (@pairs, $set, $quads, $straight, $flush);
while (my ($card, $count) = each %cards) {
if ($count == 1) {
$kicker = $card if $kicker < $card;
}
elsif ($count == 2) {
push @pairs, $card;
}
elsif ($count == 3) {
$set = $card;
}
elsif ($count == 4) {
$quads = $card;
}
else {
die "Five of a kind? Cheater!\n";
}
}
$flush = 1 if $hand->{suits} =~ m/\A (.) \1 {4}/x;
$straight = check_straight(@ranks);
return get_high($kicker, \@pairs, $set, $quads, $straight, $flush,);
}
sub check_straight {
my $sequence = join ' ', sort { $a <=> $b } @_;
return 1 if index('2 3 4 5 6 7 8 9 10 11 12 13 14', $sequence) != -1;
return 'wheel' if index('2 3 4 5 14 6 7 8 9 10 11 12 13', $sequence) == 0;
return undef;
}
sub get_high {
my ($kicker, $pairs, $set, $quads, $straight, $flush) = @_;
$kicker = to_s($kicker, 's');
return 'straight-flush: Royal Flush!'
if $straight && $flush && $kicker eq 'Ace' && $straight ne 'wheel';
return "straight-flush: Steel Wheel!"
if $straight && $flush && $straight eq 'wheel';
return "straight-flush: $kicker high"
if $straight && $flush;
return 'four-of-a-kind: '. to_s($quads, 'p')
if $quads;
return 'full-house: '. to_s($set, 'p') .' full of '. to_s($pairs->[0], 'p')
if $set && @$pairs;
return "flush: $kicker high"
if $flush;
return 'straight: Wheel!'
if $straight && $straight eq 'wheel';
return "straight: $kicker high"
if $straight;
return 'three-of-a-kind: '. to_s($set, 'p')
if $set;
return 'two-pairs: '. to_s($pairs->[0], 'p') .' and '. to_s($pairs->[1], 'p')
if @$pairs == 2;
return 'one-pair: '. to_s($pairs->[0], 'p')
if @$pairs == 1;
return "high-card: $kicker";
}
my %to_str = (
2 => 'Two', 3 => 'Three', 4 => 'Four', 5 => 'Five', 6 => 'Six',
7 => 'Seven', 8 => 'Eight', 9 => 'Nine', 10 => 'Ten', 11 => 'Jack',
12 => 'Queen', 13 => 'King', 14 => 'Ace',
);
my %to_str_diffs = (2 => 'Deuces', 6 => 'Sixes',);
sub to_s {
my ($num, $verb) = @_;
# verb is 'singular' or 'plural' (or 's' or 'p')
if ($verb =~ m/\A p/xi) {
return $to_str_diffs{ $num } if $to_str_diffs{ $num };
return $to_str{ $num } .'s';
}
return $to_str{ $num };
}
}
my @cards = (
'10♥ j♥ q♥ k♥ a♥',
'2♥ 3♥ 4♥ 5♥ a♥',
'2♥ 2♣ 2♦ 3♣ 2♠',
'10♥ K♥ K♦ K♣ 10♦',
'q♣ 10♣ 7♣ 6♣ 3♣',
'5♣ 10♣ 7♣ 6♣ 4♣',
'9♥ 10♥ q♥ k♥ j♣',
'a♥ a♣ 3♣ 4♣ 5♦',
'2♥ 2♦ 2♣ k♣ q♦',
'6♥ 7♥ 6♦ j♣ j♦',
'2♥ 6♥ 2♦ 3♣ 3♦',
'7♥ 7♠ k♠ 3♦ 10♠',
'4♥ 4♠ k♠ 2♦ 10♠',
'2♥ 5♥ j♦ 8♣ 9♠',
'2♥ 5♥ 7♦ 8♣ 9♠',
'a♥ a♥ 3♣ 4♣ 5♦', # INVALID: duplicate aces
);
say Hand::describe($_) for @cards;
- Output:
straight-flush: Royal Flush! straight-flush: Steel Wheel! four-of-a-kind: Deuces full-house: Kings full of Tens flush: Queen high flush: Ten high straight: King high one-pair: Aces three-of-a-kind: Deuces two-pairs: Sixes and Jacks two-pairs: Threes and Deuces one-pair: Sevens one-pair: Fours high-card: Jack high-card: Nine a♥ a♥ 3♣ 4♣ 5♦: INVALID
Phix
Woke up this morning with a neat idea for detecting straights, though jokers messed it up a bit.
Uses an ad-hoc ranking system/tie breaker, not recommended for use in tournaments! Displays hands best-first.
Note: I have left a copy of this in demo\HelloUTF8.exw to prove it works, but non-ascii on a Windows
console is not Phix's forte.
For an(other) example of using the unicode card characters see Playing_cards#Phix
You can run this online here.
with javascript_semantics function poker(string hand) hand = substitute(hand,"10","t") sequence cards = split(hand) if length(cards)!=5 then return "invalid hand" end if sequence ranks = repeat(0,13), suits = repeat(0,4) integer jokers = 0 for i=1 to length(cards) do sequence ci = utf8_to_utf32(cards[i]) if ci="joker" then jokers += 1 if jokers>2 then return "invalid hand" end if else if length(ci)!=2 then return "invalid hand" end if integer rank = find(lower(ci[1]),"23456789tjqka") integer suit = find(ci[2],utf8_to_utf32("♥♣♦♠")) if rank=0 or suit=0 then return "invalid hand" end if ranks[rank] += 1 suits[suit] += 1 end if end for integer straight = match({1,1,1,1,1},ranks) if not straight then straight = sort(deep_copy(ranks))[$]=1 and match({0,0,0,0,0,0,0,0},ranks) end if integer _flush = (max(suits)+jokers = 5) integer _pairs = max(ranks)+jokers integer pair = find(2,ranks) integer full_house = _pairs=3 and pair and (jokers=0 or find(2,ranks,pair+1)) integer two_pair = find(2,ranks,pair+1) integer high_card = rfind(1,sq_ne(ranks,0))+1 if jokers and _pairs=jokers+1 then straight = 1 integer k = find(1,ranks), j = jokers for l=k to min(k+5-j,13) do if ranks[l]=0 then if j=0 then straight = 0 exit end if j -= 1 end if end for if straight and j then high_card = min(high_card+j,14) end if elsif straight and ranks[1]!=0 then high_card = find(0,ranks) end if if _pairs=5 then return {10,"five of a kind", find(5-jokers,ranks)+1} end if if straight and _flush then return {9,"straight flush", high_card} end if if _pairs=4 then return {8,"four of a kind", find(4-jokers,ranks)+1} end if if full_house then return {7,"full house", find(3-jokers,ranks)+1} end if if _flush then return {6,"flush", high_card} end if if straight then return {5,"straight", high_card} end if if _pairs=3 then return {4,"three of a kind", find(3-jokers,ranks)+1} end if if pair and two_pair then return {3,"two pair", two_pair+1} end if if pair then return {2,"one pair", pair+1} end if if jokers then return {2,"one pair", high_card} end if return {1,"high card",high_card} end function sequence hands = {{0,"2♥ 2♦ 2♣ k♣ q♦"}, {0,"2♥ 5♥ 7♦ 8♣ 9♠"}, {0,"a♥ 2♦ 3♣ 4♣ 5♦"}, {0,"2♥ 3♥ 2♦ 3♣ 3♦"}, {0,"2♥ 7♥ 2♦ 3♣ 3♦"}, {0,"2♥ 7♥ 7♦ 7♣ 7♠"}, {0,"10♥ j♥ q♥ k♥ a♥"}, {0,"4♥ 4♠ k♠ 5♦ 10♠"}, {0,"q♣ 10♣ 7♣ 6♣ 4♣"}, {0,"joker 2♦ 2♠ k♠ q♦"}, {0,"joker 5♥ 7♦ 8♠ 9♦"}, {0,"joker 2♦ 3♠ 4♠ 5♠"}, {0,"joker 3♥ 2♦ 3♠ 3♦"}, {0,"joker 7♥ 2♦ 3♠ 3♦"}, {0,"joker 7♥ 7♦ 7♠ 7♣"}, {0,"joker j♥ q♥ k♥ A♥"}, {0,"joker 4♣ k♣ 5♦ 10♠"}, {0,"joker k♣ 7♣ 6♣ 4♣"}, {0,"joker 2♦ joker 4♠ 5♠"}, {0,"joker Q♦ joker A♠ 10♠"}, {0,"joker Q♦ joker A♦ 10♦"}, {0,"joker 2♦ 2♠ joker q♦"}} for i=1 to length(hands) do hands[i][1] = poker(hands[i][2]) end for hands = reverse(sort(deep_copy(hands))) integer rank string hand,desc for i=1 to length(hands) do {{rank,desc},hand} = hands[i] printf(1,"%d. %s %s\n",{11-rank,hand,desc}) end for
- Output:
1. joker 7♥ 7♦ 7♠ 7♣ five of a kind 2. joker j♥ q♥ k♥ A♥ straight flush 2. joker Q♦ joker A♦ 10♦ straight flush 2. 10♥ j♥ q♥ k♥ a♥ straight flush 3. 2♥ 7♥ 7♦ 7♣ 7♠ four of a kind 3. joker 3♥ 2♦ 3♠ 3♦ four of a kind 3. joker 2♦ 2♠ joker q♦ four of a kind 4. 2♥ 3♥ 2♦ 3♣ 3♦ full house 5. joker k♣ 7♣ 6♣ 4♣ flush 5. q♣ 10♣ 7♣ 6♣ 4♣ flush 6. joker Q♦ joker A♠ 10♠ straight 6. joker 5♥ 7♦ 8♠ 9♦ straight 6. joker 2♦ joker 4♠ 5♠ straight 6. joker 2♦ 3♠ 4♠ 5♠ straight 6. a♥ 2♦ 3♣ 4♣ 5♦ straight 7. joker 7♥ 2♦ 3♠ 3♦ three of a kind 7. joker 2♦ 2♠ k♠ q♦ three of a kind 7. 2♥ 2♦ 2♣ k♣ q♦ three of a kind 8. 2♥ 7♥ 2♦ 3♣ 3♦ two pair 9. joker 4♣ k♣ 5♦ 10♠ one pair 9. 4♥ 4♠ k♠ 5♦ 10♠ one pair 10. 2♥ 5♥ 7♦ 8♣ 9♠ high card
Picat
go =>
Hands = [
[[2,h], [7,h], [2,d], [3,c], [3,d]], % two-pair
[[2,h], [5,h], [7,d], [8,c], [9,s]], % high-card
[[a,h], [2,d], [3,c], [4,c], [5,d]], % straight
[[2,h], [3,h], [2,d], [3,c], [3,d]], % full-house
[[2,h], [7,h], [2,d], [3,c], [3,d]], % two-pair
[[2,h], [7,h], [7,d], [7,c], [7,s]], % four-of-a-kind
[[10,h],[j,h], [q,h], [k,h], [a,h]], % straight-flush
[[4,h], [4,s], [k,s], [5,d], [10,s]], % one-pair
[[q,c], [10,c],[7,c], [6,c], [4,c]], % flush
[[q,c], [q,d], [q,s], [6,c], [4,c]], % three-of-a-kind
[[q,c], [10,c], [7,c], [7,c], [4,c]], % invalid (duplicates)
[[q,c], [10,c], [7,c], [7,d]] % invalid (too short)
],
foreach(Hand in Hands)
print_hand(Hand),
analyse(Hand, H),
println(hand=H),
nl
end,
nl.
% Print the hand
print_hand(Hand) =>
println([ F.to_string() ++ S.to_string() : [F,S] in Hand]).
% Faces and suites
faces(Faces) => Faces = [a, 2, 3, 4, 5, 6, 7, 8, 9, 10, j, q, k].
faces_order1(Order1) =>
Order1 = new_map([a=1, 2=2, 3=3, 4=4, 5=5, 6=6, 7=7, 8=8, 9=9, 10=10, j=11, q=12, k=13]).
faces_order2(Order2) =>
Order2 = new_map([2=2, 3=3, 4=4, 5=5, 6=6, 7=7, 8=8, 9=9, 10=10, j=11, q=12, k=13, a=14]).
suites(Suites) => Suites = [h,d,c,s].
% Order of the hand
hand_order(HandOrder) =>
HandOrder = [straight_flush,
four_of_a_kind,
full_house,
flush,
straight,
three_of_a_kind,
two_pair,
one_pair,
high_card,
invalid].
% for the straight
in_order(List) =>
foreach(I in 2..List.length)
List[I] = List[I-1] + 1
end.
% Some validity tests first
analyse(Hand,Value) ?=>
(
Hand.remove_dups.length == 5,
faces(Faces),
foreach([F,_] in Hand)
member(F,Faces)
end,
suites(Suites),
foreach([_,S] in Hand)
member(S,Suites)
end,
analyse1(Hand,Value)
;
Value = invalid
).
% Identify the specific hands
% straight flush
analyse1(Hand,Value) ?=>
permutation(Hand,Hand1),
Hand1 = [ [_F1,S], [_F2,S], [_F3,S], [_F4,S], [_F5,S] ],
(
faces_order1(Order1),
in_order([Order1.get(F) : [F,S1] in Hand1])
;
faces_order2(Order2),
in_order([Order2.get(F) : [F,S1] in Hand1]),
println("Royal Straight Flush!")
),
Value=straight_flush.
% four of a kind
analyse1(Hand,Value) ?=>
faces(Faces),
member(A,Faces),
[1 : [F,_S] in Hand, F = A].length == 4,
Value=four_of_a_kind.
% full house
analyse1(Hand,Value) ?=>
permutation(Hand,Hand1),
Hand1 = [ [F1,_S1], [F1,_S2], [F2,_S3], [F2,_S4], [F2,_S5] ],
Value = full_house.
% flush
analyse1(Hand,Value) ?=>
permutation(Hand,Hand1),
Hand1 = [ [_,S], [_,S], [_,S], [_,S], [_,S] ],
Value = flush.
% straight
analyse1(Hand,Value) ?=>
permutation(Hand,Hand1),
(
faces_order1(Order1),
in_order([Order1.get(F) : [F,_S] in Hand1])
;
faces_order2(Order2),
in_order([Order2.get(F) : [F,_S] in Hand1])
),
Value = straight.
% three of a kind
analyse1(Hand,Value) ?=>
faces(Faces),
member(A,Faces),
[1 : [F,_S] in Hand, F = A].length == 3,
Value = three_of_a_kind.
% two pair
analyse1(Hand,Value) ?=>
permutation(Hand,Hand1),
Hand1 = [ [F1,_S1], [F1,_S2], [F2,_S3], [F2,_S4], [_F3,_S5] ],
Value = two_pair.
% one pair
analyse1(Hand,Value) ?=>
faces(Faces),
member(A,Faces),
[1 : [F,_S] in Hand, F = A].length == 2,
Value = one_pair.
% high card
analyse1(_Hand,Value) =>
Value = high_card.
- Output:
[2h,7h,2d,3c,3d] hand = two_pair [2h,5h,7d,8c,9s] hand = high_card [ah,2d,3c,4c,5d] hand = straight [2h,3h,2d,3c,3d] hand = full_house [2h,7h,2d,3c,3d] hand = two_pair [2h,7h,7d,7c,7s] hand = four_of_a_kind [10h,jh,qh,kh,ah] Royal Straight Flush! hand = straight_flush [4h,4s,ks,5d,10s] hand = one_pair [qc,10c,7c,6c,4c] hand = flush [qc,qd,qs,6c,4c] hand = three_of_a_kind [qc,10c,7c,7c,4c] hand = invalid [qc,10c,7c,7d] hand = invalid
For generating and checking random hands:
go2 =>
_ = random2(),
Hand = random_hand(5),
print_hand(Hand),
analyse(Hand, H),
println(hand=H),
nl.
% Get one element of list L
oneof(L) = L[random(1,L.len)].
% Get a random hand
random_hand(N) = Hand =>
faces(Faces),
suites(Suites),
M = new_map(),
while (M.keys().length < N)
M.put([oneof(Faces),oneof(Suites)],1)
end,
Hand = [C : C=_ in M].sort().
- Output:
[3d,5h,10c,10h,qs] hand = one_pair
PicoLisp
(rassoc) function in picolisp after 3.1.9.10.
(setq *Rank
'(("2" . 0) ("3" . 1) ("4" . 2)
("5" . 3) ("6" . 4) ("7" . 5)
("8" . 6) ("9" . 7) ("t" . 8)
("j" . 9) ("q" . 10) ("k" . 11)
("a" . 12) ) )
(de poker (Str)
(let (S NIL R NIL Seq NIL)
(for (L (chop Str) (cdr L) (cdddr L))
(accu 'R (cdr (assoc (car L) *Rank)) 1)
(accu 'S (cadr L) 1) )
(setq Seq
(make
(for (L (by car sort R) (cdr L) (cdr L))
(link (- (caar L) (caadr L))) ) ) )
(cond
((and
(= 5 (cdar S))
(or
(= (-1 -1 -1 -1) Seq)
(= (-1 -1 -1 -9) Seq) ) )
'straight-flush )
((rassoc 4 R) 'four-of-a-kind)
((and (rassoc 2 R) (rassoc 3 R)) 'full-house)
((= 5 (cdar S)) 'flush)
((or
(= (-1 -1 -1 -1) Seq)
(= (-1 -1 -1 -9) Seq) )
'straight )
((rassoc 3 R) 'three-of-a-kind)
((=
2
(cnt '((L) (= 2 (cdr L))) R) )
'two-pair )
((rassoc 2 R) 'pair)
(T 'high-card) ) ) )
Prolog
Not very efficient version.
:- initialization(main).
faces([a,k,q,j,10,9,8,7,6,5,4,3,2]).
face(F) :- faces(Fs), member(F,Fs).
suit(S) :- member(S, ['♥','♦','♣','♠']).
best_hand(Cards,H) :-
straight_flush(Cards,C) -> H = straight-flush(C)
; many_kind(Cards,F,4) -> H = four-of-a-kind(F)
; full_house(Cards,F1,F2) -> H = full-house(F1,F2)
; flush(Cards,S) -> H = flush(S)
; straight(Cards,F) -> H = straight(F)
; many_kind(Cards,F,3) -> H = three-of-a-kind(F)
; two_pair(Cards,F1,F2) -> H = two-pair(F1,F2)
; many_kind(Cards,F,2) -> H = one-pair(F)
; many_kind(Cards,F,1) -> H = high-card(F)
; H = invalid
.
straight_flush(Cards, c(F,S)) :- straight(Cards,F), flush(Cards,S).
full_house(Cards,F1,F2) :-
many_kind(Cards,F1,3), many_kind(Cards,F2,2), F1 \= F2.
flush(Cards,S) :- maplist(has_suit(S), Cards).
has_suit(S, c(_,S)).
straight(Cards,F) :-
select(c(F,_), Cards, Cs), pred_face(F,F1), straight(Cs,F1).
straight([],_).
pred_face(F,F1) :- F = 2 -> F1 = a ; faces(Fs), append(_, [F,F1|_], Fs).
two_pair(Cards,F1,F2) :-
many_kind(Cards,F1,2), many_kind(Cards,F2,2), F1 \= F2.
many_kind(Cards,F,N) :-
face(F), findall(_, member(c(F,_), Cards), Xs), length(Xs,N).
% utils/parser
parse_line(Cards) --> " ", parse_line(Cards).
parse_line([C|Cs]) --> parse_card(C), parse_line(Cs).
parse_line([]) --> [].
parse_card(c(F,S)) --> parse_face(F), parse_suit(S).
parse_suit(S,In,Out) :- suit(S), atom_codes(S,Xs), append(Xs,Out,In).
parse_face(F,In,Out) :- face(F), face_codes(F,Xs), append(Xs,Out,In).
face_codes(F,Xs) :- number(F) -> number_codes(F,Xs) ; atom_codes(F,Xs).
% tests
test(" 2♥ 2♦ 2♣ k♣ q♦").
test(" 2♥ 5♥ 7♦ 8♣ 9♠").
test(" a♥ 2♦ 3♣ 4♣ 5♦").
test(" 2♥ 3♥ 2♦ 3♣ 3♦").
test(" 2♥ 7♥ 2♦ 3♣ 3♦").
test(" 2♥ 7♥ 7♦ 7♣ 7♠").
test("10♥ j♥ q♥ k♥ a♥").
test(" 4♥ 4♠ k♠ 5♦ 10♠").
test(" q♣ 10♣ 7♣ 6♣ 4♣").
run_tests :-
test(Line), phrase(parse_line(Cards), Line), best_hand(Cards,H)
, write(Cards), write('\t'), write(H), nl
.
main :- findall(_, run_tests, _), halt.
- Output:
[c(2,♥),c(2,♦),c(2,♣),c(k,♣),c(q,♦)] three-of-a-kind(2) [c(2,♥),c(5,♥),c(7,♦),c(8,♣),c(9,♠)] high-card(9) [c(a,♥),c(2,♦),c(3,♣),c(4,♣),c(5,♦)] straight(5) [c(2,♥),c(3,♥),c(2,♦),c(3,♣),c(3,♦)] full-house(3,2) [c(2,♥),c(7,♥),c(2,♦),c(3,♣),c(3,♦)] two-pair(3,2) [c(2,♥),c(7,♥),c(7,♦),c(7,♣),c(7,♠)] four-of-a-kind(7) [c(10,♥),c(j,♥),c(q,♥),c(k,♥),c(a,♥)] straight-flush(c(a,♥)) [c(4,♥),c(4,♠),c(k,♠),c(5,♦),c(10,♠)] one-pair(4) [c(q,♣),c(10,♣),c(7,♣),c(6,♣),c(4,♣)] flush(♣)
Python
Goes a little further in also giving the ordered tie-breaker information from the wikipedia page.
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)
assert len(set(hand)) == 5, "Invalid: All cards in the hand must be unique %r" % cards
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]))
- 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']
Racket
#lang racket
(require (only-in srfi/1 car+cdr))
;;; --------------------------------------------------------------------------------------------------
;;; The analyser is first... the rest of it is prettiness surrounding strings and parsing!
;;; --------------------------------------------------------------------------------------------------
;; (cons f _) and (cons _ s) appear too frequently in patterns to not factor out
(define-match-expander F._ (λ (stx) (syntax-case stx () [(_ f) #'(cons f _)])))
(define-match-expander _.S (λ (stx) (syntax-case stx () [(_ s) #'(cons _ s)])))
;; Matches are easier when the cards are lined up by face: and I always put the cards in my hand with
;; the highest card on the left (should I be telling this?)... anyway face<? is written to leave high
;; cards on the left. There is no need to sort by suit, flushes are all-or-nothing
(define (face-sort hand)
(sort hand (match-lambda** [(_ 'joker) #f] [('joker _) #t] [((F._ f1) (F._ f2)) (> f1 f2)])))
;; even playing poker for money, I never managed to consistently determine what effect jokers were
;; having on my hand... so I'll do an exhaustive search of what's best!
;;
;; scoring hands allows us to choose a best value for joker(s)
;; hand-names provides an order (and therefore a score) for each of the available hands
(define hand-names (list 'five-of-a-kind 'straight-flush 'four-of-a-kind 'full-house 'flush 'straight
'three-of-a-kind 'two-pair 'one-pair 'high-card))
(define hand-order# (for/hash ((h hand-names) (i (in-range (add1 (length hand-names)) 0 -1)))
(values h i)))
;; The score of a hand is (its order*15^5)+(first tiebreaker*15^4)+(2nd tiebreaker*15^3)...
;; powers of 15 because we have a maxmium face value of 14 (ace) -- even though there are 13 cards
;; in a suit.
(define (calculate-score analysis)
(define-values (hand-name tiebreakers) (car+cdr analysis))
(for/sum ((n (in-naturals)) (tb (cons (hash-ref hand-order# hand-name -1) tiebreakers)))
(* tb (expt 15 (- 5 n)))))
;; score hand produces an analysis of a hand (which can then be returned to analyse-sorted-hand,
;; and a score that can be maximised by choosing the right jokers.
(define (score-hand hand . jokers) ; gives an orderable list of hands with tiebreakers
(define analysis (analyse-sorted-hand (face-sort (append jokers hand))))
(cons analysis (calculate-score analysis)))
;; if we have two (or more) jokers, they will be consumed by the recursive call to
;; analyse-sorted-hand score-hand
(define all-cards/promise (delay (for*/list ((f (in-range 2 15)) (s '(h d s c))) (cons f s))))
(define (best-jokered-hand cards) ; we've lost the first joker from cards
(define-values (best-hand _bhs)
(for*/fold ((best-hand #f) (best-score 0))
((joker (in-list (force all-cards/promise)))
(score (in-value (score-hand cards joker)))
#:when (> (cdr score) best-score))
(car+cdr score)))
best-hand)
;; we can abbreviate 2/3/4/5-of-a-kind 2-pair full-house with 2 and 3
(define-match-expander F*2 (λ (stx) (syntax-case stx () [(_ f) #'(list (F._ f) (F._ f))])))
(define-match-expander F*3 (λ (stx) (syntax-case stx () [(_ f) #'(list (F._ f) (F._ f) (F._ f))])))
;; note that flush? is cheaper to calculate than straight?, so do it first when we test for
;; straight-flush
(define flush?
(match-lambda [(and `(,(_.S s) ,(_.S s) ,(_.S s) ,(_.S s) ,(_.S s)) `(,(F._ fs) ...)) `(flush ,@fs)]
[_ #f]))
(define straight?
(match-lambda
;; '(straight 5) puts this at the bottom of the pile w.r.t the ordering of straights
[`(,(F._ 14) ,(F._ 5) ,(F._ 4) ,(F._ 3) ,(F._ 2)) '(straight 5)]
[`(,(F._ f5) ,(F._ f4) ,(F._ f3) ,(F._ f2) ,(F._ f1))
(and (= f1 (- f5 4)) (< f1 f2 f3 f4 f5) `(straight ,f5))]))
(define analyse-sorted-hand
(match-lambda
[(list 'joker cards ...) (best-jokered-hand cards)]
[`(,@(F*3 f) ,@(F*2 f)) `(five-of-a-kind ,f)]
;; get "top" from the straight. a the top card of the flush when there is a (straight 5) will
;; be the ace ... putting it in the wrong place for the ordering.
[(and (? flush?) (app straight? (list 'straight top _ ...))) `(straight-flush ,top)]
[(or `(,@(F*2 f) ,@(F*2 f) ,_) `(,_ ,@(F*2 f) ,@(F*2 f))) `(four-of-a-kind ,f)]
[(or `(,@(F*3 fh) ,@(F*2 fl)) `(,@(F*2 fh) ,@(F*3 fl))) `(full-house ,fh, fl)]
[(app flush? (and rv (list 'flush _ ...))) rv]
[(app straight? (and rv (list 'straight _ ...))) rv]
;; pairs and threes may be padded to the left, middle and right with tie-breakers; the lists of
;; which we will call l, m and r, respectively (four and 5-of-a-kind don't need tiebreaking,
;; they're well hard!)
[`(,(F._ l) ... ,@(F*3 f) ,(F._ r) ...) `(three-of-a-kind ,f ,@l ,@r)]
[`(,(F._ l) ... ,@(F*2 f1) ,(F._ m) ... ,@(F*2 f2) ,(F._ r) ...) `(two-pair ,f1 ,f2 ,@l ,@m ,@r)]
[`(,(F._ l) ... ,@(F*2 f) ,(F._ r) ...) `(one-pair ,f ,@l ,@r)]
[`(,(F._ f) ...) `(high-card ,@f)]
[hand (error 'invalid-hand hand)]))
(define (analyse-hand/string hand-string)
(analyse-sorted-hand (face-sort (string->hand hand-string))))
;;; --------------------------------------------------------------------------------------------------
;;; Strings to cards, cards to strings -- that kind of thing
;;; --------------------------------------------------------------------------------------------------
(define suit->unicode (match-lambda ('h "♥") ('d "♦") ('c "♣") ('s "♠") (x x)))
(define unicode->suit (match-lambda ("♥" 'h) ("♦" 'd) ("♣" 'c) ("♠" 's) (x x)))
(define (face->number f)
(match (string-upcase f)
["T" 10] ["J" 11] ["Q" 12] ["K" 13] ["A" 14] [(app string->number (? number? n)) n] [else 0]))
(define number->face (match-lambda (10 "T") (11 "J") (12 "Q") (13 "K") (14 "A") ((app ~s x) x)))
(define string->card
(match-lambda
("joker" 'joker)
((regexp #px"^(.*)(.)$" (list _ (app face->number num) (app unicode->suit suit)))
(cons num suit))))
(define (string->hand str)
(map string->card (regexp-split #rx" +" (string-trim str))))
(define card->string
(match-lambda ['joker "[]"]
[(cons (app number->face f) (app suit->unicode s)) (format "~a~a" f s)]))
(define (hand->string h)
(string-join (map card->string h) " "))
;; used for both testing and output
(define e.g.-hands
(list " 2♥ 2♦ 2♣ k♣ q♦" " 2♥ 5♥ 7♦ 8♣ 9♠" " a♥ 2♦ 3♣ 4♣ 5♦" "10♥ j♦ q♣ k♣ a♦"
" 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♣"
" joker 2♦ 2♠ k♠ q♦" " joker 5♥ 7♦ 8♠ 9♦" " joker 2♦ 3♠ 4♠ 5♠"
" joker 3♥ 2♦ 3♠ 3♦" " joker 7♥ 2♦ 3♠ 3♦" " joker 7♥ 7♦ 7♠ 7♣"
" joker j♥ q♥ k♥ A♥" " joker 4♣ k♣ 5♦ 10♠" " joker k♣ 7♣ 6♣ 4♣"
" joker 2♦ joker 4♠ 5♠" " joker Q♦ joker A♠ 10♠" " joker Q♦ joker A♦ 10♦"
" joker 2♦ 2♠ joker q♦"))
;;; --------------------------------------------------------------------------------------------------
;;; Main and test modules
;;; --------------------------------------------------------------------------------------------------
(module+ main
(define scored-hands
(for/list ((h (map string->hand e.g.-hands)))
(define-values (analysis score) (car+cdr (score-hand h)))
(list h analysis score)))
(for ((a.s (sort scored-hands > #:key third)))
(match-define (list (app hand->string h) a _) a.s)
(printf "~a: ~a ~a" h (~a (first a) #:min-width 15) (number->face (second a)))
(when (pair? (cddr a)) (printf " [tiebreak: ~a]" (string-join (map number->face (cddr a)) ", ")))
(newline)))
(module+ test
(require rackunit)
(let ((e.g.-strght-flsh '((14 . h) (13 . h) (12 . h) (11 . h) (10 . h))))
(check-match (straight? e.g.-strght-flsh) '(straight 14))
(check-match (flush? e.g.-strght-flsh) '(flush 14 13 12 11 10))
(check-match e.g.-strght-flsh (and (? flush?) (app straight? (list 'straight top _ ...)))))
(define expected-results
'((three-of-a-kind 2 13 12)
(high-card 9 8 7 5 2) (straight 5) (straight 14) (full-house 3 2) (two-pair 3 2 7)
(four-of-a-kind 7) (straight-flush 14) (one-pair 4 13 10 5) (flush 12 10 7 6 4)
(three-of-a-kind 2 13 12) (straight 9) (straight 6) (four-of-a-kind 3) (three-of-a-kind 3 7 2)
(five-of-a-kind 7) (straight-flush 14) (one-pair 13 10 5 4) (flush 14 13 7 6 4) (straight 6)
(straight 14) (straight-flush 14) (four-of-a-kind 2)))
(for ((h e.g.-hands) (r expected-results)) (check-equal? (analyse-hand/string h) r)))
- Output:
[] 7♥ 7♦ 7♠ 7♣: five-of-a-kind 7 T♥ J♥ Q♥ K♥ A♥: straight-flush A [] J♥ Q♥ K♥ A♥: straight-flush A [] Q♦ [] A♦ T♦: straight-flush A 2♥ 7♥ 7♦ 7♣ 7♠: four-of-a-kind 7 [] 3♥ 2♦ 3♠ 3♦: four-of-a-kind 3 [] 2♦ 2♠ [] Q♦: four-of-a-kind 2 2♥ 3♥ 2♦ 3♣ 3♦: full-house 3 [tiebreak: 2] [] K♣ 7♣ 6♣ 4♣: flush A [tiebreak: K, 7, 6, 4] Q♣ T♣ 7♣ 6♣ 4♣: flush Q [tiebreak: T, 7, 6, 4] T♥ J♦ Q♣ K♣ A♦: straight A [] Q♦ [] A♠ T♠: straight A [] 5♥ 7♦ 8♠ 9♦: straight 9 [] 2♦ 3♠ 4♠ 5♠: straight 6 [] 2♦ [] 4♠ 5♠: straight 6 A♥ 2♦ 3♣ 4♣ 5♦: straight 5 [] 7♥ 2♦ 3♠ 3♦: three-of-a-kind 3 [tiebreak: 7, 2] 2♥ 2♦ 2♣ K♣ Q♦: three-of-a-kind 2 [tiebreak: K, Q] [] 2♦ 2♠ K♠ Q♦: three-of-a-kind 2 [tiebreak: K, Q] 2♥ 7♥ 2♦ 3♣ 3♦: two-pair 3 [tiebreak: 2, 7] [] 4♣ K♣ 5♦ T♠: one-pair K [tiebreak: T, 5, 4] 4♥ 4♠ K♠ 5♦ T♠: one-pair 4 [tiebreak: K, T, 5] 2♥ 5♥ 7♦ 8♣ 9♠: high-card 9 [tiebreak: 8, 7, 5, 2]
Raku
(formerly Perl 6) This solution handles jokers. It has been written to use a Raku grammar.
use v6;
grammar PokerHand {
# Raku Grammar to parse and rank 5-card poker hands
# E.g. PokerHand.parse("2♥ 3♥ 2♦ 3♣ 3♦");
# 2013-12-21: handle 'joker' wildcards; maximum of two
rule TOP {
:my %*PLAYED;
{ %*PLAYED = () }
[ <face-card> | <joker> ]**5
}
token face-card {<face><suit> <?{
my $card = ~$/.lc;
# disallow duplicates
++%*PLAYED{$card} <= 1;
}>
}
token joker {:i 'joker' <?{
my $card = ~$/.lc;
# allow two jokers in a hand
++%*PLAYED{$card} <= 2;
}>
}
token face {:i <[2..9 jqka]> | 10 }
token suit {<[♥ ♦ ♣ ♠]>}
}
class PokerHand::Actions {
method TOP($/) {
my UInt @n = n-of-a-kind($/);
my $flush = 'flush' if flush($/);
my $straight = 'straight' if straight($/);
make rank(@n[0], @n[1], $flush, $straight);
}
multi sub rank(5,*@) { 'five-of-a-kind' }
multi sub rank($,$,'flush','straight') { 'straight-flush' }
multi sub rank(4,*@) { 'four-of-a-kind' }
multi sub rank($,$,'flush',$) { 'flush' }
multi sub rank($,$,$,'straight') { 'straight' }
multi sub rank(3,2,*@) { 'full-house' }
multi sub rank(3,*@) { 'three-of-a-kind' }
multi sub rank(2,2,*@) { 'two-pair' }
multi sub rank(2,*@) { 'one-pair' }
multi sub rank(*@) { 'high-card' }
sub n-of-a-kind($/) {
my %faces := bag @<face-card>.map: -> $/ {~$<face>.lc};
my @counts = %faces.values.sort.reverse;
@counts[0] += @<joker>;
return @counts;
}
sub flush($/) {
my @suits = unique @<face-card>.map: -> $/ {~$<suit>};
return +@suits == 1;
}
sub straight($/) {
# allow both ace-low and ace-high straights
constant @Faces = [ "a 2 3 4 5 6 7 8 9 10 j q k a".split: ' ' ];
constant @Possible-Straights = [ (4 ..^ @Faces).map: { set @Faces[$_-4 .. $_] } ];
my $faces = set @<face-card>.map: -> $/ {~$<face>.lc};
my $jokers = +@<joker>;
return ?( @Possible-Straights.first: { +($faces ∩ $_) + $jokers == 5 } );
}
}
my PokerHand::Actions $actions .= new;
for ("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
"a♥ a♥ 3♣ 4♣ 5♦", # invalid
## EXTRA CREDIT ##
"joker 2♦ 2♠ k♠ q♦", # three-of-a-kind
"joker 5♥ 7♦ 8♠ 9♦", # straight
"joker 2♦ 3♠ 4♠ 5♠", # straight
"joker 3♥ 2♦ 3♠ 3♦", # four-of-a-kind
"joker 7♥ 2♦ 3♠ 3♦", # three-of-a-kind
"joker 7♥ 7♦ 7♠ 7♣", # five-of-a-kind
"joker j♥ q♥ k♥ A♥", # straight-flush
"joker 4♣ k♣ 5♦ 10♠", # one-pair
"joker k♣ 7♣ 6♣ 4♣", # flush
"joker 2♦ joker 4♠ 5♠", # straight
"joker Q♦ joker A♠ 10♠", # straight
"joker Q♦ joker A♦ 10♦", # straight-flush
"joker 2♦ 2♠ joker q♦", # four of a kind
) {
my $rank = do with PokerHand.parse($_, :$actions) {
.ast;
}
else {
'invalid';
}
say "$_: $rank";
}
- Output:
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 a♥ a♥ 3♣ 4♣ 5♦: invalid joker 2♦ 2♠ k♠ q♦: three-of-a-kind joker 5♠ 7♦ 8♠ 9♦: straight joker 2♦ 3♠ 4♠ 5♠: straight joker 3♥ 2♦ 3♠ 3♦: four-of-a-kind joker 7♥ 2♦ 3♠ 3♦: three-of-a-kind joker 7♥ 7♦ 7♠ 7♣: five-of-a-kind joker j♠ q♠ k♠ A♠: straight-flush joker 4♣ k♣ 5♦ 10♠: one-pair joker k♣ 7♣ 6♣ 4♣: flush joker 2♦ joker 4♠ 5♠: straight joker Q♦ joker A♠ 10♠: straight joker Q♦ joker A♦ 10♦: straight-flush joker 2♦ 2♠ joker q♦: four-of-a-kind
REXX
version 1
/* 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
- 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
version 2 with suit glyphs
This REXX version supports:
- upper/lower/mixed case for suits and pips
- allows commas or blanks for card separation
- alternate names for aces and tens
- alphabetic letters for suits and/or glyphs
- specification of number of cards in a hand
- the dealt hands can be in a file (blank lines are ignored)
- dealt hands in the file can have comments after a semicolon (;)
/*REXX program analyzes an N─card poker hand, and displays what the poker hand is. */
parse arg iFID .; if iFID=='' | iFID=="," then iFID= 'POKERHAN.DAT'
/* [↓] read the poker hands dealt. */
do while lines(iFID)\==0; ox= linein(iFID); if ox='' then iterate
say right(ox, max(30, length(ox) ) ) ' ◄─── ' analyze(ox)
end /*while*/ /* [↑] analyze/validate the poker hand*/
exit /*stick a fork in it, we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
analyze: procedure; arg x ';',mc; hand= translate(x, '♥♦♣♠1', "HDCSA,"); flush= 0
kinds= 0; suit.= 0; pairs= 0; @.= 0; run= copies(0, 13); pips= run
if mc=='' then mc= 5; n= words(hand); if n\==mc then return 'invalid'
/* [↓] PIP can be 1 or 2 characters.*/
do j=1 for n; _= word(hand, j) /*obtain a card from the dealt hand. */
pip= left(_, length(_) - 1); ws= right(_, 1) /*obtain the card's pip; and the suit.*/
if pip==10 then pip= 'T' /*allow an alternate form for a "TEN". */
@._= @._ + 1 /*bump the card counter for this hand. */
#= pos(pip, 123456789TJQK) /*obtain the pip index for the card. */
if pos(ws, "♥♣♦♠")==0 then return 'invalid suit in card:' _
if #==0 then return 'invalid pip in card:' _
if @._\==1 then return 'invalid, duplicate card:' _
suit.ws= suit.ws + 1 /*count the suits for a possible flush.*/
flush= max(flush, suit.ws) /*count number of cards in the suits. */
run= overlay(., run, #) /*convert runs to a series of periods. */
_= substr(pips, #, 1) + 1 /*obtain the number of the pip in hand.*/
pips= overlay(_, pips, #) /*convert the pip to legitimate number.*/
kinds= max(kinds, _) /*convert certain pips to their number.*/
end /*i*/ /* [↑] keep track of N─of─a─kind. */
run= run || left(run, 1) /*An ace can be high ─or─ low. */
pairs= countstr(2, pips) /*count number of pairs (2s in PIPS).*/
straight= pos(....., run || left(run, 1) ) \== 0 /*does the RUN contains a straight? */
if flush==5 & straight then return 'straight-flush'
if kinds==4 then return 'four-of-a-kind'
if kinds==3 & pairs==1 then return 'full-house'
if flush==5 then return 'flush'
if straight then return 'straight'
if kinds==3 then return 'three-of-a-kind'
if kinds==2 & pairs==2 then return 'two-pair'
if kinds==2 then return 'one-pair'
return 'high-card'
Programming note: some older REXXes don't have the countstr BIF, so that REXX statement (above, line 48) can be replaced with:
pairs= 13 - length( space( translate( pips, , 2), 0) ) /*count # of 2's in PIPS.*/
- input file:
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♣ t♣ 7♣ 6♣ 4♣ J♥ Q♦ K♠ A♠ 10♠ ah 2h 3h 4h
- output when using the (above) input file
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♣ t♣ 7♣ 6♣ 4♣ ◄─── flush J♥ Q♦ K♠ A♠ 10♠ ◄─── straight ah 2h 3h 4h ◄─── invalid
version 3 with suit glyphs and jokers
This REXX version has three additional features:
- "invalid" hands have additional diagnostic information
- supports up to two jokers
- the joker card may be abbreviated (and can be in upper/lower/mixed case)
/*REXX program analyzes an N-card poker hand, and displays what the poker hand is, */
/*──────────────────────────────────────────── poker hands may contain up to two jokers.*/
parse arg iFID .; if iFID=='' | iFID=="," then iFID= 'POKERHAJ.DAT'
/* [↓] read the poker hands dealt. */
do while lines(iFID)\==0; ox= linein(iFID); if ox='' then iterate
say right(ox, max(30, length(ox) ) ) ' ◄─── ' analyze(ox)
end /*while*/ /* [↑] analyze/validate the poker hand*/
exit /*stick a fork in it, we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
analyze: procedure; arg x ';',mc; hand=translate(x, '♥♦♣♠1', "HDCSA,"); flush= 0
kinds= 0; suit.= 0; pairs= 0; @.= 0; run= copies(0 ,13); pips= run
if mc=='' then mc= 5; n= words(hand) /*N is the number of cards in hand. */
if n\==mc then return 'invalid number of cards, must be' mc
/* [↓] the PIP can be 1 or 2 chars. */
do j=1 for n; _= word(hand, j) /*obtain a card from the dealt hand. */
pip= left(_, length(_) - 1); ws= right(_, 1) /*obtain card's pip; obtain card's suit*/
if pip==10 then pip= 'T' /*allow alternate form for a TEN pip.*/
if abbrev('JOKER', _, 1) then _= "JK" /*allow altername forms of JOKER names.*/
@._= @._ + 1 /*bump the card counter for this hand. */
#= pos(pip, 123456789TJQK) /*obtain the pip index for the card. */
if _=='JK' then do; if @.j>2 then return 'invalid, too many jokers'
iterate
end
if pos(ws, "♥♣♦♠")==0 then return 'invalid suit in card:' _
if #==0 then return 'invalid pip in card:' _
if @._\==1 then return 'invalid, duplicate card:' _
suit.ws= suit.ws + 1 /*count the suits for a possible flush.*/
flush= max(flush, suit.ws) /*count number of cards in the suits. */
run= overlay(., run, #) /*convert runs to a series of periods. */
_= substr(pips, #, 1) + 1 /*obtain the number of the pip in hand.*/
pips= overlay(_, pips, #) /*convert the pip to legitimate number.*/
kinds= max(kinds, _) /*convert certain pips to their number.*/
end /*i*/ /* [↑] keep track of N─of─a─kind. */
run= run || left(run, 1) /*An ace can be high ─or─ low. */
jok= @.jk; kinds= kinds+jok; flush= flush+jok /*N─of─a─kind; adjustments for jokers.*/
straight= pos(..... , run)\==0 |, /*does the RUN contain a straight? */
(pos(.... , run)\==0 & jok>=1) |, /* " " " " " " */
(pos(..0.. , run)\==0 & jok>=1) |, /* " " " " " " */
(pos(...0. , run)\==0 & jok>=1) |, /* " " " " " " */
(pos(.0... , run)\==0 & jok>=1) |, /* " " " " " " */
(pos(... , run)\==0 & jok>=2) |, /* " " " " " " */
(pos(..0. , run)\==0 & jok>=2) |, /* " " " " " " */
(pos(.0.. , run)\==0 & jok>=2) |, /* " " " " " " */
(pos(.00.. , run)\==0 & jok>=2) |, /* " " " " " " */
(pos(..00. , run)\==0 & jok>=2) |, /* " " " " " " */
(pos(.0.0. , run)\==0 & jok>=2) /* " " " " " " */
pairs= countstr(2, pips) /*count number of pairs (2s in PIPS). */
if jok\==0 then pairs= pairs - 1 /*adjust number of pairs with jokers. */
if kinds>=5 then return 'five-of-a-kind'
if flush>=5 & straight then return 'straight-flush'
if kinds>=4 then return 'four-of-a-kind'
if kinds>=3 & pairs>=1 then return 'full-house'
if flush>=5 then return 'flush'
if straight then return 'straight'
if kinds>=3 then return 'three-of-a-kind'
if kinds==2 & pairs==2 then return 'two-pair'
if kinds==2 then return 'one-pair'
return 'high-card'
Programming note: the method used for analyzing hands that contain jokers are limited to a maximum of two jokers.
A different methodology would be needed for a generic number of jokers (and/or wild cards [such as deuces and one─eyed jacks]).
- input file:
joker 2♦ 2♠ k♠ q♦ joker 5♥ 7♦ 8♠ 9♦ joker 2♦ 3♠ 4♠ 5♠ joker 3♥ 2♦ 3♠ 3♦ joker 7♥ 2♦ 3♠ 3♦ joker 7♥ 7♦ 7♠ 7♣ joker j♥ q♥ k♥ A♥ joker 4♣ k♣ 5♦ 10♠ joker t♣ 7♣ 6♣ 4♣ joker Q♦ K♠ A♠ 10♠ joker 2h 3h 4h 2♥ 2♦ 2♠ k♠ jok 2♥ 5♥ 7♦ 8♠ jok a♥ 2♦ 5♠ 4♠ jok 2♥ 3♥ 2♦ 3♠ jok 2♥ 7♥ 2♦ 3♠ jok 2♥ 7♥ 7♦ 7♠ jok 10♥ j♥ q♥ k♥ jok 4♥ 4♣ k♣ 5♦ jok q♣ t♣ 7♣ 6♣ jok J♥ Q♦ K♠ A♠ jok
- output when using the (above) input file
joker 2♦ 2♠ k♠ q♦ ◄─── three-of-a-kind joker 5♥ 7♦ 8♠ 9♦ ◄─── straight joker 2♦ 3♠ 4♠ 5♠ ◄─── straight joker 3♥ 2♦ 3♠ 3♦ ◄─── four-of-a-kind joker 7♥ 2♦ 3♠ 3♦ ◄─── three-of-a-kind joker 7♥ 7♦ 7♠ 7♣ ◄─── five-of-a-kind joker j♥ q♥ k♥ A♥ ◄─── straight-flush joker 4♣ k♣ 5♦ 10♠ ◄─── one-pair joker t♣ 7♣ 6♣ 4♣ ◄─── flush joker Q♦ K♠ A♠ 10♠ ◄─── straight joker 2h 3h 4h ◄─── invalid number of cards, must be 5 2♥ 2♦ 2♠ k♠ jok ◄─── four-of-a-kind 2♥ 5♥ 7♦ 8♠ jok ◄─── one-pair a♥ 2♦ 5♠ 4♠ jok ◄─── straight 2♥ 3♥ 2♦ 3♠ jok ◄─── full-house 2♥ 7♥ 2♦ 3♠ jok ◄─── three-of-a-kind 2♥ 7♥ 7♦ 7♠ jok ◄─── four-of-a-kind 10♥ j♥ q♥ k♥ jok ◄─── straight-flush 4♥ 4♣ k♣ 5♦ jok ◄─── three-of-a-kind q♣ t♣ 7♣ 6♣ jok ◄─── flush J♥ Q♦ K♠ A♠ jok ◄─── straight
RPL
≪ → hand ≪ { } 1 15 FOR j "A23456789TJQK" hand j DUP SUB POS 1 - "CDHS" hand j 1 + DUP SUB POS 13 * + + 3 STEP ≫ ≫ 'HANDCODE' STO ≪ → diffs ≪ { } 1 1 4 FOR j IF diffs j GET THEN + 1 ELSE 1 + END NEXT + SORT REVLIST 1 2 SUB ≫ ≫ 'GROUPS' STO ≪ DUP ΠLIST 1 == SWAP { 9 1 1 1 } 1 == OR ≫ 'STRAIGHT?' STO ≪ HANDCODE DUP 13 / IP ≪ == ≫ DOSUBS ΠLIST SWAP 13 MOD SORT ΔLIST DUP GROUPS SWAP STRAIGHT? → flush groups straight ≪ CASE straight THEN flush "Straight flush" "Straight" IFTE END groups { 4 1 } == THEN "Four of a kind" END groups { 3 2 } == THEN "Full house" END groups { 3 1 } == THEN "Three of a kind" END groups { 2 2 } == THEN "Two pairs" END groups { 2 1 } == THEN "One pair" END flush "Flush" "High card" IFTE END ≫ ≫ '→HAND' STO
{ "2H 2D 2S KS QD" "2H 5H 7D 8S 9D" "AH 2D 3S 4S 5S" "2H 3H 2D 3S 3D" "2H 7H 2D 3S 3D" "2H 7H 7D 7S 7C" "TH JH QH KH AH" "4H 4C KC 5D TC" "QC TC 7C 6C 4C" }
1 ≪ →HAND ≫ DOLIST
- Output:
1: { "Three of a kind" "High card" "Straight" "Full house" "Two pairs" "Four of a kind" "Straight flush" "One pair" "Flush" }
Ruby
Joker-less hands are sorted high to low.
class Card
include Comparable
attr_accessor :ordinal
attr_reader :suit, :face
SUITS = %i(♥ ♦ ♣ ♠)
FACES = %i(2 3 4 5 6 7 8 9 10 j q k a)
def initialize(str)
@face, @suit = parse(str)
@ordinal = FACES.index(@face)
end
def <=> (other) #used for sorting
self.ordinal <=> other.ordinal
end
def to_s
"#@face#@suit"
end
private
def parse(str)
face, suit = str.chop.to_sym, str[-1].to_sym
raise ArgumentError, "invalid card: #{str}" unless FACES.include?(face) && SUITS.include?(suit)
[face, suit]
end
end
class Hand
include Comparable
attr_reader :cards, :rank
RANKS = %i(high-card one-pair two-pair three-of-a-kind straight flush
full-house four-of-a-kind straight-flush five-of-a-kind)
WHEEL_FACES = %i(2 3 4 5 a)
def initialize(str_of_cards)
@cards = str_of_cards.downcase.tr(',',' ').split.map{|str| Card.new(str)}
grouped = @cards.group_by(&:face).values
@face_pattern = grouped.map(&:size).sort
@rank = categorize
@rank_num = RANKS.index(@rank)
@tiebreaker = grouped.map{|ar| [ar.size, ar.first.ordinal]}.sort.reverse
end
def <=> (other) # used for sorting and comparing
self.compare_value <=> other.compare_value
end
def to_s
@cards.map(&:to_s).join(" ")
end
protected # accessible for Hands
def compare_value
[@rank_num, @tiebreaker]
end
private
def one_suit?
@cards.map(&:suit).uniq.size == 1
end
def consecutive?
sort.each_cons(2).all? {|c1,c2| c2.ordinal - c1.ordinal == 1 }
end
def sort
if @cards.sort.map(&:face) == WHEEL_FACES
@cards.detect {|c| c.face == :a}.ordinal = -1
end
@cards.sort
end
def categorize
if consecutive?
one_suit? ? :'straight-flush' : :straight
elsif one_suit?
:flush
else
case @face_pattern
when [1,1,1,1,1] then :'high-card'
when [1,1,1,2] then :'one-pair'
when [1,2,2] then :'two-pair'
when [1,1,3] then :'three-of-a-kind'
when [2,3] then :'full-house'
when [1,4] then :'four-of-a-kind'
when [5] then :'five-of-a-kind'
end
end
end
end
# Demo
test_hands = <<EOS
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♥ 6♥ 2♦ 3♣ 3♦
10♥ j♥ q♥ k♥ a♥
4♥ 4♠ k♠ 2♦ 10♠
4♥ 4♠ k♠ 3♦ 10♠
q♣ 10♣ 7♣ 6♣ 4♣
q♣ 10♣ 7♣ 6♣ 3♣
9♥ 10♥ q♥ k♥ j♣
2♥ 3♥ 4♥ 5♥ a♥
2♥ 2♥ 2♦ 3♣ 3♦
EOS
hands = test_hands.each_line.map{|line| Hand.new(line) }
puts "High to low"
hands.sort.reverse.each{|hand| puts "#{hand}\t #{hand.rank}" }
puts
str = <<EOS
joker 2♦ 2♠ k♠ q♦
joker 5♥ 7♦ 8♠ 9♦
joker 2♦ 3♠ 4♠ 5♠
joker 3♥ 2♦ 3♠ 3♦
joker 7♥ 2♦ 3♠ 3♦
joker 7♥ 7♦ 7♠ 7♣
joker j♥ q♥ k♥ A♥
joker 4♣ k♣ 5♦ 10♠
joker k♣ 7♣ 6♣ 4♣
joker 2♦ joker 4♠ 5♠
joker Q♦ joker A♠ 10♠
joker Q♦ joker A♦ 10♦
joker 2♦ 2♠ joker q♦
EOS
# Neither the Card nor the Hand class supports jokers
# but since hands are comparable, they are also sortable.
# Try every card from a deck for a joker and pick the largest hand:
DECK = Card::FACES.product(Card::SUITS).map(&:join)
str.each_line do |line|
cards_in_arrays = line.split.map{|c| c == "joker" ? DECK.dup : [c]} #joker is array of all cards
all_tries = cards_in_arrays.shift.product(*cards_in_arrays).map{|ar| Hand.new(ar.join" ")} #calculate the Whatshisname product
best = all_tries.max
puts "#{line.strip}: #{best.rank}"
end
- Output:
High to low 10♥ j♥ q♥ k♥ a♥ straight-flush 2♥ 3♥ 4♥ 5♥ a♥ straight-flush 2♥ 3♥ 2♦ 3♣ 3♦ full-house 2♥ 2♥ 2♦ 3♣ 3♦ full-house q♣ 10♣ 7♣ 6♣ 4♣ flush q♣ 10♣ 7♣ 6♣ 3♣ flush 9♥ 10♥ q♥ k♥ j♣ straight a♥ 2♦ 3♣ 4♣ 5♦ straight 2♥ 2♦ 2♣ k♣ q♦ three-of-a-kind 2♥ 7♥ 2♦ 3♣ 3♦ two-pair 2♥ 6♥ 2♦ 3♣ 3♦ two-pair 4♥ 4♠ k♠ 3♦ 10♠ one-pair 4♥ 4♠ k♠ 2♦ 10♠ one-pair 2♥ 5♥ 7♦ 8♣ 9♠ high-card joker 2♦ 2♠ k♠ q♦: three-of-a-kind joker 5♥ 7♦ 8♠ 9♦: straight joker 2♦ 3♠ 4♠ 5♠: straight joker 3♥ 2♦ 3♠ 3♦: four-of-a-kind joker 7♥ 2♦ 3♠ 3♦: three-of-a-kind joker 7♥ 7♦ 7♠ 7♣: five-of-a-kind joker j♥ q♥ k♥ A♥: straight-flush joker 4♣ k♣ 5♦ 10♠: one-pair joker k♣ 7♣ 6♣ 4♣: flush joker 2♦ joker 4♠ 5♠: straight joker Q♦ joker A♠ 10♠: straight joker Q♦ joker A♦ 10♦: straight-flush joker 2♦ 2♠ joker q♦: four-of-a-kind
Rust
Unicode version with jokers. Also checks for Royal Flush (AKQJ10 suited).
fn main() {
let hands = vec![
"🂡 🂮 🂭 🂫 🂪",
"🃏 🃂 🂢 🂮 🃍",
"🃏 🂵 🃇 🂨 🃉",
"🃏 🃂 🂣 🂤 🂥",
"🃏 🂳 🃂 🂣 🃃",
"🃏 🂷 🃂 🂣 🃃",
"🃏 🂷 🃇 🂧 🃗",
"🃏 🂻 🂽 🂾 🂱",
"🃏 🃔 🃞 🃅 🂪",
"🃏 🃞 🃗 🃖 🃔",
"🃏 🃂 🃟 🂤 🂥",
"🃏 🃍 🃟 🂡 🂪",
"🃏 🃍 🃟 🃁 🃊",
"🃏 🃂 🂢 🃟 🃍",
"🃏 🃂 🂢 🃍 🃍",
"🃂 🃞 🃍 🃁 🃊",
];
for hand in hands{
println!("{} {}", hand, poker_hand(hand));
}
}
fn poker_hand(cards: &str) -> &str {
let mut suits = vec![0u8; 4];
let mut faces = vec![0u8; 15];
let mut hand = vec![];
for card in cards.chars(){
if card == ' ' { continue; }
let values = get_card_value(card);
if values.0 < 14 && hand.contains(&values) {
return "invalid";
}
hand.push(values);
faces[values.0 as usize]+=1;
if values.1 >= 0 {
suits[values.1 as usize]+=1;
}
}
if hand.len()!=5 {
return "invalid";
}
faces[13] = faces[0]; //add ace-high count
let jokers = faces[14];
//count suits
let mut colors = suits.into_iter()
.filter(|&x| x > 0).collect::<Vec<_>>();
colors.sort_unstable();
colors[0] += jokers; // add joker suits to the highest one;
let is_flush = colors[0] == 5;
//straight
let mut is_straight = false;
//pointer to optimise some work
//avoids looking again at cards that were the start of a sequence
//as they cannot be part of another sequence
let mut ptr = 14;
while ptr>3{
let mut jokers_left = jokers;
let mut straight_cards = 0;
for i in (0..ptr).rev(){
if faces[i]==0 {
if jokers_left == 0 {break;}
jokers_left -= 1;
}
else if i==ptr-1 { ptr-=1; }
straight_cards+=1;
}
ptr-=1;
if straight_cards == 5 {
is_straight = true;
break;
}
}
//count values
let mut values = faces.into_iter().enumerate().take(14).filter(|&x| x.1>0).collect::<Vec<_>>();
//sort by quantity, then by value, high to low
values.sort_unstable_by(|a, b| if b.1 == a.1 { (b.0).cmp(&a.0) } else { (b.1).cmp(&a.1)} );
let first_group = values[0].1 + jokers;
let second_group = if values.len()>1 {values[1].1} else {0};
match (is_flush, is_straight, first_group, second_group){
(_,_,5,_) => "five-of-a-kind",
(true, true, _, _) => if ptr == 8 {"royal-flush"} else {"straight-flush"},
(_,_,4,_) => "four-of-a-kind",
(_,_,3,2) => "full-house",
(true,_,_,_) => "flush",
(_,true,_,_) => "straight",
(_,_,3,_) => "three-of-a-kind",
(_,_,2,2) => "two-pair",
(_,_,2,_) => "one-pair",
_ => "high-card"
}
}
fn get_card_value(card: char) -> (i8,i8) {
// transform glyph to face + suit, zero-indexed
let base = card as u32 - 0x1F0A1;
let mut suit = (base / 16) as i8;
let mut face = (base % 16) as i8;
if face > 11 && face < 14 { face-=1; } // Unicode has a Knight that we do not want
if face == 14 { suit = -1; } //jokers do not have a suit
(face, suit)
}
- Output:
🂡 🂮 🂭 🂫 🂪 royal-flush 🃏 🃂 🂢 🂮 🃍 three-of-a-kind 🃏 🂵 🃇 🂨 🃉 straight 🃏 🃂 🂣 🂤 🂥 straight 🃏 🂳 🃂 🂣 🃃 four-of-a-kind 🃏 🃂 🂢 🂮 🃍 three-of-a-kind 🃏 🂵 🃇 🂨 🃉 five-of-a-kind 🃏 🃂 🂣 🂤 🂥 straight-flush 🃏 🂳 🃂 🂣 🃃 one-pair 🃏 🃂 🂢 🂮 🃍 flush 🃏 🂵 🃇 🂨 🃉 straight 🃏 🃂 🂣 🂤 🂥 straight 🃏 🂳 🃂 🂣 🃃 straight-flush 🃏 🃂 🂢 🂮 🃍 four-of-a-kind 🃏 🂵 🃇 🂨 🃉 invalid 🃏 🃞 🃍 🃁 🃊 high-card
Scala
Including jokers, but not special suit characters. Aiming for readability more than performance.
val faces = "23456789TJQKA"
val suits = "CHSD"
sealed trait Card
object Joker extends Card
case class RealCard(face: Int, suit: Char) extends Card
val allRealCards = for {
face <- 0 until faces.size
suit <- suits
} yield RealCard(face, suit)
def parseCard(str: String): Card = {
if (str == "joker") {
Joker
} else {
RealCard(faces.indexOf(str(0)), str(1))
}
}
def parseHand(str: String): List[Card] = {
str.split(" ").map(parseCard).toList
}
trait HandType {
def name: String
def check(hand: List[RealCard]): Boolean
}
case class And(x: HandType, y: HandType, name: String) extends HandType {
def check(hand: List[RealCard]) = x.check(hand) && y.check(hand)
}
object Straight extends HandType {
val name = "straight"
def check(hand: List[RealCard]): Boolean = {
val faces = hand.map(_.face).toSet
faces.size == 5 && (faces.min == faces.max - 4 || faces == Set(0, 1, 2, 3, 12))
}
}
object Flush extends HandType {
val name = "flush"
def check(hand: List[RealCard]): Boolean = {
hand.map(_.suit).toSet.size == 1
}
}
case class NOfAKind(n: Int, name: String = "", nOccur: Int = 1) extends HandType {
def check(hand: List[RealCard]): Boolean = {
hand.groupBy(_.face).values.count(_.size == n) >= nOccur
}
}
val allHandTypes = List(
NOfAKind(5, "five-of-a-kind"),
And(Straight, Flush, "straight-flush"),
NOfAKind(4, "four-of-a-kind"),
And(NOfAKind(3), NOfAKind(2), "full-house"),
Flush,
Straight,
NOfAKind(3, "three-of-a-kind"),
NOfAKind(2, "two-pair", 2),
NOfAKind(2, "one-pair")
)
def possibleRealHands(hand: List[Card]): List[List[RealCard]] = {
val realCards = hand.collect { case r: RealCard => r }
val nJokers = hand.count(_ == Joker)
allRealCards.toList.combinations(nJokers).map(_ ++ realCards).toList
}
def analyzeHand(hand: List[Card]): String = {
val possibleHands = possibleRealHands(hand)
allHandTypes.find(t => possibleHands.exists(t.check)).map(_.name).getOrElse("high-card")
}
val testHands = List(
"2H 2D 2S KS QD",
"2H 5H 7D 8S 9D",
"AH 2D 3S 4S 5S",
"2H 3H 2D 3S 3D",
"2H 7H 2D 3S 3D",
"2H 7H 7D 7S 7C",
"TH JH QH KH AH",
"4H 4C KC 5D TC",
"QC TC 7C 6C 4C",
"QC TC 7C 7C TD",
"2H 2D 2S KS joker",
"2H 5H 7D 8S joker",
"AH 2D 3S 4S joker",
"2H 3H 2D 3S joker",
"2H 7H 2D 3S joker",
"2H 7H 7D joker joker",
"TH JH QH joker joker",
"4H 4C KC joker joker",
"QC TC 7C joker joker",
"QC TC 7H joker joker"
)
for (hand <- testHands) {
println(s"$hand -> ${analyzeHand(parseHand(hand))}")
}
- 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 QC TC 7C 7C TD -> two-pair 2H 2D 2S KS joker -> four-of-a-kind 2H 5H 7D 8S joker -> one-pair AH 2D 3S 4S joker -> straight 2H 3H 2D 3S joker -> full-house 2H 7H 2D 3S joker -> three-of-a-kind 2H 7H 7D joker joker -> four-of-a-kind TH JH QH joker joker -> straight-flush 4H 4C KC joker joker -> four-of-a-kind QC TC 7C joker joker -> flush QC TC 7H joker joker -> three-of-a-kind
Seed7
$ include "seed7_05.s7i";
include "console.s7i";
const string: face is "A23456789TJQK";
const string: suit is "♥♦♣♠";
const func string: analyzeHand (in array integer: faceCnt, in array integer: suitCnt) is func
result
var string: handValue is "";
local
var boolean: pair1 is FALSE;
var boolean: pair2 is FALSE;
var boolean: three is FALSE;
var boolean: four is FALSE;
var boolean: flush is FALSE;
var boolean: straight is FALSE;
var integer: sequence is 0;
var integer: x is 0;
begin
for x range 1 to 13 do
case faceCnt[x] of
when {2}: if pair1 then pair2 := TRUE; else pair1 := TRUE; end if;
when {3}: three := TRUE;
when {4}: four := TRUE;
end case;
end for;
for x range 1 to 4 until flush do
if suitCnt[x] = 5 then
flush := TRUE;
end if;
end for;
if not pair1 and not three and not four then
for x range 1 to 13 until sequence = 5 do
if faceCnt[x] <> 0 then incr(sequence); else sequence := 0; end if;
end for;
straight := sequence = 5 or (sequence = 4 and faceCnt[1] <> 0);
end if;
if straight and flush then handValue := "straight-flush";
elsif four then handValue := "four-of-a-kind";
elsif pair1 and three then handValue := "full-house";
elsif flush then handValue := "flush";
elsif straight then handValue := "straight";
elsif three then handValue := "three-of-a-kind";
elsif pair1 and pair2 then handValue := "two-pair";
elsif pair1 then handValue := "one-pair";
else handValue := "high-card";
end if;
end func;
const proc: analyze (in string: cards) is func
local
var array integer: faceCnt is 13 times 0;
var array integer: suitCnt is 4 times 0;
var string: card is "";
begin
for card range split(upper(cards), ' ') do
incr(faceCnt[pos(face, card[1])]);
incr(suitCnt[pos(suit, card[2])]);
end for;
writeln(cards <& ": " <& analyzeHand(faceCnt, suitCnt));
end func;
const proc: main is func
begin
OUT := STD_CONSOLE;
analyze("2♥ 2♦ 2♠ k♠ q♦");
analyze("2♥ 5♥ 7♦ 8♠ 9♦");
analyze("a♥ 2♦ 3♠ 4♠ 5♠");
analyze("2♥ 3♥ 2♦ 3♠ 3♦");
analyze("2♥ 7♥ 2♦ 3♠ 3♦");
analyze("2♥ 7♥ 7♦ 7♠ 7♣");
analyze("t♥ j♥ q♥ k♥ a♥");
analyze("4♥ 4♣ k♣ 5♦ t♣");
analyze("q♣ t♣ 7♣ 6♣ 4♣");
end func;
- Output:
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 t♥ j♥ q♥ k♥ a♥: straight-flush 4♥ 4♣ k♣ 5♦ t♣: one-pair q♣ t♣ 7♣ 6♣ 4♣: flush
Standard ML
local
val rec ins = fn x : int*'a => fn [] => [x]
| ll as h::t => if #1 x<= (#1 h) then x::ll else h::ins x t
val rec acount = fn
(n,a::(b::t)) => if a=b then acount (n+1,b::t) else (n,a)::acount(1,b::t)
| (n,[t]) => [(n,t)]
in (* helper count and sort functions *)
val rec sortBy1st = fn [] => [] | h::t => ins h (sortBy1st t)
val addcount = fn ll => acount (1,ll)
end;
val showHand = fn input =>
let
exception Cheat of string
(* replace a j q k by their numbers *)
val translateCardstrings = fn inputstr =>
String.tokens (fn #" "=>true|_=>false) (String.translate (fn #"a"=>"1"| #"j"=>"11"| #"q"=>"12"| #"k"=>"13" | a=>str a ) inputstr )
(* parse numbers and characters into int*strings and order those *)
val parseFacesSuits = fn cardcodes =>
sortBy1st (List.map (fn el => (valOf (Int.fromString el ),String.extract (el,String.size el -1,NONE) )) cardcodes )
handle Option => raise Cheat "parse"
(* replace the list of face numbers by a list of every face with its count and order it / descending count *)
val countAndSort =fn li =>
let val hand = ListPair.unzip li in (rev (sortBy1st (addcount (#1 hand))) , #2 hand ) end;
val score = fn
( (4,_)::t , _ ) => "four-of-a-kind"
| ( (3,_)::(2,_)::t , _ ) => "full-house"
| ( (3,_)::t,_) => "three-of-a-kind"
| ( (2,_)::(2,_)::t,_) => "two-pair"
| ( (2,_)::t,_) => "one-pair"
| (x as (1,_)::t,ll) => if #2 (hd x ) - (#2 (hd (rev x))) =4 orelse ( #2 (hd (rev x)) = 1 andalso #2 (hd (tl (rev x))) =10 )
then
if List.all (fn x => hd ll=x) ll
then "straight-flush"
else "straight"
else if List.all (fn x => hd ll=x) ll then "flush" else "high-card"
| _ => "invalid"
(* return 'invalid' if any duplicates or invalid codes *)
val validate = fn lpair : (int * string) list =>
let val rec uniq = fn ([],y) =>true|(x,y) => List.filter (fn a=>a= hd x) y = [hd x] andalso uniq(tl x,y)
in
if List.all (fn x :int*string => #1 x > 0 andalso #1 x < 14 ) lpair
andalso List.all (fn (x) => Option.isSome ( List.find (fn a=> a= #2x) ["c","d","h","s"] ) ) lpair
andalso uniq (lpair ,lpair)
then lpair
else raise Cheat "value"
end
in
( score o countAndSort o validate o parseFacesSuits o translateCardstrings ) input handle Cheat ch => "invalid"
end;
Example (interpreter):
val rec printio = fn
[] => ()
| s::t => (print (s^" : "); print ((showHand s)^"\n") before printio t) ;
printio
[ "2h 2d 2c kc qd" ,
"2h 5h 7d 8c 9s",
"ah 2d 3c 4c 5d" ,
"2h 3h 2d 3s 3d",
"2h 7h 2d 3c 3d",
"2h 7h 7d 7c 7s",
"10h jh qh kh ah",
"4h 4s ks 5d 10s",
"qc 10c 7c 6c 4c",
"ac ah ac ad 10h"] ;
2h 2d 2c kc qd : three-of-a-kind
2h 5h 7d 8c 9s : high-card
ah 2d 3c 4c 5d : straight
2h 3h 2d 3s 3d : full-house
2h 7h 2d 3c 3d : two-pair
2h 7h 7d 7c 7s : four-of-a-kind
10h jh qh kh ah : straight-flush
4h 4s ks 5d 10s : one-pair
qc 10c 7c 6c 4c : flush
ac ah ac ad 10h : invalid
val it = (): unit
Tcl
package require Tcl 8.6
namespace eval PokerHandAnalyser {
proc analyse {hand} {
set norm [Normalise $hand]
foreach type {
invalid straight-flush four-of-a-kind full-house flush straight
three-of-a-kind two-pair one-pair
} {
if {[Detect-$type $norm]} {
return $type
}
}
# Always possible to use high-card if the hand is legal at all
return high-card
}
# This normalises to an internal representation that is a list of pairs,
# where each pair is one number for the pips (ace == 14, king == 13,
# etc.) and another for the suit. This greatly simplifies detection.
proc Normalise {hand} {
set PipMap {j 11 q 12 k 13 a 14}
set SuitMap {♥ 2 h 2 ♦ 1 d 1 ♣ 0 c 0 ♠ 3 s 3}
set hand [string tolower $hand]
set cards [regexp -all -inline {(?:[akqj98765432]|10)[hdcs♥♦♣♠]} $hand]
lsort -command CompareCards [lmap c [string map {} $cards] {
list [string map $PipMap [string range $c 0 end-1]] \
[string map $SuitMap [string index $c end]]
}]
}
proc CompareCards {a b} {
lassign $a pipA suitA
lassign $b pipB suitB
expr {$pipA==$pipB ? $suitB-$suitA : $pipB-$pipA}
}
# Detection code. Note that the detectors all assume that the preceding
# detectors have been run first; this simplifies the logic a lot, but does
# mean that the individual detectors are not robust on their own.
proc Detect-invalid {hand} {
if {[llength $hand] != 5} {return 1}
foreach c $hand {
if {[incr seen($c)] > 1} {return 1}
}
return 0
}
proc Detect-straight-flush {hand} {
foreach c $hand {
lassign $c pip suit
if {[info exist prev] && $prev-1 != $pip} {
# Special case: ace low straight flush ("steel wheel")
if {$prev != 14 && $suit != 5} {
return 0
}
}
set prev $pip
incr seen($suit)
}
return [expr {[array size seen] == 1}]
}
proc Detect-four-of-a-kind {hand} {
foreach c $hand {
lassign $c pip suit
if {[incr seen($pip)] > 3} {return 1}
}
return 0
}
proc Detect-full-house {hand} {
foreach c $hand {
lassign $c pip suit
incr seen($pip)
}
return [expr {[array size seen] == 2}]
}
proc Detect-flush {hand} {
foreach c $hand {
lassign $c pip suit
incr seen($suit)
}
return [expr {[array size seen] == 1}]
}
proc Detect-straight {hand} {
foreach c $hand {
lassign $c pip suit
if {[info exist prev] && $prev-1 != $pip} {
# Special case: ace low straight ("wheel")
if {$prev != 14 && $suit != 5} {
return 0
}
}
set prev $pip
}
return 1
}
proc Detect-three-of-a-kind {hand} {
foreach c $hand {
lassign $c pip suit
if {[incr seen($pip)] > 2} {return 1}
}
return 0
}
proc Detect-two-pair {hand} {
set pairs 0
foreach c $hand {
lassign $c pip suit
if {[incr seen($pip)] > 1} {incr pairs}
}
return [expr {$pairs > 1}]
}
proc Detect-one-pair {hand} {
foreach c $hand {
lassign $c pip suit
if {[incr seen($pip)] > 1} {return 1}
}
return 0
}
}
Demonstrating:
foreach hand {
"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♣"
} {
puts "${hand}: [PokerHandAnalyser::analyse $hand]"
}
- Output:
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
VBScript
option explicit
class playingcard
dim suit
dim pips
'public property get gsuit():gsuit=suit:end property
'public property get gpips():gpips=pips:end property
public sub print
dim s,p
select case suit
case "S":s=chrW(&h2660)
case "D":s=chrW(&h2666)
case "C":s=chrW(&h2663)
case "H":s=chrW(&h2665)
end select
select case pips
case 1:p="A"
case 11:p="J"
case 12:p="Q"
case 13:p="K"
case else: p=""& pips
end select
wscript.stdout.write right(" "&p & s,3)&" "
end sub
end class
sub printhand(byref h,start)
dim i
for i =start to ubound(h)
h(i).print
next
'wscript.stdout.writeblanklines(1)
end sub
function checkhand(byval arr)
dim ss,i,max,last,uses,toppip,j,straight, flush,used,ace
redim nn(13)
ss=arr(0).suit '?????
straight=true:flush=true
max=0:last=0:used=0:toppip=0:ace=0
for i=0 to ubound(arr)
j=arr(i).pips
if arr(i).suit<>ss then flush=false
if j>toppip then toppip=j
if j=1 then ace=1
nn(j)=nn(j)+1
if nn(j)>max then max= nn(j)
if abs(j-toppip)>=4 then straight=0
next
for i=1 to ubound(nn)
if nn(i) then used=used+1
next
if max=1 then
if nn(1) and nn(10) and nn(11) and nn(12) and nn(13) then straight=1
end if
if flush and straight and max=1 then
checkhand= "straight-flush"
elseif flush then
checkhand= "flush"
elseif straight and max=1 then
checkhand= "straight"
elseif max=4 then
checkhand= "four-of-a-kind"
elseif max=3 then
if used=2 then
checkhand= "full-house"
else
checkhand= "three-of-a-kind"
end if
elseif max=2 then
if used=3 then
checkhand= "two-pair"
else
checkhand= "one-pair"
end if
else
checkhand= "Top "& toppip
End If
end function
function readhand(h)
dim i,b,c,p
redim a(4)
for i=0 to ubound(a)
b=h(i)
set c=new playingcard
p=left(b,1)
select case p
case "j": c.pips=11
case "q": c.pips=12
case "k": c.pips=13
case "t": c.pips=10
case "a": c.pips=1
case else c.pips=cint(p)
end select
c.suit=ucase(right(b,1))
set a(i)=c
next
readhand=a
erase a
end function
dim hands,hh,i
hands = Array(_
Array("2h", "5h", "7d", "8c", "9s"),_
Array("2h", "2d", "2c", "kc", "qd"),_
Array("ah", "2d", "3c", "4c", "5d"),_
Array("2h", "3h", "2d", "3c", "3d"),_
Array("2h", "7h", "2d", "3c", "3d"),_
Array("th", "jh", "qh", "kh", "ah"),_
Array("4h", "4s", "ks", "5d", "ts"),_
Array("qc", "tc", "7c", "6c", "4c"),_
Array("ah", "ah", "7c", "6c", "4c"))
for i=1 to ubound(hands)
hh=readhand(hands(i))
printhand hh,0
wscript.stdout.write vbtab & checkhand(hh)
wscript.stdout.writeblanklines(1)
'exit for
next
- Output:
2♥ 2♦ 2♣ K♣ Q♦ three-of-a-kind A♥ 2♦ 3♣ 4♣ 5♦ straight 2♥ 3♥ 2♦ 3♣ 3♦ full-house 2♥ 7♥ 2♦ 3♣ 3♦ two-pair 10♥ J♥ Q♥ K♥ A♥ straight-flush 4♥ 4♠ K♠ 5♦ 10♠ one-pair Q♣ 10♣ 7♣ 6♣ 4♣ flush A♥ A♥ 7♣ 6♣ 4♣ one-pair
Wren
Basic Version
import "./dynamic" for Tuple
import "./sort" for Sort
import "./str" for Str
import "./seq" for Lst
var Card = Tuple.create("Card", ["face", "suit"])
var FACES = "23456789tjqka"
var SUITS = "shdc"
var isStraight = Fn.new { |cards|
var cmp = Fn.new { |i, j| (i.face - j.face).sign }
var sorted = Sort.merge(cards, cmp)
if (sorted[0].face + 4 == sorted[4].face) return true
if (sorted[4].face == 14 && sorted[0].face == 2 && sorted[3].face == 5) return true
return false
}
var isFlush = Fn.new { |cards|
var suit = cards[0].suit
if (cards.skip(1).all { |card| card.suit == suit }) return true
return false
}
var analyzeHand = Fn.new { |hand|
var h = Str.lower(hand)
var split = Lst.distinct(h.split(" ").where { |c| c != "" }.toList)
if (split.count != 5) return "invalid"
var cards = []
for (s in split) {
if (s.count != 2) return "invalid"
var fIndex = FACES.indexOf(s[0])
if (fIndex == -1) return "invalid"
var sIndex = SUITS.indexOf(s[1])
if (sIndex == -1) return "invalid"
cards.add(Card.new(fIndex + 2, s[1]))
}
var groups = Lst.groups(cards) { |card| card.face }
if (groups.count == 2) {
if (groups.any { |g| g[1].count == 4 }) return "four-of-a-kind"
return "full-house"
} else if (groups.count == 3) {
if (groups.any { |g| g[1].count == 3 }) return "three-of-a-kind"
return "two-pair"
} else if (groups.count == 4) {
return "one-pair"
} else {
var flush = isFlush.call(cards)
var straight = isStraight.call(cards)
if (flush && straight) return "straight-flush"
if (flush) return "flush"
if (straight) return "straight"
return "high-card"
}
}
var hands = [
"2h 2d 2c kc qd",
"2h 5h 7d 8c 9s",
"ah 2d 3c 4c 5d",
"2h 3h 2d 3c 3d",
"2h 7h 2d 3c 3d",
"2h 7h 7d 7c 7s",
"th jh qh kh ah",
"4h 4s ks 5d ts",
"qc tc 7c 6c 4c",
"ah ah 7c 6c 4c"
]
for (hand in hands) {
System.print("%(hand): %(analyzeHand.call(hand))")
}
- Output:
2h 2d 2c kc qd: three-of-a-kind 2h 5h 7d 8c 9s: high-card ah 2d 3c 4c 5d: straight 2h 3h 2d 3c 3d: full-house 2h 7h 2d 3c 3d: two-pair 2h 7h 7d 7c 7s: four-of-a-kind th jh qh kh ah: straight-flush 4h 4s ks 5d ts: one-pair qc tc 7c 6c 4c: flush ah ah 7c 6c 4c: invalid
Extra Credit Version
import "./dynamic" for Tuple
import "./sort" for Sort
import "./seq" for Lst
var Card = Tuple.create("Card", ["face", "suit"])
var cmp = Fn.new { |i, j| (i.face - j.face).sign }
var isStraight = Fn.new { |cards, jokers|
var sorted = Sort.merge(cards, cmp)
if (jokers == 0) {
if (sorted[0].face + 4 == sorted[4].face) return true
if (sorted[4].face == 14 && sorted[3].face == 5) return true
return false
} else if (jokers == 1) {
if (sorted[0].face + 3 == sorted[3].face) return true
if (sorted[0].face + 4 == sorted[3].face) return true
if (sorted[3].face == 14 && sorted[2].face == 4) return true
if (sorted[3].face == 14 && sorted[2].face == 5) return true
return false
} else {
if (sorted[0].face + 2 == sorted[2].face) return true
if (sorted[0].face + 3 == sorted[2].face) return true
if (sorted[0].face + 4 == sorted[2].face) return true
if (sorted[2].face == 14 && sorted[1].face == 3) return true
if (sorted[2].face == 14 && sorted[1].face == 4) return true
if (sorted[2].face == 14 && sorted[1].face == 5) return true
return false
}
}
var isFlush = Fn.new { |cards|
var sorted = Sort.merge(cards, cmp)
var suit = sorted[0].suit
if (sorted.skip(1).all { |card| card.suit == suit || card.suit == "j" }) return true
return false
}
var analyzeHand = Fn.new { |hand|
var split = Lst.distinct(hand.split(" ").where { |c| c != "" }.toList)
if (split.count != 5) return "invalid"
var cards = []
var jokers = 0
for (s in split) {
if (s.bytes.count != 4) return "invalid"
var cp = s.codePoints[0]
var card =
cp == 0x1f0a1 ? Card.new(14, "s") :
cp == 0x1f0b1 ? Card.new(14, "h") :
cp == 0x1f0c1 ? Card.new(14, "d") :
cp == 0x1f0d1 ? Card.new(14, "c") :
cp == 0x1f0cf ? Card.new(15, "j") : // black joker
cp == 0x1f0df ? Card.new(16, "j") : // white joker
(cp >= 0x1f0a2 && cp <= 0x1f0ab) ? Card.new(cp - 0x1f0a0, "s") :
(cp >= 0x1f0ad && cp <= 0x1f0ae) ? Card.new(cp - 0x1f0a1, "s") :
(cp >= 0x1f0b2 && cp <= 0x1f0bb) ? Card.new(cp - 0x1f0b0, "h") :
(cp >= 0x1f0bd && cp <= 0x1f0be) ? Card.new(cp - 0x1f0b1, "h") :
(cp >= 0x1f0c2 && cp <= 0x1f0cb) ? Card.new(cp - 0x1f0c0, "d") :
(cp >= 0x1f0cd && cp <= 0x1f0ce) ? Card.new(cp - 0x1f0c1, "d") :
(cp >= 0x1f0d2 && cp <= 0x1f0db) ? Card.new(cp - 0x1f0d0, "c") :
(cp >= 0x1f0dd && cp <= 0x1f0de) ? Card.new(cp - 0x1f0d1, "c") :
Card.new(0, "j") // invalid
if (card.face == 0) return "invalid"
if (card.suit == "j") jokers = jokers + 1
cards.add(card)
}
var groups = Lst.groups(cards) { |card| card.face }
if (groups.count == 2) {
if (groups.any { |g| g[1].count == 4 }) {
if (jokers == 0) return "four-of-a-kind"
return "five-of-a-kind"
}
return "full-house"
} else if (groups.count == 3) {
if (groups.any { |g| g[1].count == 3 }) {
if (jokers == 0) return "three-of-a-kind"
if (jokers == 1) return "four-of-a-kind"
return "five-of-a-kind"
}
return (jokers == 0) ? "two-pair" : "full-house"
} else if (groups.count == 4) {
if (jokers == 0) return "one-pair"
if (jokers == 1) return "three-of-a-kind"
return "four-of-a-kind"
} else {
var flush = isFlush.call(cards)
var straight = isStraight.call(cards, jokers)
if (flush && straight) return "straight-flush"
if (flush) return "flush"
if (straight) return "straight"
return (jokers == 0) ? "high-card" : "one-pair"
}
}
var hands = [
"🃏 🃂 🂢 🂮 🃍",
"🃏 🂵 🃇 🂨 🃉",
"🃏 🃂 🂣 🂤 🂥",
"🃏 🂳 🃂 🂣 🃃",
"🃏 🂷 🃂 🂣 🃃",
"🃏 🂷 🃇 🂧 🃗",
"🃏 🂻 🂽 🂾 🂱",
"🃏 🃔 🃞 🃅 🂪",
"🃏 🃞 🃗 🃖 🃔",
"🃏 🃂 🃟 🂤 🂥",
"🃏 🃍 🃟 🂡 🂪",
"🃏 🃍 🃟 🃁 🃊",
"🃏 🃂 🂢 🃟 🃍",
"🃏 🃂 🂢 🃍 🃍",
"🃂 🃞 🃍 🃁 🃊"
]
for (hand in hands) {
System.print("%(hand): %(analyzeHand.call(hand))")
}
- Output:
🃏 🃂 🂢 🂮 🃍: three-of-a-kind 🃏 🂵 🃇 🂨 🃉: straight 🃏 🃂 🂣 🂤 🂥: straight 🃏 🂳 🃂 🂣 🃃: four-of-a-kind 🃏 🂷 🃂 🂣 🃃: three-of-a-kind 🃏 🂷 🃇 🂧 🃗: five-of-a-kind 🃏 🂻 🂽 🂾 🂱: straight-flush 🃏 🃔 🃞 🃅 🂪: one-pair 🃏 🃞 🃗 🃖 🃔: flush 🃏 🃂 🃟 🂤 🂥: straight 🃏 🃍 🃟 🂡 🂪: straight 🃏 🃍 🃟 🃁 🃊: straight-flush 🃏 🃂 🂢 🃟 🃍: four-of-a-kind 🃏 🃂 🂢 🃍 🃍: invalid 🃂 🃞 🃍 🃁 🃊: high-card