Project 2: Poker
Due: Thursday, March 10, 2016 by 9:00PM
Objective
The objectives of this project are 1) to practice using C++ class syntax, 2) to practice implementing member functions, and 3) to practice object-oriented thinking.Background
For this project, you will implement member functions of two C++ classes that can be used to play the game of poker. In object-oriented design, the classes should model things in the problem domain. In this situation, the two classes that you will work with are Card (which models a single playing card) and PokerHand (which models a five-card poker hand).
Card is the simpler class. It has just two data members. One stores the suit of the playing card and another stores the point value. Besides the constructors, the Card class only has accessors and a member function to print the card to standard output.
The PokerHand class is more complicated. First, it has an array of five Card objects. There is a member function to print a PokerHand and a member function to get the rank of a PokerHand. There is also a member function that compares two PokerHand objects and figures out which one is better according to the rules of poker. (If you are not familiar with poker, please see the list of poker hands.) There are several more member functions of PokerHand that you will have to implement. (See below.)
C++ class definitions include data members and member functions. They can also include type definitions and constants. For example, in the definition of the Card class, we have:
This defines a new type called cSuit. Variables of type cSuit can be assigned a constant value like Clubs. (Please see the section on enumeration types in your textbook.) For example, the data member in Card that holds the suit of a card is declared as:
Note that within the class definition or within the body of a member function, you may use cSuit, Invalid, Clubs, Diamonds, ... without any qualification. Outside the class definition and the body of a member function, you must use Card::cSuit, Card::Clubs... For example, even in the implementation of the Card class, the return value of accessor function for m_suit, must be declared as:
Similarly, there is a type definition for the point value of a card. These are stored as an unsigned int.
In the PokerHand class, we also have a type definition for an enumeration type:
Again, within the PokerHand class definition and within the body of PokerHand member functions, variables that store the rank of a poker hand should be declared using pRank. Outside, PokerHand::pRank should be used. Note that pRank variables hold int values and may be compared. For example,
In the PokerHand class, we also have the constant:
Assignment
Your assignment is to implement the member functions of the Card class and the PokerHand class. The class definitions are given in separate files:
For the Card class, you are also provided with a skeleton for the implementation card.cpp that has the function declaration of each function. For the PokerHand class, you are given pokerhand.cpp that just has the default constructor and part of the alternate constructor implemented. You have to fill in the rest yourself.You have also been provided two main programs that use the Card and PokerHand classes: play5.cpp and play7.cpp. The main program in play5.cpp simply creates two five-card poker hands and uses the cmp() member function to compare them. The main program in play7.cpp is more elaborate and uses PokerHand member functions to determine which two cards should be dropped at the end of a game of seven-card stud. It tries all possible ways of dropping two cards from a hand of seven cards so that the remaining five cards form the best possible poker hand.
These two programs do not fully exercise the member functions that you will implement. They do not even call every function. You should write your own test programs to check the correctness of all of the functions. Even if your code compiles and runs with these two programs, it might not compile and run with the programs that we will use for grading. You are responsible for testing your own code.
The header file for each class has comments describing what each member function has to accomplish. We make some additional comments here:
For the Card class:
- The default constructor:
Card() ; The default constructor does not take any arguments. This means the card does not have a suit or any points. Thus, the default constructor should store Invalid in m_suit. If we do not have a default constructor, then we cannot have the array of Card in the Pokerhand class, since the default constructor is called for each item in an array of objects. - The alternate constructor:
Card(cSuit s, cPoints p) ; should make sure that only meaningful values are stored in a card. For example, if the number of points p is 17, then the alternate constructor should create an invalid Card object instead of creating a Card with bogus values. - You also have to implement the member functions:
Card::cPoints Card::points() ; Card::cSuit Card::suit() ; void Card::print() ;
For the PokerHand class, there are some complications. First, it is much easier to determine the rank of a poker hand if the cards are sorted by points. For example, if you want to check if a hand is a Two Pair, there are only 3 possible patterns when the cards are sorted. A Two Pair hand must have a 2-2-1, 2-1-2 or 1-2-2 pattern. The 2-2-1 pattern means the first two cards are a pair with the same point value. The next two cards are a pair with a different (higher) point value. Finally, the last card is by itself and has a point value higher than the second pair. Because the cards are sorted, there are no other patterns possible to make a Two Pair. Since sorting is so useful, it should be done by the constructors. That way, all member functions can assume that the hand is already sorted.
Note that poker ranks are mutually exclusive. A Full House is not a Two Pair, even though there are two pairs in a Full House. Similarly, a Straight Flush is not a Straight and a Royal Flush is not a Straight Flush. Thus, if two poker hands have different ranks, we can quickly determine which hand is better. Your implementation should store the rank of the hand in the data member m_rank after it has been computed for the first time. This way you can avoid having to recompute the rank when the client calls getRank(). Before the rank has been computed, m_rank should hold the value NoRank. That is how you can tell if the rank has been computed previously. The constructors must make sure that m_rank is initialized to NoRank.
If two PokerHand objects have the same rank, your cmp() function should still determine which hand is better according to the rules of poker. A tie is possible, but this is not determined just by the rank. For example, if two hands are both Two Pair, the hand whose higher pair has higher point value wins. If that's a tie, then the hand whose lower pair has higher point value wins. If that's a tie, then the fifth card, the one that is not in any pair, is used to break the tie. If the fifth cards are once again tied, then the two hands are tied. Thus, to determine which of the Two Pair hands is better, we need three values: the point value of the higher pair, the point value of the lower pair and the point value of the last card. These three values can be determined easily when you first discovered that the rank of the hand is two pair. For example, when you check if the hand is a Two Pair with the 2-2-1 pattern, you can do the following:
Then, the cmp() function can use the data members m_firstPairPoints, m_secondPairPoints and m_lastCardPoints to determine which of two hands with rank of Two Pair is better.
Overall, we just need five data members to store this "additional information" in order the compare any two poker hands:
Full explanation for these data members are in pokerhand.h.
Here is a list of the member functions in PokerHand that you must implement:- A default constructor:
PokerHand() ; - An alternate constructor:
PokerHand(Card c0, Card c1, Card c2, Card c3, Card c4) ; - A comparison function:
int cmp(PokerHand &otherHand) ; Note that cmp() compares the host object versus the otherHand object. The return value being negative, zero or positive indicates whether the host is worse than, tied with or better than otherHand, respectively. The cmp() function should not assume that the cards in the two hands come from the same deck of cards. For example, cmp() may be asked to compare two hands that both have 4 Aces. This is not possible for two competing hands in a poker game. However, the main program in play7.cpp may ask cmp() which of two hands with 4 Aces is better in order to determine which 2 out of 7 cards to drop.
- Two print functions:
void printRank() ; void printCards() ; - A function to report the rank of the host (and if necessary,
compute the rank):
pRank getRank() ; - For each rank, a boolean function that says whether the host has
that rank:
bool isRoyalFlush() ; bool isStraightFlush() ; bool isFourOfAKind() ; bool isFullHouse() ; bool isFlush() ; bool isStraight() ; bool isThreeOfAKind() ; bool isTwoPair() ; bool isOnePair() ; bool isHighCard() ; - Some private helper functions:
void sort() ; bool isAllOneSuit() ; bool isSequence() ; Consult the header file for more documentation for these functions. Note that isAllOneSuit() does not do the same thing as isFlush(), because isAllOneSuit() returns true when the hand is a Royal Flush or a Straight Flush. Similarly, isSequence() is not the same as isStraight().
Implementation Issues
Here are some issues to consider and pitfalls to avoid. (You should read these before coding!)
- Reminder: do not develop your programs in the submission directories. Develop your programs in some other location in GL or on your own computer. The files that you have in the submission directory should not be your only copy of your code.
- There should not be any dynamically allocated objects in your program. That is, you should not be using new or delete anywhere in your code.
- Practice incremental development. This means you should write a small piece of code, then test it and debug it. You should finish the testing and debugging before you write more code. This project can easily have a few hundred lines of code (some of it quite repetitive). You do not want to write 500 lines of code and then find a bug that requires you to change your code in 300 places. You want to find that bug after writing 10 lines of code and then not make the same mistake again for the next 490 lines.
- There is no reason to change the class definition of Card, thus you should make no changes to the card.h file. On the other hand, you are permitted to add helper functions that are member functions in the PokerHand class. You may add declarations of more member functions near the end of pokerhand.h. Do not otherwise change the header file for the PokerHand class.
- If you have absolutely no need for the helper functions PokerHand::isAllOneSuit() or PokerHand::isSequence() just implement dummy functions that always return false. Since these are private member functions, they will not be tested by the grading programs.
- Remember that you can compare two variables with enumeration
types! It is a convenient way to compare two poker hands.
- On GL, you can compile the test programs with your implementation
with one command, like this:
linux3% g++ -Wall -ansi -gstabs play5.cpp pokerhand.cpp card.cpp or separately, like this:linux3% g++ -Wall -ansi -gstabs -c card.cpp linux3% g++ -Wall -ansi -gstabs -c pokerhand.cpp linux3% g++ -Wall -ansi -gstabs -c play5.cpp linux3% g++ -gstabs card.o pokerhand.o play5.o You only need to recompile a .cpp file if you change it. - Sample output from
play5.cpp
and
play7.cpp
are available:
play5-output.txt,
play7-output.txt.
Your program does not have to produce output that is identical
to the sample output (but it still has to be correct according
to the rules of poker).
- The source files linked on this web page are also available in the
following directory on GL.
/afs/umbc.edu/users/c/h/chang/pub/cs202stuff/proj2/ This is a directory, not a command. Use the Unix cp command to copy files.
Submitting your program
You should submit these files to the proj2 subdirectory:- card.cpp
This should contain your implementations of all of the Card member functions. This file should not have a main() function. - pokerhand.h
The only change should be the addition of private member function prototypes, if any. - pokerhand.cpp
This should contain your implementations of all of the PokerHand member functions. This file should not have a main() function. - mytest.cpp
This file should have a main() function that exercises the working parts of your submission. This is where you tell us what works. If you could not get some member function to work, don't test it here. We will test all functions with other programs.
You do not need to submit card.h, play5.cpp or play7.cpp because those files should not have changed.
If you followed the directions in setting up shared directories, then you can copy your code to the submission directory with:
If you are submitting late, you need to copy to proj2-late1, or proj2-late2 instead of proj2. Make sure you understand the course late submission policy.