❶ 怎么写一个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;
}