9

I am trying to write a poker hand evaluation method in c#. I have managed to do this for every poker hand using linq except a straight. For those that don't play a straight is made up of 5 cards with increments of 1 for each card. Ace can be high or low.

I have created an object called card which has a suit, rank and value (J = 11, Q =12 etc..). My method will be passed a list of this object containing 7 cards (hole cards and the board.)

Another thing to bear in mind is that a straight can only be made if the player has a 5 or 10.

See below my methods for other poker hands and please let me know if you have an idea for the straight method. Pseudo code would be fine also.


public bool CheckPair(List<Card> cards)
{
    //see if exactly 2 cards card the same rank.
    return cards.GroupBy(card => card.Rank).Count(group => group.Count() == 2) == 1;
}

public bool CheckTwoPair(List<Card> cards)
{
    //see if there are 2 lots of exactly 2 cards card the same rank.
    return cards.GroupBy(card => card.Rank).Count(group => group.Count() >= 2) == 2;
}

public bool CheckTrips(List<Card> cards)
{
    //see if exactly 3 cards card the same rank.
    return cards.GroupBy(card => card.Rank).Any(group => group.Count() == 3);
}
public bool CheckStraight(List<Card> cards)
{
    // order by decending to see order
    var cardsInOrder = cards.OrderByDescending(a => a.Value).ToList();
    // check for ace as can be high and low
    if (cardsInOrder.First().Rank == "A")
    {
        // check if straight with ace has has 2 values
        bool highStraight = cards.Where(a => a.Rank == "K" || a.Rank == "Q" || a.Rank == "J" || a.Rank == "10").Count() == 4;
        bool lowStraight = cards.Where(a => a.Rank == "2" || a.Rank == "3" || a.Rank == "4" || a.Rank == "5").Count() == 4;
        // return true if straight with ace
        if (lowStraight == true || highStraight == true)
        {
            return true;
        }
    }
    else
    {
        // check for straight here
        return true;
    }
    // no straight if reached here.
    return false;

}

public bool CheckFlush(List<Card> cards)
{
    //see if 5 or more cards card the same rank.
    return cards.GroupBy(card => card.Suit).Count(group => group.Count() >= 5) == 1;
}

public bool CheckFullHouse(List<Card> cards)
{
    // check if trips and pair is true
    return CheckPair(cards) && CheckTrips(cards);
}
public bool CheckQuads(List<Card> cards)
{
    //see if exactly 4 cards card the same rank.
    return cards.GroupBy(card => card.Rank).Any(group => group.Count() == 4);
}

// need to check same 5 cards
public bool CheckStraightFlush(List<Card> cards)
{
    // check if flush and straight are true.
    return CheckFlush(cards) && CheckStraight(cards);
}
RamenChef
  • 5,557
  • 11
  • 31
  • 43
  • Are you going this just for fun, or for real usage? Because for real usage there are existing libraries carefully designed to be very fast. – Evk Sep 22 '16 at 11:42
  • Possible duplicate of [Functional way to check if array of numbers is sequential](http://stackoverflow.com/questions/18225010/functional-way-to-check-if-array-of-numbers-is-sequential) – fubo Sep 22 '16 at 11:46
  • 5
    Hi, it's just for abit of a challange – Charlie Beesley Sep 22 '16 at 12:17
  • `lowStraight == true` is equivalent to `lowStraight`, so write that instead. – Jacob Krall Sep 22 '16 at 13:40
  • Your current check for a straight with an Ace is incorrect, BTW. As it stands AKQJJ would be counted as a straight. – D Stanley Sep 22 '16 at 14:18
  • This is very late but the check for two pair is also incorrect. In a 7 card per hand game (2 hole cards and 5 community cards), a player can have three pair but only the two highest pair count. For example, a player can have AAKKQQ2 cards as a valid hand but only AAKK are counted for the two pair. – Breakskater Feb 18 '21 at 02:49

4 Answers4

2

This might not be the best performing check, but I'd say it's very readable which is usually a good property.

Just grab 5 cards, skipping cards you've already seen every iteration and check for a straight for each sequence. An ordered sequence is a straight if it does not contain doubles and if the first and last cards difference is 5.

public bool CheckStraight(List<Card> cards)
{
     //maybe check 5 and 10 here first for performance

     var ordered = cards.OrderByDescending(a => a.Value).ToList();
     for(i = 0; i < ordered.Count - 5; i++) {
          var skipped = ordered.Skip(i);
          var possibleStraight = skipped.Take(5);
          if(IsStraight(possibleStraight)) {
               return true;
          }
     }
     return false;
}

public bool IsStraight(List<Card> fiveOrderedCards) {
     var doubles = cards.GroupBy(card => card.Rank).Count(group => group.Count() > 1);
     var inARow = cards[4] - cards[0] = 5; //Ace is 0

     return !doubles && inARow;
}
Glubus
  • 2,819
  • 1
  • 12
  • 26
  • This does not seem to detect a 10-J-Q-K-A straight. – wimh Sep 22 '16 at 12:01
  • That's probably because it's not happy about the "A". Are you sure the "A" is moddeled as a 15 there? Perhaps my comment in the code where I say Ace = 0 makes things confusing (lol). An Ace has to be equal to 0 when its a low straight, and 15 when it's a high straight. In what way you model that is up to you. (Hint: You could detect an ace and manually add it to the ordered list on both ends in the first method when the detection returns true). – Glubus Sep 22 '16 at 12:11
0

I have made some small changes to Glubus answer. The code below does the job however you will have to check manually for a wheel (A,1,2,3,4,5) straight.

public bool CheckStraight(List<Card> cards)
    {
        //maybe check 5 and 10 here first for performance

        var ordered = cards.OrderByDescending(a => a.Value).ToList();
        for (var i = 0; i < ordered.Count - 4; i++)
        {
            var skipped = ordered.Skip(i);
            var possibleStraight = skipped.Take(5).ToList();
            if (IsStraight(possibleStraight))
            {
                return true;
            }
        }
        return false;
    }
public bool IsStraight(List<Card> cards)
{
    return cards.GroupBy(card => card.Value).Count() == cards.Count() && cards.Max(card => (int)card.Value) - cards.Min(card => (int)card.Value) == 4;
    }
  • Make sure to close your question, either by marking the question as answered or deleting the question altogether. – Glubus Sep 26 '16 at 11:28
0

I cannot think about real one liner, since A can be 1 or 14, but this should be good:

int c = 5; // How many cards straight
bool Ais1 = cards.OrderBy(a => a.Value).Select((i,j) => i.Value-j).Distinct().Skip(1).Count() <= (cards.Count - c);
bool Ais14 = cards.OrderBy(a => (a.Value == 1 ? 14 : a.Value)).Select((i,j) => (i.Value == 1 ? 14 : i.Value)-j).Distinct().Skip(1).Count() <= (cards.Count - c);
return Ais1 || Ais14;

(Updated - thank you Janne, I've fixed the code)

Yoram Snir
  • 21
  • 3
  • BTW, if card will have {Rank, Value, HighValue} so {"A", 1, 14}, {"2", 2, 2} ... {"K", 13, 13} then code can look even nicer. – Yoram Snir Sep 24 '16 at 11:55
0

erm,

function bool IsStraight(IEnumerable<int> cards)
{
    var orderedCards = cards.OrderBy(n => n).ToList();
    var test = orderdCards.Zip(orderdCards.Skip(1), (a, b) => b - a);

    var count = 0;
    foreach(var n in test)
    {
        if (n == 1)
        {
            count++;
            if (count == 4)
            {
                return true;
            }
        }
        else
        {
            count = 0;
        }
    }

    return false;
}
Jodrell
  • 34,946
  • 5
  • 87
  • 124