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).
#include <vector>
#include <string>
#include <iostream>
#include <algorithm>
#include <stdexcept>
#include <time.h>
#ifndef H_PLAYINGCARDS
#define H_PLAYINGCARDS
enum frenchValue {
ACE = 1,
TWO = 2,
THREE = 3,
FOUR = 4,
FIVE = 5,
SIX = 6,
SEVEN = 7,
EIGHT = 8,
NINE = 9,
TEN = 10,
JACK = 11,
QUEEN = 12,
KING = 13
};
enum frenchSuit { HEARTS = 1, DIAMONDS = 2, CLUBS = 3, SPADES = 4 };
class frenchCard;
class frenchDeck;
class emptyDeckException;
std::ostream& operator<<(std::ostream& lhs, frenchCard& obj);
std::ostream& operator<<(std::ostream& lhs, frenchDeck& obj);
bool frenchSuitCompare(frenchCard op1, frenchCard op2);
bool frenchValueCompare(frenchCard op1, frenchCard op2);
class frenchCard {
public:
frenchCard(frenchValue v, frenchSuit s);
friend std::ostream& operator<<(std::ostream& lhs, frenchCard& obj);
friend bool frenchSuitCompare(frenchCard op1, frenchCard op2);
friend bool frenchValueCompare(frenchCard op1, frenchCard op2);
const static std::string strValues[13];
const static std::string strSuits[4];
frenchSuit getSuit() { return suit; }
frenchValue getValue() { return value; }
friend class frenchDeck;
friend class frenchHand;
friend class frenchPlayer;
friend class frenchGame;
private:
frenchSuit suit;
frenchValue value;
};
class frenchDeck {
public:
frenchDeck();
virtual void build();
void shuffle();
void reinsert();
void discard(std::vector<frenchCard> ds);
std::vector<frenchCard> deal(int nC);
friend std::ostream& operator<<(std::ostream& lhs, frenchDeck& obj);
protected:
std::vector<frenchCard> deck;
std::vector<frenchCard> discarded;
int numCards;
int position;
};
class emptyDeckException : public std::runtime_error {
public:
emptyDeckException() : std::runtime_error("the deck is empty") { };
};
#endif // H_PLAYINGCARDS
#include "playingcards.h"
bool frenchSuitCompare(frenchCard op1, frenchCard op2) {
return (op1.suit < op2.suit) ? true : false;
}
bool frenchValueCompare(frenchCard op1, frenchCard op2) {
return (op1.value < op2.value) ? true : false;
}
const std::string frenchCard::strValues[13] = { "Ace", "Two", "Three",
"Four", "Five", "Six",
"Seven", "Eight", "Nine",
"Ten", "Jack", "Queen", "King" };
const std::string frenchCard::strSuits[4] = { "Hearts", "Diamonds", "Clubs", "Spades" };
frenchCard::frenchCard(frenchValue v, frenchSuit s) {
value = v;
suit = s;
}
std::ostream& operator<<(std::ostream& lhs, frenchCard& obj) {
lhs << obj.strValues[obj.value - 1] << " of " << obj.strSuits[obj.suit - 1];
return lhs;
}
void frenchDeck::build() {
numCards = 52;
position = 0;
for(int i = 1; i <= 4; i++) {
for(int j = 1; j <= 13; j++) {
frenchSuit s = (frenchSuit) i;
frenchValue v = (frenchValue) j;
deck.push_back(frenchCard(v, s));
}
}
}
frenchDeck::frenchDeck() {
build();
}
void frenchDeck::shuffle() {
deck.clear();
build();
srand((unsigned)time(NULL));
std::random_shuffle(deck.begin(), deck.end());
}
void frenchDeck::discard(std::vector<frenchCard> ds) {
for(unsigned int i = 0; i < ds.size(); i++) {
discarded.push_back(ds[i]);
}
}
void frenchDeck::reinsert() {
for(unsigned int i = 0; i < discarded.size(); i++) {
deck.push_back(discarded[i]);
}
numCards += discarded.size();
discarded.clear();
srand((unsigned)time(NULL));
std::random_shuffle(deck.begin()+position, deck.end());
}
std::vector<frenchCard> frenchDeck::deal(int nC) {
std::vector<frenchCard> hand;
if(numCards-position<nC) {
if(discarded.empty()) {
throw emptyDeckException();
}
else {
reinsert();
return deal(nC);
}
}
for(int i = position; i < (position + nC); i++) {
hand.push_back(deck[i]);
}
position += nC;
return hand;
}
std::ostream& operator<<(std::ostream& lhs, frenchDeck& obj) {
for(int i = obj.position; i < obj.numCards; i++) {
lhs << obj.deck[i] << std::endl;
}
return lhs;
}
#include "playingcards.h"
#ifndef H_HAND
#define H_HAND
class frenchHand;
class frenchHand {
public:
frenchHand();
frenchHand(std::vector<frenchCard> h);
std::vector<frenchCard> operator=(std::vector<frenchCard> h);
void setValue(int value) { handValue = value; }
void sortByValue();
void sortBySuit();
int getValue() { return handValue; }
int getNumCards() { return numCards; }
frenchCard& operator[](int index) { return hand[index]; }
std::vector<frenchCard> operator*() { return hand; }
protected:
std::vector<frenchCard> hand;
int numCards;
int handValue;
};
#endif // H_HAND
#include "hand.h"
frenchHand::frenchHand() {
handValue = 0;
}
frenchHand::frenchHand(std::vector<frenchCard> h) {
hand = h;
numCards = h.size();
handValue = 0;
}
std::vector<frenchCard> frenchHand::operator=(std::vector<frenchCard> h) {
hand = h;
numCards = h.size();
handValue = 0;
return h;
}
void frenchHand::sortBySuit() {
std::sort(hand.begin(), hand.end(), frenchSuitCompare);
}
void frenchHand::sortByValue() {
std::sort(hand.begin(), hand.end(), frenchValueCompare);
}
#include "playingcards.h"
#include "hand.h"
#include <iostream>
using namespace std;
int main(int argc, char *argv[]) {
frenchDeck myDeck;
myDeck.shuffle();
frenchHand myHand;
myHand = myDeck.deal(5);
cout << "RAW HAND:" << endl;
for(int i = 0; i < myHand.getNumCards(); i++) {
cout << " " << myHand[i] << endl;
}
myHand.sortBySuit();
cout << "HAND SORTED BY SUIT:" << endl;
for(int i = 0; i < myHand.getNumCards(); i++) {
cout << " " << myHand[i] << endl;
}
myHand.sortByValue();
cout << "HAND SORTED BY VALUE:" << endl;
for(int i = 0; i < myHand.getNumCards(); i++) {
cout << " " << myHand[i] << endl;
}
return EXIT_SUCCESS;
}
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

)