Card Games

Please support our C++ advertiser: Intel Parallel Studio Home
Reply

Join Date: Jun 2008
Posts: 182
Reputation: mrboolf will become famous soon enough mrboolf will become famous soon enough 
Solved Threads: 18
mrboolf mrboolf is offline Offline
Junior Poster

Card Games

 
1
  #1
Dec 11th, 2008
Hi all there.

Given the huge amount of blackjack/card games threads and me being personally interested in poker and other games, I was feeling like starting to play with a few useful (at least I hope so) classes for dealing (no pun intended ) with card games.

I started my work on the paper first and I thought to write the following classes:

1 - A card class;
2 - A deck class containing all the (used) cards in the deck and functions such as deal, shuffle, etc.
3 - A hand class containing a variable set of cards (a deck object can deal hand objects) and a member value that can be accessed to give a score to the hand (for example in a poker game an unlucky hand containing only an Ace (high card) could have the value of 1, in a blackjack game the hand [A, J] will have the value of 21 etc.)
4 - A player class that contains a hand and a score (i.e. the money)
5 - A game class that contains a set of players, a deck and some functions implementing the rules.

Is that approach acceptable in your opinion? Do you have any criticism / suggestions? As always any hint is welcomed.

Please note that this thread is NOT urgent it's not homework of any sort.

here's what I did so far (could be useful for some of the threads I read throughout the forum).

playingcards.h
  1. #include <vector>
  2. #include <string>
  3. #include <iostream>
  4. #include <algorithm>
  5. #include <stdexcept>
  6. #include <time.h>
  7.  
  8. #ifndef H_PLAYINGCARDS
  9. #define H_PLAYINGCARDS
  10.  
  11. enum frenchValue {
  12. ACE = 1,
  13. TWO = 2,
  14. THREE = 3,
  15. FOUR = 4,
  16. FIVE = 5,
  17. SIX = 6,
  18. SEVEN = 7,
  19. EIGHT = 8,
  20. NINE = 9,
  21. TEN = 10,
  22. JACK = 11,
  23. QUEEN = 12,
  24. KING = 13
  25. };
  26.  
  27. enum frenchSuit { HEARTS = 1, DIAMONDS = 2, CLUBS = 3, SPADES = 4 };
  28.  
  29. class frenchCard;
  30. class frenchDeck;
  31. class emptyDeckException;
  32. std::ostream& operator<<(std::ostream& lhs, frenchCard& obj);
  33. std::ostream& operator<<(std::ostream& lhs, frenchDeck& obj);
  34. bool frenchSuitCompare(frenchCard op1, frenchCard op2);
  35. bool frenchValueCompare(frenchCard op1, frenchCard op2);
  36.  
  37. class frenchCard {
  38. public:
  39. frenchCard(frenchValue v, frenchSuit s);
  40. friend std::ostream& operator<<(std::ostream& lhs, frenchCard& obj);
  41. friend bool frenchSuitCompare(frenchCard op1, frenchCard op2);
  42. friend bool frenchValueCompare(frenchCard op1, frenchCard op2);
  43. const static std::string strValues[13];
  44. const static std::string strSuits[4];
  45. frenchSuit getSuit() { return suit; }
  46. frenchValue getValue() { return value; }
  47. friend class frenchDeck;
  48. friend class frenchHand;
  49. friend class frenchPlayer;
  50. friend class frenchGame;
  51. private:
  52. frenchSuit suit;
  53. frenchValue value;
  54. };
  55.  
  56. class frenchDeck {
  57. public:
  58. frenchDeck();
  59. virtual void build();
  60. void shuffle();
  61. void reinsert();
  62. void discard(std::vector<frenchCard> ds);
  63. std::vector<frenchCard> deal(int nC);
  64. friend std::ostream& operator<<(std::ostream& lhs, frenchDeck& obj);
  65. protected:
  66. std::vector<frenchCard> deck;
  67. std::vector<frenchCard> discarded;
  68. int numCards;
  69. int position;
  70. };
  71.  
  72. class emptyDeckException : public std::runtime_error {
  73. public:
  74. emptyDeckException() : std::runtime_error("the deck is empty") { };
  75. };
  76.  
  77. #endif // H_PLAYINGCARDS

playingcards.cpp
  1. #include "playingcards.h"
  2.  
  3. bool frenchSuitCompare(frenchCard op1, frenchCard op2) {
  4. return (op1.suit < op2.suit) ? true : false;
  5. }
  6.  
  7. bool frenchValueCompare(frenchCard op1, frenchCard op2) {
  8. return (op1.value < op2.value) ? true : false;
  9. }
  10.  
  11. const std::string frenchCard::strValues[13] = { "Ace", "Two", "Three",
  12. "Four", "Five", "Six",
  13. "Seven", "Eight", "Nine",
  14. "Ten", "Jack", "Queen", "King" };
  15.  
  16. const std::string frenchCard::strSuits[4] = { "Hearts", "Diamonds", "Clubs", "Spades" };
  17.  
  18. frenchCard::frenchCard(frenchValue v, frenchSuit s) {
  19. value = v;
  20. suit = s;
  21. }
  22.  
  23. std::ostream& operator<<(std::ostream& lhs, frenchCard& obj) {
  24. lhs << obj.strValues[obj.value - 1] << " of " << obj.strSuits[obj.suit - 1];
  25. return lhs;
  26. }
  27.  
  28. void frenchDeck::build() {
  29. numCards = 52;
  30. position = 0;
  31. for(int i = 1; i <= 4; i++) {
  32. for(int j = 1; j <= 13; j++) {
  33. frenchSuit s = (frenchSuit) i;
  34. frenchValue v = (frenchValue) j;
  35. deck.push_back(frenchCard(v, s));
  36. }
  37. }
  38. }
  39.  
  40. frenchDeck::frenchDeck() {
  41. build();
  42. }
  43.  
  44. void frenchDeck::shuffle() {
  45. deck.clear();
  46. build();
  47. srand((unsigned)time(NULL));
  48. std::random_shuffle(deck.begin(), deck.end());
  49. }
  50.  
  51. void frenchDeck::discard(std::vector<frenchCard> ds) {
  52. for(unsigned int i = 0; i < ds.size(); i++) {
  53. discarded.push_back(ds[i]);
  54. }
  55. }
  56.  
  57. void frenchDeck::reinsert() {
  58. for(unsigned int i = 0; i < discarded.size(); i++) {
  59. deck.push_back(discarded[i]);
  60. }
  61. numCards += discarded.size();
  62. discarded.clear();
  63. srand((unsigned)time(NULL));
  64. std::random_shuffle(deck.begin()+position, deck.end());
  65. }
  66.  
  67. std::vector<frenchCard> frenchDeck::deal(int nC) {
  68. std::vector<frenchCard> hand;
  69. if(numCards-position<nC) {
  70. if(discarded.empty()) {
  71. throw emptyDeckException();
  72. }
  73. else {
  74. reinsert();
  75. return deal(nC);
  76. }
  77. }
  78. for(int i = position; i < (position + nC); i++) {
  79. hand.push_back(deck[i]);
  80. }
  81. position += nC;
  82. return hand;
  83. }
  84.  
  85. std::ostream& operator<<(std::ostream& lhs, frenchDeck& obj) {
  86. for(int i = obj.position; i < obj.numCards; i++) {
  87. lhs << obj.deck[i] << std::endl;
  88. }
  89. return lhs;
  90. }

hand.h
  1. #include "playingcards.h"
  2.  
  3. #ifndef H_HAND
  4. #define H_HAND
  5.  
  6. class frenchHand;
  7.  
  8. class frenchHand {
  9. public:
  10. frenchHand();
  11. frenchHand(std::vector<frenchCard> h);
  12. std::vector<frenchCard> operator=(std::vector<frenchCard> h);
  13. void setValue(int value) { handValue = value; }
  14. void sortByValue();
  15. void sortBySuit();
  16. int getValue() { return handValue; }
  17. int getNumCards() { return numCards; }
  18. frenchCard& operator[](int index) { return hand[index]; }
  19. std::vector<frenchCard> operator*() { return hand; }
  20. protected:
  21. std::vector<frenchCard> hand;
  22. int numCards;
  23. int handValue;
  24.  
  25. };
  26.  
  27. #endif // H_HAND

hand.cpp
  1. #include "hand.h"
  2.  
  3. frenchHand::frenchHand() {
  4. handValue = 0;
  5. }
  6.  
  7. frenchHand::frenchHand(std::vector<frenchCard> h) {
  8. hand = h;
  9. numCards = h.size();
  10. handValue = 0;
  11. }
  12.  
  13. std::vector<frenchCard> frenchHand::operator=(std::vector<frenchCard> h) {
  14. hand = h;
  15. numCards = h.size();
  16. handValue = 0;
  17. return h;
  18. }
  19.  
  20. void frenchHand::sortBySuit() {
  21. std::sort(hand.begin(), hand.end(), frenchSuitCompare);
  22. }
  23.  
  24. void frenchHand::sortByValue() {
  25. std::sort(hand.begin(), hand.end(), frenchValueCompare);
  26. }

a little testfile.cpp
  1. #include "playingcards.h"
  2. #include "hand.h"
  3. #include <iostream>
  4.  
  5. using namespace std;
  6.  
  7. int main(int argc, char *argv[]) {
  8. frenchDeck myDeck;
  9. myDeck.shuffle();
  10. frenchHand myHand;
  11. myHand = myDeck.deal(5);
  12. cout << "RAW HAND:" << endl;
  13. for(int i = 0; i < myHand.getNumCards(); i++) {
  14. cout << " " << myHand[i] << endl;
  15. }
  16. myHand.sortBySuit();
  17. cout << "HAND SORTED BY SUIT:" << endl;
  18. for(int i = 0; i < myHand.getNumCards(); i++) {
  19. cout << " " << myHand[i] << endl;
  20. }
  21. myHand.sortByValue();
  22. cout << "HAND SORTED BY VALUE:" << endl;
  23. for(int i = 0; i < myHand.getNumCards(); i++) {
  24. cout << " " << myHand[i] << endl;
  25. }
  26. return EXIT_SUCCESS;
  27. }

Thanks for your consideration and sorry for the long post

P.S.
In case you wonder, the prefix "french" is there because I'm developing also the "italian" equivalents of these classes (different cards, different decks ... definitely different games to play with )
Reply With Quote Quick reply to this message  
Join Date: Jul 2005
Posts: 1,678
Reputation: Lerner is a name known to all Lerner is a name known to all Lerner is a name known to all Lerner is a name known to all Lerner is a name known to all Lerner is a name known to all 
Solved Threads: 262
Lerner Lerner is offline Offline
Posting Virtuoso

Re: Card Games

 
1
  #2
Dec 11th, 2008
I'd put the deal protocol in the game section as not all card games are dealt the same way.
Klatu Barada Nikto
Reply With Quote Quick reply to this message  
Join Date: Jan 2008
Posts: 3,813
Reputation: VernonDozier has a reputation beyond repute VernonDozier has a reputation beyond repute VernonDozier has a reputation beyond repute VernonDozier has a reputation beyond repute VernonDozier has a reputation beyond repute VernonDozier has a reputation beyond repute VernonDozier has a reputation beyond repute VernonDozier has a reputation beyond repute VernonDozier has a reputation beyond repute VernonDozier has a reputation beyond repute VernonDozier has a reputation beyond repute 
Solved Threads: 501
Featured Poster
VernonDozier VernonDozier is offline Offline
Senior Poster

Re: Card Games

 
1
  #3
Dec 11th, 2008
There is no "right" and "wrong" in these types of designs, but rather "pros" and "cons". You are trying to write some generic classes so that you don't have to rewrite everything for each different game, which is good. At some point, the games get so dissimilar that it's no longer worth generalizing (i.e. the game of War might be so different from 7-card stud that it may be better to write them separately, whereas 5-card stud and 7-card stud are sufficiently similar to use most of the same underlying code). Where that cutoff is when you have to write different underlying classes is not always an easily defined line.

I don't know exactly what you are going for, but this may be a job for polymorphism, particularly on something like the card, deck, and game classes. Your mention of "Italian" versus "French" is a perfect example. The rules of 5-card stud will be the same in either, but you would want to change the "enum" values of both (i.e. "King" in French English, Italian, etc.). You have a "frenchHand" class. Maybe you should have a "hand" class and have "italianHand" and "frenchHand" be derived classes. Similarly when you get to implementing the different games, have a "Poker" class, then derived classes like "Stud" and "Draw" and then "5-card stud" and "7-card stud" deriving from "Stud".

If it were my design, I would pick about ten games, decide how they are similar and different, and derive my classes/design from that.
Reply With Quote Quick reply to this message  
Join Date: Jun 2008
Posts: 182
Reputation: mrboolf will become famous soon enough mrboolf will become famous soon enough 
Solved Threads: 18
mrboolf mrboolf is offline Offline
Junior Poster

Re: Card Games

 
0
  #4
Dec 14th, 2008
I'd put the deal protocol in the game section as not all card games are dealt the same way.
Sounds good to me, I'll make that change :-) Thank you.

@VernonDozier: Thank you too for the advices.
My short-term goal would be to make a Poker game - not that I'd like to play it more than others already existing, I want to do it mainly in the attempt of teaching myself something in the process. I like the subject and this seems a good exercise to me.

I'd like (long-term goal) to write a few games (some similar, some others not quite) for the same purpose: learn something.
I thought that I'd like to reuse as much code as it would be possible, so I'm walking this way and I'll keep you informed of any significant progress. Anything is welcomed as usual, many opinions feed the growing mind it's said

Thank you again for the advices, I liked particularly that one of making frenchHand a derived class of a general hand class.
Reply With Quote Quick reply to this message  
Join Date: Jun 2008
Posts: 182
Reputation: mrboolf will become famous soon enough mrboolf will become famous soon enough 
Solved Threads: 18
mrboolf mrboolf is offline Offline
Junior Poster

Re: Card Games

 
0
  #5
Dec 23rd, 2008
Ok, 11 days have passed and though busy with many other things I brought this some steps further.

I restructured slightly the whole design - basically now all the classes are enough abstract to deal with card games (exception: build method in deck class, but it could be overriden), or at least I hope so.
I confined the specialization in a pokerGame class that'll handle the actual game action.

As of now I'm stuck - there's probably a stupid reason to that but I can't see it. I find it difficult to manage this project because it's bigger than the other things I attempted ^^"
I was trying to write a deal and a showdown functions in the pokerGame class to test the mechanism of dealing cards to every player. Unexpectedly what caused me troubles was the showdown function.

  1. // game.cpp
  2. void pokerGame::showdown() {
  3. for(unsigned int i = 0; i < m_players.size(); ++i) {
  4. if(m_players[i].getHand().getSize()!=0) {
  5. std::cout << m_players[i].getName() << "'s hand: ";
  6. std::cout /*<< m_players[i].getHand()*/ << std::endl;
  7. }
  8. }
  9. return;
  10. }

If I uncomment that line it gives me segmentation fault when executing. I am using Code::Blocks and I have tried the debugger and though not being confident with it could have caused some misunderstanding I really can't see any problem there. Perhaps I'm just trying something still beyond my reach and I can't even use the debugger properly

The program is too long to post it here - I am going to post just a few extracts and attach an archive with the whole thing.

  1. /* this is the main program (if you download the file, the commented part not present here is another (previous) test that works fine */
  2. #include "card.h"
  3. #include "hand.h"
  4. #include "deck.h"
  5. #include "game.h"
  6.  
  7. int main() {
  8. std::vector<std::string> names;
  9. names.push_back("Ludovico");
  10. names.push_back("Cpu");
  11. std::vector<bool> areCpu;
  12. areCpu.push_back(false);
  13. areCpu.push_back(true);
  14. std::vector<float> initialScores;
  15. initialScores.push_back(4000.0);
  16. initialScores.push_back(4000.0);
  17. pokerGame myGame(names, areCpu, initialScores);
  18. myGame.deal();
  19. myGame.showdown();
  20. return EXIT_SUCCESS;
  21. }

these are the other pieces of code that I think may be connected with the problem:

  1. // pokerGame class declaration
  2. class pokerGame {
  3. public:
  4. pokerGame(std::vector<std::string> names, std::vector<bool> areCpu,
  5. std::vector<float> initialScores);
  6. void deal();
  7. void showdown();
  8. std::vector<player> getPlayers() { return m_players; }
  9. deck getDeck() { return m_deck; }
  10. int getNumPlayers() { return m_numPlayers; }
  11. protected:
  12. value m_minInDeck;
  13. std::vector<player> m_players;
  14. deck m_deck;
  15. int m_numPlayers;
  16. unsigned int m_dealer;
  17. // position of the dealer relative to m_players vector
  18. };
  1. // pokerGame class constructor and deal function
  2. pokerGame::pokerGame(std::vector<std::string> names, std::vector<bool> areCpu,
  3. std::vector<float> initialScores) : m_deck(names.size()) {
  4. m_numPlayers = names.size();
  5. m_minInDeck = (value) (11 - m_numPlayers);
  6. unsigned int handsize = 5;
  7. for(int i = 0; i < m_numPlayers; ++i) {
  8. m_players.push_back(player(handsize, initialScores[i], names[i], areCpu[i]));
  9. }
  10. srand((unsigned)time(NULL));
  11. m_dealer = rand() % m_numPlayers;
  12. }
  13.  
  14. void pokerGame::deal() {
  15. m_deck.build();
  16. m_deck.shuffle();
  17. for(int j = 0; j < 5; ++j) {
  18. for(unsigned int i = 0; i < m_players.size(); ++i) {
  19. m_players[(i+m_dealer+1)%m_numPlayers].deal(m_deck.deal());
  20. std::cout << "dealing to " << m_players[(i+m_dealer+1)%m_numPlayers].getName() << std::endl;
  21. }
  22. }
  23. return;
  24. }
  1. // player class declaration
  2. class player {
  3. public:
  4. player(unsigned int handsize, float initialScore = 0., std::string name = "Player", bool isCpu = false);
  5. bool deal(card c) { return m_hand.deal(c); }
  6. hand& getHand() { return m_hand; }
  7. float getScore() { return m_score; }
  8. std::string getName() { return m_name; }
  9. bool isCpu() { return m_isCpu; }
  10. protected:
  11. unsigned int m_handsize;
  12. hand m_hand;
  13. bool m_isCpu;
  14. float m_score; // it's money in poker, could be anything in other games
  15. std::string m_name;
  16. };

Well I think that's all... if I forgot something important please ask.
This is not homework and I'm not in a hurry but any hint would be much appreciated anyway

Meanwhile, thank you very much for your time if you read all this mess

P.S.
I tried to make the code readable and understandable but any criticism in that regard would be greatly appreciated too!
Attached Files
File Type: zip pokerGame.zip (5.6 KB, 7 views)
Reply With Quote Quick reply to this message  
Join Date: Jul 2008
Posts: 320
Reputation: cikara21 is an unknown quantity at this point 
Solved Threads: 63
cikara21's Avatar
cikara21 cikara21 is offline Offline
Posting Whiz

Re: Card Games

 
0
  #6
Dec 23rd, 2008
let me tasted.....
.:-cikara21-:.
Reply With Quote Quick reply to this message  
Join Date: May 2008
Posts: 538
Reputation: Murtan is a jewel in the rough Murtan is a jewel in the rough Murtan is a jewel in the rough Murtan is a jewel in the rough 
Solved Threads: 86
Murtan Murtan is offline Offline
Posting Pro

Re: Card Games

 
1
  #7
Dec 23rd, 2008
I took the zip file and made a MS project around it.

I had to add #include <algorithm> to hand.cpp but other than that it compiled.

I then uncommented your troublesome line:
  1. void pokerGame::showdown() {
  2. for(unsigned int i = 0; i < m_players.size(); ++i) {
  3. if(m_players[i].getHand().getSize()!=0) {
  4. std::cout << m_players[i].getName() << "'s hand: ";
  5. std::cout << m_players[i].getHand() << std::endl;
  6. }
  7. }
  8. return;
  9. }
  10. void pokerGame::showdown() {
  11. for(unsigned int i = 0; i < m_players.size(); ++i) {
  12. if(m_players[i].getHand().getSize()!=0) {
  13. std::cout << m_players[i].getName() << "'s hand: ";
  14. std::cout << m_players[i].getHand() << std::endl;
  15. }
  16. }
  17. return;
  18. }

It runs ok, but the output is strange:
  1. dealing to Ludovico
  2. dealing to Cpu
  3. dealing to Ludovico
  4. dealing to Cpu
  5. dealing to Ludovico
  6. dealing to Cpu
  7. dealing to Ludovico
  8. dealing to Cpu
  9. dealing to Ludovico
  10. dealing to Cpu
  11. Ludovico's hand: Ace of Nine, Two of Spades, Four of Five, Three of Six, Four of
  12. Six
  13. Cpu's hand: Ace of Ace, Ace of Five, Two of Four, Ace of Six, Two of Seven

I think the problem is here:
  1. void deck::build(int numPlayers) {
  2. m_discarded.clear();
  3. m_dealt.clear();
  4. m_deck.clear();
  5. if(numPlayers == 0) {
  6. for(int i = 1; i <= 4; ++i) {
  7. for(int j = 1; j <= 13; ++j) {
  8. // -- a card expects value , suit which is what we say we're passing
  9. // -- but j is 1-13 and i is 1-4
  10. m_deck.push_back(card((value) i, (suit) j));
  11. }
  12. }
  13. return;
  14. }
  15. for(int i = 1; i <= 4; ++i) {
  16. m_deck.push_back(card((value) 1, (suit) i));
  17. for(int j = 11 - numPlayers; j <= 13; ++j) {
  18. m_deck.push_back(card((value) j, (suit) i));
  19. }
  20. }
  21. return;
  22. }
Reply With Quote Quick reply to this message  
Join Date: Jun 2008
Posts: 182
Reputation: mrboolf will become famous soon enough mrboolf will become famous soon enough 
Solved Threads: 18
mrboolf mrboolf is offline Offline
Junior Poster

Re: Card Games

 
0
  #8
Dec 23rd, 2008
You're great that was driving me mad.

Actually there was a double error, it shouldn't have entered the if because the line m_deck.build(); in pokerGame::deal() should have been m_deck.build(m_numPlayers); .

A lucky error though, because it made "me" (well, you!) discover the other, nasty error.

I still don't understand why it would caused a segfault to me while just a wrong output to you though.

Well, thank you again!
Last edited by mrboolf; Dec 23rd, 2008 at 5:40 pm.
Reply With Quote Quick reply to this message  
Join Date: Jul 2008
Posts: 320
Reputation: cikara21 is an unknown quantity at this point 
Solved Threads: 63
cikara21's Avatar
cikara21 cikara21 is offline Offline
Posting Whiz

Re: Card Games

 
0
  #9
Dec 24th, 2008
can i get the different result every time i deal???...
Last edited by cikara21; Dec 24th, 2008 at 4:17 am.
.:-cikara21-:.
Reply With Quote Quick reply to this message  
Join Date: Jun 2008
Posts: 182
Reputation: mrboolf will become famous soon enough mrboolf will become famous soon enough 
Solved Threads: 18
mrboolf mrboolf is offline Offline
Junior Poster

Re: Card Games

 
0
  #10
Dec 24th, 2008
That's what it's supposed to do - shuffle the deck and deal different cards every time you run the program.
Reply With Quote Quick reply to this message  
Reply

This thread is more than three months old.
Perhaps start a new thread instead?
Message:



Similar Threads
Other Threads in the C++ Forum
Thread Tools Search this Thread



About Us | Contact Us | Advertise | DaniWeb | Acceptable Use Policy | RSS Feed

©2003 - 2009 DaniWeb® LLC