Skip to content

Instantly share code, notes, and snippets.

@ndpar
Last active December 16, 2015 05:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ndpar/5384996 to your computer and use it in GitHub Desktop.
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
; 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)))
%% 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].
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]]
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
# 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