def rankHand(cls, hand): """Rank a hold'em hand (pair or high card).""" if hand[0].rank == hand[1].rank: return PokerRank.pair(hand[0].rank, kickers=None) if hand[0].rank > hand[1].rank: return PokerRank.highCard(hand[0].rank, hand[1:]) else: return PokerRank.highCard(hand[1].rank, hand[:1])
def _rankFiveCardHand(cls, cards): """Get rank of five cards. This method is more efficient than _getRankSixPlusCards().""" cards.sort(reverse=True) isFlush = cards.sameSuit() straightRank = cls._isStraight(cards) # Do we have a straight flush? if (straightRank and isFlush): return PokerRank.straightFlush(straightRank) # Check for four of a kind if (cards[0].rank == cards[1].rank == cards[2].rank == cards[3].rank): return PokerRank.quads(cards[0].rank, cards[4:]) if (cards[1].rank == cards[2].rank == cards[3].rank == cards[4].rank): return PokerRank.quads(cards[1].rank, cards[0:1]) # Check for full house # -First two and last two cards must match each other # -Then middle card either matches first two cards # or last two cards if ((cards[0].rank == cards[1].rank) and (cards[3].rank == cards[4].rank)): if (cards[2].rank == cards[0].rank): # XXXYY return PokerRank.fullHouse(cards[0].rank, cards[3].rank) elif (cards[2].rank == cards[3].rank): # XXYYY return PokerRank.fullHouse(cards[2].rank, cards[0].rank) # Check for flush, which we've already done if isFlush: return PokerRank.flush(cards[0].rank, cards[1:]) # Check for Straight, which we've already done if straightRank: return PokerRank.straight(straightRank) # Check for trips if ((cards[0].rank == cards[1].rank == cards[2].rank) or (cards[1].rank == cards[2].rank == cards[3].rank) or (cards[2].rank == cards[3].rank == cards[4].rank)): # cards[2] will always be one of the trips primaryRank = cards[2].rank cards.removeRank(primaryRank) return PokerRank.trips(primaryRank, cards) # Check for two pair # At this point we know we don't have trips, so can optimize some if (cards[0].rank == cards[1].rank): if (cards[2].rank == cards[3].rank): return PokerRank.twoPair(cards[0].rank, cards[2].rank, cards[4:]) if (cards[3].rank == cards[4].rank): return PokerRank.twoPair(cards[0].rank, cards[3].rank, cards[2:3]) elif ((cards[1].rank == cards[2].rank) and (cards[3].rank == cards[4].rank)): return PokerRank.twoPair(cards[1].rank, cards[3].rank, cards[0:1]) # Check for a pair # At this point we know we don't have two pair or trips foundPair = False for index in range(4): if cards[index].rank == cards[index+1].rank: foundPair = True break if foundPair: pairRank = cards[index].rank del cards[index:index+2] return PokerRank.pair(pairRank, cards) # Just a high card return PokerRank.highCard(cards[0].rank, cards[1:])
def _rankSixOrSevenCardHand(cls, cards): """Given a set of 6 or 7 cards, return a PokerRank for its best hand. Will work for 5 cards, but is less efficient than _rankFiveCardHand().""" if len(cards) > 7: raise ValueError("Hand has too many cards (%d > 7)" % len(cards)) if len(cards) < 5: raise ValueError("Hand has too few cards (%d < 5)" % len(cards)) # Array of bitfields of cards by suit suitedBitFields = cls._handToSuitedBitFields(cards) # Check for straight-flush highRank = None for suit in Suit.suits: rank = cls._hasStraight(suitedBitFields[suit]) if rank and ((highRank is None) or (rank > highRank)): highRank = rank if highRank is not None: return PokerRank.straightFlush(highRank) # Get bitfields representing singletons, pairs, trips and quads (singletonsBitField, pairsBitField, tripsBitField, quadsBitField) = cls._suitedBitFieldsToRankedBitFields(suitedBitFields) # Single bitfield with all four suits merged for kickers and straights bitfield = (suitedBitFields[Suit.CLUBS] | suitedBitFields[Suit.DIAMONDS] | suitedBitFields[Suit.HEARTS] | suitedBitFields[Suit.SPADES]) # Check for quads if quadsBitField.setCount() > 0: rank = quadsBitField.highestSet() # Highest remaining card is our kicker kickerRank = bitfield.filterBits(rank).highestSet() return PokerRank.quads(rank, [kickerRank]) # Check for full house if ((tripsBitField > 0) & (pairsBitField > 0)): return PokerRank.fullHouse(tripsBitField.highestSet(), pairsBitField.highestSet()) # Check for flush flushBitField = None for suit in Suit.suits: if suitedBitFields[suit].setCount() >= 5: if ((flushBitField is None) or (suitedBitFields[suit] > flushBitField)): flushBitField = suitedBitFields[suit] if flushBitField: rank = flushBitField.highestSet() kickers = flushBitField.filterBits(rank).highestNSet(4) return PokerRank.flush(rank, kickers) # Check for straight # We don't care about suit anymore so can just operate on bitfield rank = cls._hasStraight(bitfield) if rank: return PokerRank.straight(rank) # Check for trips if (tripsBitField > 0): rank = tripsBitField.highestSet() kickers = bitfield.filterBits(rank).highestNSet(2) kickers = bitfield.highestNSet(2) return PokerRank.trips(rank, kickers) # Check for two pair if (pairsBitField.setCount() > 1): ranks = pairsBitField.highestNSet(2) kickers = bitfield.filterBits(ranks[0], ranks[1]) kicker = kickers.highestSet() return PokerRank.twoPair(ranks[0], ranks[1], [kicker]) # Check for pair if (pairsBitField > 0): rank = pairsBitField.highestSet() kickers = bitfield.filterBits(rank).highestNSet(3) return PokerRank.pair(rank, kickers) # High card highCard = bitfield.highestSet() kickers = bitfield.filterBits(highCard).highestNSet(4) return PokerRank.highCard(highCard, kickers)
def _rankHand(cls, hand): """Given a Hand, return its PokerRank for its best low hand. Limited to Hands of 5-7 cards.""" if len(hand) > 7: raise ValueError("Hand has too many cards (%d > 7)" % len(hand)) if len(hand) < 5: raise ValueError("Hand has too few cards (%d < 5)" % len(hand)) rankCounts = hand.countRanks() # Count ranks which we have at least one of atLeastOne = filter(lambda rank: rankCounts[rank] > 0, Rank.rankRange) # If we don't have at least two different ranks, we have five-of-a-kind if len(atLeastOne) < 2: raise PokerInternalException("Apparently have five of a kind with hand %s" % hand) if len(atLeastOne) >= 5: # We can make a hand without a pair return PokerRank.highCard(atLeastOne[4], atLeastOne[0:4]) # We didn't find 5 unpaired cards, figure out where we stand atLeastTwo = filter(lambda rank: rankCounts[rank] > 1, Rank.rankRange) if len(atLeastTwo) == 0: raise PokerInternalException("Invalid state (no pair) with hand %s" % hand) if len(atLeastOne) == 4: # We will have one pair which will be lowest rank remaining pairRank = atLeastTwo[0] # Remove pair to obtain kickers atLeastOne.remove(pairRank) return PokerRank.pair(pairRank, atLeastOne) elif (len(atLeastOne) == 3) & (len(atLeastTwo) >= 2): # We have two pair pairRank1 = atLeastTwo[0] pairRank2 = atLeastTwo[1] # Remove pairs to obtain kicker atLeastOne.remove(pairRank1) atLeastOne.remove(pairRank2) return PokerRank.twoPair(pairRank1, pairRank2, atLeastOne) # If we reach this point, we have at least trips. atLeastThree = filter(lambda rank: rankCounts[rank] > 2, Rank.rankRange) if len(atLeastThree) == 0: raise PokerInternalException("Invalid state (no trips) with hand %s" % hand) if len(atLeastOne) == 3: tripsRank = atLeastThree[0] # Remove trips to obtain kickers atLeastOne.remove(tripsRank) return PokerRank.trips(tripsRank, atLeastOne) # If we reach this point, we have a full house or quads if len(atLeastOne) != 2: raise PokerInternalException("Invalid state (less than two ranks) with hand %s" % hand) if len(atLeastTwo) > 1: # Remove trips from pairs to find full house fillers tripsRank = atLeastThree[0] atLeastTwo.remove(tripsRank) return PokerRank.fullHouse(tripsRank, atLeastTwo[0]) # If we reach this point, we have quads atLeastFour = filter(lambda rank: rankCounts[rank] > 3, Rank.rankRange) if len(atLeastFour) == 0: raise PokerInternalException("Invalid state (no quads) with hand %s" % hand) quadsRank = atLeastFour[0] # Remove quads to obtain kicker atLeastOne.remove(quadsRank) return PokerRank.quads(quadsRank, atLeastOne)