Introduction
We programmers like when problems can be solved with a single, general formula and we generally dislike when we need to manually handle small edge cases. I want to talk about such an edge case that occurred in a little poker program I am writing.
Order please!
My poker program is a hand simulator: given two starting hands, it’ll compute their probabilities of winning at showdown (this is going to be pretty much the same thing as PokerStove). One of the thing I need to do in my program is determine what kind of hand a player has. There are 9 different types of hands in poker. Here they are in decreasing strength order:
- Straight flush
- Four of a kind
- Full house
- Flush
- Straight
- Three of a kind
- Two pairs
- One pair
- High card
(Note: a royal flush is a kind of straight flush.)
Before a hand is properly classified, the cards are sorted to make the classification functions easier to code. The cards are first ordered by the number of cards of the same rank that appear in the hand and in decreasing order of strength. Here are some examples:
- 5-5-5-T-T: The three fives come first because there’s three of them, then the tens
- 9-9-3-3-6: We have two pairs, so we put the highest one first, the second pair next and the lonely card last
- A-8-7-4-3: There are no two cards of the same rank here, so the cards are simply ordered by rank
The hand is then tested for all types of hands from strongest to weakest and the classification stops as soon as a match has been found. For example, to test for four of a kind we verify that card[0] = card[1] = card[2] = card[3] and later we can test for two pairs with card[0] = card[1] and card[2] = card[3] without having to test that card[2] and card[3] are different from card[0] and card[1].
This ordering also makes it easy to determine the winner when two hands have the same type: we just go from the first card to the last, testing which one is the higher card. If after the fifth card no card was higher, we have a tie. This works for all types of hands. Almost.
Why did they invent the wheel?
In poker terms, a wheel is a 5-high straight; the ace is considered both high and low, so A-2-3-4-5 is actually a straight. And this hand, which occurs less than 1% of the time, is the one that requires the most attention.
First, it makes testing for a straight harder. The function for testing a straight is easy with our ordering: starting at the second card, we test that the current card is exactly one rank lower than the card before. This work from A-K-Q-J-T down to 6-5-4-3-2. With a wheel however, we have A-5-4-3-2 and 5 is not one rank lower than an ace and a straight isn’t found. If no straight was found, we need to manually check if the hand is a wheel.
Second, if we used a simple card-by-card analysis to determine which of two straights (or straight flushes) is the higher one, the wheel would actually lose only to broadway (A-K-Q-J-T); it would (incorrectly) win against a straight such as 7-6-5-4-3, because the ace is higher than the 7, even though a 7-high straight beats a 5-high straight. To fix this problem, we must add extra code to our comparison function when we are dealing with straights and straight flushes:
- If the two hands are wheel, we have a tie
- If the first hand is a wheel, then the second hand is the winner
- If the second hand is a wheel, then the first hand is the winner
If none of these tests pass, we can use our card-by-card analysis. (Hands that aren’t straights or straight flushes just skip this part, of course.)
The net result of the wheel is that my functions to test for straights and for comparing hands are twice as long as they would be if wheels were not considered straights. The little less-than-1% case causes the code of two functions to increase by 100%.
Hi gnuvince,
Instead of handling the wheel as a particular kind of straight, could you treat it as another kind of hand that would loose to a straight, but win to a three of a kind?
It would be quite easy to test for it (card[0] == A && card[1] == 5 && card[4] == 2) and function for comparing hands would work as is since all wheels are the same, they will always compare as a draw, which is what you want.
@mathmoi: I actually have a Git branch that works like that. I have WheelFlush and Wheel that are just below StraightFlush and Straight in strength. It works, and makes the comparison code simpler, but I’m a little uneasy about the code no longer completely reflecting the rules of poker; a wheel is not a separate kind of hand, it’s just a straight.
I’m not sure which branch I’ll eventually settle on, but that’s the beauty of branches: I can put that off until I need to decide.
I recently solved this in Ruby. You can see my solution here: https://github.com/gvaughn/ruby_kata/blob/master/poker/lib/hand.rb
Note the is_straight? method. It’s the only ‘if’ statement in my entire solution.
Hello there, I found your website via Google while looking for a related topic, your web site came up, it looks good. I’ve bookmarked it in my google bookmarks.
Youre so cool! I dont suppose Ive read anything like this before. So good to find anyone with some original ideas on this subject. realy thanks for starting this up. this web site is something that’s wanted on the web, somebody with slightly originality. helpful job for bringing one thing new to the internet!