7

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;
}
Bill the Lizard
  • 398,270
  • 210
  • 566
  • 880
Logan Serman
  • 29,447
  • 27
  • 102
  • 141
  • 1
    I think your definition of a straight is not quite right: http://en.wikipedia.org/wiki/Poker_straight#Straight – Scott W Feb 09 '09 at 22:06

10 Answers10

10

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.

shsteimer
  • 28,436
  • 30
  • 79
  • 95
5

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)
Austin Salonen
  • 49,173
  • 15
  • 109
  • 139
3

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.

supercat
  • 77,689
  • 9
  • 166
  • 211
2

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.

Rik
  • 28,507
  • 14
  • 48
  • 67
0

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.

Instantsoup
  • 14,825
  • 5
  • 34
  • 41
0

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)

Bill K
  • 62,186
  • 18
  • 105
  • 157
  • Oh, when you say "8 spaces in a row", I guess that's where you're allowing it to wrap; is that it? – Michael Myers Feb 09 '09 at 23:04
  • Wouldn't it be better to just check for 5 "non-spaces" in a row? You wouldn't have 8 spaces in a row, for example, if your cards were 5, 6, 7, 8, 9 – Adnan Feb 14 '09 at 05:46
0

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

Eric
  • 19,525
  • 19
  • 84
  • 147
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
hacken
  • 2,135
  • 11
  • 11
  • Going to bits when an array can do the same thing--probably more efficiently? (Bitfields tend to require a read+write instead of just a write). Dude, you should re-evaluate your programming priorities. Readability before all others, optimization last, etc... – Bill K Feb 10 '09 at 00:51
  • Bitfields are orders of magnitude more efficient if your writing a hand evaluator. You turn all the hand evaluation into a half dozen table look ups. I find the resulting code easier to use in a poker program but ymmv. If your interest in this program a google will run up a bunch of fast one. – hacken Feb 10 '09 at 02:48
  • Strange assumption there. Source? I know setting a single bit is less efficient than writing a byte to an array, but maybe you're thinking of something else. I work with them all the time (processing video streams), it's not like I'm afraid or anything, just a bad solution unless necessary. – Bill K Feb 10 '09 at 20:58
  • Setting single bit is slower. But you don't set a single bit. You set all the bits in 1 register for all the cards and then write out that register. Most of the speed is from avoiding having to do a sort and being able to tell if something is a straight with some math and then 1 memory access. – hacken Feb 12 '09 at 00:58
  • I have a soft spot in my heart for bitfields, but this is going pretty far out of your way to avoid sorting five cards. With these constraints, selection sort will outperform Merge Sort and QuickSort. If you were building the game to serve thousands of users, sure, but in this context it's overkill. – rtperson Feb 13 '09 at 14:07
0

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.

Apocalisp
  • 34,834
  • 8
  • 106
  • 155
-2

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.

Germán Rodríguez
  • 4,284
  • 1
  • 19
  • 19