《C++游戏编程入门》第10章 继承与多态:Blackjack

130 篇文章 4 订阅
92 篇文章 19 订阅

10.1 继承简介

从已有类派生新类,自动继承已有类的数据成员与成员函数。
可重用性的好处:

  • 减少工作量。功能函数不必再次编写。
  • 减少错误。
  • 代码更加清晰。
10.simple_boss.cpp
#include <iostream>
using namespace std;

class Enemy
{
public:
    int m_Damage;

    Enemy();
    void Attack() const;
};

Enemy::Enemy()
    : m_Damage(10)
{
}

void Enemy::Attack() const
{
    cout << "Attack inflicts " << m_Damage << " damage points!\n";
}

class Boss : public Enemy//基类派生,构造、析构、拷贝构造、拷贝赋值运算符未被继承
{
public:
    int m_DamageMultiplier;

    Boss();
    void SpecialAttack() const;
};

Boss::Boss()
    : m_DamageMultiplier(3)
{
}

void Boss::SpecialAttack() const
{
    cout << "Special Attack inflicts " << (m_DamageMultiplier * m_Damage);
    cout << " damage points!\n";
}

int main()
{
    cout << "Creating an enemy.\n";
    Enemy enemy1;
    enemy1.Attack();

    cout << "\nCreating a boss.\n";
    Boss boss1;
    boss1.Attack();
    boss1.SpecialAttack();

    return 0;
}

10.2 继承访问权的控制

public、protected、private只为派生类提供基类成员的必要的访问权。

  • public:可被程序中所有代码访问。
  • protected:本类和派生类访问。
  • private:只能本类访问。
10.simple_boss2.cpp
#include <iostream>
using namespace std;

class Enemy
{
public:
    Enemy();
    void Attack() const;

protected:
    int m_Damage;
};

Enemy::Enemy()
    : m_Damage(10)
{
}

void Enemy::Attack() const
{
    cout << "Attack inflicts " << m_Damage << " damage points!\n";
}

class Boss : public Enemy
{
public:
    Boss();
    void SpecialAttack() const;

private:
    int m_DamageMultiplier;
};

Boss::Boss()
    : m_DamageMultiplier(3)
{
}

void Boss::SpecialAttack() const
{
    cout << "Special Attack inflicts " << (m_DamageMultiplier * m_Damage);
    cout << " damage points!\n";
}

int main()
{
    cout << "Creating an enemy.\n";
    Enemy enemy1;
    enemy1.Attack();

    cout << "\nCreating a boss.\n";
    Boss boss1;
    boss1.Attack();
    boss1.SpecialAttack();

    return 0;
}

10.3 调用与重写基类成员函数

派生类中可重写基类的成员函数或者调用。

10.overriding_boss.cpp
#include <iostream>
using namespace std;

class Enemy
{
public:
    Enemy(int damage = 10);
    virtual void Taunt() const;  // 声明虚基类成员函数
    virtual void Attack() const; // 重写函数声明成虚函数

private:
    int m_Damage;
};

Enemy::Enemy(int damage)
    : m_Damage(damage)
{
}

void Enemy::Taunt() const
{
    cout << "The enemy says he will fight you.\n";
}

void Enemy::Attack() const
{
    cout << "Attack! Inflicts " << m_Damage << " damage points.";
}

class Boss : public Enemy
{
public:
    Boss(int damage = 30);
    virtual void Taunt() const;           // virtual可省略
    virtual void Attack() const override; // override建议增加
};

Boss::Boss(int damage)
    : Enemy(damage) // 调用基类构造函数
{
    cout << "damage in Boss(int damage): " << damage << endl;
}

void Boss::Taunt() const // override base class member function
{
    cout << "The boss says he will end your pitiful existence.\n";
}

void Boss::Attack() const // override base class member function
{
    Enemy::Attack(); // 调用基类成员函数
    cout << " And laughs heartily at you.\n";
}

int main()
{
    cout << "Enemy object:\n";
    Enemy anEnemy;
    anEnemy.Taunt();
    anEnemy.Attack();

    cout << "\n\nBoss object:\n";
    Boss aBoss;
    aBoss.Taunt();
    aBoss.Attack();

    return 0;
}

10.4 派生类中调用父类赋值运算符与拷贝构造函数

Boss& operator=(const Boss& b)
{
	Enemy::operator=(b);
}
Boss(const Boss& b)
	: Enemy(b)
{
}

10.5 多态简介

运行时,对象类型决定实际调用的成员函数。
虚函数通过引用和指针来产生多态行为。
虚析构函数作用:编译器在派生类中插入对基类虚析构函数的调用。

10.polymorphic_bad_guy.cpp
#include <iostream>
using namespace std;

class Enemy
{
public:
    Enemy(int damage = 10);
    virtual ~Enemy();
    void virtual Attack() const;

protected:
    int *m_pDamage;
};

Enemy::Enemy(int damage)
{
    m_pDamage = new int(damage);
}

Enemy::~Enemy()
{
    cout << "In Enemy destructor, deleting m_pDamage.\n";
    delete m_pDamage;
    m_pDamage = nullptr;
}

void Enemy::Attack() const
{
    cout << "An enemy attacks and inflicts " << *m_pDamage << " damage points.";
}

class Boss : public Enemy
{
public:
    Boss(int multiplier = 3);
    virtual ~Boss();
    void virtual Attack() const;

protected:
    int *m_pMultiplier;
};

Boss::Boss(int multiplier)
{
    m_pMultiplier = new int(multiplier);
}

Boss::~Boss()
{
    cout << "In Boss destructor, deleting m_pMultiplier.\n";
    delete m_pMultiplier;
    m_pMultiplier = nullptr;
}

void Boss::Attack() const
{
    cout << "A boss attacks and inflicts " << (*m_pDamage) * (*m_pMultiplier)
         << " damage points.";
}

int main()
{
    cout << "Calling Attack() on Boss object through pointer to Enemy:\n";
    Enemy *pBadGuy = new Boss();//基类指针指向派生类
    pBadGuy->Attack();
    cout << "\n\nDeleting pointer to Enemy:\n";
    delete pBadGuy;
    pBadGuy = nullptr;

    return 0;
}

10.6 使用抽象类

纯虚函数是不需要定义的函数。
不能实例化的基类,抽象类(含纯虚函数)。
派生抽象类时,重写纯虚函数(未完全重写时仍是抽象类)。

10.abstract_creature.cpp
#include <iostream>
using namespace std;

class Creature // 抽象类
{
public:
    Creature(int health = 100);
    virtual void Greet() const = 0; // 纯虚函数
    virtual void DisplayHealth() const;

protected:
    int m_Health;
};

Creature::Creature(int health)
    : m_Health(health)
{
}

void Creature::DisplayHealth() const
{
    cout << "Health: " << m_Health << endl;
}

class Orc : public Creature
{
public:
    Orc(int health = 120);
    virtual void Greet() const override;
};

Orc::Orc(int health)
    : Creature(health)
{
}

void Orc::Greet() const // 重写纯虚函数
{
    cout << "The orc grunts hello.\n";
}

int main()
{
    Creature *pCreature = new Orc();
    pCreature->Greet();
    pCreature->DisplayHealth();
    delete pCreature;
    pCreature = nullptr;

    return 0;
}

10.7 Blackjack游戏

21点,A为1或11(玩家决定),J、Q、K为10。
开始,玩家和庄家分别发两张牌,玩家牌可见,庄家一张牌可见。
接着,可要牌,超过21,输。
最后,庄家小于等于16,必须要牌,比较庄家与玩家点数。

10.blackjack.cpp
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <random>
#include <chrono>
#include <ctime>
using namespace std;

class Card // 扑克牌
{
public:
    enum rank
    {
        ACE = 1,
        TWO,
        THREE,
        FOUR,
        FIVE,
        SIX,
        SEVEN,
        EIGHT,
        NINE,
        TEN,
        JACK,
        QUEEN,
        KING
    };
    enum suit
    {
        CLUBS,
        DIAMONDS,
        HEARTS,
        SPADES
    };

    Card(rank r = ACE, suit s = SPADES, bool ifu = true)
        : m_Rank(r), m_Suit(s), m_IsFaceUp(ifu) {}

    int GetValue() const // 获取点数, 0 - 11
    {
        int value = 0; // 牌朝下为0
        if (m_IsFaceUp)
        {
            value = m_Rank;
            if (value > 10) // 人脸牌,点数为10
                value = 10;
        }
        return value;
    }

    void Flip() // 翻牌
    {
        m_IsFaceUp = !m_IsFaceUp;
    }

    friend ostream &operator<<(ostream &os, const Card &aCard);

private:
    rank m_Rank;     // 大小
    suit m_Suit;     // 花色
    bool m_IsFaceUp; // 是否正面朝上
};

class Hand // 持有牌
{
public:
    Hand()
    {
        m_Cards.reserve(7);
    }

    virtual ~Hand()
    {
        Clear();
    }

    void Add(Card *pCard)
    {
        m_Cards.push_back(pCard);
    }

    void Clear()
    {
        for (vector<Card *>::iterator iter = m_Cards.begin(); iter != m_Cards.end(); ++iter)
            delete *iter;
        m_Cards.clear();
    }

    int GetTotal() const // 获取牌的最大点数,aces取1或11
    {
        if (m_Cards.empty() || m_Cards[0]->GetValue() == 0) // 无牌或者牌朝下
            return 0;

        int total = 0; // 所有牌点数加起来,假定Ace点数为1
        for (vector<Card *>::const_iterator iter = m_Cards.begin(); iter != m_Cards.end(); ++iter)
            total += (*iter)->GetValue();

        if (total <= 11) // 点数和够低,Ace才可能取11
        {
            for (vector<Card *>::const_iterator iter = m_Cards.begin(); iter != m_Cards.end(); ++iter)
            {
                if ((*iter)->GetValue() == Card::ACE) // 是否存在Ace
                {
                    total += 10; // 1变11,增加10
                    break;
                }
            }
        }

        return total;
    }

protected:
    vector<Card *> m_Cards;
};

class GenericPlayer : public Hand // 一般玩家
{
    friend ostream &operator<<(ostream &os, const GenericPlayer &aGenericPlayer);

public:
    GenericPlayer(const string &name = "")
        : m_Name(name) {}

    virtual ~GenericPlayer() {}

    virtual bool IsHitting() const = 0; // 是否要牌

    bool IsBusted() const
    {
        return GetTotal() > 21;
    }

    void Bust() const
    {
        cout << m_Name << " busts.\n";
    }

protected:
    string m_Name;
};

class Player : public GenericPlayer // 人类
{
public:
    Player(const string &name = "")
        : GenericPlayer(name) {}

    virtual ~Player() {}

    virtual bool IsHitting() const
    {
        cout << m_Name << ", do you want a hit? (Y/N): ";
        char response;
        cin >> response;
        return (response == 'y' || response == 'Y');
    }

    void Win() const
    {
        cout << m_Name << " wins.\n";
    }
    void Lose() const
    {
        cout << m_Name << " loses.\n";
    }
    void Push() const
    {
        cout << m_Name << " pushes.\n";
    }
};

class House : public GenericPlayer // 计算机
{
public:
    House(const string &name = "House") : GenericPlayer(name) {}

    virtual ~House() {}

    virtual bool IsHitting() const
    {
        return GetTotal() <= 16;
    }

    void FlipFirstCard() // 翻转第一张牌
    {
        if (!m_Cards.empty())
            m_Cards[0]->Flip();
        else
            cout << "No card to flip!\n";
    }
};

class Deck : public Hand // 堆牌,洗牌和发牌
{
public:
    Deck()
    {
        m_Cards.reserve(52);
        Populate();
    }

    virtual ~Deck() {}

    void Populate() // 生成标准52张牌
    {
        Clear();
        for (int s = Card::CLUBS; s <= Card::SPADES; ++s)
            for (int r = Card::ACE; r <= Card::KING; ++r)
                Add(new Card(static_cast<Card::rank>(r), static_cast<Card::suit>(s)));
    }

    void Shuffle() // 乱序
    {
        // random_device rd;
        // shuffle(m_Cards.begin(), m_Cards.end(), default_random_engine(rd()));//非确定性随机整数作为随机种子

        unsigned seed = chrono::system_clock::now().time_since_epoch().count();
        shuffle(m_Cards.begin(), m_Cards.end(), default_random_engine(seed)); // 时间作为随机种子
    }

    void Deal(Hand &aHand) // 分发一张牌给手牌
    {
        if (!m_Cards.empty())
        {
            aHand.Add(m_Cards.back());
            m_Cards.pop_back();
        }
        else
        {
            cout << "Out of cards. Unable to deal.";
        }
    }

    void AdditionalCards(GenericPlayer &aGenericPlayer) // 给通用玩家发牌
    {
        cout << endl;

        // 只要通用玩家未超过21点,且想要牌
        while (!(aGenericPlayer.IsBusted()) && aGenericPlayer.IsHitting())
        {
            Deal(aGenericPlayer);
            cout << aGenericPlayer << endl;

            if (aGenericPlayer.IsBusted())
                aGenericPlayer.Bust();
        }
    }
};

class Game // 游戏
{
public:
    Game(const vector<string> &names)
    {
        for (vector<string>::const_iterator pName = names.begin(); pName != names.end(); ++pName)
            m_Players.push_back(Player(*pName)); // 添加人类玩家
    }

    ~Game() {}

    void Play()
    {
        srand(static_cast<unsigned int>(time(0))); // 随机种子
        m_Deck.Populate();                         // 生成牌
        m_Deck.Shuffle();                          // 洗牌

        vector<Player>::iterator pPlayer;
        for (int i = 0; i < 2; ++i) // 所有玩家发两张牌
        {
            for (pPlayer = m_Players.begin(); pPlayer != m_Players.end(); ++pPlayer)
                m_Deck.Deal(*pPlayer);
            m_Deck.Deal(m_House);
        }

        m_House.FlipFirstCard(); // 计算机第一张牌朝下隐藏

        // 显示所有玩家手牌
        for (pPlayer = m_Players.begin(); pPlayer != m_Players.end(); ++pPlayer)
            cout << *pPlayer << endl;
        cout << m_House << endl;
        cout << endl;

        for (pPlayer = m_Players.begin(); pPlayer != m_Players.end(); ++pPlayer)
            m_Deck.AdditionalCards(*pPlayer); // 给每一位玩家发牌

        m_House.FlipFirstCard(); // 计算机第一张牌朝上显示
        cout << m_House;

        m_Deck.AdditionalCards(m_House); // 给计算机发牌

        if (m_House.IsBusted()) // 计算机超过21点
        {
            for (pPlayer = m_Players.begin(); pPlayer != m_Players.end(); ++pPlayer)
                if (!(pPlayer->IsBusted()))
                    pPlayer->Win();
        }
        else
        {
            // 比较玩家与庄家的点数
            for (pPlayer = m_Players.begin(); pPlayer != m_Players.end(); ++pPlayer)
                if (!pPlayer->IsBusted())
                    if (pPlayer->GetTotal() > m_House.GetTotal())
                        pPlayer->Win();
                    else if (pPlayer->GetTotal() < m_House.GetTotal())
                        pPlayer->Lose();
                    else
                        pPlayer->Push();
        }

        // 移除所有玩家的牌
        for (pPlayer = m_Players.begin(); pPlayer != m_Players.end(); ++pPlayer)
            pPlayer->Clear();
        m_House.Clear();
    }

private:
    Deck m_Deck;
    House m_House;
    vector<Player> m_Players;
};

ostream &operator<<(ostream &os, const Card &aCard)
{
    const string RANKS[] = {"0", "A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"};
    const string SUITS[] = {"c", "d", "h", "s"};

    if (aCard.m_IsFaceUp)
        os << RANKS[aCard.m_Rank] << SUITS[aCard.m_Suit];
    else
        os << "XX";

    return os;
}

ostream &operator<<(ostream &os, const GenericPlayer &aGenericPlayer)
{
    os << aGenericPlayer.m_Name << ":\t";

    if (!aGenericPlayer.m_Cards.empty())
    {
        for (vector<Card *>::const_iterator pCard = aGenericPlayer.m_Cards.begin(); pCard != aGenericPlayer.m_Cards.end(); ++pCard)
            os << *(*pCard) << "\t";

        if (aGenericPlayer.GetTotal() != 0)
            cout << "(" << aGenericPlayer.GetTotal() << ")";
    }
    else
        os << "<empty>";

    return os;
}

int main()
{
    cout << "\t\tWelcome to Blackjack!\n\n";

    int numPlayers = 0;
    while (numPlayers < 1 || numPlayers > 7)
    {
        cout << "How many players? (1 - 7): ";
        cin >> numPlayers;
    }

    vector<string> names;
    string name;
    for (int i = 0; i < numPlayers; ++i)
    {
        cout << "Enter player name: ";
        cin >> name;
        names.push_back(name);
    }
    cout << endl;

    // 游戏循环
    Game aGame(names);
    char again = 'y';
    while (again != 'n' && again != 'N')
    {
        aGame.Play();
        cout << "\nDo you want to play again? (Y/N): ";
        cin >> again;
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值