❶ 怎麼寫一個c++程序判斷麻將是否胡牌(只討論清一色的情況)
以前我寫了一個判斷麻將是否胡牌的演算法,不過不支持百搭。最近有一個朋友問我,如何有百搭,演算法如何寫。我寫了一個,貼出來,讓網友看看。
演算法輸入: 整數數組 a[0..n-1]表示一手牌,其中,n 是牌的張數,比如 14。
牌的編碼可自定,比如: 101-109 表示一萬到九萬,
201-209表示一條到九條,
301-309表示一筒到九筒,
411,421,431,441,451,461,471表示東南西北中發白,
500表示百搭。
演算法預處理:
若 n 模 3不等於 2,直接輸出:牌數不對,是相公,演算法結束。否則:
把百搭刪除,把剩下的普通牌進行排序:
int i,m;
m=0;
for (i=0;i<n;i++)
if (a[i]不是百搭) a[m++]=a[i]; // m 就是普通牌的張數
把 a 中前 m 個元素進行排序;
隨後,我把 a 看成左、中、右三段,其中,左側段表示「成牌區」,即:它們由刻、順組成;
中段表示試探區,演算法要重點處理它們;而右側段則是「雜牌」區,即:它們由非刻非順組成。
在演算法預處理後,顯然,a 的左段長度為0,中段長度是m,右段長度是0,調用下面的「理牌」演算法:
LiPai(a,0,m,0,n-m);
其中,n-m是指百搭的張數。
理牌演算法:
void LiPai(int a[],int LeftCount,int MidCount,int RightCount,int CountOfBaiDa)
{
if (MidCount<3) // 試探區已不足 3 張,理牌過程結束,進入「理雜牌」階段
{
調用測試演算法; // 見後文
}
else // 試探區至少 3 張,可以試著從中取出刻子和順子
{
int * p=&a[LeftCount]; // 讓 p 指向試探區首張
int x=p[0]; // 取出試探區首張
if (p[1]==x && p[2]==x) // 發現一個刻子
{
LiPai(a,LeftCount+3,MidCount-3,RightCount,CountOfBaiDa); // 把刻子放到成牌區,遞歸地調用理牌演算法
}
在 p[0],p[1],...p[MidCount-1] 中尋找 x+1 和 x+2;
if (找到)
{
把 x,x+1,x+2 放入 p[0],p[1],p[2];
把剩下的牌放入 p[3],p[4],...,p[MidCount-1]中;
LiPai(a,LeftCount+3,MidCount-3,RightCount,CountOfBaiDa); // 把順子放到成牌區,遞歸地調用理牌演算法
對p[0],p[1],...,p[MidCount-1] 排序; // 恢復原樣
}
讓 p[0],p[1],...p[MidCount-1] 循環左移一次; // 這樣,X 就成為雜牌區左邊的元素了
LiPai(a,LeftCount,MidCount-1,RightCount+1,CountOfBaiDa); // 把x放入雜牌區,遞歸地調用理牌演算法
讓 p[0],p[1],...p[MidCount-1] 循環右移一次; // 這樣,X 又回到試探區最左側了
}
上述遞歸演算法的終止條件是 MidCount<3,當該條件滿足時,調用下面演算法(即上文提到的測試演算法)
雜牌總張數=MidCount+RightCount; // 不足 2 張的中段,實際上也是雜牌
if (CountOfBaiDa==0) // 如果沒有百搭
{
if (雜牌總張數==2 && 兩張雜牌相同) // 雜牌只能是一個對子,它將是麻將頭
{
輸出一個胡牌方案:刻、順是 a[0],a[1],...,a[LeftCount-1],麻將頭(對子)是 剩下的兩張雜牌;
}
}
else // 如果有百搭, 讓一張百搭配2張雜牌
{
if (雜牌總張數-2*CountOfBaiDa<=2) // 配完之後,剩下的牌數若不超過 2,則有希望胡牌,需要進一步探測
{
申請數組 b[];
把 a[LeftCount],a[LeftCount+1],...,a[m-1] 放入 b[0],b[1],...,b[MidCount+RightCount-1] 中;
把b[]排序;
LiZaPai(a,LeftCount,b,0,MidCount+RightCount,0,CountOfBaiDa); // 總雜牌區也被劃分成 3 段:左段 中段 右段,見下文
釋放b[];
}
}
「理雜牌」LiZaPai(...) 演算法,與普通理牌演算法類似,不過,它的目標是理出對子或搭子。它也把待理區劃分成 3 段:
左段:成對/搭區
中段:待測區
右段:雜牌區
void LiZaPai(int a[],int OKCount,int b[],int LeftCount,int MidCount,int RightCount,int CountOfBaiDa) // OKCount 是刻子/順子的張數,在 a 的最左側
{
if (MidCount<2) // 待測區不足 2 張,
{
調用試配演算法; // 見下文
}
else
{
int * p=&b[LeftCount]; // 讓 p 指向試探區;
int x=p[0]; // 取出首張
if (x==p[1]) // 找到一個對子
{
LiZaPai(a,OKCount,b,LeftCount+2,MidCount-2,RightCount,CountOfBaiDa); // 遞歸地求解
}
在 p[1],p[2],...p[MidCount-1] 中尋找 x+1;
if (找到)
{
把 x,x+1 放入 p[0],p[1];
把剩下的牌放入 p[2],p[3],...,p[MidCount-1];
LiZaPai(a,OKCount,b,LeftCount+2,MidCount-2,RightCount,CountOfBaiDa); // 遞歸地求解
把 p[0],p[1],...,p[MidCount-1] 排序;
}
在 p[1],p[2],...p[MidCount-1] 中尋找 x+2;
if (找到)
{
把 x,x+2 放入 p[0],p[1];
把剩下的牌放入 p[2],p[3],...,p[MidCount-1];
LiZaPai(a,OKCount,b,LeftCount+2,MidCount-2,RightCount,CountOfBaiDa); // 遞歸地求解
把 p[0],p[1],...,p[MidCount-1] 排序;
}
讓 p[0],p[1]....,p[MidCount-1] 循環左移; // x 稱到雜牌區;
LiZaPai(a,OKCount,b,LeftCount,MidCount-1,RightCount+1,CountOfBaiDa); // 遞歸地求解
讓 p[0],p[1]....,p[MidCount-1] 循環右移; // x 回到首位
}
}
試配演算法如下:
雜牌總數=MidCount+RightCount; // 此時,試探區成了雜牌區
if (雜牌總數==0) // 全是對子/搭子
{
for (i=0;i<LeftCount;i+=2) // 尋找對子
if (b[i]==b[i+1]) // 找到一個對子, 它是麻將頭
{
if (LeftCount/2-1<=CountOfBaiDa) // 剩下的對子搭子必須全部由百搭配成刻/順
{
輸出一個胡牌方案:刻/順是 a[0],a[1],...,a[OKCount-1],以及 b[0]到b[LeftCount-1] 中除去 i,i+1 兩元素後, 剩下的對/搭與百搭配成的刻/順,以及剩下的百搭組成的刻子;
麻將頭是是 b[i],b[i+1];
}
}
// 現在,必須用兩個百搭配成麻將頭, 所有搭子要由剩下的百搭配成刻/順
if (CountOfBaiDa>=2 && LeftCount/2<=CountOfBaiDa-2)
{
輸出一個胡牌方案:刻/順是 a[0],a[1],...,a[OKCount-1],以及 b[0]到b[LeftCount-1] 每個對/搭與百搭配成的刻/順,以及剩下的百搭組成的刻子;
麻將頭是百搭,百搭;
}
}
else
if (雜牌總數==1) // 只有一張雜牌, 此時,必須用一個百搭與此雜牌配成麻將頭
{
if (CountOfBaiDa>=1 && LeftCount/2<=CountOfBaiDa-1) // 用一個百搭與雜牌配成對子,而對子/搭子的副數不能比剩下的百搭數多, 這樣就可以胡牌
{
輸出一個胡牌方案:刻子/順子是 a[0],a[1],...,a[OKCount-1],以及 b[0],b[1],百搭,b[2],b[3],百搭,...b[LeftCount-2],b[LeftCount-1],百搭,以及
配完對子、刻/順後剩下的百搭組成的刻子;
麻將頭是:雜牌,百搭。
}
}
else // 有2張或以上雜牌,不胡,因為,最多隻能用一個百搭+一張雜牌形成麻將頭
{
}
上面演算法我用 VC++ 實現,運算速度很快,一般耗時 0.04368ms。我的 CPU:2.3GHz ,i7
望採納,謝謝
❷ 麻將游戲的演算法(13張牌的那種)
國標麻將規則的番種
88番
1 大四喜 由4副風刻(杠)組成的和牌。不計圈風刻、門風刻、三風刻、碰碰和
2 大三元 和牌中,有中發白3副刻子。不計箭刻
3 綠一色 由23468條及發字中的任何牌組成的順子、刻五、將的和牌。不計混一色。如無「發」字組成的各牌,可計清一色
4 九蓮寶燈 由一種花色序數牌子按1112345678999組成的特定牌型,見同花色任何1張序數牌即成和牌。不計清一色
5 四杠 4個杠
6 連七對 由一種花色序數牌組成序數相連的7個對子的和牌。不計清一色、不求人、單釣
7 十三幺 由3種序數牌的一、九牌,7種字牌及其中一對作將組成的和牌。不計五門齊、不求人、單釣
64番
8 清幺九 由序數牌一、九刻子組成的和牌。不計碰碰和、同刻、無字
9 小四喜 和牌時有風牌的3副刻子及將牌。不計三風刻
10 小三元 和牌時有箭牌的兩副刻子及將牌。不計箭刻
11 字一色 由字牌的刻子(杠)、將組成的和牌。不計碰碰和
12 四暗刻 4個暗刻(暗杠)。不計門前清、碰碰和
13 一色雙龍會 一種花色的兩個老少副,5為將牌。不計平各、七對、清一色
48番
14 一色四同順 一種花色4副序數相同的順子,不計一色三節高、一般高、四歸一
15 一色四節高 一種花色4副依次遞增一位數的刻子不計一色三同順、碰碰和
32番
16 一色四步高 一種花色4副依次遞增一位數或依次遞增二位數的順子
17 三杠 3個杠
18 混幺九 由字牌和序數牌一、九的刻子用將牌組成的和牌。不計碰碰和
24番
19 七對 由7個對子組成和牌。不計不求人、單釣
20 七星不靠 必須有7個單張的東西南北中發白,加上3種花色,數位按147、258、369中的7張序數牌組成沒有將牌的和牌。不計五門齊、不求人、單釣
21 全雙刻 由2、4、6、8序數牌的刻了、將牌組成的和牌。不計碰碰和、斷幺
22 清一色 由一種花色的序數牌組成和各牌。不無字
23 一色三同順 和牌時有一種花色3副序數相同的順了。不計一色三節高
24 一色三節高 和牌時有一種花色3副依次遞增一位數字的刻了。不計一色三同順
25 全大 由序數牌789組成的順了、刻子(杠)、將牌的和牌。不計無字
26 全中 由序數牌456組成的順子、刻子(杠)、將牌的和牌。不計斷幺
27 全小 由序數牌123組成的順子、刻子(杠)將牌的的和牌。不計無字
16番
28 清龍 和牌時,有一種花色1-9相連接的序數牌
29 三色雙龍會 2種花色2個老少副、另一種花色5作將的和牌。不計喜相逢、老少副、無字、平和
30 一色三步高 和牌時,有一種花色3副依次遞增一位或依次遞增二位數字的順子
31 全帶五 每副牌及將牌必須有5的序數牌。不計斷幺
32 三同刻 3個序數相同的刻子(杠)
33 三暗刻 3個暗刻
12番
34 全不靠 由單張3種花色147、258、369不能錯位的序數牌及東南西北中發白中的任何14張牌組成的和牌。不計五門齊、不求人、單釣
35 組合龍 3種花色的147、258、369不能錯位的序數牌
36 大於五 由序數牌6-9的順子、刻子、將牌組成的和牌。不計無字
37 小於五 由序數牌1-4的順子、刻子、將牌組成的和牌。不計無字
38 三風刻 3個風刻
8 番
39 花龍 3種花色的3副順子連接成1-9的序數牌
40 推不倒 由牌面圖形沒有上下區別的牌組成的和牌,包括1234589餅、245689條、白板。不計缺一門
41 三色三同順 和牌時,有3種花色3副序數相同的順子
42 三色三節高 和牌時,有3種花色3副依次遞增一位數的刻子
43 無番和 和牌後,數不出任何番種分(花牌不計算在內)
44 妙手回春 自摸牌牆上最後一張牌和牌。不計自摸
45 海底撈月 和打出的最後一張牌
46 杠上開花 開杠抓進的牌成和牌(不包括補花)不計自摸
47 搶杠和 和別人自抓開明杠的牌。不計和絕張
6 番
48 碰碰和 由4副刻子(或杠)、將牌組成的和牌
49 混一色 由一種花色序數牌及字牌組成的和牌
50 三色三步高 3種花色3副依次遞增一位序數的順子
51 五門齊 和牌時3種序數牌、風、箭牌齊全
52 全求人 全靠吃牌、碰牌、單釣別人批出的牌和牌。不計單釣
53 雙暗杠 2個暗杠
54 雙箭刻 2副箭刻(或杠)
4 番
55 全帶幺 和牌時,每副牌、將牌都有幺牌
56 不求人 4副牌及將中沒有吃牌、碰牌(包括明杠),自摸和牌
57 雙明杠 2個明杠
58 和絕張 和牌池、桌面已亮明的3張牌所剩的第4張牌(搶杠和不計和絕張)
2 番
59 箭刻 由中、發、白3張相同的牌組成的刻子
60 圈風刻 與圈風相同的風刻
61 門風刻 與本門風相同的風刻
62 門前清 沒有吃、碰、明杠,和別人打出的牌
63 平和 由4副順子及序數牌作將組成的和牌,邊、坎、釣不影響平和
64 四歸一 和牌中,有4張相同的牌歸於一家的順、刻子、對、將牌中(不包括杠牌)
65 雙同刻 2副序數相同的刻子
66 雙暗刻 2個暗刻
67 暗杠 自抓4張相同的牌開杠
68 斷幺 和牌中沒有一、九及字牌
1 番
69 一般高 由一種花色2副相同的順子組成的牌
70 喜相逢 2種花色2副序數相同的順子
71 連六 一種花色6張相連接的序數牌
72 老少副 一種花色牌的123、789兩副順子
73 幺九刻 3張相同的一、九序數牌及字牌組成的刻子(或杠)
74 明杠 自己有暗刻,碰別人打出的一張相同的牌開杠:或自己抓進一張與碰的明刻相同的牌開杠
75 缺一門 和牌中缺少一種花色序數牌
76 無字 和牌中沒有風、箭牌
77 邊張 單和123的3及789的7或1233和3、77879和7都為張。手中有12345和3,56789和6不算邊張
78 坎張 和2張牌之間的牌。4556和5也為坎張,手中有45567和6不算坎張
79 單釣將 釣單張牌作將成和
80 自摸 自己抓進牌成和牌
81 花牌 即春夏秋冬,梅蘭竹菊,每花計一分。不計在起和分內,和牌後才能計分。花牌補花成和計自摸分,不計杠上開花
❸ 用c語言編寫24點代碼分析
#include<stdio.h>
double fun(double a1,double a2,int b) //用於嘗試著計算的函數,b為運算控制
{
switch(b)
{
case 0:return (a1+a2);
case 1:return (a1-a2);
case 2:return (a1*a2);
case 3:return (a1/a2);
}
}
void main()
{
int i,j,k,l,n,m,r,save[4];
double num[4]={1,1,1,1},tem1,tem2,tem3,abc=1111;
char sign[5]="+-*/"; //列印時候用的符號,需要和fun函數里的順序保持一致
printf("input 4 numbers:");
for(i=0;i<4;i++)
{
scanf("%lf",num+i); //輸入數據
save[i]=num[i]; //保存原始數據
}
//下面程序的思想,就是利用窮舉(其實就是使用的排列組合方法)來計算可能的組合。
//先把輸入的4個數進行排列(前4個for語句就這個用途)
//再依次插入三個運算符(後3個for語句就這個用途)
//事實上,從這里看,這個程序是不怎樣的。七層循環嵌套,這是編程的大忌。一般循環嵌套最好不要超過兩層。
for(i=0;i<4;i++)
for(j=0;j<4;j++)
if(j!=i)
{
for(k=0;k<4;k++)
if(k!=i&&k!=j)
{
for(l=0;l<4;l++)
if(l!=i&&l!=j&&l!=k)
{
for(n=0;n<4;n++)
for(m=0;m<4;m++)
for(r=0;r<4;r++)
{
tem1=fun(num[i],num[j],n);
tem2=fun(tem1,num[k],m);
tem3=fun(tem2,num[l],r);
//以下五種處理方法,涵蓋了有可能的全部運算順序
//這也是本程序最精妙的地方。
if(tem3==24.0)//如果直接算得了24,說明次序不變,直接輸出就是
printf("{(%d%c%d)%c%d}%c%d=24\n",save[i],sign[n],save[j],sign[m],save[k],sign[r],save[l]);
else if(tem3==-24.0)//如果算得的是負的,說明需要顛倒第二次運算(第三次運算不可能是加減)
printf("{%d%c(%d%c%d)}%c%d=24\n",save[k],sign[m],save[i],sign[n],save[j],sign[r],save[l]);
else if(tem3==1.0/24.0)//如果是倒數,說明需要顛倒最後一次運算(第三次運算同樣不可能是加減)
printf("%d%c{(%d%c%d)%c%d}=24\n",save[l],sign[r],save[i],sign[n],save[j],sign[m],save[k]);
else if(tem3==-1.0/24.0)//如果是負倒數,則說明第二次和第三次運算都要顛倒(第三次運算同樣不可能是加或減)
printf("%d%c{%d%c(%d%c%d)}=24\n",save[l],sign[r],save[k],sign[n],save[i],sign[m],save[j]);
else
{ //處理()*/+/-()的情況
tem1=fun(num[i],num[j],n);
tem2=fun(num[k],num[l],r);
tem3=fun(tem1,tem2,m);
if(tem3==24.0)
printf("(%d%c%d)%c(%d%c%d)=24\n",save[i],sign[n],save[j],sign[m],save[k],sign[r],save[l]);
}
}
}
}
}
}
//後面我再研究了下,發現"第三次不可能是加減法"這種思想是錯誤的,而程序作者在設計的時候,確實是這么認為的,所以,這個程序是有問題的.
//但程序里的主體思想沒有問題,如果需要修改這個錯誤,程序需要在運算順序判斷上下功夫.結果只能取==24的情況.
❹ 求一個麻將的出牌的演算法思路(麻將人工智慧),最好可以提供資料,本人感激不盡
沒接觸過程序設計.不過我想:首先 你要知道麻將里的輸贏規則,那些牌克制那些牌.然後監視玩家手裡的牌,最後讓AI針對玩家出牌就好.
因為麻將的張數很多,讓AI直接出牌克制, 也讓玩家不易察覺AI作弊.再次之,如果嫌AI設計體積太大了,可以讓玩家得組合牌的幾率降低.
當然,好不好玩,哪的另算
❺ C語言 洗牌演算法
/*洗牌程序:用任何語言,隨機分配52張撲克牌到52個位置上,每個位置只容許放一張牌
用1-13表示紅心A--K
14-26表示黑桃A,2,3-,Q,K
27-39表示方塊A,2,3-,Q,K
40-52表示黑桃A,2,3-,Q,K
也就是生成1-52不重復的隨機數,放到數組中*/
#include<iomanip.h>
#include<stdlib.h>
#include<time.h>
const int N=52;
static int a[N];
int create(int n)
{
return (1+rand()%52);
}
int main()
{
int i,j;
srand(time(0));
for(i=0;i<N;++i)
{
a[i]=create(N);
for(j=0;j<i;++j)
{
if(a[j]==a[i])
{
a[i]=(a[i]+1)%52;
}
}
cout<<setw(5)<<a[i];
}
cout<<endl;
return 0;
}