Question

For a homework assignment I was given a Card class that has enumerated types for the Rank and Suit. I am required to compare two poker hands (each hand is an ArrayList of 5 cards) and decide the winner.

The isStraight() function is really bothering me, because I have to start over the count after the Ace. For example,

QUEEN, KING, ACE, TWO, THREE

Is still considered a straight. What is the best way to code this functionality?

Here is the Rank/Suit enumerated type code, if that helps.

public enum Rank
{
    TWO(2), THREE(3), FOUR(4), FIVE(5), SIX(6), SEVEN(7), EIGHT(8), NINE(9),
    TEN(10), JACK(11), QUEEN(12), KING(13), ACE(14);

    private final int points;

    private Rank(int points)
    {
        this.points = points;
    }

    public int points()
    {
        return this.points;
    }
}

public enum Suit
{
    DIAMONDS, CLUBS, HEARTS, SPADES;
}
Was it helpful?

Solution

You do realize that by the rules of any poker game I've ever played or heard of a straight cannot wrap right? Ace can be low [A,2,3,4,5] or high [10,J,Q,K,A] but it can't wrap. According to those rules (not yours) I've implemented something similar before. Basically you sort the array and walk it, making sure the current card is one higher than the previous. In the first iteration, if it is an ace, then you explicitly check for [A,2,3,4,5]. If it is you return true and if it isn't you continue with the normal straight logic. This should set you in the right direction.

OTHER TIPS

A nice approach for resolving poker hands in general is to assign each card a bit value with bit ((rank-2)*2) bit set as well as bit (suit+28) set, (so 2=1, 3=4, 4=16, etc. up to A=0x1000000). Then add together all the cards (call that result 'Sum'. Compute V1=(Sum & 0x2AAAAAA)>>1, V0=(Sum & 0x1555555), and V2=V1 & V0. Also OR together the values for the five cards, and compute V3=OrValue & 0xF0000000;

  1. For a pair, V1 will have a single bit set, V0 will have multiple bits set, and V2 will be zero.
  2. For two-pair, V1 will have two bits set and V2 will equal zero.
  3. For a three-of-a-kind, V1 will have a single bit set, and V2 will equal V1.
  4. For a straight, V0 will either be 0x1000055 or else a power-of-two multiple of 0x155.
  5. For a flush, V2 will have precisely one bit set.
  6. For a full house, V1 will have two bits set, and V2 will be non-zero.
  7. For four-of-a-kind, either V1 will be twice v0, with both having one bit set, or V0 will have precisely two bits set and V1 will be zero.
  8. For a straight flush, conditions for straight and flush will be met.

This tests required for this approach should be implementable quickly with a minimal amount of branching.

You could write a fancy algorithm to return true despite the number of possible cards but if you realize there are only 10 valid combinations on a sorted hand, you could just look for these:

2-6, 3-7, 4-8, 5-9, 6-T, 7-J, 8-Q, 9-K, T-A, (2-5,A)

Since there's only 5 cards in you list, you could sort it and determine the difference between 2 consecutive cards. If it contains an ace, you need to consider it as a low card too. if all the differences are 1 (or -1, depending on the sort order), you have your straight.

I'd argue that given that definition of RANK, that straights can only start with a max of ACE.points() - 4.

So if you sort your hand and the lowest RANK is > ACE.points() - 4 then you can't have a straight, otherwise you just iterate over the hand to see that each card is previous RANK + 1.

If ACE can be high or low then go with what SHS answered.

With an inner loop it's pretty trivial, the challenge would be to do it without an inner loop...

Also, it depends on if you understood your teacher or your teacher misunderstood (or misrepresented) the rules of the game.

I think I'd be tempted to just create an array [2..14] and place the cards in the location that corresponds to their rank. If you hit a duplicate, it's not a straight, and when you are done, you should have 8 spaces in a row. If you have less than 8 spaces in a row, it's not a straight.

All the other solutions I can come up with require an inner loop--and inner loops are one of those sloppy programming things you need to avoid whenever you can if you're going to ever be a respectable programmer.

edit: Also, if you misunderstood the teacher and the only wrapping condition is "10,j,q,k,a" (like in the real rules), then you need an additional test that if all of 2, 13 and 14 are set, it's also a failure (2-a-k wraparound).

(Edited again to replace 1 for ace with 14 after re-reading the question)

I dont use enum much, i prefer named constants but i'll assume going from "ACE" to "14" is trivial

i'm too lazy to write real java code (beside you actually have to do your homework ^^)

check if the list has 5 cards
convert card names to a card number list named array
sort the list array
for i=1 to 4
if not (array[i] + 1) % 13 == (array[i+1]) % 13
then it is not a straight

The % operator is called modulo so (15 % 13) == 2 I use this operator whenever I face the "wrap over" challenge

Edit : After re-reading your question my solution cannot work out of the box. You should reorder your enum so that TWO == 0

I recommend using a bit vector to represent the cards. This avoids having to sort. You can add the ace twice (once as a 1 the other times as a king) or you can special case the starting situation by checking if the ace bit is set before checking if the 2 is set). You can build a big look up table if speed matters. This approach also cleaning scales to find the rest of the hands (flushes, 2 pair, full houses, trips, and so on). It also makes it easy to figure out if a given straight is higher than another. And it expands cleanly to 7 card evaluator

In pseudo code it looks something like this for a very general case (you can have any number of cards. It returns the first straight)

 long cardBitMask
 for each card in hand
   setBit in cardBitMask

 hearts = mask(cardBitMask)
 diamonds = mask(cardBitMask)
 clubs = mask(cardBitMask)
 spades = mask(cardBitMask)

 // find straight
 uniqueCards = hearts|diamonds|clubs|spades
 int cardsInaRow = 0
 if uniqueCards&AceCardMask:
    cardsInaRow = 1
 for card = 2...King
   if uniqueCards&(1<<card)
      cardsInARow++
   else 
      if cardsInARow == 5
         break
      cardsInARow = 0
 if cardsInARow==5:
     return true
 return false

Add all the ranks in order to a list, twice. Then, to check if a hand is a straight, sort the hand by rank and then check if the hand is a sublist of that list.

you could write a class that converts every card to an specific card value

Joker = 11 Queen = 12 King = 13 Ace = 0 or 14

it will make a lot easier card handling and looking for possible hands.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top