It’s the classic: Poker Hands. Write a program to evaluate and compare two hands of cards. It’s a fun way to stretch some algorithm-building muscles and practice a little OOP while you’re at at.
The Rules
I started this challenge with very little card game knowledge. Aside from Go Fish, War and Spit, I don’t really play cards. My first step was to track down the rules. In order:
-
public enum HandStrength { RoyalFlush, // same suit, consecutively ranked, 10-Ace StraightFlush, // same suit, consecutively ranked cards FourOfAKind, // four cards same rank, break a tie with the high card FullHouse, // a set of three and a pair Flush, // same suit, ranks not consecutive Straight, // different suits, consecutively ranked cards ThreeOfAKind, // three cards same rank, same tie break as four of a kind TwoPair, // two pairs same rank, same tie break as four of a kind OnePair, // a single pair a same rank cards, same tie break as above HighCard // highest ranking card in the hand }
The job is to compare several pairs of poker hands and to indicate which, if either, has a higher rank. My eureka moment (yes, new to the game, remember?) was when I realized that this can be solved with three basic tests (ok, 4 if you count the HighCard):
-
// Straight: Returns true if all cards are consecutive values. public static bool Straight(PlayerHand playerHand) { SortByRank(playerHand); for (int i = 0; i < playerHand.Cards.Count-1; i++) { if ((playerHand.Cards[i].Rank + 1) != playerHand.Cards[i + 1].Rank) { return false; } } return true; } // Flush: All cards of the same suit. public static bool Flush(PlayerHand playerHand) { if (playerHand.Cards[0].Suit == playerHand.Cards[1].Suit) { Suit handSuit = playerHand.Cards[0].Suit; foreach (Card c in playerHand.Cards) { if (c.Suit != handSuit) { return false; } else { continue; } } return true; } else { return false; } return false; } // One Pair: Two cards of the same value. public static bool OnePair(PlayerHand playerHand) { SortByRank(playerHand); if (rankCount.ContainsValue(2)) { return true; } else { return false; } }
Most of these methods rely on a sorted hand of cards. One of the first things I discovered, though, was I needed to find a way to compare the ranks of two cards. To implement a Comparable for rank as one part of a Card, I used this:
// take two cards, compare Rank - return 1 if x > y, 0 if x == y, -1 if x < y public static int CompareCards(Card x, Card y) { if ((x == null && y == null) || (x.Rank == y.Rank)) { return 0; } // either they're both null or both equal else if (x.Rank > y.Rank) { return 1; } else // y.Rank > x.Rank is the only other option { return -1; } }
Then I used that Comparable in the Sort() function:
sorted.Cards.Sort(CompareCards);
As I wrote each evaluator, I wrote its matching tests (both for the practice with MS Unit Tests) and to check my logic.
[TestMethod] public void CanDeterminIfHandIsNotAStraightFlush() { Assert.IsFalse(HandEvaluator.StraightFlush(basicHand)); }
The next step was to stitch the evaluators together. My first thought was to populate an array with the evaluators and step through it. My initial attempts were, well, clunky.
Func<uint>[] evaluators = { isStraight, isFlush, twoPair, ...}; foreach (var e in evaluators) { // decision here }
During a code session, a colleague suggested I dig into lambdas as a solution. Pluralsight has been a terrific resource and my next post will take a deep dive into what I’m learning there.
That brings us to the next post: Learning Lambdas!