2012/04/28

7 card hands

Further to what I discussed here, interestingly enough one of the pieces of work in the Udacity course CS212 is to write a function to find the best hand of five cards in a group of 7.

So I've still got the problem of dealing out the flop (cards on the table) as well as the hands for each of the players, but I can come back to that, this is a 'killing two birds with one stone' moment.

Now, out of seven cards, let's call them C1-C7 (or C0-C6 if you prefer), how many different hands could I have?
Hmmm. Well, out of my set of 7, I can discard any two; and working through the sets 1-2, 1-3, etc up to 6-7, I can get 21 different sets; leaving me with, conversely, 21 sets of 5.

Remembering good coding practices, however, we want to generalise. So rather than doing something like:

for i in 21:
    hand = [1, 2, 3, 4, 5, 6, 7] - discard[i]

where discard = [(1,2), (1,3)...]

Actually, hang on - maybe that isn't so bad. As long as discard is generated by a function which is more general, discard itself can be fixed values.
To explain what I'm talking about here, discard takes the form of a list, with each item on the list being the two cards to discard, leaving a five-card hand.
It is possible to have a program where discard is 'hard-coded', meaning that my code literally says:
discard = [(1,2), (1,3), (1,4), (1,5), (1,6), (1,7), (2,3), (2,4), (2,5), (2,6), (2,7), (3,4), (3,5), (3,6), (3,7), (4,5), (4,6), (4,7), (5,6), (5,7), (6,7)]
Now that has three problems with it:
1) it's ugly. Yes, this matters!
2) it's error prone - what if I mis-typed one of those options? A hand will appear twice, or not appear at all. What is more, this means that this scales poorly - or in other words, if I had to do this for sets of 100, it's massively more difficult.
3) it's hard to adjust.
Having all that data hidden away in discard is fine.
In fact, I think it will be clearer if I generate discard elsewhere and then just refer to it here.

Right, enough messing about. I decided that Python must have a way of handling what I want to do; so I did some research and found: this Python documentation of something called itertools - or methods for achieving things which require a lot of iteration.
Specifically something called combinations. Combinations("ABC", 2) would give AB AC BC - perfect!
No need to mess about with what I was talking about with discard as a first step, I've jumped to having all the possible 5-card hands output from a 7-card input.
Now what I need to do is rank the 5-card hands against each other - well, all I need is to run hand_rank (a function already written as part of the Udacity material) - and it's job done.
So after all that, the result looks like this:
def best_hand(hand):
    "From a 7-card hand, return the best 5 card hand."
    hands = itertools.combinations(hand,5)
    return max(hands, key=hand_rank)
Back to the problem of dealing.

No comments:

Post a Comment