Quote:
Originally Posted by CallMeIshmael
Some of this might be personal preference, but the entire use of the hardcode rank variable seems overly confusing, with no real upside that I can see. (super fast hand evaluators that ive seen have some confusing ass dictionaries but that is for performance reasons)
I think if you convert the straight and flush variables to bool, you can get code that looks like:
if (straight and flush):
return "straight flush"
if (flush):
return "flush"
if (straight):
return "straight"
How you want to handle the counts variable could go a couple ways, but, again imo, you should always have a reason to do something other than the most readable thing in a function, and the readable thing is to sort the counts variable, and say
if 1,1,1,1,1, return high card
if 1,1,1,2, return 1 pair etc
to me, this lines up better with the actual logic of the problem we are solving
I agree that the handcode calculation is confusing. It turns out that I don't need it, and I can instead use the count variable plus the straight and flush indicators as dictionary keys for the hand ranks.
Quote:
Originally Posted by daveT
Code:
flush = False
if len(set(s)) == 1:
flush = True
I don't like that either, because you are assuming 5 cards and this breaks with 7 cards, but if you are strictly dealing with 5 cards, you only have to do:
This is going to be much clearer. I'm not entirely sure on this, but I think that len() is also a referenced number, so that you don't do an iteration over the list each time you ask for len() as the len() value is carried around. Minor speed optimization if this is true, but something else to consider.
I also think you should put this in a function, like so:
Code:
def is_flush (s):
do some test:
return True
flush = is_flush(c)
The rank function is only going to be used with 5-card hands. My plan is to add other functions that break 7-card hands into all possible 5 card hands to determine the best 5-card hand.
The way I have set it up, the hand_suits variable will take values like ['s', 'c', 's', 'h', 'd'] and will always have a length of 5. So I could test for a flush by asking something like
Code:
hand_suits.count(hand_suits[0]) == 5
or by asking
Code:
len(set(hand_suits)) == 1
Not sure if there is a reason to prefer one over the other.
Does it only make sense to define flush as a separate function if I expect other functions to call it, or is it good practice to break it out even if it only used in the rank function? What is the cutoff for breaking out sub-programs?
Anyway, here is the latest version of my code, now with a testing function.
Code:
VALUES = map(str, range(2, 10)) + ['T', 'J', 'Q', 'K', 'A']
CARD_RANK = dict(zip(VALUES, range(0, 13)))
HAND_RANK = {
(1,1,1,1,1,'s','f'): ('Straight flush', 9), (4,1): ('Four of a kind', 8),
(3,2): ('Full house', 7), (1,1,1,1,1,'f'): ('Flush', 6),
(1,1,1,1,1,'s'): ('Straight', 5), (3,1,1):('Three of a kind', 4),
(2,2,1): ('Two pair', 3), (2,1,1,1): ('One pair', 2),
(1,1,1,1,1):('High card', 1)
}
def rank(hand, card_rank, hand_rank):
"""Assumes hand is a list of format ['Tc', '2h', 'As', '2c', 'Qd']
Returns tuple with a string describing the hand's rank and a number
corresponding to the rank's relative strength, e.g., ('One pair', 2)"""
hand_vals, hand_suits, counts, i, j = [], [], [], 0, 0
for v, s in hand:
hand_vals.append(card_rank[v])
hand_suits.append(s)
hand_vals.sort()
while i < len(hand_vals):
counts.append(hand_vals.count(hand_vals[i]))
i += counts[j]
j += 1
counts.sort(reverse = True)
if ((hand_vals[4] - hand_vals[0] == 4) or (hand_vals == [0, 1, 2, 3, 12])) \
and counts[0] == 1:
counts.append('s')
if len(set(hand_suits)) == 1:
counts.append('f')
return hand_rank[tuple(counts)]
def test_rank(hand, card_rank, hand_rank):
answer = rank(hand, card_rank, hand_rank)
print 'Test hand is:', hand
print 'Test hand rank is:', answer
h1 = ['5s', '4s', '8s', '6s', '7s']
h2 = ['3h', '3s', '4c', '3c', '3d']
h3 = ['7h', '7d', 'Ac', 'Ac', '7c']
h4 = ['2c', '5c', 'Jc', '7c', 'Tc']
h5 = ['Ac', '5c', '3s', '4h', '2h']
h6 = ['8h', '7s', 'Jh', '9d', 'Tc']
h7 = ['6c', '3h', 'As', '6d', '6h']
h8 = ['Ac', '8d', '8h', '2c', 'Ah']
h9 = ['7s', '7h', '9d', 'Td', 'Jh']
h10 = ['2c', '4c', '9s', 'Jh', '7h']
test_hands = [h1, h2, h3, h4, h5, h6, h7, h8, h9, h10]
for h in test_hands:
test_rank(h, CARD_RANK, HAND_RANK)