C++程序 :“21 点“纸牌游戏

C++程序 :“21 点"纸牌游戏

实验题目:

​ “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, 返回操作系统
}
  • 9
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

拾亿-唯一

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值