Thread: Playing card data structure?

  1. #1
    Registered User
    Join Date
    Aug 2006
    Posts
    16

    Playing card data structure?

    I've decided to write a texas hold'em game that can be played solitare style, just for kicks.

    Anyway, I'm hung up on the best way to represent the cards. I have a class Card that contains a pair of ints, one for the suit and the other for rank. Then a deck would just be an array of Cards, etc. Now, I want to spit the value of these cards onto the console. What's the best way to do this? Just using switch statements? Is there some way to have a global hash table that Card can access? Something else?

    (sorry if this is better off in the game forum, I couldn't decide where to stick this)

  2. #2
    Just Lurking Dave_Sinkula's Avatar
    Join Date
    Oct 2002
    Posts
    5,005
    How about just use an int for the card? You can get rank and suit easily from this value.
    Code:
       int r = value % 13;
       int s = value / 13;
    Also, you could even apply this technique to have multiple decks in the shoe.
    7. It is easier to write an incorrect program than understand a correct one.
    40. There are two ways to write error-free programs; only the third one works.*

  3. #3
    Registered User
    Join Date
    Feb 2006
    Posts
    312
    Personally, I would store the cards in an STL container, such as a vector - this would allow you to easily add/remove cards from the deck. (You may want to transfer them from a container holding the deck, to a container holding the player's current hand, or maybe a discard pile, depending on the card game.)

    assuming you've got something like
    Code:
    struct card_type
    {
        int rank;  // number from 0 to 12
        int suit;  // number from 0 to 3
    }
    you could hold tables of words to output to the screen.. (Again, maybe better to put these into a vector<string> )
    Code:
    const std::string suits[] = { "Hearts", "Diamonds", "Clubs", "Spades" };
    const std::string ranks[] = { "Ace", "Two",  "Three", /* ... */ "Jack", "Queen", "King" };
    which may be represented something like this...
    Code:
    card_type mycard;
    mycard.rank = 2;
    mycard.suit = 1;   //You'd probably have gotten these values another way
    
    std::cout << "You have the " << ranks[mycard.rank] 
              << " of " << suits[mycard.suit] << std::endl;
    This is a minimalist example without error checking, so you'd need to add some data validation before accessing any array elements.
    Last edited by Bench82; 12-30-2006 at 07:48 PM. Reason: Missing a " - thanks dwks for spotting that!

  4. #4
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    You're missing a ".
    Code:
    const std::string suits[] = { "Hearts", "Diamonds", "Clubs", "Spades" };
    Is there some way to have a global hash table that Card can access?
    You could have a static array instead of a global variable, for the suits for example.
    Code:
    class Card {
    private:
        int rank, suit;
        static const char *suits[], *ranks[];
    public:
        const char *get_suit() { return suits[suit]; }
    };
    
    const char *Card::suits[] = {"Hearts", "Diamonds", "Clubs", "Spades"};
    Or maybe you could have a class Suit to represent this.
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  5. #5
    Registered User
    Join Date
    Aug 2006
    Posts
    16
    Quote Originally Posted by dwks

    You could have a static array instead of a global variable, for the suits for example.
    Code:
    class Card {
    private:
        int rank, suit;
        static const char *suits[], *ranks[];
    public:
        const char *get_suit() { return suits[suit]; }
    };
    
    const char *Card::suits[] = {"Hearts", "Diamonds", "Clubs", "Spades"};
    The problem with this is these arrays would be created for every card, right?

  6. #6
    Registered User
    Join Date
    Aug 2006
    Posts
    16
    Quote Originally Posted by Bench82
    Personally, I would store the cards in an STL container, such as a vector - this would allow you to easily add/remove cards from the deck.
    Actually I already had 'em in a vector. To shuffle, a simple call to random_shuffle is all that's needed.

  7. #7
    Registered User
    Join Date
    Aug 2006
    Posts
    16
    Quote Originally Posted by Dave_Sinkula
    How about just use an int for the card? You can get rank and suit easily from this value.
    Code:
       int r = value % 13;
       int s = value / 13;
    Also, you could even apply this technique to have multiple decks in the shoe.
    Certainly another way to do it. For convenience sake I was going to store them in a pair<int, int> for passing around and such.

    It's a toss up really. Do you use one int (really could be a char if we were concerned that much about memory use) and take the processing time to do the mods and divides every time you need to evaluate the card? Or do you use twice as much memory but avoid the computations? If we were talking about anything larger it might be a genuine debate, but really who cares if you use 52 bytes of memory vs 104 bytes of memory...or if you do a couple of mods and divides or not?

    Just because I'm lazy, I think I'll store them seperately. Seems like kind of a pain to have to calculate out the values everytime you want to use them.

  8. #8
    Registered User
    Join Date
    Sep 2006
    Posts
    835
    How about
    Code:
    class Card {
      int value;
    public:
      void set_card(int rank, int suit) {value = rank+13*suit;}
      int get_rank() {return value%13;}
      int get_suit() {return value/13;}
    };
    That minimizes memory use but makes it reasonably easy to use (you can replace int with unsigned char, and modify suitably if you want the rank to go from 1 to 13 instead of from 0 to 12 as it does now).

    Edit: Same for suit which presently goes from 0 to 3.

  9. #9
    Registered User
    Join Date
    Aug 2006
    Posts
    16
    Good idea, and I can return them back in a pair for convenience sake. Thanks.

  10. #10
    Registered User
    Join Date
    Feb 2006
    Posts
    312
    Quote Originally Posted by crypticgeek
    The problem with this is these arrays would be created for every card, right?
    'static' member variables are created exactly once, regardless of how many instances you make. (or even if you don't make any instances at all). They essentially stand alone, and don't belong to any particular object

  11. #11
    Registered User
    Join Date
    Dec 2006
    Location
    Washington
    Posts
    18
    Quote Originally Posted by crypticgeek
    Certainly another way to do it. For convenience sake I was going to store them in a pair<int, int> for passing around and such.
    It doesn't make sense to me to be passing around a pair when you can pass around a single char...

    Quote Originally Posted by crypticgeek
    It's a toss up really. Do you use one int (really could be a char if we were concerned that much about memory use) and take the processing time to do the mods and divides every time you need to evaluate the card? Or do you use twice as much memory but avoid the computations?
    ...when was a char only half the size of an int? ...in the days of DOS?

    Quote Originally Posted by crypticgeek
    If we were talking about anything larger it might be a genuine debate, but really who cares if you use 52 bytes of memory vs 104 bytes of memory...or if you do a couple of mods and divides or not?
    Imagine the additional cycles required for creating pair<int, int> for every value that could more easily be a single char? Compare that with the execution requirements for the mods and division.

    Quote Originally Posted by crypticgeek
    Just because I'm lazy, I think I'll store them separately. Seems like kind of a pain to have to calculate out the values everytime you want to use them.
    Whose pain? Yours or the processor? Let's say that you have a really incredibly massively slow, 1 MHz PC. How many instructions can it execute in just the time that it takes you to press and release one key on the keyboard? ...even if you were typing 120 WPM? How fast would you have to be able to type to keep up with a 1 GHz PC if you could type a character as fast as it executes a single-cycle instruction? ...or even the most-cycles instruction?

    You only need a single char for storage. Who really cares if you use twice as much memory? Anyone who is a worthwhile programmer should care. Besides, two ints is 8 bytes on a 32-bit machine and 16 bytes on a 64-bit machine, a single char is 1 byte whether the machine is 32 or 64 bits. Not exactly twice, huh?

    Note that you are NOT creating a "Card" class, even though that has been offered here by others. A Card is not necessarily a Playing Card, which is what is used for Texas Hold'em. A Hallmark birthday card is a kind of card as is any of one of the members of a deck of Old Maid or Pinnocle cards. We should understand the difference and CARE about these important differences.

    Here is a very quick hack of a (probably too) simple "PlayingCard" class implementation. Note that I tossed everything into a single file for simplicity and brevity:

    Code:
    #include <iostream>
    #include <iomanip>
    #include <string>
    #include <vector>
    
    using namespace std;
    
    const static char FACE_VALUES[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 };
    const static char* FACE_NAMES[] = { "A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K" };
    const static char SUIT_VALUES[] = { 0x10, 0x20, 0x30, 0x40 };
    const static char* SUIT_NAMES[] = { "Spades", "Hearts", "Clubs", "Diamonds" };
    const static char MIN_CARD_VALUE = 1 | 0x10;
    const static char MAX_CARD_VALUE = 13 | 0x40;
    
    class PlayingCard
    {
    public:
        const static char SPADES	= 0x10;
        const static char HEARTS	= 0x20;
        const static char CLUBS	= 0x30;
        const static char DIAMONDS	= 0x40;
        
        PlayingCard() : m_value( 0 ){}
        PlayingCard( const char value ) : m_value( value ){}
        PlayingCard( PlayingCard const& rhs ){ m_value = rhs.m_value; }
        const char getValue() const { return m_value; }
        void setValue( const char value ){ m_value = validateValue( value ) ? value : m_value; }
        std::string toString()
        {
    	std::string value;
    	if( validateValue( m_value ) )
    	{
    	    std::string face( FACE_NAMES[ (m_value % 0x10) -1 ] );
    	    std::string suit( SUIT_NAMES[ (m_value / 0x10) -1 ] ); 
    	    value = face;
    	    value += " ";
    	    value += suit;
    	}
    	else
    	{
    	    value = "Invalid Playing Card!";
    	}
    	return value;
        }
        const char getFaceValue() const { return FACE_VALUES[ (m_value % 0x10) -1 ]; }
        const char getSuitValue() const { return SUIT_VALUES[ (m_value / 0x10) -1 ]; }
        static char makeValue( const char face, const char suit ) { return validateValue( face | suit ) ? ( face | suit ) : 0; }
    private:
        char m_value;
        static bool validateValue( const char value )
        {
    	bool bIsValid = false;
    	if( value >= MIN_CARD_VALUE && value <= MAX_CARD_VALUE )
    	{
    	    bIsValid = true;
    	}
    	return bIsValid;
        } 
    };
    
    typedef std::vector<PlayingCard> vPlayingCardDeck;
    
    void spew_deck( vPlayingCardDeck& v )
    {
        vPlayingCardDeck::iterator it;
        for( it = v.begin(); it != v.end(); it++ )
        {
    	cout << (*it).toString() << endl;
        }
    }
    
    int main()
    {
        vPlayingCardDeck v;
    
        for( int i = 0x10; i < 0x50; i += 0x10 )
        {
    	for( int j = 1; j < 14; j++ )
    	{
    	    PlayingCard card( i | j );
    	    v.push_back( card );
    	}
        }
    
        spew_deck( v );
    
        PlayingCard card;
        cout << card.toString() << endl;
    
        card.setValue( 12 | PlayingCard::SPADES );
        cout << card.toString() << endl;
    
        cout << "\t" << static_cast<int>( card.getFaceValue() ) << endl;
        cout << setfill( '0' ) << showbase << hex << internal;
        cout << "\t" << static_cast<int>( card.getSuitValue() ) << endl;
    
        card.setValue( PlayingCard::makeValue( 11, PlayingCard::DIAMONDS ) );
        cout << card.toString() << endl;
    
        return 0;
    }
    Output:

    Code:
    A Spades
    2 Spades
    3 Spades
    4 Spades
    5 Spades
    6 Spades
    7 Spades
    8 Spades
    9 Spades
    10 Spades
    J Spades
    Q Spades
    K Spades
    A Hearts
    2 Hearts
    3 Hearts
    4 Hearts
    5 Hearts
    6 Hearts
    7 Hearts
    8 Hearts
    9 Hearts
    10 Hearts
    J Hearts
    Q Hearts
    K Hearts
    A Clubs
    2 Clubs
    3 Clubs
    4 Clubs
    5 Clubs
    6 Clubs
    7 Clubs
    8 Clubs
    9 Clubs
    10 Clubs
    J Clubs
    Q Clubs
    K Clubs
    A Diamonds
    2 Diamonds
    3 Diamonds
    4 Diamonds
    5 Diamonds
    6 Diamonds
    7 Diamonds
    8 Diamonds
    9 Diamonds
    10 Diamonds
    J Diamonds
    Q Diamonds
    K Diamonds
    Invalid Playing Card!
    Q Spades
            12
            0x10
    J Diamonds

    :davis:

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Data structure for storing serial port data in firmware
    By james457 in forum C Programming
    Replies: 4
    Last Post: 06-15-2009, 09:28 AM
  2. pthread question how would I init this data structure?
    By mr_coffee in forum C Programming
    Replies: 2
    Last Post: 02-23-2009, 12:42 PM
  3. xor linked list
    By adramalech in forum C Programming
    Replies: 23
    Last Post: 10-14-2008, 10:13 AM
  4. Replies: 48
    Last Post: 09-26-2008, 03:45 AM
  5. Dynamic Data Structure -- Which one is better?
    By Yin in forum C++ Programming
    Replies: 0
    Last Post: 04-10-2002, 11:38 PM