2010-03-31 13:54:35 +00:00
|
|
|
import operator
|
|
|
|
|
|
|
|
class InvalidCard(Exception):
|
|
|
|
pass
|
|
|
|
class InvalidHand(Exception):
|
|
|
|
pass
|
|
|
|
|
2010-03-31 19:48:53 +00:00
|
|
|
def unique_combinations(items, n):
|
|
|
|
if n==0: yield []
|
|
|
|
else:
|
|
|
|
for i in xrange(len(items)):
|
|
|
|
for cc in unique_combinations(items[i+1:],n-1):
|
|
|
|
yield [items[i]]+cc
|
|
|
|
|
|
|
|
|
|
|
|
|
2010-03-31 13:54:35 +00:00
|
|
|
class Card:
|
|
|
|
values = {'T': 10, 'J': 11, 'Q': 12, 'K': 13, 'A': 14}
|
|
|
|
def __init__(self, string):
|
|
|
|
self.value = string[0]
|
|
|
|
if self.value in '23456789':
|
|
|
|
self.value = int(self.value)
|
|
|
|
else:
|
|
|
|
try:
|
|
|
|
self.value = Card.values[self.value]
|
|
|
|
except KeyError as e:
|
|
|
|
raise InvalidCard
|
|
|
|
self.suit = string[1]
|
|
|
|
if not self.suit in ['H', 'C', 'S', 'D']:
|
|
|
|
raise InvalidCard
|
|
|
|
@staticmethod
|
|
|
|
def compare(a, b):
|
|
|
|
if a.value > b.value:
|
|
|
|
return 1
|
|
|
|
elif a.value == b.value:
|
|
|
|
return 0
|
|
|
|
else:
|
|
|
|
return -1
|
|
|
|
|
|
|
|
class Hand:
|
|
|
|
HIGH_CARD = 0
|
|
|
|
ONE_PAIR = 1
|
|
|
|
TWO_PAIRS = 2
|
|
|
|
THREE_OF_A_KIND = 3
|
|
|
|
STRAIGHT = 4
|
|
|
|
FLUSH = 5
|
|
|
|
FULL_HOUSE = 6
|
|
|
|
FOUR_OF_A_KIND = 7
|
|
|
|
STRAIGHT_FLUSH = 8
|
|
|
|
ROYAL_FLUSH = 9
|
|
|
|
RANKS = [
|
|
|
|
'High Card',
|
|
|
|
'One Pair',
|
|
|
|
'Two Pairs',
|
|
|
|
'Three of a Kind',
|
|
|
|
'Straight',
|
|
|
|
'Flush',
|
|
|
|
'Full House',
|
|
|
|
'Four of a Kind',
|
|
|
|
'Straight Flush',
|
|
|
|
'Royal Flush'
|
|
|
|
]
|
|
|
|
def __init__(self, cards):
|
2010-03-31 14:06:26 +00:00
|
|
|
self.__rank = None
|
|
|
|
self.__cards = sorted([Card(c) for c in cards], cmp=Card.compare, reverse=True)
|
|
|
|
self.__values = []
|
2010-03-31 13:54:35 +00:00
|
|
|
self.rank()
|
|
|
|
def __str__(self):
|
|
|
|
return str.format("Cards: {0} Rank: '{1}' Values: {2}",
|
2010-03-31 14:06:26 +00:00
|
|
|
[str(c.value) + c.suit for c in self.__cards],
|
2010-03-31 13:54:35 +00:00
|
|
|
Hand.RANKS[self.rank()],
|
|
|
|
self.values())
|
|
|
|
def rank(self):
|
2010-03-31 14:06:26 +00:00
|
|
|
if self.__rank:
|
|
|
|
return self.__rank
|
2010-03-31 13:54:35 +00:00
|
|
|
flush = True
|
|
|
|
straight = False
|
|
|
|
last = None
|
|
|
|
merged = {}
|
2010-03-31 14:06:26 +00:00
|
|
|
for c in self.__cards:
|
2010-03-31 13:54:35 +00:00
|
|
|
if last:
|
|
|
|
if flush and c.suit != last.suit:
|
|
|
|
flush = False
|
|
|
|
last = c
|
|
|
|
if c.value in merged:
|
|
|
|
merged[c.value] = merged[c.value] + 1
|
|
|
|
else:
|
|
|
|
merged[c.value] = 1
|
|
|
|
if (len(merged)) == 5:
|
|
|
|
# All unique cards, check for a straight
|
2010-03-31 14:06:26 +00:00
|
|
|
if self.__cards[0].value - self.__cards[4].value == 4 or \
|
|
|
|
(self.__cards[4].value == 2 and self.__cards[1].value == 5 and self.__cards[0].value == 14):
|
2010-03-31 13:54:35 +00:00
|
|
|
straight = True
|
|
|
|
if straight and flush:
|
2010-03-31 14:06:26 +00:00
|
|
|
if self.__cards[0].value == 14:
|
|
|
|
self.__rank = Hand.ROYAL_FLUSH
|
2010-03-31 13:54:35 +00:00
|
|
|
else:
|
2010-03-31 14:06:26 +00:00
|
|
|
self.__rank = Hand.STRAIGHT_FLUSH
|
2010-03-31 13:54:35 +00:00
|
|
|
elif flush:
|
2010-03-31 14:06:26 +00:00
|
|
|
self.__rank = Hand.FLUSH
|
2010-03-31 13:54:35 +00:00
|
|
|
elif straight:
|
2010-03-31 14:06:26 +00:00
|
|
|
self.__rank = Hand.STRAIGHT
|
2010-03-31 13:54:35 +00:00
|
|
|
else:
|
2010-03-31 14:06:26 +00:00
|
|
|
self.__rank = Hand.HIGH_CARD
|
|
|
|
self.__values = [c.value for c in self.__cards]
|
2010-03-31 13:54:35 +00:00
|
|
|
else:
|
|
|
|
multiples = [m for m in sorted(merged.items(), key = operator.itemgetter(1), reverse = True) if m[1] > 1]
|
|
|
|
if len(multiples) > 1:
|
|
|
|
if multiples[0][1] == multiples[1][1]:
|
2010-03-31 14:06:26 +00:00
|
|
|
self.__rank = Hand.TWO_PAIRS
|
2010-03-31 13:54:35 +00:00
|
|
|
else:
|
2010-03-31 14:06:26 +00:00
|
|
|
self.__rank = Hand.FULL_HOUSE
|
2010-03-31 13:54:35 +00:00
|
|
|
else:
|
|
|
|
if multiples[0][1] > 3:
|
2010-03-31 14:06:26 +00:00
|
|
|
self.__rank = Hand.FOUR_OF_A_KIND
|
2010-03-31 13:54:35 +00:00
|
|
|
elif multiples[0][1] == 3:
|
2010-03-31 14:06:26 +00:00
|
|
|
self.__rank = Hand.THREE_OF_A_KIND
|
2010-03-31 13:54:35 +00:00
|
|
|
else:
|
2010-03-31 14:06:26 +00:00
|
|
|
self.__rank = Hand.ONE_PAIR
|
2010-03-31 13:54:35 +00:00
|
|
|
mvalues = [m[0] for m in multiples]
|
2010-03-31 14:06:26 +00:00
|
|
|
self.__values = mvalues + [c.value for c in self.__cards if c.value not in mvalues]
|
2010-03-31 13:54:35 +00:00
|
|
|
|
2010-03-31 14:06:26 +00:00
|
|
|
return self.__rank
|
2010-03-31 13:54:35 +00:00
|
|
|
def values(self):
|
2010-03-31 14:06:26 +00:00
|
|
|
if not self.__values:
|
2010-03-31 13:54:35 +00:00
|
|
|
self.rank()
|
2010-03-31 14:06:26 +00:00
|
|
|
return self.__values
|
2010-03-31 13:54:35 +00:00
|
|
|
@staticmethod
|
2010-03-31 19:48:53 +00:00
|
|
|
def create_best_hand(cards):
|
|
|
|
if len(cards) == 5:
|
|
|
|
return Hand(cards)
|
|
|
|
elif len(cards) < 5:
|
|
|
|
raise InvalidHand
|
|
|
|
else:
|
|
|
|
return Hand.create_best_hand_bruteforce(cards)
|
|
|
|
return false
|
|
|
|
@staticmethod
|
|
|
|
def create_best_hand_bruteforce(cards):
|
|
|
|
combos = unique_combinations(cards, 5)
|
|
|
|
hands = [Hand(combo) for combo in combos]
|
|
|
|
hands = sorted(hands, cmp=Hand.compare, reverse=True)
|
|
|
|
return hands[0]
|
|
|
|
@staticmethod
|
|
|
|
def create_best_hand_smart(cards):
|
|
|
|
#TODO: Figure out a smarter algorithm for getting the best hand!
|
|
|
|
pass
|
|
|
|
@staticmethod
|
2010-03-31 13:54:35 +00:00
|
|
|
def compare(a, b):
|
|
|
|
# Compare hand rankings
|
|
|
|
result = cmp(a.rank(), b.rank())
|
|
|
|
if (result == 0):
|
|
|
|
# Compare hand values
|
|
|
|
for i in range(len(a.values())):
|
|
|
|
result = cmp(a.values()[i], b.values()[i])
|
|
|
|
if (result != 0):
|
|
|
|
return result
|
|
|
|
return result
|