Skip to content

Instantly share code, notes, and snippets.

@satyajeetkrjha
Forked from michaelsafyan/card.cpp
Created June 5, 2022 01:14
Show Gist options
  • Save satyajeetkrjha/fd5fab8cfe25434eaa84a7ed450031f6 to your computer and use it in GitHub Desktop.
Save satyajeetkrjha/fd5fab8cfe25434eaa84a7ed450031f6 to your computer and use it in GitHub Desktop.

Revisions

  1. @michaelsafyan michaelsafyan revised this gist Feb 19, 2015. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions card.h
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,6 @@
    // An illustrative example of how one might define a playing card in C++
    // Intended to illustrate some commments in response to StackOverflow question:
    // http://stackoverflow.com/questions/28599150/pass-vector-of-card-into-function-to-print-and-use
    #ifndef __CARD__
    #define __CARD__

  2. @michaelsafyan michaelsafyan created this gist Feb 19, 2015.
    52 changes: 52 additions & 0 deletions card.cpp
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,52 @@
    #include "card.h"

    #include <sstream>

    namespace playing_cards {

    Card::Card(int value, Suit suit) : value_(value), suit_(suit) {}
    Card::Card(const Card& o) : value_(o.value_), suit_(o.suit_) {}
    Card::~Card() {}

    bool Card::IsValid() const { return (value_ >= ACE) && (value <= KING); }

    std::string Card::ToString() const {
    std::ostringstream out;
    out << *this;
    return out.str();
    }

    bool Card::operator==(const Card& o) const {
    return ((value_ == o.value_) && (suit_ == o.suit_));
    }

    Card::bool color() const { return ((suit_ == SPADES) || (suit_ == CLUBS)); }
    int Card::value() const { return value_; }
    Suit Card::suit() const { return suit_; }

    } // namespace playing_cards


    std::ostream& operator<<(std::ostream& out, const Card& card) {
    if (!card.IsValid()) {
    out << "<Invalid>";
    return out;
    }

    int value = card.value();
    switch (card.value()) {
    case Card::ACE: out << "Ace"; break;
    case Card::JACK: out << "Jack"; break;
    case Card::QUEEN: out << "Queen"; break;
    case Card::KING: out << "King"; break;
    default: out << value; break;
    }
    out << " of ";
    switch (card.suit()) {
    case Card::SPADES: out << "Spades"; break;
    case Card::CLUBS: out << "Clubs"; break;
    case Card::HEARTS: out << "Hearts"; break;
    case Card::DIAMONDS: out << "Diamonds"; break;
    }
    return out;
    }
    68 changes: 68 additions & 0 deletions card.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,68 @@
    // An illustrative example of how one might define a playing card in C++
    #ifndef __CARD__
    #define __CARD__

    #include <string>
    #include <iostream>>

    // We always want libraries to go in an appropriate namespace to avoid collisions.
    namespace playing_cards {

    class Card {
    public:
    // These could also be declared using "const static" instead of "constexpr" for pre-C++11.
    // Using "constexpr" increases the ways in which these can be used, though, and also avoids
    // needing to declare these variables in the *.cpp file, which is handy.
    //
    // Defining these improves the API by making code referencing these values clearer than if
    // they simply used the numeric value (especially since Ace is sometimes higher valued than King).
    constexpr int ACE = 1;
    constexpr int JACK = 11;
    constexpr int QUEEN = 12;
    constexpr int KING = 13;

    // Defining these as an enum ensures that an invalid value cannot be supplied. Putting these
    // inside the outer "Card" class ensures that the values are well scoped (alternatively,
    // one could use an enum class in C++11 outside of Card to keep the values scoped that way).
    enum Suit {
    SPADES,
    CLUBS,
    HEARTS,
    DIAMONDS
    };

    // Constructor with the value and suit. A problem with this particular declaration, though,
    // is that it makes it trivial to construct an invalid instance of this object. That could
    // be resolved by throwing an exception in the event of invalid values, allowing invalid
    // states and providing a way to detect them (what we do here), or making this constructor
    // private and requiring that this object be instantiated through a factory method that can
    // fail and return null. The factory approach is typically my preference, but this is a very
    // small object that we expect to be copyable, and so that is a little bit heavy-handed.
    // Second to that, I generally prefer objects always be valid and throw an exception if given
    // invalid values; however, invalid values may be handy in the case of a card to indicate
    // additional, non-standard cards (or perhaps to indicate a Joker?), so in this specific
    // case, it is reasonable to defer error handling to the deck or application.
    Card(int value, Suit suit);
    Card(const Card& o);
    ~Card();

    bool IsValid() const;
    std::string ToString() const;

    // Technically not necessary (the builtin implementation works just fine), but can be illustrative.
    bool operator==(const Card& o) const;

    bool color() const;
    int value() const;
    Suit suit() const;
    private:
    int value_;
    Suit suit_;
    };

    } // namespace playing_cards

    std::ostream& operator<<(std::ostream& out, const playing_cards::Card& card);


    #endif