Cards Decks and classes

I am struggling with getting my playing cards classes to work as I would like. I can create 52 cards and then put them into a deck, shuffle the deck. However I need a hand class where I can put 5 cards in to check if it the hand is a flush/ 2 pair full house etc. Im a bit stuck.
I appreciate my code is messy and there will be easier more succinct way to do things, but I have been on with for a bit and can't work out how to do it. I'm not beyond starting again...

Thanks for any help!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
  class Card
{
private:
    std::string value;
    std::string suit;
public:
    Card() = default;
    //constructor
    Card(std::string _val, std::string _suit)  : value {_val}, suit{_suit} { }
    //set suit
    void setSuit(std::string _suit) { suit = _suit; }
    //set value
    void setValue(std::string _value) { value = _value; }
    //get suit
    std::string getSuit() { return suit; }
    //get value
    std::string getValue() {return value;}
    void showCard() { std::cout << getValue() << getSuit(); }

class Deck
{
private:
    std::vector<Card> deck;
    const std::string spades {u8"\u2660"};
    const std::string clubs {u8"\u2663"};
    const std::string hearts {u8"\u2665"};
    const std::string diamonds {u8"\u2666"};
    const std::string card_values[13] {"A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"};
public:
    
    //constructor
    Deck() { filDeck(); }
    
    void filDeck();
    void showDeck();
    void shuffleDeck();
    Card getCard(int pos);
    
};

void Deck::filDeck()
{
    for(int j{};j < 52; j++)
    {
        if(j <= 12)
        {
            deck.push_back(Card(card_values[j], spades));
        }
        if(j >= 13 && j <= 25)
        {
            deck.push_back(Card(card_values[j - 13], clubs));
        }
        if(j >= 26 && j <= 38)
        {
            deck.push_back(Card(card_values[j - 26], hearts));
        }
        if(j >= 39)
        {
            deck.push_back(Card(card_values[j - 39], diamonds));
        }
    }
}

void Deck::showDeck()
{
    for(int i{};i < 52;i++)
    {
        Card temp(deck[i]);
        std:: cout << temp.getValue() << temp.getSuit() << " ";
        if(i == 12 || i == 25 || i == 38)
        {
            std::cout << std::endl;
        }
    }
}

void Deck::shuffleDeck()
{
    for(int i{}; i < 52; i++)
    {
        int pos_1 = ( rand() % 51);
        int pos_2 = ( rand() % 51);
        if(pos_1 == pos_2)
        {
            pos_1 = ( rand() % 51);
            pos_2 = ( rand() % 51);
        }
        std::swap(deck[pos_1], deck[pos_2]);
    }
}

Card Deck::getCard(int pos)
{
    //Card temp;
    return deck[pos];
    //return temp;
}

int main(int argc, const char * argv[]) {
    
    srand(static_cast<unsigned int>(time(NULL))); // randomise seed
    
    Deck mydeck;
    mydeck.filDeck();
    mydeck.showDeck();
    
    std::cout << "\n\n" << std::endl;
    
    mydeck.shuffleDeck();
    mydeck.showDeck();
    
    std::cout << std::endl;
    
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <iostream>
#include <vector>
#include <random>
#include <algorithm>
using namespace std;

class Card
{
    int n;
public:
    static const int NumRanks = 13, NumSuits =  4;
    static const char * const Suits;
    static const char * const Ranks;
    static int next_n;
    
    static void reset() { next_n = 0; }

    Card() : n(next_n++) {}
    int rank() const { return n % NumRanks + 2; } // rank from 2 to 14
    int suit() const { return n / NumRanks; }
    char rankChar() const { return Ranks[rank()]; }
    char suitChar() const { return Suits[suit()]; }
};

int Card::next_n = 0;
const char * const Card::Suits = "CDHS";
const char * const Card::Ranks = "  23456789TJQKA";

ostream& operator<<(ostream& out, Card c)
{
    return out << c.rankChar() << c.suitChar();
}

class Deck
{
    vector<Card> cards;
public:
    static default_random_engine Rnd;
    static const int NumCards = Card::NumRanks * Card::NumSuits;
    Deck() : cards(NumCards) {}
    void shuffle() { std::shuffle(cards.begin(), cards.end(), Rnd); }
    Card deal() { Card c = cards.back(); cards.pop_back(); return c; }
    void reset() { Card::reset(); cards.clear(); cards.resize(NumCards); }
    bool empty() const { return cards.empty(); }
};

default_random_engine Deck::Rnd{random_device{}()};

int main()
{
    Deck deck;
    deck.shuffle();
    while (!deck.empty()) cout << deck.deal() << ' ';
    cout << '\n';
}

Last edited on
Some time ago I was working on poker game, and didn't like the code because of performance reasons and then did a whole new thing which would be mostly not useful to you and hard to answer your question.

Anyway I didn't throw the old code away but rather archived it for reference...

You need a CardSet class, CardSet can hold any amount of cards, however Player class has CardSet object which can have no more than 5 cards in poker!

Therefore your CardSet class implements methods to determine the strength of a hand, example:

1
2
3
4
5
6
7
8
9
class CardSet
{
public:
     // public methods...

private:
     // collection of cards
     std::vector<Card> mCards;
};



The class contains a container of cards, the most core methods you need to implement is methods to sort cards by value and suit:

1
2
3
4
5
6
7
8
9
10
11
class CardSet
{
public:
      /** Sort card set by card rank */
      void SortByRank();

     /** Sort card set by card suit */
     void SortBySuit();

     // ...
};


If you don't sort cards, it will be hard and probably inefficient to tell the strength of a hand.

To be able to sort your cards you need to define suits and ranks, like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
	enum class CardRank
	{
		Two = 2,
		Three,
		Four,
		Five,
		Six,
		Seven,
		Eight,
		Nine,
		Ten,
		Jack,
		Queen,
		King,
		Ace
	};


1
2
3
4
5
6
7
	enum class Suit
	{
		Clubs = 1,
		Spades,
		Diamonds,
		Hearts
	};


This also means you'll have to define a set of operators for those enums to compare suits and ranks!

Your class also needs to save sorting state of cards for 2 reasons:
1. performance
2. determine the state of cards

So you need an enum for that too:

1
2
3
4
5
6
	enum class SortState
	{
		Unsorted,
		ByRank,
		BySuit
	} mSortStatus = SortState::Unsorted;



And then here is example how to get at least 5 cards of same suit (assumes you have implemented sorting functions:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
	bool CardSet::MakeSuits()
	{
		// we need at least 5 cards
		if (mCards.size() < 5)
			return false;

                // NEED TO SORT FIRST
		SortBySuit();

		// make sure at least 5 cards are of same suit
		for (auto first = mCards.begin(), last = mCards.begin() + 4; last != mCards.end(); ++first, ++last)
		{
			// if 5 consecutive cards are of same suit
			if (const Suit match = first->GetSuit(); last->GetSuit() == match)
			{
				// include the rest of cards of same suit
				while (++last != mCards.end())
					if (last->GetSuit() != match)
						break;

				// keep only suits, sort status is up to date
				// first is included, last is not, last is already incremented therefore OK
				mCards.erase(std::move(first, last, mCards.begin()), mCards.end());
				mRankStatus = HandRank::Flush;

				// at least 5 cards of same suit
				return true;
			}
		}

		// less than 5 cards of same suit
		return false;
	}


Here as you can see, if cards are not sorted, the implementation of this function would be more complicated.

Your Card class ofc. needs info about suit and rank and also hand rank:

1
2
3
4
5
6
7
8
9
10
11
12
class Card
{
       private:
	     // Suit of this card
	     Suit mSuit;

	     // CardRank of this card
	     CardRank mRank;

            /** Hand rank status of current cards */
            HandRank mRankStatus = HandRank::Unkown;
};


Sample enum for rank:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
	enum class HandRank
	{
		Unkown,		// used for card sets below 2 cards
		HighCard,
		OnePair,
		TwoPairs,
		ThreeOfAKind,
		Straight,
		Flush,
		FullHouse,
		Poker,
		StraightFlush,
		RoyalFlush
	};

Last edited on
I'm not beyond starting again...
I think Dutch and malibor have shown that you should do that.

You've made a classic mistake: representing the data in a way that's convenient for the user rather than a way that's convenient for the code. You need to be able to compare cards quickly and easily, both for equality and for less-than and greater-than.

I recall writing code to evaluate a poker hand once. I think it was in this forum. It's surprisingly difficult and I think a key was to check for the highest value hands first. So you check for a straight flush before checking for a flush. I think the result of the evaluation was also a type of hand (straight-flush, flush, straight, 3-of-a-kind etc), and a card that represented the tie breaker in case two players had the same hand.
Thanks everyone. I am still new to C++, as can be seen.
I find it difficult to visualise things as specified by dhayden.
I will start again tomorrow. The last thing I need to do though is copy someone else’s code. I will not learn anything that way!
I have started again and come up with the code below. I think its better than my initial attempt.
Now I can construct a card and deck, shuffle the deck and sort it gain.
Below is my card class and deck class.
Now I need to have a hand class, ask how may hands are required, deal them and then sort and categorise each hand.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
//use these to be able to compare cards
enum class CardRank
{
    Ace, Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King
};

enum class Suit
{
    Clubs, Spades, Diamonds, Hearts
};


class Card
{
private:
    CardRank face_value;
    Suit card_suit;
public:
    //array of strings that shows the suits in ASCII
    std::string ASCII_suits[4] { /*spades*/ u8"\u2663", /*clubs*/u8"\u2660",
                                 /*hearts*/ u8"\u2666",/*diamonds*/ u8"\u2665"};
    
    //array of strings that shows jack Queen King 10 etc
    std::string denomination[13]{"A", "2", "3", "4", "5", "6", "7", "8", "9",
                                                "10", "J", "Q", "K"};

    //constructor
    Card(CardRank _val, Suit _suit)
    {
        //std::cout << "constructor using enum" << std::endl;
        face_value = _val;
        card_suit = _suit;
    }
    
    //constructor
    Card(int value, int suit)
    {
        //std::cout << "constructor using int" << std::endl;
        face_value = static_cast<CardRank>(value);
        card_suit = static_cast<Suit>(suit);
    }
    
    //copy constructor
    Card(const Card &card)
    {
        face_value = card.face_value;
        card_suit = card.card_suit;
        //std::cout << "copy constructor" << std::endl;
    }
    
    //get ASCII pictorial suit
    std::string getSuitPic(int pos) const
    {
        return ASCII_suits[pos];
    }
    
    //get Face of card
    std::string getCardFace(int pos) const
    {
        return denomination[pos];
    }
    
    //get suit
    Suit getSuit() const { return card_suit; }
    //get value
    CardRank getValue() const {return face_value;}
    
    //print out card value/suit using member function
    void showCard()
    {
        std::cout << getCardFace(static_cast<int>(getValue()))
                    << getSuitPic(static_cast<int>(getSuit())) << std::endl;
    }
    
    //overload < than operator to enable Card comparisons
    bool operator<(const Card &other) const
    {
        return this->face_value < other.face_value;
    }
    
    //overload > than operator to enable Card comparisons
    bool operator>(const Card &other)
    {
        return this->face_value > other.face_value;
    }
    
    //overlaod equality operator to enable Card comparisons
    bool operator==(const Card &other)
    {
        return this->face_value == other.face_value && this->card_suit == other.card_suit;
    }
    
    //assignment operator overload
    Card& operator=(const Card &other)
    {
        std::cout << "assignment op" << std::endl;
        if(this == &other)
        {
            return *this;
        }
        face_value = other.face_value;
        card_suit = other.card_suit;
        return *this;
    }
    
    
   /* friend std::ostream& operator<<(std::ostream& out, const Card &rhs)
    {
        
        return out << rhs.getCardFace(static_cast<int>(rhs.getValue()))
                    << rhs.getSuitPic(static_cast<int>(rhs.getSuit()));
        
    }*/
       
};

//print out card value/suit using operator overload
inline std::ostream& operator<<(std::ostream& out, const Card &rhs)
{
    
    return out << rhs.getCardFace(static_cast<int>(rhs.getValue()))
    << rhs.getSuitPic(static_cast<int>(rhs.getSuit()));
    
}

#ifndef Deck_hpp
#define Deck_hpp


#include <vector>
#include <array>
#include <random>
#include <iterator>
#include <algorithm>
#include "Card.hpp"

class Deck
{
private:
    std::array<Card*, 52> deck;
public:
    
    //constructor
    Deck() { fillDeck(); }
    ~Deck();
    
    void fillDeck();
    void showDeck();
    void shuffleDeck();
    void sort();
    const Card& getCard(int pos) const;
    
    const Card& operator[](int i)const
    {
        return *deck[i];
    }
    
};
 

#endif /* Deck_hpp */


#include "Deck.hpp"
#include "Card.hpp"
#include <iostream>

Deck::~Deck()
{
    for(int i{}; i < 52; i ++)
    {
        delete deck[i];
        deck[i] = nullptr;
    }
    //std::cout << "deck dest" << std::endl;
}



void Deck::fillDeck()
{
    std::cout << "deck cons" << std::endl;
    for(int j{};j < 52; j++)
    {
        deck[j] = new Card(j % 13, j / 13);
    }
}


void Deck::showDeck()
{
    for(int i{};i < 52;i++)
    {
        std:: cout << *deck[i] << " ";
        if(i == 12 || i == 25 || i == 38)
        {
            std::cout << std::endl;
        }
    }
}

void Deck::shuffleDeck()
{
    std::random_device rd;
    std::mt19937 g(rd());
    std::shuffle(deck.begin(), deck.end(), g);
}


void Deck::sort()
{
    std::sort(deck.begin(), deck.end(), []( const Card *lhs, const Card *rhs){
        return lhs < rhs; } );

}

const Card& Deck::getCard(int pos) const
{
    return *deck[pos];
}




what can a 'hand' do that a 'deck' cannot do?
Or a vector of <deck> as "several hands" ?

Look at what things DO, not what they are CALLED, and your code reuse and cleanliness will go way up. If that in turn shows that something is poorly named, rename it if it is yours.
Last edited on
I have to be able to deal several hands of 5 cards, then sort each hand in ascending order. Then determine each hand in rank i.e. as full house, 4 of kind, 2 pair etc.

thinking of having a std::array<Card*, 5> but how can I get the cards or pointers into this array, then how can I create multiple hands?

Thanks for your help.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <iostream>
#include <iomanip>
#include <vector>
#include <algorithm>
#include <random>
using namespace std;

const int NumRanks = 13, NumSuits = 4, DeckSize = NumRanks * NumSuits;
const int HandSize = 5, MaxHands = DeckSize / HandSize;

default_random_engine RndEngine{random_device{}()};

class Card
{
    int n;
public:
    Card(int n) : n(n) { }
    int rank() const { return n % NumRanks + 2; } // 2 to 14
    int suit() const { return n / NumRanks; }
    char rank_char() const { return "23456789TJQKA"[n % NumRanks]; }
    char suit_char() const { return "CDHS"[n / NumRanks]; }
    bool operator<(Card c) const { return rank() < c.rank(); }
};

ostream& operator<<(ostream& out, Card c)
{
    return out << c.rank_char() << c.suit_char();
}

class Deck
{
    vector<Card> cards;
public:
    Deck() { for (int i = 0; i < DeckSize; ++i) cards.push_back(Card(i)); }
    Card deal() { Card c = cards.back(); cards.pop_back(); return c; }
    void shuffle() { std::shuffle(cards.begin(), cards.end(), RndEngine); }
};

int main()
{
    vector<Card> hands[MaxHands];
    
    Deck deck;
    deck.shuffle();
    for (int c = 0; c < HandSize; ++c)
        for (int h = 0; h < MaxHands; ++h)
            hands[h].push_back(deck.deal());

    for (int h = 0; h < MaxHands; ++h)
    {
        sort(hands[h].begin(), hands[h].end());
        cout << "Hand " << setw(2) << h + 1 << ":  ";
        for (int c = 0; c < HandSize; ++c)
            cout << hands[h][c] << ' ';
        cout << '\n';
    }
}

Last edited on
@jamesfarrow
I see you have addopted 2 approaches in your code, one from example given by Dutch, and other from example given by me in my post.

This is wrong, a code bloat with no clear objective, you have to choose either one and stick to the plan! because you either fight your cards via char array or via enums, but not both.

Know that both approaches are correct, the difference is that method given by Dutch will give you slightly more performance which is needed for fast hand evaluation, but is harder to implement. especially if you later want to evaluate hands ex. for AI to be able to play autonomously.

Now if you want to follow my advice then please get rid of "Deck" class as suggested by jonnin because you don't need a Deck, but CardSet.

I already told you in my previous post about class CardSet, CardSet class is a generic class that can hold any amount of cards, up to the limit of playing deck.

Now you see? a player has CardSet which is his hand with up to 5 cards in texas holdem poker, with community cards its up to 7 cards.

Now sooner or later you will need a class Dealer, dealer is "human" or "AI" player which just like other players also has CardSet class, which is the Deck of 52 cards which you have implemented.

Now you see why you don't need Deck class right?

Let's repeat and expand this design ever further, because class CardSet is the core of the game and most important class, CardSet can be:

1. A deck of cards owned by dealer
2. A player hand
3. Set of dead cards (cards out of game)
4. Community cards ...

As you can see CardSet can not be std::array which is fixed length, it must be both expandable and reducable, because player hand can be empty, grow and shrink, and so does dealer's deck and so does community cards, dead cards etc...

Interface of CardSet class:

The CardSet interface must expose a whole set of functionality:

1. Sorting cards according to suit or rank (both player and dealer want this)
2. Adding and removing cards from set (both player and dealer need this)
3. Shuffling the cards (both player and dealer may want to shuffle their cards)
4. Throwing cards away (useful for player only)
5. Replacing cards (useful for player only in 5 cards poker game)
6. A set of functions that will determine if cards can compose a call such as poker, flush etc..
7. A set of functions that do compose final hand and discard cards which are irrelevant for call
8. Internally the card set class maintains sorting state, and a configurable container such as std::vector which holds the actual cards.
9. It must also support showing cards (ie. in GUI or in console, doesn't matter)
10. it must be copy/move assignable/constructible, this will make sense only later.

Now please rename your Deck class and at a minimum prototype it according to this list, then we'll see how to proceed next.

You may have noticed that the CardSet is not 100% useful for all types of players or poker style games. but for now let's ignore this, you can solve this later by splitting the class into one base and various derivates later, for now just focus on fully functional class.

EDIT:
Btw, one side suggestion, don't think of a player as yourself playing poker here (at least not yet!), you are programmer not player, and the player will be somebody else.

What this means is you want to split and design your code into 2 areas:
1. program functionality, is centered around how to make things easy for you and efficient for maintanence and execution.
2. program interface, which is the part of a program your users (players) will interact with.

The difference is that interface does not dictate how you structure your code, the purpose of the interface is to provide functionality not to tell how program will internally run.

For example, the Dealer doesn't shuffle cards, he tells his CardSet object to shuffle itself instead, but user is unaware of this internal trick! he only sees how dealer shuffles those cars ;)

makes sense right? Internall functionality vs interface!
Try to understand and adopt this sense and you'll become better programmer ;)
Last edited on
Topic archived. No new replies allowed.