c#实现麻将胡牌判定-----对子法

目录

麻将基础

在介绍代码前先介绍一下麻将的基础:
一副麻将牌里分别有4张1-9万,4张1-9饼,4张1-9索和四张东南西北白发中
万、饼、索花色三张连续的牌叫顺子,例如:1万2万3万,5索6索7索
任何一种花色三张相同的牌叫刻字,例如:4饼4饼4饼,9索9索9索
任何一种花色两张相同的牌叫对子,例如:5万5万,东风东风

文章中定义1-9万为1-9m,1-9饼为1-9p,1-9索为1-9s,东南西北白发中分别为1-7z,例如:1饼2饼3饼为1p2p3p

这里介绍下胡牌的基本公式:m顺子+n刻子+1对子(其中m+n=4)
光看这个公式可能有点抽象,化成文字理解就是:手牌中有一个对子,且顺子和刻子数量相加等于4,就是胡牌了。
大部分地区都是这样的规则:手上有13张牌+一张摸进的牌,如果这张摸进的牌和剩下的13张牌符合上述胡牌公式,就可以推牌喊一声:“胡啦!”

为什么用对子法?

讲了那么多,现在开始进入正题:用c#如何判定一套14张牌是否符合胡牌牌型呢?那么就聊一聊我的方法—对子法, 以及对子法的好处

要让电脑判定是否胡牌,首先得让电脑知道手牌中有几个顺子、刻子、对子,对应刚刚已经介绍了的胡牌公式:m顺子+n刻子+1对子(其中m+n=4),那第一步要先分解手牌,例如4m5m6m 7p8p9p 6s6s6s就分解为两个顺子加一个刻子,但如果直接正向分解(即先分解出顺子刻子,再分解对子),就会出现这样的情况:3m4m4m4m4m5m6m可以分解为一个顺子3m4m5m 一个刻子4m4m4m和一张孤立牌6m或者两个对子4m4m 4m4m和孤立牌3m 5m6m等多种牌型,这样的话某些牌型可能衍生出非常多种分解方法,不利于程序快速运算。

顺子和刻子虽然有很多种情况,但是对子只有一种情况,因此先分解出对子,再分解顺子和对子,面对的情况就会减少,以刚刚的3m4m4m4m4m5m6m为例,先分解出一对4m4m,再分解出3m4m5m,剩下4m和6m孤立,这样就能马上判断出一套手牌是否满足胡牌牌型了。一套牌中最多也只会有7个对子,但顺子和刻子是能组合出很多出来的,一副牌没有对子,也马上就能判定不能胡牌,因此先找出对子,分解的情况最少,程序运行效率也最高。

程序思路

上文提到定义1-9万为1-9m,1-9饼为1-9p,1-9索为1-9s,东南西北白发中分别为1-7z,c#程序中也是用这个代码代替对应的牌。

先说一个对子法思路例子:1m1m1m2m3m4m5m6m7m8m9m9m9m9m
c#实现麻将胡牌判定-----对子法
这个是大名鼎鼎的纯正九莲宝灯
用对子法先找出对子,再分解顺子刻子,这里只有有两个对子:一万和九万
找出一万对子的话,剩下的牌就分解为1m2m3m 4m5m6m 7m8m9m 9m9m9m,正好符合胡牌公式
找出九万对子的话,剩下的牌为1m1m1m 2m3m4m 5m6m7m和8m9m9m此时顺子加刻子的数量为3,不符合胡牌公式,但找出一万对子的话是符合的,这副牌也自然就是胡牌状态了。

再看一个例子:3m3m7p8p9p3s4s4s5s5s6s7z7z7z
c#实现麻将胡牌判定-----对子法
这一套手牌有四个可找出来的对子,先找出三万,7p8p9p和7z7z7z这两个显而易见了,那一堆索子需要稍微分析一下也可以看出3s4s5s和4s5s6s这两个顺子,如果把四索或者五索当成对子,那么3s和6s就会被孤立,把红中当对子那就更不能组成胡牌了。由此可见先找对子这种方法能大大降低判定的次数和难度,如果要算番数的话,也可以覆盖全部情况。

源代码

代码实现原理:将分解出的顺子、刻子、对子加入temp_brand_arr中,最后判断temp_brand_arr是否有null,如果有,那就是不能胡牌,如果没有那就是手牌成功分解成了四面子+一对子的形式,可以胡牌

下面是c#实现的源代码,如果有不懂的地方可以私信我或者加我QQ,联系方式在文章末尾。

using System;

namespace mahjong
{
    class Program
    {
        static void Main(string[] args)
        {
            while (true)
            {
                Console.Write("请输入牌型:");
                string input = Console.ReadLine();
                Console.WriteLine(IfRon(input));
            }
        }
        
        // 主函数
        private static string IfRon(string brand)
        {
            int count;
            int brand_j;
            int brand_k;
            int brand_l;
            string ifron = "No";
            string[] brand_arr = new string[14]; //传进的牌的数组
            string[] temp_brand_arr = new string[14]; //输出的牌的数组
            for (int i = 0; i < 27; i += 2)
            {
                brand_arr[i / 2] = brand.Substring(i, 2); //将字符串分解
            }
            for (int i = 0; i < 13; i++)
            {
                count = 0;
                //先找对子
                if (Array.IndexOf(temp_brand_arr, null) != -1)
                {
                    for (int j = 0; j < brand_arr.Length; j++)
                    {
                        temp_brand_arr[j] = brand_arr[j];
                    }
                }
                if (temp_brand_arr[i] == temp_brand_arr[i + 1])
                {
                    temp_brand_arr[i] = null;
                    temp_brand_arr[i + 1] = null;
                    for (int j = 0; j < 12; j++)
                    {
                        if (temp_brand_arr[j] == null)
                        {
                            continue;
                        }
                        for (int k = j + 1; k < 13;)
                        {
                            if (temp_brand_arr[k] == null)
                            {
                                k++;
                                continue;
                            }
                            if (temp_brand_arr[k][1] == temp_brand_arr[j][1])
                            {
                                brand_k = int.Parse("" + string.Format("{0}", temp_brand_arr[k][0]));
                                brand_j = int.Parse("" + string.Format("{0}", temp_brand_arr[j][0]));
                                if (brand_k == brand_j + 1 && temp_brand_arr[k][1] != 'z')
                                {
                                    for (int l = k + 1; l < 14;)
                                    {
                                        if (temp_brand_arr[l] == null)
                                        {
                                            l++;
                                            continue;
                                        }
                                        if (temp_brand_arr[l][1] == temp_brand_arr[k][1])
                                        {
                                            brand_l = int.Parse("" + string.Format("{0}", temp_brand_arr[l][0]));
                                            if (brand_l == brand_k + 1)
                                            {
                                                temp_brand_arr[j] = null;
                                                temp_brand_arr[k] = null;
                                                temp_brand_arr[l] = null;
                                                break;
                                            }
                                            else
                                            {
                                                l++;
                                            }
                                        }
                                        else
                                        {
                                            break;
                                        }
                                    }
                                    break;
                                }
                                else if (brand_k == brand_j)
                                {
                                    for (int l = k + 1; l < 14;)
                                    {
                                        if (temp_brand_arr[l] == null)
                                        {
                                            l++;
                                            continue;
                                        }
                                        if (temp_brand_arr[l][1] == temp_brand_arr[k][1])
                                        {
                                            brand_l = int.Parse("" + string.Format("{0}", temp_brand_arr[l][0]));
                                            if (brand_l == brand_k)
                                            {
                                                temp_brand_arr[j] = null;
                                                temp_brand_arr[k] = null;
                                                temp_brand_arr[l] = null;
                                                break;
                                            }
                                            else
                                            {
                                                l++;
                                            }
                                        }
                                        else
                                        {
                                            break;
                                        }
                                    }
                                    break;
                                }
                                else
                                {
                                    k++;
                                }
                            }
                            else
                            {
                                break;
                            }
                        }
                    }
                    for (int j = 0; j < temp_brand_arr.Length; j++)
                    {
                        if (temp_brand_arr[j] == null)
                        {
                            count++;
                        }
                    }
                    if (count == 14)
                    {
                        ifron = "Yes";
                        return ifron;
                    }
                }
            }
            return ifron;
        }
    }
}

使用示例:
最后一个例子不能胡是因为3z4z5z为西风北风白板,不能构成顺子
c#实现麻将胡牌判定-----对子法
到这里有关胡牌判定的算法介绍就结束了,如果对游戏开发感兴趣的朋友欢迎加入我们的小群或者加我QQ研究探讨!

QQ:792006305
群号:385075578

上一篇:设计模式之桥接模式


下一篇:第十一周上机练习