c语言如何统计输赢次数,C语言模拟掼蛋中一方最多炸弹数的数学期望

掼蛋是一种在江苏、安徽地区广为流传的牌类游戏。由地方的扑克牌局“跑得快”、“八十分”发展而来。牌局采用四人结对竞赛,输赢升级的方式进行。由于使用两副牌,并且有“逢人配”、“同花顺”规则,故炸弹的个数对于牌局来说非常重要。通过C语言模拟的方式,可以预估出一位玩家手中拿到的炸弹数的数学期望。对于评估自己的牌型有着一定的意义。

名词解释

炸弹:大于等于4张相同点数的牌,或4张王(称为天王炸弹),或同花顺

逢人配:两张♥的主牌称为逢人配,在与其他牌配合时可以当除了王之外的任意花色任意点数的牌,主牌的点数可从2取到A

同花顺:相同花色连续的五张牌,最大的为同花10JQKA,最小的为同花A2345,可当炸弹使用

程序框架

程序完全模拟掼蛋的操作流程,总体上共有以下5个模块

1.印制牌:按照顺序给108张牌赋上花色和点数

2.洗牌:多次随机交换牌的位置,打乱牌的顺序

3.发牌:将洗完的牌轮流发给4位玩家

4.理牌:(按顺序排列玩家1手中的牌【可不做】)找出玩家1手中炸弹个数的最大值

5.循环重复以上步骤若干次,统计玩家1拿到炸弹个数的平均值并输出结果

其中,1、5在主程序中实现,2、3、4通过函数实现。

算法阐述

首先需要建立一个存储一张牌的花色与点数的结构体poker。

1.印制牌

创建包含108个poker结构体的结构体数组deck(一套牌),随后第i张牌的点数即为i模13取余。第1-13、53-65张牌为第一种花色;第14-26、66-78张牌为第二种花色……以此类推,第105、106张牌为小王,算作第5种花色;第107、108张牌为大王,算作第6种花色。

2.洗牌

将印制好的牌堆deck传入函数randsort。以伪随机数种子——系统时间产生两个伪随机数i和j,将牌堆中的第i张牌和第j张牌位置交换,重复上述操作1000次。

3.发牌

创建四个poker结构体的结构体数组p1、p2、p3、p4,用于存储4个玩家手中的牌。将结构体数组deck和p1,p2,p3,p4传入函数dealpoker,将deck中的第i张牌发给第j个人,其中j为i模4取余。

4.理牌

(1)不考虑逢人配

共分以下两种情况考虑炸弹的个数:

非同花顺:统计玩家1手中所有点数牌的张数和王的个数,找出所有张数大于等于4的点数,统计其个数,结果即为点数炸弹的炸弹数。特别地,当某一个点数的牌张数为8张时,需要拆成两个炸弹使用,即炸弹数+1;当王的个数为4时,炸弹数+1.

同花顺:需要按顺序检索从A开头到10开头的同花顺。当检索A开头的同花顺时,首先指定一种花色,搜索同花顺的第1张牌,若找到,将这张牌与【非整理好的同花顺】的牌的最后一张交换位置,继续寻找第二张,若找到,将这张牌与【非整理好的同花顺】的牌的倒数第二张交换位置,继续寻找……依此类推。如果五张都能找到,检查这五个点数中牌的张数恰好为4的有几个。若多于1个(如:22223455556),则不算作同花顺。若只有1个,则算作同花顺,同花顺个数+1,但是炸弹数-1,那五张牌称之为【整理好的同花顺】;若没有,则算作同花顺,同花顺个数+1。考虑到可能出现两个完全一样的同花顺,上述5张牌检索的流程需要进行两遍。

最后,将炸弹数加上同花顺个数,得到最终结果

(2)考虑逢人配

首先指定逢人配的点数,随后检索玩家一手中的逢人配的位置与个数。将逢人配依次视作各张牌面的牌,再统计其炸弹数,选择最大值作为最终结果。

有两张逢人配时,同理。

5.重复

重复上述操作相当大次数,计算出平均值,作为玩家一手中拿到最多炸弹个数的数学期望的估计值,并打印结果。

结果呈现

99fbd767d059?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

output1.png

另外,也可单独统计同花顺的期望

99fbd767d059?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

output2.png

当不算逢人配时,统计纯数字炸弹的期望,约为1.365

99fbd767d059?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

output3.png

与贴吧大神的纯概率计算较为接近

99fbd767d059?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

tieba.png

代码实现

#include

#include

#include

#include

struct poker //结构体poker中存储了一张牌的花色和点数

{

int suit;

int num;

};

void randsort(struct poker *a) //randsort表示洗牌,传入一套牌的结构体数组

{

int i,j,T=1000;

struct poker tmp;

srand(time(NULL)); //产生一个伪随机数种子

for(;T>0;T--)

{

i=rand()%108;

j=rand()%108;

tmp=a[i];

a[i]=a[j];

a[j]=tmp;

} //随机交换两张牌的顺序,进行1000次

}

void dealpoker(struct poker *p,struct poker *a,struct poker *b,struct poker *c,struct poker *d)//发牌函数

{

int i;

for(i=0;i<27;i++)

{

a[i]=p[i*4];

b[i]=p[i*4+1];

c[i]=p[i*4+2];

d[i]=p[i*4+3];

}

}

void bubblesort(struct poker *a) //理牌函数,调试程序时用

{

int i,j;

struct poker tmp;

for(i=0;i<26;i++)

for(j=0;j<26-i;j++)

{

if(a[j].num>a[j+1].num)

{

tmp=a[j];

a[j]=a[j+1];

a[j+1]=tmp;

}

}

}

int bombcheck(struct poker *a) //检验炸弹的数量

{

int i,j,n,ans=0,t,l=0,ths=0,king=0,pd;

int numb[13]={0}; //numb数组存储每一个数字的牌有几张

struct poker tmp; //临时牌结构体,检索同花顺时用

for(i=1;i<=13;i++)

{

for(j=0;j<27;j++)

if(a[j].num==i)

numb[i-1]++;

if(numb[i-1]>=4) ans++;//如果某一数字的张数不少于4,炸弹数+1

if(numb[i-1]==8) ans++;//如果某一数字的张数为8,则拆成两个炸弹,炸弹数+1

if(a[j].suit>4)

king++; //统计王的张数

}

if(king==4) ans++; //如果有4张王,炸弹数+1

for(i=1;i<=10;i++) //检索同花顺,从A开头检验到10开头

{

for(j=0;j<8;j++) //先指定一种花色

{

for(n=0;n<27-5*ths;n++) //ths表示同花顺的个数

if(a[n].num==i&&a[n].suit==j%4+1)

{

tmp=a[n];a[n]=a[26-5*ths];a[26-5*ths]=tmp; //在前27-5ths中找指定花色的第一张牌,如果找到,将其与牌组中除同花顺的最后一张牌交换顺序,如果找不到,不执行操作

for(n=0;n<27-5*ths;n++)

if(a[n].num==i+1&&a[n].suit==j%4+1)

{

tmp=a[n];a[n]=a[25-5*ths];a[25-5*ths]=tmp;//在前27-5ths中找指定花色的第二张牌,如果找到,将其与牌组中除同花顺的倒数第二张牌交换顺序

for(n=0;n<27-5*ths;n++)

if(a[n].num==i+2&&a[n].suit==j%4+1)

{

tmp=a[n];a[n]=a[24-5*ths];a[24-5*ths]=tmp; //同理

for(n=0;n<27-5*ths;n++)

if(a[n].num==i+3&&a[n].suit==j%4+1)

{

tmp=a[n];a[n]=a[23-5*ths];a[23-5*ths]=tmp; //同理

if(i!=10)

pd=i+4;

else

pd=1; //pd确定同花顺的最后一张牌值为多少

for(n=0;n<27-5*ths;n++)

if(a[n].num==pd&&a[n].suit==j%4+1)

{

tmp=a[n];a[n]=a[22-5*ths];a[22-5*ths]=tmp; //同理

l=0; //l为这个同花顺中数字对应的牌数恰好为4的个数

for(t=0;t<5;t++)

{

if(numb[i+t]==4)

l++;

}

if(l<=1)

ths++; //如果不多于2个,则同花顺个数+1(多于两个则组炸弹而不是同花顺)

if(l==1)

ans--; //如果恰为1个,则组同花顺不组炸弹,同花顺个数+1,炸弹个数-1

}

}

}

}

}

}

}

ans+=ths; //把炸弹数加上同花顺的个数

return ans;

}

int bomb(struct poker *a,int frp) //考虑到逢人配以后的炸弹个数

{

int i,j,m,n,p=0,t,ans=0;

int location[2]={0}; //location数组表示逢人配的位置

struct poker tmp[27]; //tmp为为避免位置信息丢失临时存储数据的结构体数组

for (i=0;i<27;i++)

if(a[i].suit==1&&a[i].num==frp)

{location[p]=i;p++;} //如果找到逢人配,逢人配的个数p +1,同时记录逢人配位置

if(p==1)

for(i=1;i<=4;i++) //让逢人配模拟每一张非王的牌

{

a[location[0]].suit=i;

for(j=1;j<=13;j++)

{

a[location[0]].num=j;

for(t=0;t<27;t++)

tmp[t]=a[t]; //改变逢人配牌面后,将牌组信息赋给临时牌组tmp,避免bombcheck函数寻找同花顺时打乱顺序

ans=(bombcheck(tmp)>ans)?bombcheck(tmp):ans; //ans取每一种情况的最大值

}

}

if(p==2) //有两个逢人配时,同理

for(i=1;i<=4;i++)

{

a[location[0]].suit=i;

for(j=1;j<=13;j++)

{

a[location[0]].num=j;

for(m=1;m<=4;m++)

{

a[location[1]].suit=m;

for(n=1;n<=13;n++)

{

a[location[1]].num=n;

for(t=0;t<27;t++)

tmp[t]=a[t];

ans=(bombcheck(tmp)>ans)?bombcheck(tmp):ans;

}

}

}

}

if(p==0) //如果没有逢人配,直接检查炸弹个数

ans=bombcheck(a);

return ans;

}

int main()

{

int i,j,bmb,test; //bmb为每次炸弹个数,test表示模拟次数

float sum=0; //sum为求和

char pm[3]={'\0'}; //pm为牌面显示数字

struct poker deck[108],p1[27],p2[27],p3[27],p4[27];//deck表示一套牌,

for(i=0;i<13;i++) //对一套牌中的每张牌按顺序赋值

{

for(j=0;j<8;j++)

deck[i+j*13].num=i+1;

}

for(i=0;i<8;i++)

{

for(j=0;j<13;j++)

deck[j+i*13].suit=i%4+1;

}

deck[104].num=100;

deck[104].suit=5;

deck[105].num=100;

deck[105].suit=5;

deck[106].num=200;

deck[106].suit=6;

deck[107].num=200;

deck[107].suit=6; //对最后4张王赋值

for(i=1;i<=13;i++) //逢人配从A到K

{

for(test=0;test<=10000;test++)

{

randsort(deck); //洗牌

dealpoker(deck,p1,p2,p3,p4); //发牌

//bubblesort(p1);

/*bubblesort(p2);

bubblesort(p3);

bubblesort(p4);*/ //理牌

bmb=bomb(p1,i);

sum+=bmb;

}

switch (i)

{

case 1: pm[0]='A';break;

case 10:pm[0]='1';pm[1]='0';break;

case 11:pm[0]='J';pm[1]='\0';break;

case 12:pm[0]='Q';pm[1]='\0';break;

case 13:pm[0]='K';pm[1]='\0';break;

default:pm[0]=i+48;

}

printf("逢人配为红桃%s时拿到炸弹的期望约为%f\n",pm,sum/10000); //打印逢人配信息

sum=0;

}

return 0;

}

总结

炸弹不多是常事,且用且珍惜啊XD

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值