I have written code like this before (but with cards for poker). A certain amount of code-sprawl is unavoidable to encode all of the rules of the game. For example, the code to look for n-of-a-kind will be completely different from the code to look for a straight.
Let's consider n-of-a-kind first. As others have suggested, create a dict
containing the counts of each element. Then:
counts = sorted(d.values())
if counts[-1] == 4:
return four_of_a_kind
if counts[-1] and counts[-2] == 3:
return two_sets_of_three
# etc.
Checking for straights requires a different approach. When checking for n-of-a-kind, you need to get the counts and ignore the values. Now we need to examine the values and ignore the counts:
ranks = set(rolls)
if len(ranks) == 6: # all six values are present
return long_straight
# etc.
In general, you should be able to identify rules with a similar flavor, abstract out code that helps with those kinds of rules, and then write just a few lines per rule. Some rules may be completely unique and will not be able to share code with other rules. That's just the way the cookie crumbles.