Assignment 17

Task

Write a text-based video poker game.

Textbook: (3.8); 12.5; 7.7; 6.5
Concepts: (wrapper classes); generics; ArrayList; interfaces (Comparable)

Steps

You are going to write a video poker game. This is basically a 5-card draw poker slot machine.

Your video poker game should take only one token/credit per hand. It should have the same payout rate as "Jacks or Better" when playing 1 credit. (See below for payout rate details.) However, unlike Jacks or Better, any pair pays 1 token; also, you may only trade in up to 3 cards (not all 5, as in video poker).

You may need to review the different kinds of hands in poker.

There are also a number of requirements given below regarding some of the classes and methods you must have.

Step 1: PlayingCard

Reuse this from A16. However, you must implement the Comparable interface:

  public class PlayingCard implements Comparable<PlayingCard> {

The compiler will then force you to add the following method:

  public int compareTo(PlayingCard pc) {

This method should return a number < 0 if this PlayingCard comes before the given PlayingCard (pc), or > 0 if it comes after, or 0 if the two cards are the same. Aces should come after Kings (Aces high).

You are required to sort cards only by value. (That is, comparing an 8 of Spades to an 8 of Hearts can return 0.) Optional: However, it would be good practice to sort cards of the same value by suit so that this method returns 0 only on truly equal cards.

Don't make any changes other than adding this interface/method. Your PlayingCard should still work with your A16 program when you're done.

Step 2: Deck

The cards of each hand of video poker should be generated as if from a Deck of cards. Therefore, you'll need to reuse your Deck class here. You should not need to make any changes, though. (Yay!)

Step 3: PokerHand

You are required to write this class so that Tamarin can easily test your hand-checking code. Also, it will help you break the problem of determining the current poker hand into smaller chunks. You are required to have the following methods in this class:

You may write whatever additional methods you need in this class.

Step 4: Testing PokerHand

It is very easy to make subtle errors that cause runtime or logic errors in the is??? methods. (For example, I only got about 1/2 of them right on the first attempt; however, each error was easy to spot once I knew there was an error to look for.) Therefore, testing is essential. Since some of these hands happen very rarely in a real video poker game, this means writing some test code that constructs a variety of different possible hands and tests each method.

To help save you time, I have provided the following test code: PokerHandTester.java

I won't guarantee that it'll catch every possible error in your code, but it should catch most of them. Feel free to add additional tests if your implementation has a lot of logical branches that each need to be tested.

Step 5: UsernameA17

Now you're finally ready to write the user interface!

Open a new deck, draw 5 cards, and form the user's initial hand. Print this in sorted order.

Then ask the user which cards (up to 3) they wish to discard. Your allowed input should be the integers 1 to 5 (each corresponding to one of the 5 cards in their hand) or 0 to stop discarding. Replace any discarded cards with new ones drawn from the deck.

Then print out the final hand and the corresponding payout (assuming an initial bet of 1 token):

Pair            1x
TwoPair         2x
ThreeOfAKind    3x
Straight        4x
Flush           6x
FullHouse       9x
FourOfAKind    25x
StraightFlush  50x
RoyalFlush    250x

Like A11, your program should only play one hand and then quit.

Sample Output

D:\TA\grading\A17>java ZtomaszeA17
--VIDEO POKER--
You have bet 1 token.
This game follows standard 5-card draw poker rules.
Make the best poker hand to earn the highest payout!

Your current hand:  4S  8D  9H  JC  AD
                    1   2   3   4   5

You may discard up to 3 cards to draw the same number of new cards.
Enter the index of a card you wish to discard (or 0 to stop): 8D
You must enter an integer between 0 and 5. Please try again.
Enter the index of a card you wish to discard (or 0 to stop): 2
Enter the index of a card you wish to discard (or 0 to stop): 2
You already selected that card to discard.
Please choose a different card or enter 0 to stop discarding.
Enter the index of a card you wish to discard (or 0 to stop): 1
Enter the index of a card you wish to discard (or 0 to stop): 9
You must enter an integer between 0 and 5. Please try again.
Enter the index of a card you wish to discard (or 0 to stop): 3
You discard : 8 of Diamonds, 4 of Spades, 9 of Hearts.

Your final hand:  4D  JC  QH  AD  AH

You have a pair.  You win 1 token.

D:\TA\grading\A17>java ZtomaszeA17
--VIDEO POKER--
You have bet 1 token.
This game follows standard 5-card draw poker rules.
Make the best poker hand to earn the highest payout!

Your current hand:  5S  6D  9C  JC  QD
                    1   2   3   4   5

You may discard up to 3 cards to draw the same number of new cards.
Enter the index of a card you wish to discard (or 0 to stop): 0
You discard no cards.

Your final hand:  5S  6D  9C  JC  QD

You have only a high card.  You lose.

D:\TA\grading\A17>java ZtomaszeA17
--VIDEO POKER--
You have bet 1 token.
This game follows standard 5-card draw poker rules.
Make the best poker hand to earn the highest payout!

Your current hand:  2D  3D  7D  8D  AD
                    1   2   3   4   5

You may discard up to 3 cards to draw the same number of new cards.
Enter the index of a card you wish to discard (or 0 to stop): 0
You discard no cards.

Your final hand:  2D  3D  7D  8D  AD

You have a flush.  You win 6 tokens.

D:\TA\grading\A17>java ZtomaszeA17
--VIDEO POKER--
You have bet 1 token.
This game follows standard 5-card draw poker rules.
Make the best poker hand to earn the highest payout!

Your current hand:  7D  JD  JC  KH  AD
                    1   2   3   4   5

You may discard up to 3 cards to draw the same number of new cards.
Enter the index of a card you wish to discard (or 0 to stop): 1
Enter the index of a card you wish to discard (or 0 to stop): 4
Enter the index of a card you wish to discard (or 0 to stop): 5
You discard : 7 of Diamonds, King of Hearts, Ace of Diamonds.

Your final hand:  2D  3C  JD  JH  JC

You have three of a kind.  You win 3 tokens.

D:\TA\grading\A17>java ZtomaszeA17
--VIDEO POKER--
You have bet 1 token.
This game follows standard 5-card draw poker rules.
Make the best poker hand to earn the highest payout!

Your current hand:  2H  6D  9D  9S  AD
                    1   2   3   4   5

You may discard up to 3 cards to draw the same number of new cards.
Enter the index of a card you wish to discard (or 0 to stop): 1
Enter the index of a card you wish to discard (or 0 to stop): 4
Enter the index of a card you wish to discard (or 0 to stop): 0
You discard : 2 of Hearts, 9 of Spades.

Your final hand:  2D  3D  6D  9D  AD

You have a flush.  You win 6 tokens.

What to Submit

Upload your UsernameA17.java file to Tamarin. Be sure to include all the classes needed to run your program!

Grading [14 points]

1 - Compiles + Coding Standards
Your program compiles successfully (no errors). Your code follows Java coding standards.
1 - PlayingCard implements Comparable
The compareTo method should sort cards by value, with Aces high (after Kings).

It is assumed that your PlayingCard also meets the requirements of A16, Part 1. In particular, it must have the specified constructor that takes a value and suit (both ints) and defines public static final int constants for the suits, named CLUBS, SPADES, HEARTS, and DIAMONDS.

0.5 - Deck
You include a Deck and use it to generate the playing cards for your poker hand.
0.5 - ArrayList
You use an ArrayList somewhere in your code.
9 - PokerHand
You have a constructor that takes 5 PlayingCard parameters, building a hand of those cards (1.0). Includes a toString() method that returns a String of the cards in sorted order (low to high, by value) (0.8). You have the 9 is??? methods specified above (0.8 each).
2 - UsernameA17
You print out the initial hand and the final hand (0.6). You allow the user to specify 0 to 3 cards to discard/replace using the integers 1 to 5, or 0 to stop entering numbers (0.6). You handle errors of bad input (0.4). You then print the correct payout (0.4).

FAQs

Demo code?
Here:
Tips
Something to consider: Video Poker vs Poker
Writing video poker is much easier than writing a regular poker game. For one thing, you don't have to worry about the artificial intelligence (AI) of designing a good betting strategy for the computer player. For another, comparing two hands is much more involved than just determining the type of a single hand. For example, imagine two player each have a pair. To determine the winner, you'd then need to determine (and compare) the value of each of those pairs. Then, on a tie, you'd have to find the high card (not in the pair) of each hand and compare those two values (which could also be a tie, requiring further comparisons).
My A16 submission wasn't perfect. Isn't that going to cause problems when I reuse those classes here for A17?
Probably not. Most of the things people miss on A16 are the finer requirements, such as: does a card properly get constructed as a Joker if the values given to the constructor are out of range; what happens if you call draw() on an empty deck; can you construct an ordered deck; can you shuffle a partial deck; etc. These requirements exist in A16 so that your classes perform correctly in any context; however, you are probably not going to encounter these contexts in A17.

The most important thing is that your PlayingCard constructor, two accessors, and toString() methods work correctly for cards in a valid range (1 to 13 values and one of the four suit constants). Also, you need public static int constants defined for the four suits.

Should the Aces be sorted low in the case of a straight?
This is allowed but not required. If you attempt it, make sure you get it right. For example, Aces should still be sorted high in an Ace-high straight, as well as all other hands.
How do I get the Aces to be sorted high by compareTo?
Get your compareTo method to sort cards into regular value order first, then worry about how to handle the Aces.

One option is to handle Aces separately in your compareTo code. For example:

  if (this.value == ACE || pc.value == ACE) {
    //have at least one ace, maybe two, to deal with...
    //if two Aces, the cards are equal;
    //otherwise, the Ace card is greater than the other card
  }else {
    //just sort cards normally
  }

Alternately, you could consider the fact that you really just want to treat any Ace as having a value of 14 while sorting. You don't want to change the underlying card's value, though. (If you did that, then you'd have an illegal card and it would mess up all the rest of your code that assumes an ACE == 1!) So use a temporary local variable to hold the value so you can change it if necessary:

  leftValue = this.value;
  if (leftValue == ACE) {
    leftValue = 14;
  }
Do the same for rightValue (which comes from pc.value), then do your comparison normally using leftValue and rightValue.
Where is the best place to use the ArrayList? I should use it to hold the cards in the PokerHand, right?
Remember that you only need to use an ArrayList once somewhere in your entire program. You can use more than one if you want to, but you only are required to have one.

The main advantage of an ArrayList over an array is when you are constructing a list of unknown length. For example, if you are reading in details from a user or something where you don't know how many elements you'll need before you start, an ArrayList is really handy!

This is not the case with a PokerHand. In a PokerHand, you should always have exactly 5 cards. (Well, I suppose you might discard some briefly, but you need to replace them again before doing anything else.)

Since you are probably more comfortable with arrays at this point, my general recommendation would be to use a PlayingCard[] instance variable to hold your cards in PokerHand. This way you don't have to wrestle with the new syntax/methods of ArrayList while writing each of your 9 is??? methods. (Using an ArrayList here would be fine though; and it would certainly give you more practice using one!)

But, if you're not using an ArrayList within PokerHand, where do you use it? My recommendation is to use an ArrayList<Integer> in main to store the card indices entered by the user. That is, they need to enter 0 to 3 numbers, but you don't know in advance just how many they'll enter. (You do know it won't be more than 3, so an array would work here too; but I'd go with an ArrayList.)

I want to use my PokerHand's toString() method to print out the player's initial hand. But then how do I replace the cards in the hand?
The current design of PokerHand does not provide any methods to let you change the cards in the hand once it is created. Therefore, you have (at least) two possible options here:

Option 1: Add a method something like this to your PokerHand:

  public PlayingCard set(int index, PlayingCard replacement) 
This method would first save the card currently at index into a temporary, local variable. Then it would replace that card in the hand with its new replacement card. Finally, it would return the original card (in case you want to print out which card the user just discarded).

Note that replacing cards like this will probably mean that your hand is no longer sorted. So, you should either sort your hand at the beginning of every is???() method, or else also add a public void sort() method in PokerHand so you can sort your hand after you finish replacing cards.

Option 2: Otherwise, you can leave PokerHand as it is.

In this approach, you would save your 5 cards in an array (or ArrayList) in main. Then sort it (so it will be in the same order as any PokerHand you create.)

To print out the initial hand, you then create a PokerHand object, passing in each of the 5 separate cards. Then call toString() on this hand object. You will not use this "initial hand" PokerHand object again.

Ask the user what cards they want to discard. Replace those cards in your array in main.

Then, create a second "final hand" PokerHand object using the 5 cards now in your array. Use this object to print out the final hand and to call all the is???() methods on to determine the payout.

Do I need to write a sort method to sort the cards?
No. Now that your PlayingCard implements Comparable, you can use the sort methods already in the Java library. If you are trying to sort an array, see java.util.Arrays.sort in the API. If you are trying to sort an ArrayList, see java.util.Collections.sort.

(If you are going with Option 1 described in the FAQ above, you might add a method named sort to your PokerHand class, but it would still just call one of the API's sort methods to do the work of actually sorting your cards.)

Where do we call compareTo from?
You don't need to directly call compareTo anywhere in your code. However, the API's sort method (see previous FAQ) will call it in order to sort your cards. If you did not have this method, the sort method would not know how to sort your PlayingCards.
How do I write the different is??? methods?
First, your constructor needs to save the five cards passed to it into either an ArrayList or an array instance variable. It should then sort these cards in that list/array.

Knowing that the cards are sorted greatly simplifies the logic of your is??? methods. For example, you then know that two cards with same value will be right next to each other.

Remember that your array or list contains PlayingCard objects. So, to compare the values, you need to call the getValue() method. To examine the suit, you would call getSuit(). So, if you used a PlayingCard[] array, you might have code like this:

  if (cards[0].getSuit() != cards[i].getSuit()) {
    return false;  //not a flush
  }

On the other hand, if you used an ArrayList<PlayingCard>, you might have something like this:

  if (cards.get(i).getValue() != cards.get(i + 1).getValue() + 1) {
     return false;  //not a straight
  }

Now, regarding the different is??? methods: most of them are easiest if you use a loop.

For example, in isPair(), you can loop through the first four cards, comparing the value of the card at i to the value of the card at i+1. (Your loop only moves i through the first four cards so that accessing i+1 does not go past the end of the array.) If you find two cards with the same value, you can immediately return true: the hand contains (at least) one pair. On the other hand, if you make it through the whole loop without returning, then you can return false after the loop: no pair was found.

isFlush() is similiar: You can use a loop to make sure every card in the hand as the same suit as the first card.

isStraight() is simple in most cases: each card should have a value that is one more than the card before it. Again, you can use a loop for this, comparing the card at i and the card at i+1. However, remember that Aces are sorted high but still have a value of 1. So you will need a special case to process any hand ending in an Ace. For example:

  2  3  4  5  1  //a straight
 10 11 12 13  1  //a straight
  5  6  7  8  1  //not a straight
  2  3  3  5  1  //not a straight

Not every method needs a loop, however. In a full house, the first two cards will have the same value, and the last two cards will have the same value. Then the middle card will either have the same value as the first pair or the same value as the second pair. Thus, you can test for a full house with an only 4 comparisons and two or three logical operators.

Occasionally, you might even be able to reuse some of your other methods. For example, isStraightFlush() would be true if isStraight() && isFlush(). And a royal flush is a kind of straight-flush.