前言

这是《C++游戏编程入门 第四版》的最后一个例子,还挺复杂的。要完成这个制作,先要弄明白Blackjack的游戏规则。

简化版Blackjack(21点)
规则:

游戏由2到6个人玩,使用除大小王之外的52张牌,游戏者的目标是使手中的牌的点数之和不超过21点且尽量大。一个庄家Houser,多个玩家Player

  1. 2至10牌,按其原点数计算;
  2. K、Q、J和10牌都算作10点(一般记作T,即ten之意);
  3. A 牌(ace)既可算作1点也可算作11点,由玩家自己决定(当玩家停牌时,点数一律视为最大而尽量不爆,如A+9为20,A+4+8为13,A+3+A视为15)。

玩法:

开局先发两张牌,庄家一明(面朝上)一暗(面朝下),玩家两张明牌。
接下来,每个玩家只要愿意,每次都有机会添加一张牌,超过21就Bust。
当所有的玩家选择好了后,庄家亮出暗牌。如果庄家的点总数低于17,则庄家必须添加新牌。如果超过21就Bust(不管玩家点数多少,玩家都赢了)。如果暗牌已经大于等于17了,庄家直接胜利。
如果某个玩家点数比庄家点数大(小/等于),则…\dots…

类的设计


Blackjack游戏Blackjack游戏Blackjack游戏

描述
Card Blackjack的游戏牌
Hand 玩家和庄家所持的牌,Card对象的集合
GenericPlayer 一般的玩家,是具体玩家类的过度,包含Player和Houser的共有元素
Player 玩家
Houser 庄家
Deck 牌堆,Hand的增强类(具有Hand类不具备的额外功能,如洗牌和发牌
Game 游戏外部接口,执行游戏流程

为了让事情变得简单再简单,几乎所有成员函数都是公用的,所有数据成员都是受保护的。继承只使用公有继承。

Card类Card类Card类

成员 描述
rank m_rank 牌的大小。rank是表示13个等级的枚举类型
suit m_suit 牌的花色。suit是4种花色的枚举类型
bool m_faceUp 是否是明牌(牌面向上)。这个影响牌的点数和显示
int Get() 返回牌的点值

Hand类Hand类Hand类

成员 描述
vector<Card*> m_cards 扑克牌的集合,存储着若干Card对象指针
void Add(Card* pCard) 向所持的牌中添加一张。在向量m_cards添加一个Card指针
void Clear() 清空所以所持的牌。移除m_cards所有的指针,删除堆里相关Card对象
int GetTotal()const 返回所持牌的点数总和

是Hand基类的实现类,是Player、Houser的抽象类。

GenericPlayer类GenericPlayer类GenericPlayer类

成员 描述
string m_name 玩家名
virtual bool AreHitting()const =0 指示玩家是否跟牌
bool AreBusted()const 指示玩家是否炸了
void Bust()const 玩家炸了后执行的函数

Player类Player类Player类

成员 描述
bool AreHitting()const 指示玩家是否跟牌
void Win()const 玩家赢了后执行的函数
void Lose()const 玩家输了后执行的函数
void Push()const 平局后执行的函数

Houser类Houser类Houser类

成员 描述
bool AreHitting()const 指示庄家是否跟牌
void FlipFirstCard() 翻转第一张牌,决定暗牌明牌

Deck类就是发牌类,对Hand类进行组合,与Player、Houser类的基类GenericPlayer互动。

Deck类Deck类Deck类

成员 描述
void Populate() 生成包含52张牌的标准牌堆
void Shuffle() 洗牌
void Deal(Hand& aHand) 发牌
void AdditionalCards(GenericPlayer& aGenericPlayer) 只要玩家跟牌,就向玩家额外发牌

游戏引擎类,将其他类抽象出来的接口使用起来。并且对客户端提供简单的使用接口。

Game类Game类Game类

成员 描述
Deck m_deck 牌堆
Houser m_houser 庄家
vector m_players 许多的玩家
void Play() 进行一轮游戏
非成员函数 描述
vector Game_Interface() 将输入玩家个数和名字的部分隐藏起来

游戏的实现

Card.h

#ifndef _CARD_H_
#define _CARD_H_#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
#include<iomanip>//setw()
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 };public:friend ostream& operator<<(ostream& os, const Card& aCard);Card(rank r = ACE, suit s = SPADES, bool faceUp = true);~Card() = default;int Get()const;void Flip();private:rank m_rank;suit m_suit;bool m_faceUp;};#endif

Card.cpp

#include "Card.h"ostream& operator<<(ostream& os, const Card& aCard) {const string THE_RANKS[] = { "0","A","2","3","4","5","6","7","8","9","10","J","Q","K" };const string THE_SUITS[] = { "c","d","h","s" };if (aCard.m_faceUp)os << THE_RANKS[aCard.m_rank] << THE_SUITS[aCard.m_suit];elseos << "XX";return os;}
Card::Card(rank r , suit s , bool faceUp ) : m_rank(r),m_suit(s),m_faceUp(faceUp){}int Card::Get()const {int val = 0;if (m_faceUp) {val = m_rank;if (val > 10)val = 10;}return val;
}
void  Card::Flip() {m_faceUp = !m_faceUp;
}

Hand.h

#ifndef _HAND_H_
#define _HAND_H_#include"Card.h"//扑克牌的集合
class Hand
{
public:Hand();virtual ~Hand();void Add(Card* pCard);//拿一张牌到手上void Clear();//丢掉手上所有牌int GetTotal()const;//返回牌的全部价值
protected:vector<Card*> m_cards;
};#endif

Hand.cpp

#include "Hand.h"Hand::Hand() {m_cards.reserve(7);
}
Hand::~Hand() {Clear();
}void   Hand::Add(Card* pCard) {m_cards.push_back(pCard);
}
void   Hand::Clear() {//删除堆内所有内存for (Card* it : m_cards) {delete it;it = NULL;}//清空容器内的指针m_cards.clear();
}int   Hand::GetTotal()const {//扑克牌为空 或者 牌面朝下if (m_cards.empty() || m_cards[0]->Get() == 0)return 0;int total = 0;//获取全部牌值for (Card* it : m_cards) total += it->Get();//看看是否牌里有尖bool contains_ace = false;for (Card* it : m_cards) {if (it->Get() == Card::ACE)contains_ace = true;}if (contains_ace &&total <= 11)total += 10;return total;
}

GenericPlayer.h

#ifndef _GENERIC_PLAYER_H_
#define _GENERIC_PLAYER_H_#include"Hand.h"//这个类并不代表一个完整的玩家,只是表示玩家和机器玩家共有的元素
class GenericPlayer : public Hand
{friend ostream& operator<<(ostream& os, const GenericPlayer& aGenericPlayer);
public:GenericPlayer(const string& name="");virtual ~GenericPlayer() = default;//写虚析构是为了下面派生的类的析构能执行到//indicates whether or not generic player wants to keep hittingvirtual bool AreHitting()const = 0;//returns whether generic player has busted - has a total greater than 21bool AreBusted()const;  //是否要炸//announces that the generic player bustsvoid Bust()const; //炸了
protected:string m_name;
};#endif

GenericPlayer.cpp

#include "GenericPlayer.h"ostream& operator<<(ostream& os, const GenericPlayer& aGenericPlayer) {os << aGenericPlayer.m_name << ">>\t";if (!aGenericPlayer.m_cards.empty()) {for (Card* it : aGenericPlayer.m_cards) {os << *it << "\t";}if (aGenericPlayer.GetTotal() != 0)cout << "(" << aGenericPlayer.GetTotal() << ")";}elseos << setw(5)<<"\t<empty>";return os;}GenericPlayer::GenericPlayer(const string& name): m_name(name) {}bool  GenericPlayer::AreBusted()const {return GetTotal() > 21;
}void  GenericPlayer::Bust()const {cout << m_name << " busts.\n==============================\n";
}

Player.h

#ifndef _PLAYER_H_
#define _PLAYER_H_#include"GenericPlayer.h"//人类玩家类 由GenericPlayer类派生而来
class Player :  public GenericPlayer
{
public:Player(const string& name="");~Player() = default;bool AreHitting()const; //是否拿牌void Win()const; //赢了void Lose()const;  //输了void Push()const;  //继续游戏
};#endif

Player.cpp

#include "Player.h"Player::Player(const string& name ) : GenericPlayer(name){}bool  Player::AreHitting()const {cout << m_name << ",do you want to hit? Y/N >>";char response;cin >> response;if (response == 'y' || response == 'Y')return true;cout << "---------------------------------------------------" << endl;return false;}
void  Player::Win()const {cout << m_name << " wins\n";
}
void  Player::Lose()const {cout << m_name << " loses\n";
}
void  Player::Push()const {cout << m_name << " pushes\n";
}

Houser.h

#ifndef _HOUSER_H_
#define _HOUSER_H_#include"GenericPlayer.h"//庄家
class Houser : public GenericPlayer
{
public:Houser(const string& name="Houser");~Houser() = default;bool AreHitting()const;void FlipFirstCard();
};#endif

Houser.cpp

#include "Houser.h"Houser::Houser(const string& name ): GenericPlayer(name){}//返回的点数小于17,表示庄家继续跟牌
/*
如果庄家的暗牌已经大于等于17了,则不管玩家多少,庄家直接赢。
*/
bool  Houser::AreHitting()const {return  (GetTotal() < 17);
}//翻开第一张庄家的牌
void  Houser::FlipFirstCard() {if (!m_cards.empty())m_cards[0]->Flip();elsecout << "no card to flip!\n";}

Deck.h

#ifndef _DECK_H_
#define _DECK_H_#include"Hand.h"
#include"GenericPlayer.h"class Deck : public Hand
{
public:Deck();~Deck() = default;void Populate();void Shuffle();void Deal( Hand& aHand);void AdditionalCards(GenericPlayer& aGenericPlayer);};#endif

Deck.cpp

#include "Deck.h"Deck::Deck()
{m_cards.reserve(52);//turn the capacity to 52 Populate();
}void Deck::Populate() {Clear();//create a standard deckfor (int s = Card::CLUBS; s<= Card::SPADES; s++) {for (int r = Card::ACE; r <=  Card::KING; r++) {Card* pNew = new Card(static_cast<Card::rank>(r),static_cast<Card::suit>(s));Add(pNew);//让牌堆里保存一副52张的牌}}}
void Deck::Shuffle() {//打乱元素的顺序random_shuffle(m_cards.begin(), m_cards.end());
}void Deck::Deal( Hand& aHand) {if ( !m_cards.empty()) {aHand.Add(m_cards.back());//返回一张牌给玩家或庄家m_cards.pop_back();//弹出最后一个}elsecout << "out of cards. unable to deal";
}void Deck::AdditionalCards(GenericPlayer& aGenericPlayer){cout << endl;//如果玩家或庄家没有爆而且选择拿牌,直到不想继续拿牌或者已经爆了while (aGenericPlayer.AreBusted() == false && aGenericPlayer.AreHitting() == true) {Deal(aGenericPlayer);//aGenericPlayer传递,拿张牌出来cout << aGenericPlayer << endl;if (aGenericPlayer.AreBusted())aGenericPlayer.Bust();}}

Game.h

#ifndef _GAME_H_
#define _GAME_H_#include"Card.h"
#include"Hand.h"
#include"GenericPlayer.h"
#include"Player.h"
#include"Houser.h"
#include"Deck.h"//这个类表示Blackjack这个游戏
class Game
{Deck m_deck;Houser m_houser;vector<Player> m_players;
public:Game() = default;Game(const vector<string>& names);Game(const Game& b) = default;Game operator=(const Game& b) = delete;~Game() = default;void Play();};vector<string> Game_Interface();#endif

Game.cpp

#include "Game.h"Game::Game(const vector<string>& names) {//seed the random number generatorsrand(static_cast<unsigned int>(time(0)));//create a vector of players from a vector of namesfor (auto hao : names)m_players.emplace_back(Player(hao));}/*
这个运行函数只游戏一遍*/void  Game::Play() {/*------------------------------------------------------------------初始化和显示-----------------------------------------------------------------------------*/m_deck.Populate();//生成52张牌的容器m_deck.Shuffle();//打乱容器元素//deal initial 2 cards to everyonefor (int i : {0,1}) {for (Player it : m_players)   //把玩家都放进Deck,拿出两张牌m_deck.Deal( it);m_deck.Deal(m_houser);  //把庄家也放进Deck,拿出两张牌}//HIde houser's first cardm_houser.FlipFirstCard();//display everyone's handfor (Player it : m_players)cout << it << endl;cout << m_houser << endl;/*由于重载了GenericPlayer和Card类的<<所以,可以对实例对象用<<直接输出*//*---------------------------------------------------------------------------继续加牌和判断---------------------------------------------------------------------------------*///deal additional cards to playersfor (Player it : m_players)m_deck.AdditionalCards(it);//reveal houser's first cardm_houser.FlipFirstCard();cout << endl << m_houser;//把暗牌翻出来//deal additional cards to houserm_deck.AdditionalCards(m_houser);if (m_houser.AreBusted()) { //庄家输了//everyone still playing winsfor (Player it : m_players) {if (it.AreBusted() == false) //某个玩家或某些玩家赢了it.Win();}}else {//庄家没输//compare each player still playing to houserfor (Player it : m_players) {if (it.AreBusted() == false) {if (it.GetTotal() > m_houser.GetTotal())//没炸反而值还比庄家大it.Win();else if (it.GetTotal() < m_houser.GetTotal())//虽然没炸,但是比庄家小it.Lose();else if (it.GetTotal() == m_houser.GetTotal())it.Push();//继续游戏}}}/*---------------------------------------------------------------清空容器-------------------------------------------------------------------------------*///remove everyone's cardsfor (Player it : m_players)it.Clear();m_houser.Clear();m_deck.Clear();cout << "===================GAME END====================" << endl;
}vector<string>  Game_Interface() {cout << "===================Game Start====================" << endl;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);}return names;}

main.cpp

#include"Game.h"void main(void) {vector<string> s = Game_Interface();Game* aGame = new Game(s);char again_flag = 'y';while (again_flag != 'n'&&again_flag != 'N') {aGame->Play();cout << "\nDo you want to play again?(Y/N)>>";cin >> again_flag;}system("pause");}

最后的显示结果

括号前的几个是牌,第一个数字是牌数,第二个是符号。括号里的是总的点数。
还剩下一个BUG,
一开始应该给每个玩家两张牌,而我那段代码并没有被执行…\dots…
反正能玩就是了。从没想过,这么个小程序竟然也这么复杂。代码量多而且类之间的调用关系真是卧槽!

参考:《C++游戏编程入门 第四版》

《C++游戏编程入门 第四版》的例子Blackjack-相关推荐

  1. 【一篇文章带你读完《C++游戏编程入门 第4版》】

    <C++游戏编程入门 第4版>下载地址:https://download.csdn.net/download/qq_23996157/10764030 有道云笔记分享:http://not ...

  2. 《C++游戏编程入门(第4版)》——1.12 习题

    本节书摘来自异步社区出版社<C++游戏编程入门(第4版)>一书中的第1章,第1.1节,作者:[美]Michael Dawson(道森),更多章节内容可以访问云栖社区"异步社区&q ...

  3. 《C++游戏编程入门(第4版)》——1.8 Lost Fortune简介

    本节书摘来自异步社区出版社<C++游戏编程入门(第4版)>一书中的第1章,第1.8节,作者:[美]Michael Dawson(道森),更多章节内容可以访问云栖社区"异步社区&q ...

  4. 《C++游戏编程入门(第4版)》——2.4 使用带else子句的if语句序列

    本节书摘来自异步社区出版社<C++游戏编程入门(第4版)>一书中的第2章,第2.4节,作者:[美]Michael Dawson(道森),更多章节内容可以访问云栖社区"异步社区&q ...

  5. 《C++游戏编程入门(第4版)》——1.11 问题讨论

    本节书摘来自异步社区出版社<C++游戏编程入门(第4版)>一书中的第1章,第1.11节,作者:[美]Michael Dawson(道森),更多章节内容可以访问云栖社区"异步社区& ...

  6. 《C++游戏编程入门(第4版)》——1.9 本章小结

    本节书摘来自异步社区出版社<C++游戏编程入门(第4版)>一书中的第1章,第1.9节,作者:[美]Michael Dawson(道森),更多章节内容可以访问云栖社区"异步社区&q ...

  7. 《C++游戏编程入门(第4版)》——1.10 问与答

    本节书摘来自异步社区出版社<C++游戏编程入门(第4版)>一书中的第1章,第1.10节,作者:[美]Michael Dawson(道森),更多章节内容可以访问云栖社区"异步社区& ...

  8. python 编程入门-python编程入门(第3版)

    python编程入门(第3版)简洁明了,通俗易懂,非常适合初学者,但是我觉得处理大型任务,多线程应该是必不可少的,这对于初学者来说也是需要了解的,当然了,多线程是个复杂的话题,高级用户可以再深入研究, ...

  9. python编程入门第3版pdf-Python编程入门(第3版) PDF扫描版[26MB]

    Python编程入门(第3版)是图文并茂的Python学习参考书,书中并不包含深奥的理论或者高级应用,而是以大量来自实战的例子.屏幕图和详细的解释,用通俗易懂的语言结合常见任务,对Python的各项基 ...

  10. python编程入门-python编程入门(第3版)

    python编程入门(第3版)简洁明了,通俗易懂,非常适合初学者,但是我觉得处理大型任务,多线程应该是必不可少的,这对于初学者来说也是需要了解的,当然了,多线程是个复杂的话题,高级用户可以再深入研究, ...

最新文章

  1. PyTorch 版 EfficientDet 比官方 TF 实现快 25 倍?这个 GitHub 项目数天狂揽千星
  2. 【记录】batch_size对cnn训练的影响
  3. IntelliJ IDEA 14.0.2破解注册码文件(2015年06月08日)
  4. MySQL授权用户及密码恢复设置
  5. WebSocket安卓客户端实现详解(一)–连接建立与重连
  6. python中if有几种使用方式_python 中if-else的多种简洁的写法
  7. CANopen | 对象字典OD 03 - 启动CANopen节点的服务数据对象SDO
  8. java final class 性能_java中final修饰基本变量后的效率问题
  9. 天空象棋——网站与用户
  10. 轻薄于型 强悍于内 拯救者9000X 2021硬核发布
  11. ASP.NET MVC之文件上传【二】(九)
  12. 小学计算机课动画制作的评课稿,小学信息技术评课稿
  13. 电脑键盘部分按键失灵_键盘按键失灵别担心 电脑达人教你几步解决方法
  14. 手机QQ聊天记录备份
  15. 面试官问你的缺点是什么,这么回答漂亮!
  16. android sdcard 不存在,安卓手机真机运行时找不到sdcard?
  17. php怎么分栏,WordPress CMS分栏制作详解
  18. Oracle增删改查
  19. python runtimewarning_训练神经网络,numpy出现runtime warning的解决思路
  20. 计算机二类中文核心期刊,我国科学院计算机网络信息中心在学研究生年终考核奖评定办法(试行).doc...

热门文章

  1. draw.io怎么安装在linux,Draw.io安装
  2. 生成树协议STP 网络冗余技术
  3. weight和weightSum的区别
  4. Alpha测试 / Beta测试 / 黑盒测试 /白盒测试概述
  5. ASEMI肖特基二极管1N5819压降是什么意思
  6. 狂奔五年后,拼多多需要静静
  7. CentOS6.5服务器端口捆绑
  8. cocos2d-x 3.2 之 三消类游戏——万圣大作战
  9. 翠竹林 Opencv+C++之人脸识别
  10. php如何本地运行_怎样在本地运行PHP