Write a text-based video poker game.
Textbook: (3.8); 12.5; 7.7; 6.5
Concepts: (wrapper classes); generics; ArrayList; interfaces (Comparable
)
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.
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.
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!)
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:
public PokerHand(PlayingCard one, PlayingCard two, PlayingCard three,
PlayingCard four, PlayingCard five)
You must have this constructor that takes 5 playing cards.
Optional: You may write additional constructors if you want to. Examples include a default PokerHand()
constructor that creates an empty hand (which you can then use a separate add
method to add cards to), or a PokerHand(PlayingCard[] cards)
or PokerHand(ArrayList<PlayingCard> cards)
that takes an array/list of 5 cards.
public boolean isPair()
public boolean isTwoPair()
public boolean isThreeOfAKind()
public boolean isStraight()
public boolean isFlush()
public boolean isFullHouse()
public boolean isFourOfAKind()
public boolean isStraightFlush()
public boolean isRoyalFlush()
Remember that an Ace in a straight can be either high or low. It must be high in a royal flush.
Sometimes more than one test may match a given set of cards. In this case, you are only required to return true
from the test that matches the highest-scoring hand possible for the given cards. From the valid lower-scoring matches, you may return either true or false, depending on how you choose to implement your methods.
For example, suppose the current PokerHand
contains the cards: 5C 5S 5H 8S 9D
. This hand is a 3-of-a-kind, so the isThreeOfAKind()
method must return true
in this case. However, isPair()
may or may not return true
(it's up to you), as there is a pair in the hand but it is not the highest-scoring possible match. Every other test should return false
in this case, since no other matches are even possible.
public String toString()
This method should return a String representing the 5 cards currently in the hand. They should be in sorted order (low to high, Aces high). Playing cards should be displayed using integers for values between 2 and 10; face card values should be Jack, Queen, King, and Ace. For both suits and face cards, you may use only the first capital letter of the word instead of the whole word, if you wish. That is, your cards can be in the format used in A16 or else you can use the 2-character format shown in the sample output below.
You may write whatever additional methods you need in this class.
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.
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.
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.
Upload your UsernameA17.java
file to Tamarin. Be sure to include all the classes needed to run your program!
PlayingCard
implements Comparable
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 int
s) and defines public static final int
constants for the suits, named CLUBS
, SPADES
, HEARTS
, and DIAMONDS
.
Deck
ArrayList
ArrayList
somewhere in your code.
PokerHand
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).
Username
A17
Comparable
Collections.sort
PlayingCard
implements Comparable
. If using an array to hold your cards, see java.util.Arrays
. If using an ArrayList, see java.util.Collections
.
is???
method by simply looking for the specified poker hand. For example, isPair()
can return true
if it finds two cards with the same value, regardless of whether there are more than 2 cards with that value or what the remaining cards are in the hand. If your hand is sorted, these two cards will be right next to each other.
isStraight()
and isFlush()
return true. However, remember how your methods work. If isPair()
also returns true on 3 of a kind, just calling isPair()
and isThreeOfAKind()
is not going to correctly detect a full house!
PokerHand
object, you are going to need one or more methods to let you later add, remove, or swap certain cards in the hand. (The alternative is to just keep the initial hand in an array or ArrayList until after the user has selected which cards to discard, and only then create a PokerHand
.)
toString()
method working, you can just return false;
from all your is???
methods to get PokerHand
to compile. Then you should test it after writing each method to gradually change all the [FAIL]s to [PASS]es.
PokerHand
is worth 9 points, while the main program is only worth 2 points. If pressed for time, apply your attention accordingly.
PokerHand
(though a regular array may be easier); you could use it in your Deck implementation; you could use an ArrayList just to hold the numbers/indexes entered by the user; or you could find some other use for it.
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.
compareTo
?
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
.
ArrayList
? I should use it to hold the cards in the PokerHand
, right?
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.)
toString()
method to print out the player's initial hand. But then how do I replace the cards in the hand?
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.
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.)
compareTo
from?
is???
methods?
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.