Last active
December 16, 2015 05:29
-
-
Save ndpar/5384996 to your computer and use it in GitHub Desktop.
Poker hand evaluator in different languages.http://en.wikipedia.org/wiki/List_of_poker_hands
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
; https://github.com/ndpar/clojure/blob/master/src/dojo/poker.clj | |
; https://github.com/ndpar/clojure/blob/master/test/dojo/poker_test.clj | |
(defn card-ranks | |
"Return a vector of the ranks, sorted with higher first" | |
[hand] | |
(let [ranks (map #(->> % first str (.indexOf "--23456789TJQKA")) hand) | |
ranks (vec (sort > ranks))] | |
(if (= [14 5 4 3 2] ranks) [5 4 3 2 1] ranks))) | |
(defn straight? | |
"Return true if the ordered ranks form a 5-card straight" | |
[ranks] | |
(and (= 4 (- (first ranks) (last ranks))) | |
(= 5 (count (distinct ranks))))) | |
(defn flush? | |
"Return true if all the cards have the same suit" | |
[hand] | |
(= 1 (count (distinct (map second hand))))) | |
(defn n-kinds [n ranks] | |
(filter #(= (count %) n) (partition-by identity ranks))) | |
(defn kind | |
"Return the first rank that this hand has exactly n of. | |
Return nil if there is no n-of-a-kind in the hand" | |
[n ranks] | |
(-> (n-kinds n ranks) first first)) | |
(defn two-pair | |
"If there are two pairs, return the two ranks as a | |
tuple: (highest, lowest); otherwise return nil" | |
[ranks] | |
(let [pairs (n-kinds 2 ranks)] | |
(cond (= 2 (count pairs)) (first (apply map vector pairs))))) | |
(defn hand-rank | |
"Return a value indicating the ranking of a hand" | |
[hand] | |
(let [ranks (card-ranks hand)] | |
(cond | |
(and (straight? ranks) (flush? hand)) [8 (first ranks)] | |
(kind 4 ranks) [7 (kind 4 ranks) (kind 1 ranks)] | |
(and (kind 3 ranks) (kind 2 ranks)) [6 (kind 3 ranks) (kind 2 ranks)] | |
(flush? hand) [5 ranks] | |
(straight? ranks) [4 (first ranks)] | |
(kind 3 ranks) [3 (kind 3 ranks) ranks] | |
(two-pair ranks) [2 (two-pair ranks) ranks] | |
(kind 2 ranks) [1 (kind 2 ranks) ranks] | |
:else [0 ranks]))) | |
(def restv (comp vec rest)) | |
(defn poker | |
"Return the best hand: (poker [hand,...]) => hand" | |
[hands] | |
(last (sort-by (comp (juxt first restv) hand-rank) hands))) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
%% https://github.com/ndpar/erlang/blob/master/src/poker.erl | |
-module(poker). | |
-export([hand_rank/1, sort_hands/1, winners/1]). | |
%% Return a list of the ranks, sorted with higher first. | |
%% Hand is a list of 2-char strings, i.e. ["6H","3D","AS","TH","JC"]. | |
card_ranks(Hand) -> | |
Ranks = [string:str("-23456789TJQKA", [R]) || [R,_] <- Hand], | |
case lists:sort(fun erlang:'>'/2, Ranks) of | |
[14,5,4,3,2] -> [5,4,3,2,1]; | |
SortedRanks -> SortedRanks | |
end. | |
%% Return a value indicating the ranking of a hand. | |
hand_rank(Hand) -> | |
CardRanks = card_ranks(Hand), | |
HandRanks = [R || F <- poker_hands(), | |
begin R = F(CardRanks, Hand), R /= undefined end], | |
hd(HandRanks). | |
poker_hands() -> | |
[ | |
fun straight_flush/2, | |
fun four_of_kind/2, | |
fun full_house/2, | |
fun flush/2, | |
fun straight/2, | |
fun three_of_kind/2, | |
fun two_pair/2, | |
fun pair/2, | |
fun high_card/2 | |
]. | |
straight_flush(Ranks, Hand) -> | |
case {straight(Ranks,Hand), flush(Ranks,Hand)} of | |
{[4,R], [5|_]} -> [8, R]; | |
_ -> undefined | |
end. | |
four_of_kind([H,L,L,L,L], _) -> [7, L, H]; | |
four_of_kind([H,H,H,H,L], _) -> [7, H, L]; | |
four_of_kind(_,_) -> undefined. | |
full_house([H,H,H,L,L], _) -> [6, H, L]; | |
full_house([H,H,L,L,L], _) -> [6, L, H]; | |
full_house(_,_) -> undefined. | |
flush(Ranks, [[_,S], [_,S], [_,S], [_,S], [_,S]]) -> [5 | Ranks]; | |
flush(_,_) -> undefined. | |
straight([R1, R2, R3, R4, R5], _) | |
when R1 == R2+1, R2 == R3+1, R3 == R4+1, R4 == R5+1 -> [4, R1]; | |
straight(_,_) -> undefined. | |
three_of_kind([R,R,R,_,_] = Ranks, _) -> [3, R | Ranks]; | |
three_of_kind([_,R,R,R,_] = Ranks, _) -> [3, R | Ranks]; | |
three_of_kind([_,_,R,R,R] = Ranks, _) -> [3, R | Ranks]; | |
three_of_kind(_,_) -> undefined. | |
two_pair([H,H,L,L,_] = Ranks, _) -> [2, H, L | Ranks]; | |
two_pair([H,H,_,L,L] = Ranks, _) -> [2, H, L | Ranks]; | |
two_pair([_,H,H,L,L] = Ranks, _) -> [2, H, L | Ranks]; | |
two_pair(_,_) -> undefined. | |
pair([R,R,_,_,_] = Ranks, _) -> [1, R | Ranks]; | |
pair([_,R,R,_,_] = Ranks, _) -> [1, R | Ranks]; | |
pair([_,_,R,R,_] = Ranks, _) -> [1, R | Ranks]; | |
pair([_,_,_,R,R] = Ranks, _) -> [1, R | Ranks]; | |
pair(_,_) -> undefined. | |
high_card(Ranks, _) -> [0 | Ranks]. | |
%% To find winners, we just sort hands according to their rankings | |
sort_hands(Hands) -> | |
lists:sort(fun(H1, H2) -> hand_rank(H2) =< hand_rank(H1) end, Hands). | |
winners(Hands) -> | |
SortedHands = sort_hands(Hands), | |
HighestRank = hand_rank(hd(SortedHands)), | |
[H || H <- SortedHands, hand_rank(H) == HighestRank]. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
String.metaClass.getCards = { | |
-> delegate.split(' ') | |
} | |
String.metaClass.getRanks = { -> | |
def result = delegate.cards.collect{'--23456789TJQKA'.indexOf(it[0])}.sort{r1, r2 -> r2 <=> r1} | |
result == [14, 5, 4, 3, 2] ? [5, 4, 3, 2, 1] : result | |
} | |
String.metaClass.getSuits = { | |
-> delegate.cards.collect{it[1]}.unique() | |
} | |
boolean straight(ranks) { | |
int r = ranks[0] | |
r..(r-4) == ranks | |
} | |
boolean flush(hand) { | |
hand.suits.size() == 1 | |
} | |
def kind(n, ranks) { | |
ranks.countBy{it}.find{it.value == n}?.key | |
} | |
def twoPair(ranks) { | |
ranks.count{it == ranks[1]} == 2 && ranks.count{it == ranks[3]} == 2 ? [ranks[1], ranks[3]] : null | |
} | |
def handRank(hand) { | |
def ranks = hand.ranks | |
if (straight(ranks) && flush(hand)) | |
[8, ranks.max()] | |
else if (kind(4, ranks)) | |
[7, kind(4, ranks), kind(1, ranks)] | |
else if (kind(3, ranks) && kind(2, ranks)) | |
[6, kind(3, ranks), kind(2, ranks)] | |
else if (flush(hand)) | |
[5, ranks] | |
else if (straight(ranks)) | |
[4, ranks.max()] | |
else if (kind(3, ranks)) | |
[3, kind(3, ranks), ranks] | |
else if (twoPair(ranks)) | |
[2, twoPair(ranks), ranks] | |
else if (kind(2, ranks)) | |
[1, kind(2, ranks), ranks] | |
else [0, ranks] | |
} | |
assert '2H 3S TD 6C AC'.cards == ['2H', '3S', 'TD', '6C', 'AC'] | |
assert '2H 3S TD 6C AC'.ranks == [14, 10, 6, 3, 2] | |
assert '2H 3S 5D 4C AC'.ranks == [5, 4, 3, 2, 1] | |
assert straight('2H 3D 4C 6S 5H'.ranks) | |
assert flush('2H AH 4H TH 5H') | |
assert kind(2, [5, 4, 5, 2, 1]) == 5 | |
assert handRank('2S 3S 4S 5S 6S') == [8, 6] | |
assert handRank('2S 3S 4S 5S AS') == [8, 5] | |
assert handRank('2D 2S 4S 2H 2C') == [7, 2, 4] | |
assert handRank('2D 2S 4S 4H 2C') == [6, 2, 4] | |
assert handRank('AS 2S 4S 5S QS') == [5, [14, 12, 5, 4, 2]] | |
assert handRank('2D 3S 4S 5H 6C') == [4, 6] | |
assert handRank('2D TS 2S JH 2C') == [3, 2, [11, 10, 2, 2, 2]] | |
assert handRank('8D 2S 4S 4H 2C') == [2, [4, 2], [8, 4, 4, 2, 2]] | |
assert handRank('8D 2S TS AH 2C') == [1, 2, [14, 10, 8, 2, 2]] | |
assert handRank('8D 2S TS AH KC') == [0, [14, 13, 10, 8, 2]] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
NB. https://github.com/ndpar/j/tree/master/poker | |
r=: @: {. | |
s=: @: {: | |
same=: 1 : '1 = [: */ 2 u/\ ]' | |
fl=: = same | |
st=: - same | |
if=: 2 : '0: ` u @. v' | |
SF=: (8 , {. r) if (fl s *. st r) | |
FL=: (5 , {.) if (fl s) | |
ifr=: 2 : '0: ` u @. v r' | |
ST=: (4 , {.) ifr st | |
freq=: [: +/"1 = | |
fs=: 1 : 'x -: [: \:~ freq' | |
hilow=: 0 _1&{ \: freq | |
K4=: (7 , hilow) ifr (4 1 fs) | |
FH=: (6 , hilow) ifr (3 2 fs) | |
K3=: (3 , 2&{ , ]) ifr (3 1 1 fs) | |
P2=: (2 , 1 3&{ , ]) ifr (2 2 1 fs) | |
pr=: 0 { (\: freq) | |
P1=: (1 , pr , ]) ifr (2 1 1 1 fs) | |
HC=: 0 , {. | |
p=: SF , K4 , FH , FL , ST , K3 , P2 , P1 ,: HC | |
poker=: 0 { [: \:~ p | |
rank=: '--23456789TJQKA' i. {. | |
suit=: 'CDHS' i. {: | |
card=: rank , suit | |
w=: ] ` (5 4 3 2 1 ,: {:) @. (14 5 4 3 2 -: {.) | |
hand=: [: w [: |: [: \:~ [: > [: card each cutopen |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Peter Norvig's algorithm | |
# http://www.youtube.com/playlist?list=PL818D7B4539EED6D3 | |
# Hand is a list of 2-char strings, i.e. ["6C","7C","8C","9C","TC"] | |
def card_ranks(hand): | |
"Return a list of the ranks, sorted with higher first." | |
ranks = ['--23456789TJQKA'.index(r) for r, s in hand] | |
ranks.sort(reverse = True) | |
return [5, 4, 3, 2, 1] if (ranks == [14, 5, 4, 3, 2]) else ranks | |
def straight(ranks): | |
"Return True if the ordered ranks form a 5-card straight." | |
return (max(ranks)-min(ranks) == 4) and len(set(ranks)) == 5 | |
def flush(hand): | |
"Return True if all the cards have the same suit." | |
return len(set([s for r,s in hand])) == 1 | |
def two_pair(ranks): | |
"""If there are two pair, return the two ranks as a | |
tuple: (highest, lowest); otherwise return None.""" | |
if ranks.count(ranks[1]) == 2 and ranks.count(ranks[3]) == 2: | |
return (ranks[1], ranks[3]) | |
return None | |
def kind(n, ranks): | |
"""Return the first rank that this hand has exactly n of. | |
Return None if there is no n-of-a-kind in the hand.""" | |
for r in ranks: | |
if ranks.count(r) == n: return r | |
return None | |
def poker(hands): | |
"Return a list of winning hands: poker([hand,...]) => [hand,...]" | |
return allmax(hands, key=hand_rank) | |
def allmax(iterable, key=None): | |
"Return a list of all items equal to the max of the iterable." | |
iterable.sort(key=key, reverse=True) | |
return [x for x in iterable if hand_rank(x) == hand_rank(iterable[0])] | |
def hand_rank(hand): | |
"Return a value indicating the ranking of a hand." | |
ranks = card_ranks(hand) | |
if straight(ranks) and flush(hand): | |
return (8, max(ranks)) | |
elif kind(4, ranks): | |
return (7, kind(4, ranks), kind(1, ranks)) | |
elif kind(3, ranks) and kind(2, ranks): | |
return (6, kind(3, ranks), kind(2, ranks)) | |
elif flush(hand): | |
return (5, ranks) | |
elif straight(ranks): | |
return (4, max(ranks)) | |
elif kind(3, ranks): | |
return (3, kind(3, ranks), ranks) | |
elif two_pair(ranks): | |
return (2, two_pair(ranks), ranks) | |
elif kind(2, ranks): | |
return (1, kind(2, ranks), ranks) | |
else: | |
return (0, ranks) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment