实验题目:
“21点”是一个古老的扑克牌游戏,游戏规则是:各个参与者设法使自己的牌达到总分21而不超过这个数值。扑克牌的分值取它们的面值,A充当1分或者11分(由玩家自己选择一种分值),J.Q和K 人头牌都是10分。
庄家对付1~7个玩家。在一局开始时,包括庄家在内的所有参与者都有两张牌。玩家可以看到他们的所有牌以及总分,而庄家有一张牌暂时是隐藏的。接下来,只要愿意,各个玩家都有机会依次再拿-张牌。 如果某个玩家的总分超过了21(称为“引爆”),那么这个玩家就了。在所有玩家都拿了额外的牌后,庄家将显示隐藏的牌。只要庄家的总分等于或小于16,那么他就必须再拿牌。如果庄家引爆了,那么还没有引爆的所有玩家都将获胜,引爆的玩家打成平局。否则,将余下的各玩家的总分与庄家的总分做比较,如果玩家的总分
大于庄家的总分,则玩家获胜。如果二者的总分相同,则玩家与庄家打成平局。
编写程序实现游戏,计算机作为庄家,1~7个人作为普通玩家参与游戏。游戏程序运行输出如下所示。
多少人加人游戏?(1~7):2
输人第1位玩家的姓名:张三
输入第2位玩家的姓名:李四
游戏开始
庄家:<隐藏>梅花7
张三:红桃7方块J总分值17
李四:红桃J红桃3总分值13
张三,你想再要一一张牌吗(y, n)?n
李四,你想再要一张牌吗(y, n)?y
李四:红桃J红桃3梅花10总分值23
李四引爆!
庄家:方块10梅花7总分值17
张三,唉,你打平局了!
李四,对不起,你输了!
你想再玩一次吗 (y, n)?
代码:
结构图:
utilitty.h 部分
#ifndef __UTILITY_H__ // 如果没有定义__UTILITY_H__
#define __UTILITY_H__ // 那么定义__UTILITY_H__
// 实用程序工具包
#ifdef _MSC_VER // 表示是Visual C++
#if _MSC_VER == 1200 // 表示Visual C++6.0
// 标准库头文件
#include <string.h> // 标准串和操作
#include <iostream.h> // 标准流操作
#include <limits.h> // 极限
#include <math.h> // 数据函数
#include <fstream.h> // 文件输入输出
#include <ctype.h> // 字符处理
#include <time.h> // 日期和时间函数
#include <stdlib.h> // 标准库
#include <stdio.h> // 标准输入输出
#include <iomanip.h> // 输入输出流格式设置
#include <stdarg.h> // 支持变长函数参数
#include <assert.h> // 支持断言
#else // 其它版本的Visual C++
// ANSI C++标准库头文件
#include <string> // 标准串和操作
#include <iostream> // 标准流操作
#include <limits> // 极限
#include <cmath> // 数据函数
#include <fstream> // 文件输入输出
#include <cctype> // 字符处理
#include <ctime> // 日期和时间函数
#include <cstdlib> // 标准库
#include <cstdio> // 标准输入输出
#include <iomanip> // 输入输出流格式设置
#include <cstdarg> // 支持变长函数参数
#include <cassert> // 支持断言
using namespace std; // 标准库包含在命名空间std中
#endif // _MSC_VER == 1200
#else // 非Visual C++
// ANSI C++标准库头文件
#include <string> // 标准串操作
#include <iostream> // 标准流操作
#include <limits> // 极限
#include <cmath> // 数据函数
#include <fstream> // 文件输入输出
#include <cctype> // 字符处理
#include <ctime> // 日期和时间函数
#include <cstdlib> // 标准库
#include <cstdio> // 标准输入输出
#include <iomanip> // 输入输出流格式设置
#include <cstdarg> // 支持变长函数参数
#include <cassert> // 支持断言
using namespace std; // 标准库包含在命名空间std中
#endif // _MSC_VER
// 实用函数
char GetChar(istream &inStream = cin); // 从输入流inStream中跳过空格及制表符获取一字符
bool UserSaysYes(); // 当用户肯定回答(yes)时, 返回true, 用户否定回答(no)时,返回false
// 函数模板
template <class ElemType >
void Swap(ElemType &e1, ElemType &e2); // 交换e1, e2之值
template<class ElemType>
void Display(ElemType elem[], int n); // 显示数组elem的各数据元素值
// 实用类
class Timer; // 计时器类Timer
class Error; // 通用异常类
class Rand; // 随机数类Rand
char GetChar(istream &in) // 从输入流in中跳过空格及制表符获取一字符
{
char ch; // 临时变量
while ((ch = in.peek()) != EOF // 文件结束符(peek()函数从输入流中接受1
// 字符,流的当前位置不变)
&& ((ch = in.get()) == ' ' // 空格(get()函数从输入流中接受1字符,流
// 的当前位置向后移1个位置)
|| ch == '\t')); // 制表符
return ch; // 返回字符
}
bool UserSaysYes() // 当用户肯定回答(yes)时, 返回true, 用户否定回答(no)时,返回false
{
char ch; // 用户回答字符
bool initialResponse = true; // 初始回答
do
{ // 循环直到用户输入恰当的回答为止
if (initialResponse) cout << "(y, n)?"; // 初始回答
else cout << "用y或n回答:"; // 非初始回答
while ((ch = GetChar()) == '\n'); // 跳过空格,制表符及换行符获取一字符
initialResponse = false; // 非初始回答
} while (ch != 'y' && ch != 'Y' && ch != 'n' && ch != 'N');
while (GetChar() != '\n'); // 跳过当前行后面的字符
if (ch == 'y' || ch == 'Y') return true; // 肯定回答返回true
else return false; // 否定回答返回false
}
template <class ElemType >
void Swap(ElemType &e1, ElemType &e2) // 交换e1, e2之值
{
ElemType temp; // 临时变量
temp = e1; e1 = e2; e2 = temp; // 循环赋值实现交换e1, e2
}
template<class ElemType>
void Show(ElemType elem[], int n) // 显示数组elem的各数据元素值
{
for (int i = 0; i < n; i++)
{ // 显示数组elem
cout << elem[i] << " "; // 显示elem[i]
}
cout << endl; // 换行
}
// 计时器类Timer
class Timer
{
private:
// 数据成员
clock_t startTime;
public:
// 方法声明
Timer(){ startTime = clock(); } // 构造函数, 由当前时间作为开始时间构造对象
double ElapsedTime() const // 返回已过的时间
{
clock_t endTime = clock(); // 结束时间
return (double)(endTime - startTime) / (double)CLK_TCK; // 计算已过时间
}
void Reset(){ startTime = clock(); } // 重置开始时间
};
// 通用异常类Error
#define MAX_ERROR_MESSAGE_LEN 100
class Error
{
private:
// 数据成员
char message[MAX_ERROR_MESSAGE_LEN]; // 异常信息
public:
// 方法声明
Error(char mes[] = "一般性异常!"){ strcpy(message, mes); } // 构造函数
void Show() const{ cout << message << endl; } // 显示异常信息
};
// 随机数类Rand
class Rand
{
public:
// 方法声明
static void SetRandSeed(){ srand((unsigned)time(NULL)); } // 设置当前时间为随机数种子
static int GetRand(int n){ return rand() % n; } // 生成0 ~ n-1之间的随机数
static int GetRand(){ return rand(); } // 生成0 ~ n-1之间的随机数
};
#endif
card.h 部分
// 文件路径名: game_of_21_point\card.h
#ifndef __CARD_H__ // 如果没有定义__CARD_H__
#define __CARD_H__ // 那么定义__CARD_H__
typedef enum
{ // 扑克牌面值:ACE(A),TWO(2)~TEN(10),JACK(J), QUEEN(Q), KING(K)
ACE = 1, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING
} RankType;
typedef enum
{ // 扑克牌花色:CLUBS(梅花), DIAMONDS(方块), HEARTS(红桃)和SPADES(黑桃)
CLUBS, DIAMONDS, HEARTS, SPADES
} SuitType;
struct Card
{ // 扑克牌结构体
RankType rank; // 扑克牌面值
SuitType suit; // 扑克牌花色
};
ostream& operator<<(ostream& outStream, const Card &card) // 重载<<运算符
{
// 输出花色
if (card.suit == CLUBS) outStream << " 梅花"; // CLUBS表示梅花
else if (card.suit == DIAMONDS) outStream << " 方块"; // DIAMONDS表示方块
else if (card.suit == HEARTS) outStream << " 红桃"; // HEARTS表示红桃
else if (card.suit == SPADES) outStream << " 黑桃"; // SPADES表示黑桃
// 输出面值
if (card.rank == ACE) outStream << "A"; // ACE表示A
else if (card.rank == JACK) outStream << "J"; // JACK表示J
else if (card.rank == QUEEN) outStream << "Q"; // QUEEN表示Q
else if (card.rank == KING) outStream << "K"; // KING表示K
else cout << (int)card.rank; // (int)card.rank为分值
return outStream;
}
#endif
game_of_21_point.h 部分
// 文件路径名: game_of_21_point\game_of_21_point.h
#ifndef __GAME_OF_21_POINT_H__ // 如果没有定义__GAME_OF_21_POINT_H__
#define __GAME_OF_21_POINT_H__ // 那么定义__GAME_OF_21_POINT_H__
#include "card.h" // 扑克牌
#define LEN_OF_MAX_NAME 21 // 最大姓名长度
// 21点游戏类GameOf21Point声明
class GameOf21Point
{
private:
// 数据成员:
Card deck[52]; // 一副扑克牌
int dealPos; // 发牌位置
Card hands[8][21]; // hand[0]存储庄家手中的扑克牌, hand[1~7]存储各位玩家手中的扑克牌
int numOfCard[8]; // 庄家(numOfCard[0])及玩家(numOfCard[1~7])手中的扑克牌数
char name[8][LEN_OF_MAX_NAME]; // 庄家与玩家姓名
int numOfPlayer; // 玩家人数
// 辅助函数
void Shuffle(); // 洗牌, 将扑克牌混在一起以便产生一种随机的排列
int GetTotalScore(Card hand[21], int n);// 返回一手扑克牌的总分值
void ShowStatus(int num, bool hideFirstCardAndTotalScore = false);
// 显示庄家(对应num=0)或玩家(对应num>0)的当前状态
Card DealOneCard(){ return deck[dealPos++]; } // 发一张扑克牌
public:
// 方法声明:
GameOf21Point(); // 无参数的构造函数
virtual ~GameOf21Point(){} // 析构函数
void Game(); // 运行游戏
};
// 21点游戏类GameOf21Point的实现部分
GameOf21Point::GameOf21Point() // 初始化扑克牌,发牌位置,庄家与各玩家手中的扑克牌数
{
int curPos = 0; // 当前扑克牌位置
for (int suitPos = 0; suitPos < 4; suitPos++)
{ // 花色
for (int rankPos = 1; rankPos <= 13; rankPos++)
{ // 面值
deck[curPos].suit = (SuitType)suitPos; // 花色
deck[curPos].rank = (RankType)rankPos; // 面值
curPos++; // 下一个位置
}
}
cout << "多少人加入游戏?(1~7):";
cin >> numOfPlayer; // 玩家人数
while (numOfPlayer < 1 || numOfPlayer > 7)
{ // 限制人数在1~7之间
cout << "人数只能在1~7之间, 请重新输入人数:";
cin >> numOfPlayer; // 玩家人数
}
dealPos = 0; // 发牌位置
int i; // 临时变量
for (i = 0; i <= numOfPlayer; i++) numOfCard[i] = 0;
// 庄家(numOfCard[0])及玩家(numOfCard[1~7])手中的扑克牌数
strcpy(name[0], "庄家"); // 庄家
for (i = 1; i <= numOfPlayer; i++)
{ // 玩家姓名
cout << "输入第" << i << "位玩家的姓名:";
cin >> name[i];
}
cout << "游戏开始" << endl;
}
void GameOf21Point::Shuffle() // 洗牌, 将扑克牌混在一起以便产生一种随机的排列组合
{
Rand::SetRandSeed(); // 设置当前时间为随机数种子
for (int curPos = 51; curPos > 0; curPos--)
{ // 产生随机的位置为curPos的扑克牌
int pos = Rand::GetRand(curPos + 1); // 生成0~curPos之间的随机数
Swap(deck[pos], deck[curPos]); // 交换deck[pos]与deck[curPos]
}
}
int GameOf21Point::GetTotalScore(Card hand[21], int n) // 返回一手扑克牌的总分值
{
int pos; // 临时变量
int totalScore = 0; // 总分值
for (pos = 0; pos < n; pos++)
{ // 循环求最大分值(A的分值为11)
if (hand[pos].rank == ACE) totalScore += 11; // A的分值为11
else if (hand[pos].rank > TEN) totalScore += 10;// J,Q,K的分值为10
else totalScore += (int)hand[pos].rank; // TWO~TEN的分值为2~10
}
for (pos = 0; totalScore > 21 && pos < n; pos++)
{ // 分值大于21时, 将A的分值改为1
if (hand[pos].rank == ACE) totalScore -= 10; // A的分值由11分改为1分
}
return totalScore; // 返回总分
}
void GameOf21Point::ShowStatus(int num, bool hideFirstCardAndTotalScore)
// 当num=0时,显示庄家当前状态,当num>0时,显示第num个玩家的当前状态,当
// hideFirstCardAndTotalScore为真时,将隐藏首张扑克牌与总分, 否则将显示首张扑克牌与总分
{
cout << name[num] << ":"; // 显示庄家或玩家姓名
if (hideFirstCardAndTotalScore) cout << " <隐藏>"; // 隐藏首张扑克牌
else cout << hands[num][0]; // 显示首张扑克牌
for (int i = 1; i < numOfCard[num]; i++)
cout << hands[num][i]; // 显示庄家或玩家手中的扑克牌
if (!hideFirstCardAndTotalScore)
cout << " 总分值" << GetTotalScore(hands[num], numOfCard[num]); // 显示庄家或玩家总分
cout << endl;
if (GetTotalScore(hands[num], numOfCard[num]) > 21)
cout << name[num] << "引爆!" << endl;
}
void GameOf21Point::Game() // 运行游戏
{
Shuffle(); // 洗牌, 将扑克牌混在一起以便产生一种随机的排列
int i, j; // 临时变量
for (i = 0; i < 2; i++)
hands[0][numOfCard[0]++] = DealOneCard(); // 为庄家发两张扑克牌
ShowStatus(0, true); // 显示庄家状态,隐藏首张扑克牌与总分
for (i = 1; i <= numOfPlayer; i++)
{ // 向各玩家发扑克牌,显示各玩家发扑克牌手中的扑克牌
for (j = 0; j < 2; j++)
hands[i][numOfCard[i]++] = DealOneCard(); // 为玩家i发两张扑克牌
ShowStatus(i); // 显示玩家i
}
cout << endl;
for (i = 1; i <= numOfPlayer; i++)
{ // 依次向玩家发额外的牌
cout << name[i] << ", 你想再要一张牌吗";
if (UserSaysYes())
{ // 玩家再要一张牌
hands[i][numOfCard[i]++] = DealOneCard(); // 为玩家i发1张扑克牌
ShowStatus(i); // 显示玩家i
}
}
ShowStatus(0); // 显示庄家
while (GetTotalScore(hands[0], numOfCard[0]) <= 16)
{ // 庄家总分小于等于16, 必须再拿牌
hands[0][numOfCard[0]++] = DealOneCard(); // 为庄家发1张扑克牌
ShowStatus(0); // 显示庄家
}
cout << endl;
if (GetTotalScore(hands[0], numOfCard[0]) > 21)
{ // 庄家引爆, 没有引爆的所有人赢
for (i = 1; i <= numOfPlayer; i++)
{ // 依次查看每位玩家
if (GetTotalScore(hands[i], numOfCard[i]) <= 21)
cout << name[i] << ", 恭喜你, 你赢了!" << endl; // 玩家没有引爆
else cout << name[i] << ", 唉, 你打平局了!" << endl; // 玩家引爆
}
}
else
{ // 庄家没有引爆
for (i = 1; i <= numOfPlayer; i++)
{ // 依次查看每位玩家
if (GetTotalScore(hands[i], numOfCard[i]) <= 21 && // 未引爆
GetTotalScore(hands[i], numOfCard[i]) > GetTotalScore(hands[0], numOfCard[0])
// 总分比庄家大
)
{ // 玩家未引爆, 且总分比庄家大, 玩家赢
cout << name[i] << ", 恭喜你, 你赢了!" << endl;
}
else if (GetTotalScore(hands[i], numOfCard[i]) == GetTotalScore(hands[0], numOfCard[0]))
{ // 玩家总分与庄家相等, 平局
cout << name[i] << ", 唉, 你打平局了!" << endl;
}
else
{ // 玩家引爆或总分比庄家小, 玩家输
cout << name[i] << ", 对不起, 你输了!" << endl;
}
}
}
}
#endif
main.cpp 部分
// 文件路径名:game_of_21_point\main.cpp
#include "utility.h" // 实用程序软件包
#include "game_of_21_point.h" // 21点游戏
int main(void) // 主函数main(void)
{
do
{
GameOf21Point objGame; // 21点游戏对象
objGame.Game(); // 运行游戏
cout << "你想再玩一次吗";
} while (UserSaysYes()); // 肯定回符循环, 直到得到否定回答为止
system("PAUSE"); // 调用库函数system()
return 0; // 返回值0, 返回操作系统
}