943,865 Members | Top Members by Rank

Ad:
  • C++ Discussion Thread
  • Unsolved
  • Views: 1707
  • C++ RSS
You are currently viewing page 1 of this multi-page discussion thread
Dec 11th, 2008
1

Card Games

Expand Post »
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).

Quote ...
playingcards.h
c++ Syntax (Toggle Plain Text)
  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

Quote ...
playingcards.cpp
c++ Syntax (Toggle Plain Text)
  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. }

Quote ...
hand.h
c++ Syntax (Toggle Plain Text)
  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

Quote ...
hand.cpp
c++ Syntax (Toggle Plain Text)
  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. }

Quote ...
a little testfile.cpp
c++ Syntax (Toggle Plain Text)
  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 )
Similar Threads
Reputation Points: 134
Solved Threads: 18
Junior Poster
mrboolf is offline Offline
182 posts
since Jun 2008
Dec 11th, 2008
1

Re: Card Games

I'd put the deal protocol in the game section as not all card games are dealt the same way.
Reputation Points: 718
Solved Threads: 373
Nearly a Posting Maven
Lerner is offline Offline
2,253 posts
since Jul 2005
Dec 11th, 2008
1

Re: Card Games

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.
Featured Poster
Reputation Points: 2614
Solved Threads: 687
Posting Expert
VernonDozier is offline Offline
5,375 posts
since Jan 2008
Dec 14th, 2008
0

Re: Card Games

Quote ...
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.
Reputation Points: 134
Solved Threads: 18
Junior Poster
mrboolf is offline Offline
182 posts
since Jun 2008
Dec 23rd, 2008
0

Re: Card Games

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.

c++ Syntax (Toggle Plain Text)
  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.

c++ Syntax (Toggle Plain Text)
  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:

c++ Syntax (Toggle Plain Text)
  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. };
c++ Syntax (Toggle Plain Text)
  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. }
c++ Syntax (Toggle Plain Text)
  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, 30 views)
Reputation Points: 134
Solved Threads: 18
Junior Poster
mrboolf is offline Offline
182 posts
since Jun 2008
Dec 23rd, 2008
0

Re: Card Games

let me tasted.....
Reputation Points: 47
Solved Threads: 69
Posting Whiz
cikara21 is offline Offline
340 posts
since Jul 2008
Dec 23rd, 2008
1

Re: Card Games

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:
c++ Syntax (Toggle Plain Text)
  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:
C++ Syntax (Toggle Plain Text)
  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:
c++ Syntax (Toggle Plain Text)
  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. }
Reputation Points: 344
Solved Threads: 116
Practically a Master Poster
Murtan is offline Offline
670 posts
since May 2008
Dec 23rd, 2008
0

Re: Card Games

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.
Reputation Points: 134
Solved Threads: 18
Junior Poster
mrboolf is offline Offline
182 posts
since Jun 2008
Dec 24th, 2008
0

Re: Card Games

can i get the different result every time i deal???...
Last edited by cikara21; Dec 24th, 2008 at 4:17 am.
Reputation Points: 47
Solved Threads: 69
Posting Whiz
cikara21 is offline Offline
340 posts
since Jul 2008
Dec 24th, 2008
0

Re: Card Games

That's what it's supposed to do - shuffle the deck and deal different cards every time you run the program.
Reputation Points: 134
Solved Threads: 18
Junior Poster
mrboolf is offline Offline
182 posts
since Jun 2008

This thread is more than three months old

No one has posted to this discussion for at least three months. Please let old threads die and do not reply to them unless you feel you have something new and valuable to contribute that absolutely must be added to make the discussion complete. Otherwise, please start a new thread in this forum instead.
Message:
Previous Thread in C++ Forum Timeline: Dependency line needs colon or double colon operator
Next Thread in C++ Forum Timeline: Help with simple classes-constructors





About Us | Contact Us | Advertise | Acceptable Use Policy
Forum Index | Build Custom RSS Feed


Follow us on Twitter


© 2011 DaniWeb® LLC