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.重复

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


结果呈现

output1.png

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

output2.png

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


output3.png

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

tieba.png

代码实现

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容

  • 你的数学直觉怎么样?你能凭借直觉,迅速地判断出谁的概率大,谁的概率小吗?下面就是 26 个这样的问题。如果你感兴趣...
    cnnjzc阅读 6,827评论 0 12
  • 我看缝纫机乐队,我记住了里面一句最心酸的话:“钱可以救命,但理想不能。”那感觉好像我们做了完全的努力,也全是白费。...
    归还不过还好阅读 191评论 2 1
  • [问答03 | 父母才是孩子的起跑线2017.07.13留言] 大多数家长非常关注对孩子的培养,从儿童抓起,各种各...
    早知今日阅读 337评论 5 5
  • 让我们坐上时光机, 一起到未来2023年这一年, 这一年, 是我们认知自我升级的第一辈子,我和007的战友们一起踏...
    绝版小贝Anni阅读 336评论 3 4
  • 《超级个体》提问官古典的每日一问:24/30 周末啊,今天的话题比较八卦)——前段时间咪蒙和自己实习生各自写文撕逼...
    Katrina程阅读 151评论 0 0