Consider sorted lists of length 7 where each entry x is a number 2 <= x <= 14 and the duplicate count of any entry can't exceed 4. The assumption here is that a high-performance algorithm has already determined that we have a non-straight / non-flush hand.
Online poker sites would be interested in high-performance algorithms taking care of the next step of getting the 5 best cards in this context.
Writing a program in Python that doesn't import modules is a great way to prototype such algorithms.
Most likely the 'monster size' online poker sites don't need any help from us, but it would be interesting to see algorithms designed for speed. In the question
from 2010 this was examined, but many links are broken. It would be nice to know the status of the fastest known algorithms used today.
Question: Is the algorithm discussed below known? Has some algorithm been determined to be a standout for performance?
My work
I noticed that a list of length 7 has a midpoint and that there is 'combinatorial symmetry' and structure that an algorithm can incorporate. We implement this logic in the following code. One can think of a lightening fast program written in assembler that calculates a GOTO
number offset with the solution.
Note: I also have a one-pass sort routine that takes any 7 cards and determines whether straight or flushes can be made. But I've been advised to keep my questions more focused, so that is not discussed here.
Python Program:
hand=[2,2,7,7,8,11,12]
hand=[2,3,4,7,7,7,11]
start_spot = 3
end_spot = 3
if hand[3] == hand[4]:
if hand[4] == hand[5]:
if hand[5] == hand[6]:
end_spot = 6
else:
end_spot = 5
else:
end_spot = 4
if hand[3] == hand[2]:
if hand[2] == hand[1]:
if hand[1] == hand[0]:
start_spot = 0
else:
start_spot = 1
else:
start_spot = 2
if end_spot - start_spot == 3:
if end_spot == 6:
Kick = hand[start_spot-1]
else:
Kick = hand[6]
best5 = [Kick,hand[start_spot],hand[start_spot+1],hand[start_spot+2],hand[start_spot+3]]
print(hand, best5, 'four of a kind')
raise SystemExit
else:
pass
def multCount(c1,c2,c3):
pc = 0
if c1 == c2: pc = pc + 1
if c2 == c3: pc = pc + 10
return pc
pc_r = multCount(hand[4],hand[5],hand[6])
pc_l = multCount(hand[2],hand[1],hand[0])
if start_spot == 3 and end_spot == 3:
if pc_l == 0 and pc_r == 0:
best5 = [hand[2],hand[3],hand[4],hand[5],hand[6]]
print(hand, best5, 'no pair')
raise SystemExit
elif pc_l == 0 and pc_r == 1:
best5 = [hand[2],hand[3],hand[6],hand[4],hand[5]]
print(hand, best5, 'one pair')
raise SystemExit
elif pc_l == 0 and pc_r == 10:
best5 = [hand[2],hand[3],hand[4],hand[5],hand[6]]
print(hand, best5, 'one pair')
raise SystemExit
elif pc_l == 0 and pc_r == 11:
best5 = [hand[2],hand[3],hand[4],hand[5],hand[6]]
print(hand, best5, 'trips')
raise SystemExit
elif pc_l == 1 and pc_r == 0:
best5 = [hand[4],hand[5],hand[6],hand[1],hand[2]]
print(hand, best5, 'one pair')
raise SystemExit
elif pc_l == 1 and pc_r == 1:
best5 = [hand[6],hand[1],hand[2],hand[4],hand[5]]
print(hand, best5, 'two pair')
raise SystemExit
elif pc_l == 1 and pc_r == 10:
best5 = [hand[4],hand[1],hand[2],hand[5],hand[6]]
print(hand, best5, 'two pair')
raise SystemExit
elif pc_l == 1 and pc_r == 11:
best5 = [hand[1],hand[2],hand[4],hand[5],hand[6]]
print(hand, best5, 'full house')
raise SystemExit
elif pc_l == 10 and pc_r == 0:
best5 = [hand[4],hand[5],hand[6],hand[0],hand[1]]
print(hand, best5, 'one pair')
raise SystemExit
elif pc_l == 10 and pc_r == 1:
best5 = [hand[6],hand[0],hand[1],hand[4],hand[5]]
print(hand, best5, 'two pair')
raise SystemExit
elif pc_l == 10 and pc_r == 10:
best5 = [hand[4],hand[0],hand[1],hand[5],hand[6]]
print(hand, best5, 'two pair')
raise SystemExit
elif pc_l == 10 and pc_r == 11:
best5 = [hand[0],hand[1],hand[4],hand[5],hand[6]]
print(hand, best5, 'full house')
raise SystemExit
elif pc_l == 11 and pc_r == 0:
best5 = [hand[5],hand[6],hand[0],hand[1],hand[2]]
print(hand, best5, 'trips')
raise SystemExit
elif pc_l == 11 and pc_r == 1:
best5 = [hand[4],hand[5],hand[0],hand[1],hand[2]]
print(hand, best5, 'full house')
raise SystemExit
elif pc_l == 11 and pc_r == 10:
best5 = [hand[5],hand[6],hand[0],hand[1],hand[2]]
print(hand, best5, 'full house')
raise SystemExit
elif pc_l == 11 and pc_r == 11:
best5 = [hand[1],hand[2],hand[4],hand[5],hand[6]]
print(hand, best5, 'full house')
raise SystemExit
else:
pass
if start_spot == 3 and end_spot == 4:
if pc_l == 0 and pc_r == 0:
best5 = [hand[2],hand[5],hand[6],hand[3],hand[4]]
print(hand, best5, 'one pair')
raise SystemExit
elif pc_l == 0 and pc_r == 1:
print("ERROR 1")
pass # can't happen
raise SystemExit
elif pc_l == 0 and pc_r == 10:
best5 = [hand[2],hand[3],hand[4],hand[5],hand[6]]
print(hand, best5, 'two pair')
raise SystemExit
elif pc_l == 0 and pc_r == 11:
print("ERROR 2")
pass # can't happen
raise SystemExit
elif pc_l == 1 and pc_r == 0:
best5 = [hand[6],hand[1],hand[2],hand[3],hand[4]]
print(hand, best5, 'two pair')
raise SystemExit
elif pc_l == 1 and pc_r == 1:
print("ERROR 3")
pass # can't happen
raise SystemExit
elif pc_l == 1 and pc_r == 10:
best5 = [hand[2],hand[3],hand[4],hand[5],hand[6]]
print(hand, best5, 'two pair')
raise SystemExit
elif pc_l == 1 and pc_r == 11:
print("ERROR 4")
pass # can't happen
raise SystemExit
elif pc_l == 10 and pc_r == 0:
best5 = [hand[6],hand[0],hand[1],hand[3],hand[4]]
print(hand, best5, 'two pair')
raise SystemExit
elif pc_l == 10 and pc_r == 1:
print("ERROR 5")
pass # can't happen
raise SystemExit
elif pc_l == 10 and pc_r == 10:
best5 = [hand[4],hand[0],hand[1],hand[5],hand[6]]
print(hand, best5, 'two pair')
raise SystemExit
elif pc_l == 10 and pc_r == 11:
print("ERROR 6")
pass # can't happen
raise SystemExit
elif pc_l == 11 and pc_r == 0:
best5 = [hand[3],hand[4],hand[0],hand[1],hand[2]]
print(hand, best5, 'full house')
raise SystemExit
elif pc_l == 11 and pc_r == 1:
print("ERROR 7")
pass # can't happen
raise SystemExit
elif pc_l == 11 and pc_r == 10:
best5 = [hand[5],hand[6],hand[0],hand[1],hand[2]]
print(hand, best5, 'full house')
raise SystemExit
elif pc_l == 11 and pc_r == 11:
print("ERROR 8")
pass # can't happen
raise SystemExit
else:
pass
if start_spot == 2 and end_spot == 3:
if pc_l == 0 and pc_r == 0:
best5 = [hand[4],hand[5],hand[6],hand[2],hand[3]]
print(hand, best5, 'one pair')
raise SystemExit
elif pc_l == 0 and pc_r == 1:
best5 = [hand[6],hand[2],hand[3],hand[4],hand[5]]
print(hand, best5, 'two pair')
raise SystemExit
elif pc_l == 0 and pc_r == 10:
best5 = [hand[4],hand[2],hand[3],hand[5],hand[6]]
print(hand, best5, 'two pair')
raise SystemExit
elif pc_l == 0 and pc_r == 11:
print("ERROR 9")
pass # can't happen
raise SystemExit
elif pc_l == 1 and pc_r == 0:
print("ERROR 10")
pass # can't happen
raise SystemExit
elif pc_l == 1 and pc_r == 1:
print("ERROR 11")
pass # can't happen
raise SystemExit
elif pc_l == 1 and pc_r == 10:
print("ERROR 12")
pass # can't happen
raise SystemExit
elif pc_l == 1 and pc_r == 11:
print("ERROR 13")
pass # can't happen
raise SystemExit
elif pc_l == 10 and pc_r == 0:
best5 = [hand[6],hand[0],hand[1],hand[2],hand[3]]
print(hand, best5, 'two pair')
raise SystemExit
elif pc_l == 10 and pc_r == 1:
best5 = [hand[6],hand[2],hand[3],hand[4],hand[5]]
print(hand, best5, 'two pair')
raise SystemExit
elif pc_l == 10 and pc_r == 10:
best5 = [hand[4],hand[2],hand[3],hand[5],hand[6]]
print(hand, best5, 'two pair')
raise SystemExit
elif pc_l == 10 and pc_r == 11:
print("ERROR 14")
pass # can't happen
raise SystemExit
elif pc_l == 11 and pc_r == 0:
print("ERROR 15")
pass # can't happen
raise SystemExit
elif pc_l == 11 and pc_r == 1:
print("ERROR 16")
pass # can't happen
raise SystemExit
elif pc_l == 11 and pc_r == 10:
print("ERROR 17")
pass # can't happen
raise SystemExit
elif pc_l == 11 and pc_r == 11:
print("ERROR 18")
pass # can't happen
raise SystemExit
else:
pass
if start_spot == 2 and end_spot == 4:
if pc_l == 0 and pc_r == 0:
best5 = [hand[5],hand[6],hand[2],hand[3],hand[4]]
print(hand, best5, 'trips')
raise SystemExit
elif pc_l == 0 and pc_r == 1:
print("ERROR 19")
pass # can't happen
raise SystemExit
elif pc_l == 0 and pc_r == 10:
best5 = [hand[5],hand[6],hand[2],hand[3],hand[4]]
print(hand, best5, 'full house')
raise SystemExit
elif pc_l == 0 and pc_r == 11:
print("ERROR 20")
pass # can't happen
raise SystemExit
elif pc_l == 1 and pc_r == 0:
print("ERROR 21")
pass # can't happen
raise SystemExit
elif pc_l == 1 and pc_r == 1:
print("ERROR 22")
pass # can't happen
raise SystemExit
elif pc_l == 1 and pc_r == 10:
print("ERROR 23")
pass # can't happen
raise SystemExit
elif pc_l == 1 and pc_r == 11:
print("ERROR 24")
pass # can't happen
raise SystemExit
elif pc_l == 10 and pc_r == 0:
best5 = [hand[0],hand[1],hand[2],hand[3],hand[4]]
print(hand, best5, 'full house')
raise SystemExit
elif pc_l == 10 and pc_r == 1:
print("ERROR 25")
pass # can't happen
raise SystemExit
elif pc_l == 10 and pc_r == 10:
best5 = [hand[5],hand[6],hand[2],hand[3],hand[4]]
print(hand, best5, 'full house')
raise SystemExit
elif pc_l == 10 and pc_r == 11:
print("ERROR 26")
pass # can't happen
raise SystemExit
elif pc_l == 11 and pc_r == 0:
print("ERROR 27")
pass # can't happen
raise SystemExit
elif pc_l == 11 and pc_r == 1:
print("ERROR 28")
pass # can't happen
raise SystemExit
elif pc_l == 11 and pc_r == 10:
print("ERROR 29")
pass # can't happen
raise SystemExit
elif pc_l == 11 and pc_r == 11:
print("ERROR 30")
pass # can't happen
raise SystemExit
else:
pass
if start_spot == 1 and end_spot == 3:
if pc_r == 0:
best5 = [hand[5],hand[6],hand[1],hand[2],hand[3]]
print(hand, best5, 'trips')
raise SystemExit
elif pc_r == 1:
best5 = [hand[4],hand[5],hand[1],hand[2],hand[3]]
print(hand, best5, 'full house')
raise SystemExit
elif pc_r == 10:
best5 = [hand[5],hand[6],hand[1],hand[2],hand[3]]
print(hand, best5, 'full house')
raise SystemExit
elif pc_r == 11:
best5 = [hand[2],hand[3],hand[4],hand[5],hand[6]]
print(hand, best5, 'full house')
raise SystemExit
else:
pass
if start_spot == 3 and end_spot == 5:
if pc_l == 0:
best5 = [hand[2],hand[6],hand[3],hand[4],hand[5]]
print(hand, best5, 'trips')
raise SystemExit
elif pc_l == 1:
best5 = [hand[1],hand[2],hand[3],hand[4],hand[5]]
print(hand, best5, 'full house')
raise SystemExit
elif pc_l == 10:
best5 = [hand[0],hand[1],hand[3],hand[4],hand[5]]
print(hand, best5, 'full house')
raise SystemExit
elif pc_l == 11:
best5 = [hand[1],hand[2],hand[4],hand[5],hand[6]]
print(hand, best5, 'full house')
raise SystemExit
else:
pass
print("ERROR 99")
pass # can't happen
raise SystemExit
I believe I've implemented the algorithm you specify, but its very easy to make a mistake so let me know if there's any issues. I pretty much just do four core evaluations on the hand
From these I determine the best hand a particular combination of 5 cards can make in a series of if-statements, and sort the cards accordingly. I then create a list of all 5-card combinations from a set of 7, determine the max / best hand using this logic (I rearrange the score and hand order to accomplish this), and return that hand.
from collections import Counter
from itertools import combinations
def num_of_kind(cards):
return Counter(c[0] for c in cards)
def count_pairs(cards):
return sum(i > 1 for i in num_of_kind(cards).values())
def largest_pair(cards):
return max(num_of_kind(cards).values())
def is_straight(cards):
values = [c[0] for c in cards]
index = "A23456789TJQKA"["K" in values:].index
indices = sorted(index(v) for v in values)
return all(x == y for x, y in enumerate(indices, indices[0]))
def is_flush(cards):
suit_pop = Counter(c[1] for c in cards)
return any(s > 4 for s in suit_pop.values())
def straight_sort(cards):
values = [c[0] for c in cards]
index = "A23456789TJQKA"["K" in values:].index
return sorted(cards, key=lambda x:index(x[0]), reverse=True)
def flush_sort(cards):
suit_pop = Counter(c[1] for c in cards)
return sorted(cards, key=lambda x: suit_pop[x[1]], reverse=True)
def pair_sort(cards):
num = num_of_kind(cards)
return sorted(cards, key=lambda x: num[x[0]], reverse=True)
def card_vals(cards):
return [c[0] for c in cards]
def score_hand(cards):
pairs = count_pairs(cards)
largest = largest_pair(cards)
straight = is_straight(cards)
flush = is_flush(cards)
cards = straight_sort(cards)
hand_score = 0
if flush and straight:
hand_score, cards = 8, flush_sort(cards)
elif largest == 4:
hand_score, cards = 7, pair_sort(cards)
elif pairs == 2 and largest == 3:
hand_score, cards = 6, pair_sort(cards)
elif flush:
hand_score, cards = 5, flush_sort(cards)
elif straight:
hand_score = 4
elif largest == 3:
hand_score, cards = 3, pair_sort(cards)
else:
hand_score, cards = pairs, pair_sort(cards)
return hand_score, card_vals(cards), cards
def best_hand(cards):
cards = max(list(combinations(cards, 5)), key=score_hand)
score, _, hand = score_hand(cards)
return hand[::-1], score
def main():
hand = ['2c','Ah','3d', '5s','4h','5d', '3h']
print(*best_hand(hand))
if __name__ == "__main__":
main()
I could bore you with the details of every method, but I feel like its all fairly self-explanatory. The only tricky bit is in is_straight()
, and that's just some indexing trickery to determine if the card values can be ordered in sequential order.