与女朋友喜欢玩斗牛纸牌游戏。一直想用C语言实现发牌,今天终于决定要写了。经过测试效果不错。
斗牛规则
可能各地区规则有所差异。我们使用的规则是,牌库只有A至10。是每人依次抽五张纸牌。使用其中三张组合成10的整倍数,剩余两张对10取余数即是玩家点数。比较玩家之间的点数大小。点数大的获胜。
创建牌库
扑克牌一共52张加两张大小王。所以可以使用1 ~ 54来表示牌面和花色。其中:
1 ~ 13表示【红桃A】 ~ 【红桃K】;
14 ~ 26表示【方片A】 ~ 【方片K】;
27 ~ 39表示【方片A】 ~ 【方片K】;
40 ~ 52表示【方片A】 ~ 【方片K】;
53表示【小王】,54表示【大王】。
由此有了一个清晰的创建牌库的思路,即创建一个1 ~ 45的数列即可。
static char Poker_num[13][3]= { "A","2","3","4","5","6","7","8","9","10","J","Q","K" };
static char Poker_col[4][5] = { "红桃","方片","黑桃","梅花"};
typedef int PokerChar ;
typedef struct Poker_Type
{
PokerChar Card[54];
}Poker_Typedef;
/*创建扑克牌库*/
Poker_Typedef* Create_Poker (void)
{
Poker_Typedef* temp;
temp = (Poker_Typedef*)malloc (sizeof (PokerChar) * 54);
if (temp == NULL)
{
printf ("创建扑克牌库失败 ( > _ < )!\r\n");
return NULL;
}
return temp;
}
/*初始化扑克牌*/
void Poker_Init (Poker_Typedef* poker)
{
int i = 0;
for (i; i < 54; i++)
{
poker->Card[i] = i + 1;
}
}
删除牌库
因为使用的是动态分配内存,所以需要释放内存。
/*删除扑克牌库*/
void Delete_Poker (Poker_Typedef* poker)
{
Poker_Init (poker);
free (poker);
}
洗牌
洗牌即是重复多次随机抽取两张扑克互换位置。
/*洗牌*/
void Shuffle_Poker (Poker_Typedef* poker, int times)
{
int i = 0;
int a = 0, b = 0;
PokerChar temp_card =0;
srand ((unsigned)time (NULL)); //设置随机数种子
for(i = 0; i< times; i++)
{
//rand()%(b-a+1)+a
a = rand() % 54; /*下标取值范围0~53*/
b = rand() % 54;
temp_card = poker->Card[a];
poker->Card[a] = poker->Card[b];
poker->Card[b] = temp_card;
}
}
扑克的打印
我们需要将每一张牌显示出来,简单思考一下我们的牌库规则,就可以想到:
任何一张牌非王牌对13取余得到的结果就应该是牌面的大小,如果是13的整倍数得到的结果是0。任何一张非王牌对13取整得到的结果可以得到花色的结果。所以需要先处理两张王牌,其他的牌就可以进行取整和取余计算。
在做这步之前我们需要对牌面的数组进行调整。原因是当扑克牌为13的倍数时取整结果为0,所以我们可以将“K”放在 Poker_num[0][3]。
/*打印单张牌面*/
void Print_Card (PokerChar card)
{
int color = NULL;
int num = NULL;
if ((card < 1) || (card > 54))
{
printf ("你拿了张假牌吧!(= o =).z Z\r\n");
}
else
{
if (card == 53)
{
printf ("【 小王 】 ");
}
else if (card == 54)
{
printf ("【 大王 】 ");
}
else
{
if (card % 13 == 0)
{
color = card / 13 - 1;
}
else
{
color = card / 13; /*得到花色*/
}
num = card % 13; /*得到牌面*/
printf ("【%s%2s】 ", Poker_col[color],Poker_num[num] );
}
}
}
/*打印整副牌面*/
void Print_Poker(Poker_Typedef* poker)
{
int i = 0;
printf("整副扑克牌是:\r\n");
for (i = 0; i < 54; i++)
{
if (i % 13 == 0) /*打印13张就换行显示*/
{
printf("\r\n");
}
Print_Card(poker->Card[i]);
}
printf("\r\n 扑克牌展示完毕(O^u^O)\r\n");
}
扑克牌相关函数试验
完成上面内容后可以测试一下扑克牌相关函数的效果。
/*打印整副牌面*/
void Print_Poker(Poker_Typedef* poker)
{
int i = 0;
printf("整副扑克牌是:\r\n");
for (i = 0; i < 54; i++)
{
if (i % 13 == 0) /*打印13张就换行显示*/
{
printf("\r\n");
}
Print_Card(poker->Card[i]);
}
printf("\r\n 扑克牌展示完毕(O^u^O)\r\n");
}
/*测试洗牌函数使用*/
int Test_Shuffle_Poker (void)
{
Poker_Typedef* POKER_Niuniu;
std::cout << "创建一副扑克 ...(* ̄0 ̄)/\n";
POKER_Niuniu = Create_Poker (); /*创建扑克牌*/
if (!POKER_Niuniu) {
return 0;
}
Poker_Init (POKER_Niuniu); /*初始化扑克牌*/
Print_Poker(POKER_Niuniu); /*打印扑克牌*/
std::cout << "开始洗牌 (. >_<).\n";
Shuffle_Poker (POKER_Niuniu, 20); /*洗牌*/
Print_Poker (POKER_Niuniu); /*打印扑克牌*/
Poker_Init (POKER_Niuniu);
Delete_Poker (POKER_Niuniu); /*释放内存*/
}
执行上面代码就能看到扑克牌创建后的效果和洗牌完毕后的效果
这里洗牌20次,显然不够乱,还可以洗牌更多次数。洗牌函数也需要优化,随机数抽取互换经常性的抽不到头和尾。
按照斗牛规则发牌
根据斗牛的规则,可以每轮为两位玩家分别从牌库发5张牌。也可以提前将牌库的牌分配好,分别发到每个玩家自己的小牌库(依次讲牌库中的牌分给玩家,没人牌库20张)然后每回合从各个咱家的小牌库顺序抽取5张。我是用的是第二种方法。(如果游戏规则里面获胜玩家在下一回合先抽牌就不能使用第二种方法)。
/*牛牛发牌函数*/
void Deal_Cards(Poker_Typedef* poker)
{
int i = 0, j = 0;
PokerChar Player1Cards[20] = { 0 };
PokerChar Player2Cards[20] = { 0 };
int pt = 0;
char input = NULL;
for (i = 0; i < 54; i++) //将牌分给两个人
{
if ((poker->Card[i] % 13) > 0 && (poker->Card[i] % 13 < 11) && (poker->Card[i] != 53) && (poker->Card[i] != 54)) //1~10以内的牌
{
if (j % 2 == 0)
{
Player1Cards[pt] = poker->Card[i];
}
else
{
Player2Cards[pt] = poker->Card[i];
pt++; /*两人都拿了一张牌*/
}
j++;
}
}
j = 0;
pt = 0;
std::cout << "开始游戏 0(^o^)0 \r\n" << " 输入任意值继续游戏 | 选择'N'键退出游戏\r\n";
std::cin >> input;
//scanf_s("%c", &input);
while ((input != 'n') | (input != 'N')) /*输入的值不是n或N*/
{
if ((input == 'n') || (input == 'N'))
{
break;
}
std::cout << "\n|-------------------------玩家一------------------------ | -------------------------玩家二-------------------------|\n";
printf("| ");
for (j = 0; j < 5; j++) /*发玩家一的牌*/
{
Print_Card(Player1Cards[pt]);
pt++;
}
printf("| ");
j = 0;
pt -= 5;
for (j = 0; j < 5; j++) /*发玩家二的牌*/
{
Print_Card(Player2Cards[pt]);
pt++;
}
printf(" |");
printf("\r\n");
std::cout << " ------------------------------------------------------------------------------------------------------------------\n\n";
if (pt >= 20) /*如果发完了*/
{
std::cout << "牌库已经发完啦\r\n";
break;
}
/*继续发牌或退出游戏*/
std::cout << "新一回合 \r\n" << " 输入任意值继续游戏 | 选择'N'键退出游戏\r\n";
std::cin >> input;
//scanf_s("%c", &input);
}
}
游戏的实现
主函数的代码如下,将所有内容串起来。
{
char input_main = 'Y'; /*键盘输入存储*/
Poker_Typedef* POKER_Niuniu;
POKER_Niuniu = Create_Poker(); /*创建扑克牌*/
if (!POKER_Niuniu) {
return 0;
}
std::cout << "-------------------------------- * 牛牛发牌器 * --------------------------------\n";
printf("\r\n");
printf(" ********* *********\r\n");
printf(" * * * * * *\r\n");
printf(" * * * * * *\r\n");
printf(" * * * * * *\r\n");
printf(" * * ********** * *\r\n");
printf(" * *\r\n");
printf(" * ******* ******* *\r\n");
printf(" * ** ** *\r\n");
printf(" * *\r\n");
printf(" ***************************\r\n");
printf(" ** ** \r\n");
printf(" ** ** ** \r\n");
printf(" ************* \r\n");
printf("\r\n\r\n");
while ((input_main != 'n') | (input_main != 'N'))
{
Poker_Init(POKER_Niuniu); /*初始化扑克牌*/
Shuffle_Poker(POKER_Niuniu, 25); /*洗牌*/
Deal_Cards(POKER_Niuniu); /*循环发牌*/
std::cout << "是否开始新一轮游戏?(Y/N) \n" << " 输入任意值继续游戏 | 选择'N'键退出游戏\r\n";
std::cin >> input_main;
// scanf_s("%c", &input_main);
if ((input_main == 'n') || (input_main == 'N'))
{
break;
}
}
/*结束游戏,清理内存*/
Delete_Poker(POKER_Niuniu); /*释放内存*/
return 0;
}
好了!!!现在我们可以没有纸牌一样玩斗牛了。
第一回合:
玩家一:
有牛!牛7
三张牌:【红桃A】 【红桃4】【黑桃5】
两张牌:【梅花4】【黑桃3】
玩家二:
有牛!牛9
三张牌:【红桃5】 【红桃7】【方片8】
两张牌:【红桃3】【方片6】
玩家二胜利!
未来可改进
1.洗牌函数可以优化,做到洗牌更无序。
2.可增加斗牛计算函数,即可以实现自动算出分值(这样好像就失去的游戏的乐趣了)
全部源码
/*poker.h*/
#ifndef _POKER_H_
#define _POKER_H_
typedef int PokerChar ;
//extern char Poker_num[13] = { '1','2','3','4','5','6','7','8','9','10','J','Q','K' };
//extern char Poker_col[4] = {'♠','♥','♣','♦'};
typedef struct Poker_Type
{
PokerChar Card[54];
}Poker_Typedef;
Poker_Typedef* Create_Poker (void);
void Delete_Poker (Poker_Typedef* poker);
void Poker_Init (Poker_Typedef* poker);
void Print_Card (PokerChar card);
void Print_Poker (Poker_Typedef* poker);
void Shuffle_Poker (Poker_Typedef* poker, int times);
#endif
/*poker.cpp*/
/*扑克牌相关函数实现*/
#include "poker.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
static char Poker_num[13][3]= {"K" , "A","2","3","4","5","6","7","8","9","10","J","Q"};
static char Poker_col[4][5] = { "红桃","方片","黑桃","梅花"}; // ASCII码对应的字符,其中,6为黑桃,5为草花,4为方片,3为红桃
/*创建扑克牌库*/
Poker_Typedef* Create_Poker (void)
{
Poker_Typedef* temp;
temp = (Poker_Typedef*)malloc (sizeof (PokerChar) * 54);
if (temp == NULL)
{
printf ("创建扑克牌库失败 ( > _ < )!\r\n");
return NULL;
}
return temp;
}
/*初始化扑克牌*/
void Poker_Init (Poker_Typedef* poker)
{
int i = 0;
for (i; i < 54; i++)
{
poker->Card[i] = i + 1;
}
}
/*删除扑克牌库*/
void Delete_Poker (Poker_Typedef* poker)
{
Poker_Init (poker);
free (poker);
}
/*洗牌*/
void Shuffle_Poker(Poker_Typedef* poker, int times)
{
int i = 0;
int a = 0, b = 0;
PokerChar temp_card = 0;
srand((unsigned)time(NULL)); //设置随机数种子
for (i = 0; i < times; i++)
{
//rand()%(b-a+1)+a
a = rand() % 54; /*下标取值范围0~53*/
b = rand() % 54;
temp_card = poker->Card[a];
poker->Card[a] = poker->Card[b];
poker->Card[b] = temp_card;
}
}
/*打印单张牌面*/
void Print_Card (PokerChar card)
{
int color = NULL;
int num = NULL;
if ((card < 1) || (card > 54))
{
printf ("你拿了张假牌吧!(= o =).z Z\r\n");
}
else
{
if (card == 53)
{
printf ("【 小王 】 ");
}
else if (card == 54)
{
printf ("【 大王 】 ");
}
else
{
if (card % 13 == 0)
{
color = card / 13 - 1;
}
else
{
color = card / 13; /*得到花色*/
}
num = card % 13; /*得到牌面*/
printf ("【%s%2s】 ", Poker_col[color],Poker_num[num] );
}
}
}
/*打印整副牌面*/
void Print_Poker(Poker_Typedef* poker)
{
int i = 0;
printf("整副扑克牌是:\r\n");
for (i = 0; i < 54; i++)
{
if (i % 13 == 0) /*打印13张就换行显示*/
{
printf("\r\n");
}
Print_Card(poker->Card[i]);
}
printf("\r\n 扑克牌展示完毕(O^u^O)\r\n");
}
// Poker_niuniu.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include "poker.h"
/*测试洗牌函数使用*/
int Test_Shuffle_Poker (void)
{
Poker_Typedef* POKER_Niuniu;
std::cout << "创建一副扑克 ...(* ̄0 ̄)/\n";
POKER_Niuniu = Create_Poker (); /*创建扑克牌*/
if (!POKER_Niuniu) {
return 0;
}
Poker_Init (POKER_Niuniu); /*初始化扑克牌*/
Print_Poker(POKER_Niuniu); /*打印扑克牌*/
std::cout << "开始洗牌 (. >_<).\n";
Shuffle_Poker (POKER_Niuniu, 20); /*洗牌*/
Print_Poker (POKER_Niuniu); /*打印扑克牌*/
Poker_Init (POKER_Niuniu);
Delete_Poker (POKER_Niuniu); /*释放内存*/
}
/*牛牛发牌函数*/
void Deal_Cards(Poker_Typedef* poker)
{
int i = 0, j = 0;
PokerChar Player1Cards[20] = { 0 };
PokerChar Player2Cards[20] = { 0 };
int pt = 0;
char input = NULL;
for (i = 0; i < 54; i++) //将牌分给两个人
{
if ((poker->Card[i] % 13) > 0 && (poker->Card[i] % 13 < 11) && (poker->Card[i] != 53) && (poker->Card[i] != 54)) //1~10以内的牌
{
if (j % 2 == 0)
{
Player1Cards[pt] = poker->Card[i];
}
else
{
Player2Cards[pt] = poker->Card[i];
pt++; /*两人都拿了一张牌*/
}
j++;
}
}
j = 0;
pt = 0;
std::cout << "开始游戏 0(^o^)0 \r\n" << " 输入任意值继续游戏 | 选择'N'键退出游戏\r\n";
std::cin >> input;
//scanf_s("%c", &input);
while ((input != 'n') | (input != 'N')) /*输入的值不是n或N*/
{
if ((input == 'n') || (input == 'N'))
{
break;
}
std::cout << "\n|-------------------------玩家一------------------------ | -------------------------玩家二-------------------------|\n";
printf("| ");
for (j = 0; j < 5; j++) /*发玩家一的牌*/
{
Print_Card(Player1Cards[pt]);
pt++;
}
printf("| ");
j = 0;
pt -= 5;
for (j = 0; j < 5; j++) /*发玩家二的牌*/
{
Print_Card(Player2Cards[pt]);
pt++;
}
printf(" |");
printf("\r\n");
std::cout << " ------------------------------------------------------------------------------------------------------------------\n\n";
if (pt >= 20) /*如果发完了*/
{
std::cout << "牌库已经发完啦\r\n";
break;
}
/*继续发牌或退出游戏*/
std::cout << "新一回合 \r\n" << " 输入任意值继续游戏 | 选择'N'键退出游戏\r\n";
std::cin >> input;
//scanf_s("%c", &input);
}
}
int main()
{
char input_main = 'Y';
Poker_Typedef* POKER_Niuniu;
POKER_Niuniu = Create_Poker(); /*创建扑克牌*/
if (!POKER_Niuniu) {
return 0;
}
std::cout << "-------------------------------- * 牛牛发牌器 * --------------------------------\n";
printf("\r\n");
printf(" ********* *********\r\n");
printf(" * * * * * *\r\n");
printf(" * * * * * *\r\n");
printf(" * * * * * *\r\n");
printf(" * * ********** * *\r\n");
printf(" * *\r\n");
printf(" * ******* ******* *\r\n");
printf(" * ** ** *\r\n");
printf(" * *\r\n");
printf(" ***************************\r\n");
printf(" ** ** \r\n");
printf(" ** ** ** \r\n");
printf(" ************* \r\n");
printf("\r\n\r\n");
while ((input_main != 'n') | (input_main != 'N'))
{
Poker_Init(POKER_Niuniu); /*初始化扑克牌*/
Shuffle_Poker(POKER_Niuniu, 25); /*洗牌*/
Deal_Cards(POKER_Niuniu); /*循环发牌*/
std::cout << "是否开始新一轮游戏?(Y/N) \n" << " 输入任意值继续游戏 | 选择'N'键退出游戏\r\n";
std::cin >> input_main;
// scanf_s("%c", &input_main);
if ((input_main == 'n') || (input_main == 'N'))
{
break;
}
}
/*结束游戏,清理内存*/
Delete_Poker(POKER_Niuniu); /*释放内存*/
//Test_Shuffle_Poker(); /*洗牌测试代码*/
return 0;
}