Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Comparing the rank of cards in a hand

Tags:

I have a homework assignment that was two parts. The first part was to make a program that puts a vector of 52 card objects when you create an object for the class Deck. I did this by making the deck first give each card the number 1-4 for suit and 2-14 for card. It then changes the numbers to strings so it can output the cards in the form of "Ace of spades, two of hearts" etc. Now I am trying to figure out how to make it draw five cards and evaluate it for

pair, two pair, three of a kind, four of a kind, and full house.

Not sure if this is possible with my strings or if I would have to change my whole code to do it a different way. Here is the code.

edit: Also, the main.cpp was provided as a template and we were forced to build around it so even if there are better ways to do it we have to do it in this form >.<

editedit: here is a hint we were given "Hint: Create a map where each pair stores a unique rank and the number of times that rank occurs in the hand. You can also use the count_if function from to determine how many pairs or threes are in the set." but to be honest I don't really get what he wants us to do here...

//main.cpp 

#include <iostream>
#include "Deck.h"

using namespace std; 



int main()
{
    Deck deck;                  // created the object called 'deck'
    deck.shuffleCards();        // puts cards in the deck and shuffles them
    while (not deck.empty())    // if the deck isn't empty loop will continue
    {
        cout << deck.draw().toString() << endl;    // first it draws a card from the deck                                                                                                                                                           
    }                                              // of the form '000' and then puts those three
                                                   // numbers into the toString function which 
}                                                  // converts them into a string of words
                                                   // in the form 'Card' of 'Suit'. Keeps drawing
                                                   // cards run out. 

and here is the functions

#include "Deck.h"

// Constructor for cards that are created

inline Card::Card(int s, int r)
{
    suit = s;
    rank = r;
};

// This function turns three int 'Cards' into three word
// strings that get returned when you call the function.

std::string Card::toString() 
{

    std::string oldS = std::to_string(suit); // this creates a string called oldS(uit) 
                                             // and changes the int into a string
    std::string oldR = std::to_string(rank); // this creates a string called oldR(ank) 
                                             // and changes the int into a string

    std::string SR = oldS + oldR;       // turns the two strings into one 
                                        // and puts them into a new string
    std::string newS;
    std::string newR;                   // These will be the new suit and rank 

    // this code turns the numbers (which are already strings) into words.
    // 'substr' lets you search any length of the string. We have a string of
    // two to three numbers but we need to analyze the first character and the
    // second / third seperately. With 'substr' you can do this. 

    if(SR.substr(0, 1) == "1") // if starting at character 0 and reading one character
        newS = "Hearts";       // is equal to '1' then make 'newS' equal to ' Hearts'
    if(SR.substr(0, 1) == "2")
        newS = "Diamonds";
    if(SR.substr(0, 1) == "3")
        newS = "Spades";
    if(SR.substr(0, 1) == "4")
        newS = "Clubs";

    if(SR.substr(1, 2) == "2") // if starting at character 1 and reading 2 characters
        newR = "Two";          // is equal to '2' then make 'newR' equal to 'Three'
    if(SR.substr(1, 2) == "3")
        newR = "Three";
    if(SR.substr(1, 2) == "4")
        newR = "Four";
    if(SR.substr(1, 2) == "5")
        newR = "Five";
    if(SR.substr(1, 2) == "6")
        newR = "Six";
    if(SR.substr(1, 2) == "7")
        newR = "Seven";
    if(SR.substr(1, 2) == "8")
        newR = "Eight";
    if(SR.substr(1, 2) == "9")
        newR = "Nine";
    if(SR.substr(1, 2) == "10")
        newR = "Ten";
    if(SR.substr(1, 2) == "11")
        newR = "Jack";
    if(SR.substr(1, 2) == "12")
        newR = "Queen";
    if(SR.substr(1, 2) == "13")
        newR = "King";
    if(SR.substr(1, 2) == "14")
        newR = "Ace";

    SR = newR + " of " + newS; // this string had the numbers in it but now we can  
                               // reassign it the string 'Card of suit'

    return SR; // returns the string which is outputted to the console when you call
               // the 'toString' function
};

// This function draws top object of the vector then pops it from 
// the vector and returns it to the call. It is of return type 'Card'.

Card Deck::draw() 
    {
        int a = Cards.size(); 
        int b = a - 1;        // -1 because the vector has 52 cards but you 
        Cards.pop_back();     // want to access [0] - [51] not [52]
        return Cards[b];
    };

// This is the function that creates the cards in the vector.
// It uses two loops and assigns a number to 'a' and 'b'. The first number is 
// in the range 1 - 4 and the second is 2 - 14. It then creates an object using 
// 'a' and 'b' which is then pushed back onto the vector. All vector objects have
// the same name as of now (but not the same data). Shuffles the objects at the end.

void Deck::shuffleCards()
{
    int a;
    int b;
    for(a = 1; a < 5; a++) // 1 - 4
    {
        for(b = 2; b < 15; b++ ) // 2 - 14
        {
            Card newCard(a, b);
            Cards.push_back(newCard);
        }
    }

    std:: mt19937 seed(rd());                        // this creates the seed
    std::shuffle(Cards.begin(), Cards.end(), seed);  // this shuffles the deck with the 
};                                                   // random seed 

// This function checks if the deck is empty 
// if it is not it will return false and when it is empty 
// it will return true which breaks the loop in main.cpp

bool Deck::empty()
{
    if(Cards.size() < 1)
        return true;
    else 
        return false;
};

// This function will reset the deck if called. It will purge 
// the vector and then repopulate it with the original contents.
// but will not shuffle them. 

void Deck::reset()
{
    Cards.clear();
    int a;
    int b;
    for(a = 1; a < 5; a++)
    {
        for(b = 2; b < 15; b++ )
        {
            Card newCard(a, b);
            Cards.push_back(newCard);
        }
    }
};

and header

#ifndef DECK_H
#define DECK_H
#include <vector> 
#include <string> 
#include <random>
#include <algorithm>

class Card
{
public:
    inline Card(int s, int r);
    int rank;
    int suit;
    std::string toString();
};

class Deck 
{
private:
    std::vector<Card> Cards;
    std::random_device rd;
public:
    void shuffleCards();
    void reset();
    Card draw();
    bool empty();
};

class Hand
{
    public: 
        std::vector<std::string> Hand;
        void fillHand()
        {
            Deck deck;
            std::string C1 = deck.draw().toString();
            std::string C2 = deck.draw().toString();
            std::string C3 = deck.draw().toString();
            std::string C4 = deck.draw().toString();
            std::string C5 = deck.draw().toString();
            Hand.push_back(C1);
            Hand.push_back(C2);
            Hand.push_back(C3);
            Hand.push_back(C4);
            Hand.push_back(C5);
        }
};
#endif

Thank you in advance :D

at this point I basically create a new object from the hand class which fills the hand up with 5 random cards and in each card is a string in the form of "card of suit". Since I only need to worry about the "card" should I change the code to throw away the "of" and "suit"? Eventually we are gonna need to also use the suit which is why I did not want to do that.

like image 942
David Bernal Avatar asked May 24 '17 21:05

David Bernal


People also ask

What are the ranks of cards?

(a) The rank of the cards used in all types of poker other than low poker, for the determination of winning hands, in order of highest to lowest rank, shall be: ace, king, queen, jack, 10, nine, eight, seven, six, five, four, three and two. All suits shall be considered equal in rank.

Which card hand is higher?

Royal Flush This is the highest poker hand. It consists of ace, king, queen, jack and ten, all in the same suit. As all suits are equal, all royal flushes are equal.

What are the rankings of a poker hand?

What is the order of poker hands? As shown in the poker hand rankings chart, the order of poker rankings (from the highest to the lowest) is: Royal Flush, Straight Flush, Four-of-a-Kind, Full House, Flush, Straight, Three-of-a-Kind, Two Pair, One Pair, High Card.

How do you compare hands in poker?

Note that when comparing hands, the highest card is compared first, just as in standard poker. So for example 6-5-4-3-2 is better than 7-4-3-2-A because the 6 is lower than the 7. The best hand containing a pair is A-A-4-3-2. This version is sometimes called "California Lowball".


2 Answers

The string has only significance to the player, as for the computer, it is only waste of memory over the int solution.

That said, I'd propose that you use enumerations:

enum Suit {Hearts, Clubs, Spades, Diamonds};
enum Rank {Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen King, Ace};

Note that enums start with 0 and add 1 if nothing else is stated, so they are in perfect order, just happens that they are better represented as with an int.

with a to_string method:

string toString(const Suit){
    switch(Suit){
        case Hearts:
        return "Hearts";
et cetera

with

class Card
{
public:
Card(const Suit suit, const Rank rank); //<--- why did you declare this inline? arguments should be const
string toString() const;
Rank rank() const;
Suit suit() const;

private: // <-- encapsule. A card should not be able to change those values
Rank _rank;
Suit _suit;
};

This class Card is the perfect container as it is. There is no need to change it into a string, at least not if you do not want to print it.

Now for the interesting part:

First of all, I'd make Hand to be something that is either returned by Deck ("create hand") or that gets a Deck in it's one and only constructor. A Hand should always be instantiated with five cards and this should be it's only possible state. Deck should always be an independent object.

I'd give Hand a vector of Card. No need for string. Makes everything easier.

As helper methods, I'd add a method that creates a vector which counts the multiplicity of each rank. From that, you can easily get a vector that counts how many doubles, triples and quadrupels there are. And if you have that, you are done. So go like this:

enum HandValue {HighCard, Pair, ThreeOfAKind, FourOfAKind, FullHouse };

class Hand //note that I omitted some methods that are not relevant for the answer
{
private:
    vector<Card> cards;
    vector<unsigned int> multiplicityOfRank() const;
    vector<unsigned int> components() const; //<-- uses multiplicityOfRank
public:
    Hand(Deck& deck); // <-- will reduce deck by five cards. Note that a reference is used.
    HandValue evaluate() const; // <-- uses components
}

components could be like position 0 storing the amount of doubles, position 1 the triples, position 2 the quadrupels

Does that help? Questions left?

(btw, personally I prefer not to write the namespaces if the namespace is obvious - you know about the using keyword, as in using std::vector;? Don't use using namespace std; as in the given main, though, std is too big to rule out name collisions.)

like image 79
Aziuth Avatar answered Sep 22 '22 11:09

Aziuth


Re: "should you change the code to throw away the 'of' and 'suit'?"

I don't see a reason to. You have a toString() method that will generate a display string for output, and I liked that the example code wasn't storing the string result in a class member.

Your .suit and .rank members can be used to evaluate if a card is one of the hoped-for sets (pair, two pair, three of a kind, four of a kind, and full house). Don't compare against the string representation--since this is a less direct comparison, is less efficient, and prone to error if a real-world situation where one might update the string representation, e.g. provide a Spanish localization.

I think most of the work is done here. Your evaluation of the cards should be pretty easy. Maybe add a new method to Hand that checks for sets within the cards you've pushed into the Hand.

like image 32
Erik Hermansen Avatar answered Sep 22 '22 11:09

Erik Hermansen