《C++游戏编程入门 第四版》的例子Blackjack-
前言
這是《C++游戲編程入門 第四版》的最后一個(gè)例子,還挺復(fù)雜的。要完成這個(gè)制作,先要弄明白Blackjack的游戲規(guī)則。
簡(jiǎn)化版Blackjack(21點(diǎn))
規(guī)則:
游戲由2到6個(gè)人玩,使用除大小王之外的52張牌,游戲者的目標(biāo)是使手中的牌的點(diǎn)數(shù)之和不超過21點(diǎn)且盡量大。一個(gè)莊家Houser,多個(gè)玩家Player
玩法:
開局先發(fā)兩張牌,莊家一明(面朝上)一暗(面朝下),玩家兩張明牌。
接下來(lái),每個(gè)玩家只要愿意,每次都有機(jī)會(huì)添加一張牌,超過21就Bust。
當(dāng)所有的玩家選擇好了后,莊家亮出暗牌。如果莊家的點(diǎn)總數(shù)低于17,則莊家必須添加新牌。如果超過21就Bust(不管玩家點(diǎn)數(shù)多少,玩家都贏了)。如果暗牌已經(jīng)大于等于17了,莊家直接勝利。
如果某個(gè)玩家點(diǎn)數(shù)比莊家點(diǎn)數(shù)大(小/等于),則…\dots…
類的設(shè)計(jì)
Blackjack游戲Blackjack游戲Blackjack游戲
| Card | Blackjack的游戲牌 |
| Hand | 玩家和莊家所持的牌,Card對(duì)象的集合 |
| GenericPlayer | 一般的玩家,是具體玩家類的過度,包含Player和Houser的共有元素 |
| Player | 玩家 |
| Houser | 莊家 |
| Deck | 牌堆,Hand的增強(qiáng)類(具有Hand類不具備的額外功能,如洗牌和發(fā)牌 |
| Game | 游戲外部接口,執(zhí)行游戲流程 |
為了讓事情變得簡(jiǎn)單再簡(jiǎn)單,幾乎所有成員函數(shù)都是公用的,所有數(shù)據(jù)成員都是受保護(hù)的。繼承只使用公有繼承。
Card類Card類Card類
| rank m_rank | 牌的大小。rank是表示13個(gè)等級(jí)的枚舉類型 |
| suit m_suit | 牌的花色。suit是4種花色的枚舉類型 |
| bool m_faceUp | 是否是明牌(牌面向上)。這個(gè)影響牌的點(diǎn)數(shù)和顯示 |
| int Get() | 返回牌的點(diǎn)值 |
Hand類Hand類Hand類
| vector<Card*> m_cards | 撲克牌的集合,存儲(chǔ)著若干Card對(duì)象指針 |
| void Add(Card* pCard) | 向所持的牌中添加一張。在向量m_cards添加一個(gè)Card指針 |
| void Clear() | 清空所以所持的牌。移除m_cards所有的指針,刪除堆里相關(guān)Card對(duì)象 |
| int GetTotal()const | 返回所持牌的點(diǎn)數(shù)總和 |
是Hand基類的實(shí)現(xiàn)類,是Player、Houser的抽象類。
GenericPlayer類GenericPlayer類GenericPlayer類
| string m_name | 玩家名 |
| virtual bool AreHitting()const =0 | 指示玩家是否跟牌 |
| bool AreBusted()const | 指示玩家是否炸了 |
| void Bust()const | 玩家炸了后執(zhí)行的函數(shù) |
Player類Player類Player類
| bool AreHitting()const | 指示玩家是否跟牌 |
| void Win()const | 玩家贏了后執(zhí)行的函數(shù) |
| void Lose()const | 玩家輸了后執(zhí)行的函數(shù) |
| void Push()const | 平局后執(zhí)行的函數(shù) |
Houser類Houser類Houser類
| bool AreHitting()const | 指示莊家是否跟牌 |
| void FlipFirstCard() | 翻轉(zhuǎn)第一張牌,決定暗牌明牌 |
Deck類就是發(fā)牌類,對(duì)Hand類進(jìn)行組合,與Player、Houser類的基類GenericPlayer互動(dòng)。
Deck類Deck類Deck類
| void Populate() | 生成包含52張牌的標(biāo)準(zhǔn)牌堆 |
| void Shuffle() | 洗牌 |
| void Deal(Hand& aHand) | 發(fā)牌 |
| void AdditionalCards(GenericPlayer& aGenericPlayer) | 只要玩家跟牌,就向玩家額外發(fā)牌 |
游戲引擎類,將其他類抽象出來(lái)的接口使用起來(lái)。并且對(duì)客戶端提供簡(jiǎn)單的使用接口。
Game類Game類Game類
| Deck m_deck | 牌堆 |
| Houser m_houser | 莊家 |
| vector m_players | 許多的玩家 |
| void Play() | 進(jìn)行一輪游戲 |
| vector Game_Interface() | 將輸入玩家個(gè)數(shù)和名字的部分隱藏起來(lái) |
游戲的實(shí)現(xiàn)
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;};#endifCard.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;//返回牌的全部?jī)r(jià)值 protected:vector<Card*> m_cards; };#endifHand.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() {//刪除堆內(nèi)所有內(nèi)存for (Card* it : m_cards) {delete it;it = NULL;}//清空容器內(nèi)的指針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"//這個(gè)類并不代表一個(gè)完整的玩家,只是表示玩家和機(jī)器玩家共有的元素 class GenericPlayer : public Hand {friend ostream& operator<<(ostream& os, const GenericPlayer& aGenericPlayer); public:GenericPlayer(const string& name="");virtual ~GenericPlayer() = default;//寫虛析構(gòu)是為了下面派生的類的析構(gòu)能執(zhí)行到//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; };#endifGenericPlayer.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類派生而來(lái) class Player : public GenericPlayer { public:Player(const string& name="");~Player() = default;bool AreHitting()const; //是否拿牌void Win()const; //贏了void Lose()const; //輸了void Push()const; //繼續(xù)游戲 };#endifPlayer.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(); };#endifHouser.cpp
#include "Houser.h"Houser::Houser(const string& name ): GenericPlayer(name){}//返回的點(diǎn)數(shù)小于17,表示莊家繼續(xù)跟牌 /* 如果莊家的暗牌已經(jīng)大于等于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);};#endifDeck.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();//彈出最后一個(gè)}elsecout << "out of cards. unable to deal"; }void Deck::AdditionalCards(GenericPlayer& aGenericPlayer){cout << endl;//如果玩家或莊家沒有爆而且選擇拿牌,直到不想繼續(xù)拿牌或者已經(jīng)爆了while (aGenericPlayer.AreBusted() == false && aGenericPlayer.AreHitting() == true) {Deal(aGenericPlayer);//aGenericPlayer傳遞,拿張牌出來(lái)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"//這個(gè)類表示Blackjack這個(gè)游戲 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();#endifGame.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));}/* 這個(gè)運(yùn)行函數(shù)只游戲一遍*/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) //把玩家都放進(jìn)Deck,拿出兩張牌m_deck.Deal( it);m_deck.Deal(m_houser); //把莊家也放進(jìn)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類的<<所以,可以對(duì)實(shí)例對(duì)象用<<直接輸出*//*---------------------------------------------------------------------------繼續(xù)加牌和判斷---------------------------------------------------------------------------------*///deal additional cards to playersfor (Player it : m_players)m_deck.AdditionalCards(it);//reveal houser's first cardm_houser.FlipFirstCard();cout << endl << m_houser;//把暗牌翻出來(lái)//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) //某個(gè)玩家或某些玩家贏了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();//繼續(xù)游戲}}}/*---------------------------------------------------------------清空容器-------------------------------------------------------------------------------*///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");}最后的顯示結(jié)果
括號(hào)前的幾個(gè)是牌,第一個(gè)數(shù)字是牌數(shù),第二個(gè)是符號(hào)。括號(hào)里的是總的點(diǎn)數(shù)。
還剩下一個(gè)BUG,
一開始應(yīng)該給每個(gè)玩家兩張牌,而我那段代碼并沒有被執(zhí)行…\dots…
反正能玩就是了。從沒想過,這么個(gè)小程序竟然也這么復(fù)雜。代碼量多而且類之間的調(diào)用關(guān)系真是臥槽!
參考:《C++游戲編程入門 第四版》
總結(jié)
以上是生活随笔為你收集整理的《C++游戏编程入门 第四版》的例子Blackjack-的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 程序猿麒麟臂打造之路(健身一)
- 下一篇: python关键字匹配_python正则