Poker Hands in Python
First impressions - this code is very clear.  I like the variable names - that makes lines like if straight and flush read very naturally.



I don't think there's a need to treat a Royal Flush separately - this is the same rank as Straight Flush, just with the largest possible high-card.  The "should not occur" comment is irrelevant - no matter how rare, we must correctly score Straight Flush (and we would expect a thorough test set to include some).



Instead of needing a separate tiebreaker function to evaluate the kickers, we could return a tuple from eval_hand, with the kickers following the rank+value.  We're already doing something like that for Full House; it's easy to extend that throughout.  I'd collect values in descending order:

values = sorted([c[0] for c in hand], reverse=True)
straight = (values == range(max(values), min(values)-1, -1)) # but see below


Then we can return as much as is necessary for the rank:

if straight and flush:
    return 8, values[0]




if three_of_a_kind and pair_present: return 6, three_value, pairs[0], values
if flush: return 5, values
if straight: return 4, values[0]
if three_of_a_kind: return 3, three_value, values
if len(pairs) == 2: return 2, pairs, values
if len(pairs) == 1: return 1, pairs[0], values




Counting pairs/trips/quads can be simplified, given that we have the values in sorted order.  We can use itertools.groupby() to get the count of identical values, rather than needing to search with values.count():

trips = []
pairs = []
for v, group in itertools.groupby(values):
    count = sum(1 for _ in group)
    if count == 4:
        return 7, v, values
    if count == 3:
        trips.append(v)
    elif count == 2:
        pairs.append(v)




Some additional test cases indicated a few problems:


The test for a straight doesn't work when I compare a list against a range (Python 3.6.5).  I needed to materialise it as a list first:

straight = values == list(range(values[0], values[-1]-1, -1))

We don't test for an ace-low straight:

straight = (values == list(range(values[0], values[0]-5, -1))
            or values == [14, 5, 4, 3, 2])

Flushes should be comparable - highest differing card still tie-breaks between flushes of different suits.


And some simplifications:


Straights and flushes can both be returned before checking for pairs/trips/quads
We don't need boolean flags to say that three_value or pairs is populated - we can directly test those variables are truthy or not.
For ranks 0, 1, 2, the rank is exactly the number of pairs found.
Instead of try/except, we can populate value_dict with the digits 2 to 9.
We're not required to find anything other than the number of Player 1 wins, so remove the code that was useful while debugging.




Applying the above, I get the following modified code:

import itertools

value_dict = {'T': 10, 'J': 11, 'Q': 12, 'K': 13, 'A': 14}
value_dict.update((str(x), x) for x in range(2,10))

def eval_hand(hand):
    # Return ranking followed by tie-break information.
    # 8. Straight Flush
    # 7. Four of a Kind
    # 6. Full House
    # 5. Flush
    # 4. Straight
    # 3. Three of a Kind
    # 2. Two pair
    # 1. One pair
    # 0. High card

    values = sorted([c[0] for c in hand], reverse=True)
    suits = [c[1] for c in hand]
    straight = (values == list(range(values[0], values[0]-5, -1))
                or values == [14, 5, 4, 3, 2])
    flush = all(s == suits[0] for s in suits)

    if straight and flush: return 8, values[1]
    if flush: return 5, values
    if straight: return 4, values[1]

    trips = []
    pairs = []
    for v, group in itertools.groupby(values):
        count = sum(1 for _ in group)
        if count == 4: return 7, v, values
        elif count == 3: trips.append(v)
        elif count == 2: pairs.append(v)

    if trips: return (6 if pairs else 3), trips, pairs, values
    return len(pairs), pairs, values


player1_wins = 0
with open("p054_poker.txt") as f:
    for line in f:
        cards = [(value_dict[x[0]], x[1]) for x in line.split(' ')]
        player1_wins += eval_hand(cards[:5]) &gt; eval_hand(cards[5:])

print(player1_wins)