《C++游戏编程入门 第四版》的例子Blackjack-
前言
这是《C++游戏编程入门 第四版》的最后一个例子,还挺复杂的。要完成这个制作,先要弄明白Blackjack的游戏规则。
简化版Blackjack(21点)
规则:
游戏由2到6个人玩,使用除大小王之外的52张牌,游戏者的目标是使手中的牌的点数之和不超过21点且尽量大。一个庄家Houser,多个玩家Player
- 2至10牌,按其原点数计算;
- K、Q、J和10牌都算作10点(一般记作T,即ten之意);
- 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-相关推荐
- 【一篇文章带你读完《C++游戏编程入门 第4版》】
<C++游戏编程入门 第4版>下载地址:https://download.csdn.net/download/qq_23996157/10764030 有道云笔记分享:http://not ...
- 《C++游戏编程入门(第4版)》——1.12 习题
本节书摘来自异步社区出版社<C++游戏编程入门(第4版)>一书中的第1章,第1.1节,作者:[美]Michael Dawson(道森),更多章节内容可以访问云栖社区"异步社区&q ...
- 《C++游戏编程入门(第4版)》——1.8 Lost Fortune简介
本节书摘来自异步社区出版社<C++游戏编程入门(第4版)>一书中的第1章,第1.8节,作者:[美]Michael Dawson(道森),更多章节内容可以访问云栖社区"异步社区&q ...
- 《C++游戏编程入门(第4版)》——2.4 使用带else子句的if语句序列
本节书摘来自异步社区出版社<C++游戏编程入门(第4版)>一书中的第2章,第2.4节,作者:[美]Michael Dawson(道森),更多章节内容可以访问云栖社区"异步社区&q ...
- 《C++游戏编程入门(第4版)》——1.11 问题讨论
本节书摘来自异步社区出版社<C++游戏编程入门(第4版)>一书中的第1章,第1.11节,作者:[美]Michael Dawson(道森),更多章节内容可以访问云栖社区"异步社区& ...
- 《C++游戏编程入门(第4版)》——1.9 本章小结
本节书摘来自异步社区出版社<C++游戏编程入门(第4版)>一书中的第1章,第1.9节,作者:[美]Michael Dawson(道森),更多章节内容可以访问云栖社区"异步社区&q ...
- 《C++游戏编程入门(第4版)》——1.10 问与答
本节书摘来自异步社区出版社<C++游戏编程入门(第4版)>一书中的第1章,第1.10节,作者:[美]Michael Dawson(道森),更多章节内容可以访问云栖社区"异步社区& ...
- python 编程入门-python编程入门(第3版)
python编程入门(第3版)简洁明了,通俗易懂,非常适合初学者,但是我觉得处理大型任务,多线程应该是必不可少的,这对于初学者来说也是需要了解的,当然了,多线程是个复杂的话题,高级用户可以再深入研究, ...
- python编程入门第3版pdf-Python编程入门(第3版) PDF扫描版[26MB]
Python编程入门(第3版)是图文并茂的Python学习参考书,书中并不包含深奥的理论或者高级应用,而是以大量来自实战的例子.屏幕图和详细的解释,用通俗易懂的语言结合常见任务,对Python的各项基 ...
- python编程入门-python编程入门(第3版)
python编程入门(第3版)简洁明了,通俗易懂,非常适合初学者,但是我觉得处理大型任务,多线程应该是必不可少的,这对于初学者来说也是需要了解的,当然了,多线程是个复杂的话题,高级用户可以再深入研究, ...
最新文章
- PyTorch 版 EfficientDet 比官方 TF 实现快 25 倍?这个 GitHub 项目数天狂揽千星
- 【记录】batch_size对cnn训练的影响
- IntelliJ IDEA 14.0.2破解注册码文件(2015年06月08日)
- MySQL授权用户及密码恢复设置
- WebSocket安卓客户端实现详解(一)–连接建立与重连
- python中if有几种使用方式_python 中if-else的多种简洁的写法
- CANopen | 对象字典OD 03 - 启动CANopen节点的服务数据对象SDO
- java final class 性能_java中final修饰基本变量后的效率问题
- 天空象棋——网站与用户
- 轻薄于型 强悍于内 拯救者9000X 2021硬核发布
- ASP.NET MVC之文件上传【二】(九)
- 小学计算机课动画制作的评课稿,小学信息技术评课稿
- 电脑键盘部分按键失灵_键盘按键失灵别担心 电脑达人教你几步解决方法
- 手机QQ聊天记录备份
- 面试官问你的缺点是什么,这么回答漂亮!
- android sdcard 不存在,安卓手机真机运行时找不到sdcard?
- php怎么分栏,WordPress CMS分栏制作详解
- Oracle增删改查
- python runtimewarning_训练神经网络,numpy出现runtime warning的解决思路
- 计算机二类中文核心期刊,我国科学院计算机网络信息中心在学研究生年终考核奖评定办法(试行).doc...