feat: finish chapter 4 program 3

This commit is contained in:
Youwen Wu 2025-02-04 21:51:21 -08:00
parent 61c0b4fce9
commit d40127ed66
7 changed files with 306 additions and 0 deletions

View file

@ -28,3 +28,13 @@ c_4_lab_4 = executable(
['src/chapter_4/lab_4/main.cpp', 'src/chapter_4/lab_4/Distance.cpp'],
install: true,
)
c_4_program_3 = executable(
'c_4_program_3',
[
'src/chapter_4/program_3/main.cpp',
'src/chapter_4/program_3/Card.cpp',
'src/chapter_4/program_3/Deck.cpp',
],
install: true,
)

View file

@ -0,0 +1,65 @@
#include "Card.h"
Card::Card() {
suit = 'c';
rank = 2;
}
Card::Card(char suit, int rank) {
char suit_lowered = tolower(suit);
if (suit_lowered != 'c' && suit_lowered != 'h' && suit_lowered != 's' &&
suit_lowered != 'd') {
this->suit = 'c';
} else {
this->suit = suit_lowered;
}
if (rank < 0 || rank > 13) {
this->rank = 2;
} else {
this->rank = rank;
}
}
char Card::getSuit() const { return this->suit; }
int Card::getRank() const { return this->rank; }
ostream &operator<<(ostream &stream, const Card &card) {
string suitName;
string cardName;
switch (card.suit) {
case 'c':
suitName = "Clubs";
break;
case 'd':
suitName = "Diamonds";
break;
case 'h':
suitName = "Hearts";
break;
case 's':
suitName = "Spades";
break;
}
switch (card.rank) {
case 1:
cardName = "Ace";
break;
case 11:
cardName = "Jack";
break;
case 12:
cardName = "Queen";
break;
case 13:
cardName = "King";
break;
default:
cardName = to_string(card.rank);
}
stream << cardName << " of " << suitName;
return stream;
}

View file

@ -0,0 +1,48 @@
//Card interface file
#ifndef __CARD_H__
#define __CARD_H__
#include <iostream>
using namespace std;
class Card {
private:
char suit;
int rank;
public:
/* Assigns a default value of 2 of Clubs
*/
Card();
/* Assigns the Card the suit and rank provided.
suits: c = Clubs, d = Diamonds, h = Hearts, s = Spades
If an invalid suit is provided, sets the suit to Clubs
ranks: 1 - 13 (1 = Ace, 11 = Jack, 12 = Queen, 13 = King)
If an invalid rank is provided, sets the rank to 2
Accepts lower or upper case characters for suit
*/
Card(char, int);
/* Returns the Card's suit
*/
char getSuit() const;
/* Returns the Card's rank as an integer
*/
int getRank() const;
/* Outputs a Card in the following format: Rank of Suit
For example, if the rank is 3 and the suit is h: 3 of Hearts
Or, if the rank is 1 and the suit is d: Ace of Diamonds
Or, if the rank is 12 and the suit is c: Queen of Clubs
etc.
*/
friend ostream & operator<<(ostream &, const Card &);
};
#endif

View file

@ -0,0 +1,29 @@
#include "Deck.h"
#include <algorithm>
Deck::Deck() {
char suits[] = {'s', 'h', 'd', 'c'};
for (char c : suits) {
for (int i = 13; i >= 1; i--) {
theDeck.push_back(Card(c, i));
}
}
}
void Deck::shuffleDeck() {
for (auto c : dealtCards) {
theDeck.push_back(c);
}
dealtCards.clear();
random_shuffle(theDeck.begin(), theDeck.end());
}
unsigned Deck::deckSize() const { return theDeck.size(); }
Card Deck::dealCard() {
Card back = theDeck.back();
dealtCards.push_back(back);
theDeck.pop_back();
return back;
}

View file

@ -0,0 +1,50 @@
#ifndef __DECK_H__
#define __DECK_H__
#include <vector>
using namespace std;
#include "Card.h"
class Deck {
private:
vector<Card> theDeck;
vector<Card> dealtCards;
public:
/* Constructs a Deck of 52 cards:
Ace, 2, 3, 4, 5, 6, 7, 8, 9, 10, Jack, Queen, King of each suit.
Cards should start off in this order with the order of suits being:
Clubs, Diamonds, Hearts, Spades. So, the Card at the top of the
Deck should be the Ace of Clubs. The next Card should be 2 of Clubs.
The 3rd Card should be 3 of Clubs, and so on ...
For best efficiency, the top of Deck should be stored at
back end of vector.
*/
Deck();
/* Deals (returns) the top card of the deck.
Removes this card from theDeck and places it in dealtCards.
As mentioned in comments for the constructor,
for best efficiency, the top card should come from the
back end of vector.
*/
Card dealCard();
/* Places all cards back into theDeck and shuffles them into random order.
Use random_shuffle function from algorithm library.
To pass test harness, this function must go forward through dealtCards
pushing each Card onto the back end of theDeck, then clear dealtCards.
Do not use pop_back on dealtCards.
*/
void shuffleDeck();
/* returns the size of the Deck (how many cards have not yet been dealt).
*/
unsigned deckSize() const;
};
#endif

View file

@ -0,0 +1,6 @@
Found Pair!! Ace of Hearts, 8 of Diamonds, 9 of Hearts, 3 of Hearts, 9 of Spades, 5 of Spades, 10 of Diamonds, 9 of Diamonds, Queen of Spades, 8 of Spades,
Found Pair!! 5 of Clubs, 9 of Hearts, 3 of Spades, 2 of Spades, King of Spades, 2 of Clubs, King of Diamonds, Jack of Spades, Jack of Diamonds, 5 of Diamonds,
Found Pair!! 3 of Spades, Jack of Diamonds, 9 of Spades, 4 of Spades, 10 of Clubs, 7 of Spades, 9 of Diamonds, 7 of Hearts, Ace of Clubs, 4 of Diamonds,
Found Pair!! Ace of Clubs, 9 of Hearts, 6 of Spades, 9 of Spades, 2 of Hearts, Queen of Diamonds, 4 of Spades, Jack of Spades, 8 of Diamonds, 5 of Clubs,
Found Pair!! 3 of Clubs, Ace of Spades, 6 of Hearts, 5 of Clubs, 7 of Clubs, King of Spades, 10 of Spades, Queen of Hearts, King of Clubs, Jack of Diamonds,

View file

@ -0,0 +1,98 @@
#include <cstdlib>
#include <ctime>
#include <fstream>
#include <iostream>
using namespace std;
#include "Card.h"
#include "Deck.h"
// Returns true if vector of Cards passed in contains at least 2 Cards with the
// same rank.
bool hasPair(const vector<Card>);
double simulateMonteCarlo(Deck, int, int, bool, ostream &);
// Sends to output stream a hand of Cards on a single line,
// each Card separated by a comma.
// If the vector is empty, do not output anything.
// Example (vector size of 3): Ace of Spades, Two of Hearts, King of Clubs
ostream &operator<<(ostream &, const vector<Card> &);
int main() {
srand(2222);
bool toFile;
int numOfHands;
int numOfDeals;
string filename;
string in;
fstream file;
cout << "Do you want to output all hands to a file?(Yes/No)";
cin >> in;
toFile = in == "Yes";
if (toFile) {
cout << endl << "Enter name of output file: ";
cin >> filename;
file.open(filename, fstream::out);
};
cout << endl << "Enter number of cards per hand: ";
cin >> numOfHands;
cout << endl << "Enter number of deals (simulations): ";
cin >> numOfDeals;
double probability =
simulateMonteCarlo(Deck(), numOfHands, numOfDeals, toFile, file);
cout << endl
<< "Chances of receiving a pair in a hand of " << numOfHands
<< " cards is: " << (probability * 100) << '%' << endl;
return 0;
}
double simulateMonteCarlo(Deck deck, int numOfHands, int numOfDeals,
bool shouldStreamOutput, ostream &stream) {
int pairCount = 0;
vector<Card> hand;
for (int i = 0; i < numOfDeals; i++) {
deck.shuffleDeck();
for (int j = 0; j < numOfHands; j++) {
hand.push_back(deck.dealCard());
}
if (hasPair(hand)) {
pairCount++;
if (shouldStreamOutput) {
stream << "Found Pair!! ";
}
} else {
if (shouldStreamOutput) {
stream << " ";
}
}
if (shouldStreamOutput) {
for (unsigned int i = 0; i < hand.size(); i++) {
stream << hand.at(i);
if (i < hand.size() - 1) {
stream << ", ";
}
}
stream << endl;
}
hand.clear();
}
return static_cast<double>(pairCount) / static_cast<double>(numOfDeals);
}
bool hasPair(const vector<Card> vec) {
for (unsigned int i = 0; i < vec.size() - 1; i++) {
for (unsigned int j = i + 1; j < vec.size(); j++) {
if (vec.at(i).getRank() == vec.at(j).getRank()) {
return true;
}
}
}
return false;
}