Hand rank comparison

I have a two-part homework. The first part was to create a program that puts a vector of 52 card objects when creating an object for the Deck class. I did this by making a deck first to give each card a number 1-4 for a suit and 2-14 for a card. Then he changes the numbers into lines so that he can display cards in the form of "Ace of spades, two hearts", etc. Now I'm trying to figure out how to get him to draw five cards and evaluate them for

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

Not sure if this is possible with my lines, or if I have to change all my code to do it differently. Here is the code.

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

editit: here’s the hint we were given: “Hint: create a map in which each pair holds a unique rank and the number of times this rank occurs in the hand. You can also use the count_if function to determine how many pairs or triples are in the set” . but honestly, I really don’t understand what he wants to do from us ...

//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 there are 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 title

 #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 

Thanks in advance: D

At this point, I basically create a new object from the manual class, which fills the hand with 5 random cards, and each card has a line in the form of a “suit card”. Since I only need to worry about the “card”, should I change the code to throw out “out” and “suit”? In the end, we will also need to use the costume, so I did not want to do this.

+5
source share
3 answers

The string matters only to the player, since for the computer it is just a waste of memory over the int solution.

However, I suggest you use the enumerations:

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

Note that enumerations start at 0 and add 1 if nothing is specified, so they are in perfect order, it just happens that they are better represented as with int.

using the 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 would do Hand to be something that either returns Deck ("create hand"), or Deck receives one and only constructor from it. A hand should always be created with five cards, and this should only be possible. The deck should always be an independent facility.

I would give the Hand a Card vector. No need for string. Makes everything easier.

As auxiliary methods, I would add a method that creates a vector that takes into account the multiplicity of each rank. From this you can easily get a vector that counts how many doubles, there are triples and quadruples. And if you have it, you are ready. So do the following:

 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 can be similar to position 0, maintaining the number of doublings, position 1 - triples, position 2 - quadrupoles

Does it help? Still have questions?

(By the way, personally, I prefer not to write namespaces if the namespace is obvious - do you know about using , as in using std::vector; ;? Do not use using namespace std; as in this main, std is too large to exclude conflicts names.)

+3
source

Re: "Should you change the code to throw" out "and" suit "?"

I see no reason. You have a toString () method that will generate a display string for output, and I liked that the example code did not store the result of the string in a class member.

Your .suit and .rank members can be used to assess whether the card is one of the sets of hopes (pairs, two pairs, three kinds, four of a kind and a full house). Do not compare with the string representation - since this is a less direct comparison, less efficient and error prone if the situation is in the real world where you can update the string representation, for example. provide Spanish localization.

I think that most of the work is done here. Your score cards should be pretty simple. Perhaps add a new Hand method that checks the sets inside the cards that you enclose in Hand.

+2
source

As suggested in another answer, I would like to focus on using enums rather than strings for this. You have a very limited list of potential values. I would start by creating the following typedefs:

 enum class Suit {Hearts, Clubs, Spades, Diamonds}; constexpr static auto num_ranks = 13; // must be consistent with below enum class Rank {Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King, Ace}; enum class PokerHand {Nada, Pair, TwoPair, ThreeOfKind, FullHouse, FourOfKind}; using Card = const struct { Suit suit; Rank rank; }; using PlayerHand = std::array<Card, 5>; 

By installing this technique, we can now continue our solution as a function:

 PokerHand determineHand(const PlayerHand& player_hand) { std::vector<int> rank_count(num_ranks); for (auto& card : player_hand) { ++rank_count[static_cast<int>(card.rank)]; } auto count_rank_counts = [&] (int count) { return std::count(rank_count.begin(), rank_count.end(), count); }; if (count_rank_counts(4)) return PokerHand::FourOfKind; if (count_rank_counts(3)) { if (count_rank_counts(2)) return PokerHand::FullHouse; else return PokerHand::ThreeOfKind; } if (count_rank_counts(2) == 2) return PokerHand::TwoPair; if (count_rank_counts(2) == 1) return PokerHand::Pair; return PokerHand::Nada; } 

A few key points. Static_cast may seem a little strange, but it is guaranteed to be legal because the type of support for the enum class, which does not explicitly declare it, is int if int is not big enough. Then we set up a function that counts how many times a certain number of ranks have occurred. I know this is a little strange because it is an account. But there it is. Then we just check the hands from the highest to the lowest. Not very effective, but easy to follow.

Real-time example: http://coliru.stacked-crooked.com/a/a0db9e552cdfbcf5 .

-1
source

Source: https://habr.com/ru/post/1268194/


All Articles