RosettaCodeData/Task/Poker-hand-analyser/Python/poker-hand-analyser.py
2023-07-01 13:44:08 -04:00

150 lines
4.7 KiB
Python

from collections import namedtuple
class Card(namedtuple('Card', 'face, suit')):
def __repr__(self):
return ''.join(self)
suit = '♥ ♦ ♣ ♠'.split()
# ordered strings of faces
faces = '2 3 4 5 6 7 8 9 10 j q k a'
lowaces = 'a 2 3 4 5 6 7 8 9 10 j q k'
# faces as lists
face = faces.split()
lowace = lowaces.split()
def straightflush(hand):
f,fs = ( (lowace, lowaces) if any(card.face == '2' for card in hand)
else (face, faces) )
ordered = sorted(hand, key=lambda card: (f.index(card.face), card.suit))
first, rest = ordered[0], ordered[1:]
if ( all(card.suit == first.suit for card in rest) and
' '.join(card.face for card in ordered) in fs ):
return 'straight-flush', ordered[-1].face
return False
def fourofakind(hand):
allfaces = [f for f,s in hand]
allftypes = set(allfaces)
if len(allftypes) != 2:
return False
for f in allftypes:
if allfaces.count(f) == 4:
allftypes.remove(f)
return 'four-of-a-kind', [f, allftypes.pop()]
else:
return False
def fullhouse(hand):
allfaces = [f for f,s in hand]
allftypes = set(allfaces)
if len(allftypes) != 2:
return False
for f in allftypes:
if allfaces.count(f) == 3:
allftypes.remove(f)
return 'full-house', [f, allftypes.pop()]
else:
return False
def flush(hand):
allstypes = {s for f, s in hand}
if len(allstypes) == 1:
allfaces = [f for f,s in hand]
return 'flush', sorted(allfaces,
key=lambda f: face.index(f),
reverse=True)
return False
def straight(hand):
f,fs = ( (lowace, lowaces) if any(card.face == '2' for card in hand)
else (face, faces) )
ordered = sorted(hand, key=lambda card: (f.index(card.face), card.suit))
first, rest = ordered[0], ordered[1:]
if ' '.join(card.face for card in ordered) in fs:
return 'straight', ordered[-1].face
return False
def threeofakind(hand):
allfaces = [f for f,s in hand]
allftypes = set(allfaces)
if len(allftypes) <= 2:
return False
for f in allftypes:
if allfaces.count(f) == 3:
allftypes.remove(f)
return ('three-of-a-kind', [f] +
sorted(allftypes,
key=lambda f: face.index(f),
reverse=True))
else:
return False
def twopair(hand):
allfaces = [f for f,s in hand]
allftypes = set(allfaces)
pairs = [f for f in allftypes if allfaces.count(f) == 2]
if len(pairs) != 2:
return False
p0, p1 = pairs
other = [(allftypes - set(pairs)).pop()]
return 'two-pair', pairs + other if face.index(p0) > face.index(p1) else pairs[::-1] + other
def onepair(hand):
allfaces = [f for f,s in hand]
allftypes = set(allfaces)
pairs = [f for f in allftypes if allfaces.count(f) == 2]
if len(pairs) != 1:
return False
allftypes.remove(pairs[0])
return 'one-pair', pairs + sorted(allftypes,
key=lambda f: face.index(f),
reverse=True)
def highcard(hand):
allfaces = [f for f,s in hand]
return 'high-card', sorted(allfaces,
key=lambda f: face.index(f),
reverse=True)
handrankorder = (straightflush, fourofakind, fullhouse,
flush, straight, threeofakind,
twopair, onepair, highcard)
def rank(cards):
hand = handy(cards)
for ranker in handrankorder:
rank = ranker(hand)
if rank:
break
assert rank, "Invalid: Failed to rank cards: %r" % cards
return rank
def handy(cards='2♥ 2♦ 2♣ k♣ q♦'):
hand = []
for card in cards.split():
f, s = card[:-1], card[-1]
assert f in face, "Invalid: Don't understand card face %r" % f
assert s in suit, "Invalid: Don't understand card suit %r" % s
hand.append(Card(f, s))
assert len(hand) == 5, "Invalid: Must be 5 cards in a hand, not %i" % len(hand)
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]))