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 :P) 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 :P 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
#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
playingcards.cpp
#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;
}
hand.h
#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
hand.cpp
#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);
}
a little testfile.cpp
#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 :P)