Hi, luiz5z.
My problem is I don’t know blackjack rules, so I’m just trying to guess what could be the problems, but I’m likely to be missing many points or say wrong things.
It seems you’ve written a lot of code before trying to compile it and that usually takes a lot of troubles along.
Just to make some examples:
In main() a semicolon is missing after
char again = 'y'
.
1 2 3 4
|
class Deck : public Hand
{
Deck();
...
|
You constructor is private. That’s not forbidden (otherwise singletons couldn’t be possible), but makes it hard to make an instance of your class. The compiler immediately points it out.
1 2 3
|
inline void Hand::Add(Card * pCard)
{
}
|
You use this function in your code, but you havent’t implemented it yet. Again, the compiler never fails to find such errors.
1 2 3 4
|
class Game
{
...
House m_House;
|
Where do you define “House”? The compiler asks it as soon as it meets that declaration.
(I suppose you meant to declare a ‘special’ Player named House, didn’t you?)
1 2 3
|
void Hand::Clear()
{
vector<Card*>::iteraotr iter = m_Cards.begin();
|
«What’s an ‘iteraotr’?» the compiler asks.
Apart from what the compiler complains of, it seems you underestimate how splitting class into headers and sources can make the code more readable. Let me suggest you to try and see! It would also help you to include the right headers, as now by now they are a mess. What algorithm do you use in Game and what vector in Player? How can Game be aware of Deck if the former doesn’t include the latter?
Couldn’t it be better if Deck managed a (std::vector) bunch of Card(s) instead of inheriting from it? (Just an opinion)
And, well, I don’t know blackjack, but doesn’t it sound weird that a Deck is aware of a GenericPlayer and a Player isn’t aware of a Deck?
Iterators are great, but their syntax is prolix. What about substituting range-for for them?
Please note that function random_shuffle() was deprecated in C++14 and removed in C++17; the main reason is its implementations usually use std::rand(), which is frown upon in C++.
https://meetingcpp.com/blog/items/stdrandom_shuffle-is-deprecated.html
I wonder whether it wouldn’t be better to restart the code design writing down what every class responsibility should be, like for example:
- what a Game should manage?
- what a Hand should manage?
- what a Deck...
and so on.
- - -
What’s above is my real advice: restart from scratch.
Anyway, not so look too harsh, I’ve spent some time making your code a bit more readable. Let me be clear: it doesn’t mean it’s better, but I succeeded in cutting down the errors to no more than 7; however, a reduced number of errors from the compiler doesn’t mean the code follows a good logic (to be sincere: I’m not sure I understood your logic).
GenericPlayer.hpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
|
#ifndef GENERICPLAYER_H_
#define GENERICPLAYER_H_
#include "Hand.hpp"
#include <iostream>
#include <string>
#include <vector>
class GenericPlayer : public Hand
{
public:
GenericPlayer(const std::string& name = "");
virtual ~GenericPlayer();
// indicate whther or not player wan to keep hitting
virtual bool IsHitting() const = 0;
// return whether generic player has busted - has a total greater than 21
bool IsBusted() const;
// announces that the player busts
void Bust() const;
protected:
std::string m_Name;
friend std::ostream& operator<<(std::ostream& os, const GenericPlayer& ap)
{
os << ap.m_Name << ":\t";
if (!ap.m_Cards.empty()) {
for(const auto& c : ap.m_Cards) { os << c << "\t"; }
if (ap.GetTotal() != 0) { std::cout << "(" << ap.GetTotal() << ")"; }
} else {
os << "<empty>";
}
return os;
}
};
#endif // GENERICPLAYER_H_
|
GenericPlayer.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
#include "GenericPlayer.hpp"
#include <iostream>
#include <string>
/*
*/
GenericPlayer::GenericPlayer(const std::string& name) : m_Name(name) {}
/*
*/
GenericPlayer::~GenericPlayer() {}
/*
*/
bool GenericPlayer::IsBusted() const { return GetTotal() > 21; }
/*
*/
void GenericPlayer::Bust() const { std::cout << m_Name << " busts.\n"; }
|
Player.hpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
|
#ifndef PLAYER_H_
#define PLAYER_H_
#include "GenericPlayer.hpp"
class Player : public GenericPlayer
{
public:
Player(const std::string& name = "");
virtual ~Player();
// return whter or not the player wants another hit
virtual bool IsHitting() const;
// announces that the plaer wins
void Win() const;
// announces that the player loses
void Lose() const;
// announces that the player pushes
void Push() const;
};
#endif // PLAYER_H_
|
Player.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
|
#include "Player.hpp"
#include <iostream>
#include <string>
/*
*/
Player::Player(const std::string& name): GenericPlayer(name) {}
/*
*/
bool Player::IsHitting() const
{
std::cout << m_Name << ", do you want a hit? (Y/N): ";
char response;
std::cin >> response;
return (response == 'y' || response == 'Y');
}
/*
*/
void Player::Win() const { /* TODO: to be done!! */ }
/*
*/
void Player::Lose() const { std::cout << m_Name << " loses.\n"; }
/*
*/
void Player::Push() const { std::cout << m_Name << " pushes.\n"; }
|
Card.hpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
|
#ifndef CARD_H_
#define CARD_H_
#include <algorithm>
#include <ctime>
#include <iostream>
#include <string>
#include <vector>
class Card
{
public:
enum rank { ACE = 1, TWO, THREE, FOUR, FIVE,
SIX, SEVEN, EIGHT, NINE, TEN,
JACK, QUEN, KING };
enum suit { CLUBS, DIAMONDS, HEARTS, SPADES };
Card(rank r = ACE, suit s = SPADES, bool ifu = true);
// return the value of a card
int GetValue() const;
// flip a card
void Flip();
private:
rank m_Rank;
suit m_Suit;
bool m_IsFaceUp;
friend std::ostream& operator<<(std::ostream& os, const Card& aCard)
{
const std::string RANKS[] = { "0", "A", "2", "3", "4",
"5", "6", "7", "8", "9",
"10", "J", "Q", "K" };
const std::string SUITS[] = { "c", "d", "h", "s" };
if (aCard.m_IsFaceUp) { os << RANKS[aCard.m_Rank] << SUITS[aCard.m_Suit]; }
else { os << "XX"; }
return os;
}
};
#endif // CARD_H_
|
Card.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
#include "Card.hpp"
/*
*/
Card::Card(rank r, suit s, bool ifu) : m_Rank(r), m_Suit(s), m_IsFaceUp(ifu) {}
/*
*/
int Card::GetValue() const
{
// value is the Rank of the card; for face cards is 10
if(m_IsFaceUp) { return m_Rank > 10 ? 10 : m_Rank; }
return 0; // if a is card face down, its value is 0
}
/*
*/
void Card::Flip() { m_IsFaceUp = !m_IsFaceUp; }
|
Hand.hpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
|
#ifndef HAND_H_
#define HAND_H_
#include "Card.hpp"
#include <vector>
class Hand
{
public:
static constexpr unsigned CARD_IN_HAND { 7 };
Hand();
virtual ~Hand();
// adds a card to the hand
inline void Add(Card* pCard);
// clears hand of all cards
void Clear();
// gets hand total value, treat ace as 1 or 11
int GetTotal() const;
protected:
std::vector<Card*> m_Cards;
};
#endif // HAND_H_
|