《C++游戏编程入门 第四版》的例子Blackjack

前言

这是《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

类的设计

在这里插入图片描述
B l a c k j a c k Blackjack游戏

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

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

C a r d Card类

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

H a n d Hand类

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

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

G e n e r i c P l a y e r GenericPlayer类

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

P l a y e r Player类

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

H o u s e r Houser类

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

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

D e c k Deck类

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

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

G a m e 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];
	else
		os << "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 hitting
	virtual bool AreHitting()const = 0;
	
	//returns whether generic player has busted - has a total greater than 21
	bool AreBusted()const;  //是否要炸
	//announces that the generic player busts
	void 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() << ")";

	}
	else
		os << 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();
	else
		cout << "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 deck
	for (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();//弹出最后一个
	}
	else
		cout << "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 generator
	srand(static_cast<unsigned int>(time(0)));

	//create a vector of players from a vector of names
	for (auto hao : names)
		m_players.emplace_back(Player(hao));



}



/*
这个运行函数只游戏一遍

*/

void  Game::Play() {


	/*------------------------------------------------------------------初始化和显示-----------------------------------------------------------------------------*/


	m_deck.Populate();//生成52张牌的容器
	m_deck.Shuffle();//打乱容器元素



	//deal initial 2 cards to everyone
	for (int i : {0,1}) {
		for (Player it : m_players)   //把玩家都放进Deck,拿出两张牌
			m_deck.Deal( it);
		m_deck.Deal(m_houser);  //把庄家也放进Deck,拿出两张牌
	}
	



	//HIde houser's first card
	m_houser.FlipFirstCard();


	//display everyone's hand
	for (Player it : m_players)
		cout << it << endl;
	cout << m_houser << endl;
	/*
	由于重载了GenericPlayer和Card类的<<
	所以,可以对实例对象用<<直接输出
	*/


	/*---------------------------------------------------------------------------继续加牌和判断---------------------------------------------------------------------------------*/
	//deal additional cards to players
	for (Player it : m_players)
		m_deck.AdditionalCards(it);


	//reveal houser's first card
	m_houser.FlipFirstCard();
	cout << endl << m_houser;//把暗牌翻出来

	//deal additional cards to houser
	m_deck.AdditionalCards(m_houser);



	if (m_houser.AreBusted()) { //庄家输了
		//everyone still playing wins
		for (Player it : m_players) {
			if (it.AreBusted() == false) //某个玩家或某些玩家赢了
				it.Win();
		}
	}
	else {//庄家没输
		
		//compare each player still playing to houser
		for (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 cards
	for (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++游戏编程入门 第四版》

猜你喜欢

转载自blog.csdn.net/weixin_41374099/article/details/88617054