牛牛赢牌概率模拟计算

最近做了一个牛牛模拟计算的小程序,总体说来不难,关键地方就是优化,提高计算速度。

规则是用户知道自己的四张手牌,然后模拟出来自己的胜率和收益。

大概思路就是给模拟用户还有用户自己模拟发牌,最后计算所有人的权值,找出最大的权值来判断收益。

52张牌我由m=1-52数字表示,m%13表示每张牌的大小,m/13表示牌的花色,当然13,26,39,52,这种特殊数字返回特定的花色。

1.计算牌的花色

 /// <summary>
        /// 返回牌的花色
        /// </summary>
        /// <param name="brand"></param>
        /// <returns></returns>
        private static int BackColor(int brand)
        {
            if (brand == 13) return 0;
            if (brand == 26) return 1;
            if (brand == 39) return 2;
            if (brand == 52) return 3;
            return brand / 13;
        }

2.计算牌的大小

 /// <summary>
        /// 返回牌的大小
        /// </summary>
        /// <param name="brand">牌</param>
        /// <returns></returns>
        private static int BackValue(int brand)
        {
            if (brand % 13 == 0) return 13;
            return brand % 13;
        }

因为牌的大小在做累加的时候,10,J,Q,K都是按照0计算的,所以需要累加的时候,需要另一个方法

  /// <summary>
        /// 返回牌的大小 如果 大于9 则返回0
        /// </summary>
        /// <param name="brand"></param>
        /// <returns></returns>
        private static int BackZero(int brand)
        {
            if (brand % 13 > 9)
            {
                return 0;
            }
            else
            {
                return brand % 13;
            }
        }

3.返回两张牌的大牌

两张牌一定有一张大一张小,因为牛牛规则是如果牌的大小相等,则比较牌的花色。

  /// <summary>
        /// 返回两张牌较大的牌
        /// </summary>
        /// <param name="Abrand"></param>
        /// <param name="Bbrand"></param>
        /// <returns></returns>
        private static int BackCompareMax(int Abrand, int Bbrand)
        {
            if (BackValue(Abrand) > BackValue(Bbrand))
            {
                return Abrand;
            }
            else if (BackValue(Abrand) < BackValue(Bbrand))
            {
                return Bbrand;
            }
            else
            {
                if (BackColor(Abrand) > BackColor(Bbrand))
                {
                    return Abrand;
                }
                else
                {
                    return Bbrand;
                }
            }
        }

4.将用户的四张手牌按照从小到大的顺序排序

 因为权值除了比较值的大小之外还要比较牌的花色,所以不能直接用list自带的排序方法

/// <summary>
        /// 将手牌按照值和花色大小排序   从小到大
        /// </summary>
        /// <returns></returns>
        private static List<int> ListSort(List<int> lis)
        {
            for (int i = 0; i < lis.Count - 1; i++)
            {
                for (int j = i + 1; j < lis.Count; j++)
                {
                    if (lis[i] == BackCompareMax(lis[i], lis[j]))
                    {
                        lis[i] = lis[i] + lis[j];
                        lis[j] = lis[i] - lis[j];
                        lis[i] = lis[i] - lis[j];
                    }
                }
            }
            return lis;
        }

 5.获取手牌的和

 获取和的时候10,J,Q,K都是按照0算的,把手牌作和是为了后面方便判断手牌的类型

/// <summary>
        /// 获取手牌的和值(10,J,Q,K算0)
        /// </summary>
        /// <param name="lis"></param>
        /// <returns></returns>
        private static int Getsum(List<int> lis)
        {
            int sum = 0;
            for (int i = 0; i < lis.Count; i++)
            {
                sum += BackZero(lis[i]);
            }
            return sum;
        }

 6.将用户输入的牌除开之后,剩下的牌组的牌作为一个list存放起来,然后随机选取牌组里面的一张牌分配给用户,再随机选取五张牌分配给一个模拟用户,然后计算五张牌的权值。

这里我的程勋是按照牛牛类型的大小从上往下排的,因为牌型都是选取最大的牌型来比较。

这里我把每个不同的牌型都是用四位数表示。

千位数表示牌的类型,百位数是专门为牛准备的,百位数就是牛X,后面的两位数就是五张牌里面的最大牌。因为牛牛规则比较大小的时候首先是比较类型,类型大的就赢,若类型相同则比较最大牌(牛除外),如果是牛的话类型相同就比较牛的大小,如果牛相同的话就比较最大牌的值,如果值相等则比较花色。

 /// <summary>
        /// 返回五张牌中最大的牌型 9-1 为五小牛 到散牌
        /// </summary>
        /// <param name="listbrand">5张手牌</param>
        /// <returns></returns>
        private static int BackMaxType(List<int> listbrand)
        {
            //ata[10]++;
            listbrand = ListSort(listbrand);
            int sums = Getsum(listbrand);
            //判断是否是五小牛
            int sum = 0;
            if(sums==10 && BackValue(listbrand[4]) <5) return (listbrand[4] + 9000);
            //判断是否是炸弹
            if (BackValue(listbrand[0]) == BackValue(listbrand[3]))
            {
                return listbrand[3] + 8000;
            }
            if (BackValue(listbrand[1]) == BackValue(listbrand[4]))
            {
                return listbrand[4] + 8000;
            }
            //判断是否是葫芦牛  (AAABB or AABBB)
            if (BackValue(listbrand[0]) == BackValue(listbrand[2]) && BackValue(listbrand[3]) == BackValue(listbrand[4]))
            {
                return listbrand[2] + 7000;
            }
            if (BackValue(listbrand[0]) == BackValue(listbrand[1]) && BackValue(listbrand[2]) == BackValue(listbrand[4]))
            {
                return listbrand[4] + 7000;
            }
            //判断是否是五花牛
            if (BackValue(listbrand[0]) > 10) return listbrand[4] + 6000;
            //判断是否是同花牛
            if (BackColor(listbrand[0]) == BackColor(listbrand[1]) && BackColor(listbrand[1]) == BackColor(listbrand[2]) && BackColor(listbrand[2]) == BackColor(listbrand[3])
                && BackColor(listbrand[3]) == BackColor(listbrand[4]))
            {
                return listbrand[4] + 5000;
            }
            //判断是否是顺子牛
            if (BackValue(listbrand[4]) - BackValue(listbrand[3]) == 1 && BackValue(listbrand[3]) - BackValue(listbrand[2]) == 1 &&
                BackValue(listbrand[2]) - BackValue(listbrand[1]) == 1 && BackValue(listbrand[1]) - BackValue(listbrand[0]) == 1)
            {
                return listbrand[4] + 4000;
            }
            //判断是否是牛牛
            //首先五张牌相加一定是10的倍数
            if (sums % 10 == 0)
            {
                //再取其中两张相加为10的倍数则另外三张加起来也一定是10的倍数
                //即牛牛
                for (int i = 0; i < listbrand.Count; i++)
                {
                    for (int j = i + 1; j < listbrand.Count; j++)
                    {
                        if ((BackZero(listbrand[i]) + BackZero(listbrand[j])) % 10 == 0)
                        {
                            return listbrand[4] + 3000;
                        }
                    }
                }
            }
            //判断 是否有牛
            sum = sums;
            for (int i = 0; i < listbrand.Count - 1; i++)
            {
                for (int j = i + 1; j < listbrand.Count; j++)
                {
                    if ((sum - BackZero(listbrand[i]) - BackZero(listbrand[j])) % 10 == 0)
                    {
                        int va = (BackZero(listbrand[i]) + BackZero(listbrand[j])) % 10; //获取牛值

                        return listbrand[4] + 2000+ va*100;
                    }
                }
            }
            //判断散牌
            return listbrand[4] + 1000;
        }

7.根据五张牌的权值来比较大小

这里的比较很简单了,规则就想6里面说的那样比较,

 /// <summary>
        /// 找出两幅牌的大牌
        /// </summary>
        /// <param name="brand"></param>
        /// <returns></returns>
        private static int BackMaxBrand(int Abrand, int Bbrand)
        {
            if (Math.Abs(Abrand - Bbrand) > 51)//不属于同一类型
            {
                return Math.Max(Abrand, Bbrand);
            }
            else//属于同一类型
            {
                int a = Abrand;
                int b = Bbrand;
                //去除类型比较牌的大小
                if (a > 2000 && a < 3000) //判断是否是牛
                {
                    //比较牛的大小
                    if ((a - 2000) / 100 > (b - 2000) / 100)
                    {
                        return Abrand;
                    }
                    else if ((a - 2000) / 100 < (b - 2000) / 100)
                    {
                        return Bbrand;
                    }
                    else //牛相等 比较最大牌
                    {
                        a = a - 2000;
                        b = b - 2000;
                        while (a > 52 && b > 52)
                        {
                            a = a - 100;
                            b = b - 100;
                        }
                        if (a == BackCompareMax(a, b))
                        {
                            return Abrand;
                        }
                        else
                        {
                            return Bbrand;
                        }
                    }
                }
                a = Abrand;
                b = Bbrand;
                while (a > 52 && b > 52)
                {
                    a = a - 1000;
                    b = b - 1000;
                }
                if (a == BackCompareMax(a, b))
                {
                    return Abrand;
                }
                else
                {
                    return Bbrand;
                }
            }
        }

 8.计算牌的倍数,不一样的牌型有不一样的倍数,可以根据自己玩的规则修改

 /// <summary>
        /// 返回牌的倍数
        /// </summary>
        /// <param name="brand"></param>
        /// <returns></returns>
        private static int BackTimes(int brand)
        {
            if (brand < 2000)
            {
                return 1;  //无牛
            }
            else if (brand < 3000)//牛
            {
                //判断是牛7 牛8 还是 牛 9 还是普通牛
                if (brand - 2900 > 0) return 3;
                if (brand - 2800 > 0) return 2;
                if (brand - 2700 > 0) return 2;
                return 1;
            }
            else if (brand < 4000) //牛牛
            {
                return 4;
            }
            else if (brand < 5000) //顺子牛
            {
                return 5;
            }
            else if (brand < 6000)//同花牛
            {
                return 5;
            }
            else if (brand < 7000)//五花
            {
                return 5;
            }
            else if (brand < 8000)//葫芦
            {
                return 6;
            }
            else if (brand < 9000) //炸弹
            {
                return 6;
            }
            else  //五小
            {
                return 8;
            }
        }

10.给用户模拟发牌

 首先给用户模拟发一张牌,计算权值,然后判断他的类型,最后做一个类型的累加。

然后给模拟用户模拟发牌,计算权值之后与用户牌比较大小,留下大牌。

最后判断留下的大牌是否是用户的牌,如果不是则收益上减去最大牌的倍数,如果是就加最大牌的倍数

当然如果输赢都记录下次数,方便计算赢牌概率。

 /// <summary>
        /// 线程跑动
        /// </summary>
        /// <param name="num">发牌次数</param>
        /// <param name="lis">剩余牌</param>
        /// <param name="bran">用户手牌</param>
        /// <param name="user">用户数</param>
        /// <returns></returns>
        private static void ThreadBackA(int num, List<int> list, List<int> brans, int user)
        {
            Random rd = new Random();
            List<int> lis = new List<int>();
            List<int> bran = new List<int>();
            while (num > 0)
            {
                lis.Clear();
                lis.AddRange(list);
                bran.Clear();
                bran.AddRange(brans);
                int next = rd.Next(0, lis.Count);
                bran.Add(lis[next]);//给用户的手牌
                lis.RemoveAt(next);
                int YH = BackMaxType(bran);
                if (YH < 2000)
                {
                    data[1]++; //无牛
                }
                else if (YH < 3000) //牛
                {
                    data[2]++;
                }
                else if (YH < 4000) //牛牛
                {
                    data[3]++;
                }
                else if (YH < 5000)//顺子牛
                {
                    data[4]++;
                }
                else if (YH < 6000)//同花牛
                {
                    data[5]++;
                }
                else if (YH < 7000)//五花牛
                {
                    data[6]++;
                }
                else if (YH < 8000)//葫芦牛
                {
                    data[7]++;
                }
                else if (YH < 9000)//炸弹
                {
                    data[8]++;
                }
                else 
                {
                    data[9]++; //五小牛
                }
                int max = YH; //最大牌
                //int cesji = BackMaxType(bran);
                for (int i = 0; i < user; i++)
                {
                    List<int> us = new List<int>();
                    for (int j = 0; j < 5; j++)
                    {
                        next = rd.Next(0, lis.Count);
                        us.Add(lis[next]);
                        lis.RemoveAt(next);
                    }
                    max = BackMaxBrand(max, BackMaxType(us));
                }
                if (max != YH)
                {
                    data[0]++;
                    data[11] = data[11] - BackTimes(max);
                }
                else
                {
                    data[10]++;
                    data[11] = data[11] + BackTimes(max);
                }
                num--;
            }
        }

 11.异步task跑动

Tsak数组有多长就有几个异步task跑动,我这里是五个线程一共模拟发牌一百万次(个人觉得没必要这么多次,10万次就够了,最后结果相差不过零点几的误差,可有可无。)

最后返回的是一个数组,里面有输赢的次数,总收益,还有用户手牌出现类型的次数

  public static int[] GetTransport(List<int> lis,int user)
        {
            data=new int[12];
            HandAardA = lis;
            GetAllHandAard(HandAardA);
            //异步task
            Task[] tasks = new Task[5];
            for (int i = 0; i < tasks.Length; i++)
            {
                tasks[i] = new Task(() => { ThreadBackA(200000, AllHandAard, HandAardA, user); });
                tasks[i].Start();
            }
            //Thread.Sleep(1000);
            Task.WaitAll(tasks);
            return data;
        }

12.简陋界面 

大概就是这么多了。

end 

可以根据不同需求更改。需要的留下微信号

猜你喜欢

转载自blog.csdn.net/weixin_40068689/article/details/81232473