Reputation: 1
I've tried to find the answer on here, but nothing I am finding is working. I have here a blackjack game. My next step is to start adding a money balance and wager into the game, but before I do that, I am working on my "play again" option. As it stands, when the player plays again, their hand is the same as it was the last round. I believe I need to call a destructor to delete the current hand and deck, and start fresh. Unfortunately I can't figure out how to clear the hand and start with a fresh deck. I tried to call this destructor in my function called play. Here, if the player says they want to play again, I would delete the hand and deck, and they would get reconstructed in main. But I have tried both "deck.Deck::~Deck()" and "deck.~Deck()" for example, and neither is working. I will upload all my code. If anyone has any ideas, please help me out. I know that the destructor is something that should just be called by the complier when it is out of scope, but how toi I start with a fresh hand and deck while the game is still in motion and main is still running? Thank you for your help.
Here is my header file:
//blackjack.h A class to represent a deck of cards
#include <iostream>
#include <string>
class Card {
friend class Deck;
private:
int card_index; //card number 0 to 51
Card(int index) { card_index = index; } //made this private so user can't say card at index 100..can use it because of friend class
public:
Card() { card_index = 52; }
char suit() const;
char value() const;
std::string str() const;
int getValue(std::string c);
};
class Deck {
private:
Card cards[52];
int pos;
public:
Deck();
~Deck();
Card deal() { return cards[pos++]; };
void shuffle();
int size() const { return 52 - pos; };
};
class Hand {
friend class Deck;
friend class Card;
private:
int handSize;
int ctotal;
Card myCards[52];
public:
Hand() { handSize = 1; };
~Hand();
Hand(int n) { handSize = n; };
void dealFrom(Deck& d);
void reveal();
int total();
void hit(Deck& d);
};
std::ostream& operator<< (std::ostream& out, const Card& c);
Here is my implementation file:
//blackjack.cpp - Implementations of the Deck and Card classes
#include "blackjack.h"
#include <cstdlib>
char Card::suit() const {
static char suits[] = { 'H', 'S', 'D', 'C' };
//return card_index < 52 ? suits[card_index % 4] : 'X';
return suits[card_index % 4];
}
char Card::value() const {
static char values[] =
{ '2', '3', '4', '5', '6', '7', '8', '9', 'T', 'J', 'Q', 'K', 'A' };
//return card_index < 52 ? values[card_index / 4] : 'X';
return values[card_index / 4];
}
std::string Card::str() const {
std::string s;
s += value();
s += suit();
return s;
}
Deck::Deck() {
for (int i = 0; i < 52; i ++) {
cards[i] = Card(i);
}
pos = 0;
}
void Deck::shuffle() {
for (int i = 0; i < 52; i++) {
int j = rand() % 52;
Card tmp = cards[i];
cards[i] = cards[j];
cards[j] = tmp;
}
pos = 0;
}
std::ostream& operator<< (std::ostream& out, const Card& c) {
out << c.str();
return out;
}
int Card::getValue(std::string c) {
char v = c[0];
int val = v - '0';
if (val > 9) {
switch(v){
case 'T':
case 'J':
case 'Q':
case 'K':
val = 10;
break;
case 'A':
val = 11;
}
}
return val;
}
void Hand::dealFrom(Deck& d) {
for(int i = 0; i < handSize; i++)
myCards[i] = d.deal();
}
void Hand::reveal(){
for (int i = 0; i < handSize; i++)
std::cout << myCards[i] << " " << std::endl;
}
void Hand::hit(Deck& d) {
int index = handSize;
handSize++;
myCards[index] = d.deal();
}
int Hand::total() {
ctotal = 0; //reset card total
for (int i = 0; i < handSize; i ++) {
ctotal += myCards[i].getValue(myCards[i].str());
}
for (int i = 0; i < handSize; i ++) { //make ace 1 if over 21
if ( (myCards[i].getValue(myCards[i].str()) == 11) && ctotal > 21 )
ctotal = ctotal - 10;
}
return ctotal;
}
And here is my main program:
#include "blackjack.h"
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
void play(bool& quitGame, bool& quit, Deck& deck, Hand& d,Hand & p) { //function to play again?
char ans;
cout << "\nPlay Again? y or n" << endl;
cin >> ans;
if (ans == 'y' || ans == 'Y') {
quitGame = false;
quit = false;
deck.Deck::~Deck(); //trying to delete the deck and hand objects before next game
d.Hand::~Hand();
p.Hand::~Hand();
//deck.~Deck();
//d.~Hand();
//p.~Hand();
} else if (ans == 'n' || ans == 'N')
quitGame = true;
else {
cout << "Incorrect response." << endl;
play(quitGame, quit, deck, d, p);
}
}
void reveal(Hand& d, Hand& p) { //function to reveal the hand
cout << "Your hand is: " << endl;
p.reveal();
cout << "Your total is: " << endl;
cout << p.total() << endl;
cout << endl;
cout << "Dealer's hand is: " << endl;
d.reveal();
cout << "Dealer's total is: " << endl;
cout << d.total() << endl;
}
void autoComplete(Hand& d, Hand& p, bool& quit, bool& quitGame, Deck& deck) { //function to check for blackjack and over 21
if (p.total() == 21 && d.total() == 21) {
cout << "You and dealer both hit blackjack. You tied." << endl;
quit = true;
play(quitGame, quit, deck, d, p);
} else if(p.total() == 21) {
cout << "Congratulations, you hit blackjack. You win!" << endl;
quit = true;
play(quitGame, quit, deck, d, p);
} else if(d.total() == 21) {
cout << "Sorry. Dealer hit blackjack. You lose." << endl;
quit = true;
play(quitGame, quit, deck, d, p);
} else if (p.total() > 21 && d.total() > 21) {
cout << "You and dealer both passed 21. Game is a tie." << endl;
quit = true;
play(quitGame, quit, deck, d, p);
} else if (p.total() > 21 && d.total() < 21) {
cout << "You passed 21. You lose.";
quit = true;
play(quitGame, quit, deck, d, p);
} else if (d.total() > 21 && p.total() < 21) {
cout << "Dealer passed 21. You win.";
quit = true;
play(quitGame, quit, deck, d, p);
}
}
int main() {
srand(time(0));
char response; // variable to hit or stand
bool quit = false; //variable to end the current round
bool quitGame = false; //variable to play game again
while (quitGame == false) { //while the player wants to continue playing
Deck deck; //create deck
Hand p(2); //player's hand
Hand d(2); //dealer's hand
deck.shuffle(); //shuffle deck
p.dealFrom(deck); //deal from deck
d.dealFrom(deck);
while (quit == false) { //while the round isn't over
reveal(d, p); //reveal the cards
autoComplete(d, p, quit, quitGame, deck); //check for blackjack and over 21
if(p.total() < 21 && quit == false) { //if games not over and player is under 21
cout << "Press 'h' to hit or 's' to stand." << endl;
cin >> response;
}
if (response == 'h') {
cout << " " << endl;
p.hit(deck);
if (d.total() < 17) //if the dealer hasn't hit 17, dealer hits deck
d.hit(deck);
}
if (response == 's') {
cout << " " << endl;
while (d.total() < 17){ //if the dealer hasn't hit 17, keep hitting deck
d.hit(deck);
}
if (d.total() < 21 && p.total() < 21) {
if (d.total() > p.total() && quit == false) { //if dealers total is higher than players total
reveal(d, p);
cout << "\nDealer wins!" << endl;
quit = true;
play(quitGame, quit, deck, d, p);
} else if (p.total() > d.total() && quit == false) { //if players total is higher than dealers total
reveal(d, p);
cout << "\nYou win!" << endl;
quit = true;
play(quitGame, quit, deck, d, p);
} else if (p.total() == d.total() && quit == false) { //if dealers total equals players total
reveal(d, p);
cout << "\nYou tied." << endl;
quit = true;
play(quitGame, quit, deck, d, p);
}
}
}
}
}
return 0;
}
Upvotes: 0
Views: 1992
Reputation: 32732
how [do] I start with a fresh hand and deck while the game is still in motion and main is still running
Fundamentally, you need to call play
in only one place: outside the inner loop ("while the round isn't over") of main
. Besides all that repeated code, your issue is that when the player wants to play again, you set both quit
and quitGame
to false
, so that inner loop never terminates and you deck never gets reshuffled.
Other issues, left as an exercise for the reader: response
can be read before it is assigned a value. Your shuffle
routine is not very good, because many of the cards won't be moved.
Upvotes: 0
Reputation: 5192
The syntax:
Class obj;
obj.~Class();
will call the destructor on that object. However, you should NOT do this. As an example:
#include <cstdio>
class Foo {
public:
Foo() {}
~Foo() {
printf("Destroya\n");
if (foo_m) delete foo_m;
}
private:
int* foo_m = new int(42);
};
int main() {
Foo foo;
foo.~Foo();
}
As you can see from the output, the destructor gets called twice. The first time when we manually "destroy" an object, and again when it actually goes off the stack.
Almost never call a destructor manually. The object doesn't go away, you'll just break its state and get undefined behavior. What you really want is probably something like a clear()
or reset()
function to re-initialize the state. Or as suggested in the comments, you can move assign a default object into your already existing one to reset the state without writing extra functions.
Upvotes: 5
Reputation: 463
The destructor isn't generally something you call manually. For memory on the stack, the destructor is called automatically when an object goes out of scope. For memory on the heap, the destructor is called when you use delete
. In this case you don't actually want to delete the hand, you just want to reset it. deck = Deck()
will 'reset' it and give you a default constructed Deck object.
Upvotes: 2