與女朋友喜歡玩鬥牛紙牌遊戲。一直想用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;
}