麻将胡牌算法

时间:2024-04-07 13:31:26

本文实现了一个胡牌算法,目前支持视频中讲解的三种番型,更多番型或者其它番型大家可以结合当地的麻将进行二次开发,项目中不仅有后台胡牌算法,而且还配备了前台页面,开发人员可以在前台中直接输入麻将对应的数据,操作十分方便。具体细节请观看视频讲解

视频和demo下载地址:wisdomdd.cn

视频中的源码可以点击【下载实例】进行下载, 环境配置: Eclipse+Maven+JDK1.7

环境下载地址: http://www.wisdomdd.cn/Wisdom/category/getAllCategoryFile.htm


 胡牌前提:  共14张牌,可以凑成4个三张,1个对子

            三张牌: 3个一样的牌(3个1万)叫刻子,或者 3个顺序牌(1万,2万,3万)叫顺子

            对子:   二张一样的牌(2个1万)

            

 番型计算:   14张牌首先要满足胡牌的前提条件,然后再根据各个地方的胡牌规则进行番型计算,如果没有番型,则不能胡牌



 下面以下图对应的番型规则进行算法介绍

麻将胡牌算法



胡牌前提对应的算法:

    类: MahjongStaticTool

     参数: MahjongTile[] mahjongTiles 麻将对应的14张牌

     参数: twoNum 对子的个数

     参数: threeNum  壳子+顺子的  数目

  我们以 threeNum=4, twoNum=1 即4个三张牌,1个二张牌为例

   tryCombination以递归方法来获取牌型数据, 最后将牌型数据存放在MahjongTile[][]二维数组中

   MahjongTile[][]共5组数据, 例: 第一组有3张牌, 第二组有3张牌,第三组有3张牌,第四组有2张牌,第五组有3张牌

public static MahjongTile[][] tryCombination(MahjongTile[] mahjongTiles, int twoNum, int threeNum)  
    {  
        return MahjongStaticTool.tryCombination(mahjongTiles, twoNum, threeNum, null);  
    }  
   
    private static MahjongTile[][] tryCombination(MahjongTile[] mahjongTiles, int twoNum, int threeNum, MahjongTile[][] saveMahjongTileses)  
    {  
        if (mahjongTiles == null)  
        {  
            if (twoNum == 0 && threeNum == 0)  
            {  
                return saveMahjongTileses;  
            }  
            else  
            {  
                return null;  
            }  
        }  
        if (mahjongTiles.length == ((twoNum * 2) + (threeNum * 3)))  
        {  
            if (threeNum > 0)  
            {  
                //int[][] indexs = siphonThreeIndexs(mahjongTiles.length);  
                int[][] indexs = getThreeSiphonByLength(mahjongTiles.length);
                if (indexs == null)  
                {  
                    return null;  
                }  
                   
                for (int[] index : indexs)  
                {  
                    if (mahjongTiles[index[0]].isCanThree(mahjongTiles[index[1]], mahjongTiles[index[2]]))  
                    {  
                        MahjongTile[][] saveMahjongTilesesCache = appendSomeMahjongTiles(saveMahjongTileses, new MahjongTile[]{mahjongTiles[index[0]], mahjongTiles[index[1]], mahjongTiles[index[2]]});  
                        MahjongTile[][] mahjongTilesesReturn = MahjongStaticTool.tryCombination(removeSomeMahjongTiles(mahjongTiles, new int[]{index[0], index[1], index[2]}), twoNum, threeNum - 1, saveMahjongTilesesCache);  
                        if (mahjongTilesesReturn != null)  
                        {  
                            return mahjongTilesesReturn;  
                        }  
                    }  
                }  
            }  
            else if (twoNum > 0)  
            {  
                //int[][] indexs = siphonTwoIndexs(mahjongTiles.length);
                if(mahjongTiles.length != 2){
                    System.out.println("aaaaaaaaaaaaaaaa");
                }
                int[][] indexs = twoSiphon;
                if (indexs == null)  
                {  
   
                    return null;  
                }  
                for (int[] index : indexs)  
                {  
                    if (mahjongTiles[index[0]].isCanTwo(mahjongTiles[index[1]]))  
                    {  
                        MahjongTile[][] saveMahjongTilesesCache = appendSomeMahjongTiles(saveMahjongTileses, new MahjongTile[]{mahjongTiles[index[0]], mahjongTiles[index[1]]});  
                        MahjongTile[][] mahjongTilesesReturn = MahjongStaticTool.tryCombination(removeSomeMahjongTiles(mahjongTiles, new int[]{index[0], index[1]}), twoNum - 1, threeNum, saveMahjongTilesesCache);  
                        if (mahjongTilesesReturn != null)  
                        {  
                            return mahjongTilesesReturn;  
                        }  
                    }  
                }  
            }  
            else  
            {  
                return saveMahjongTileses;  
            }  
        }  
        return null;  
    }

番型对应的算法:

    满足基本胡牌要求后,就可以根据番型来计算, 下在举例本人的番型计算规则

     由于番型可以叠加,所以需要核对每一种番型算法,满足一种则加一番

1. 麻将手牌13张, 最后一张为自摸牌,进行标记,表明为最后一张
2. 将麻将牌进行分组, 万牌放一组, 桶牌放一组, 条牌一组, 东风牌一组,南风牌一组, 西风牌一组, 北风牌一组, 红中一组, 发财一组, 白皮一组,  赖子一组
   赖子牌只有一组,其它牌10
3.1. 赖子牌组没有牌,如果没牌,则将其它牌组进行组合,看14能否组成 4个三张牌, 1个二张牌, 如果能组成则存在胡的可能性,记录下4个三张和对子
3.2  赖子牌组有牌,则将赖子牌分别投入到其它牌组,每张牌投放牌组的可能是10(10个牌组), 每张牌在每个组按照牌组的种类进行循环遍历,如赖子在万牌组,
    则进行1-9的循环遍历, 若赖子在北牌组,只能变成相应的风, 赖子变成相应的牌时,如果赖子是最后一张,则变成的牌也要标记为最后一张
     赖子牌型确定后,看14张牌能否组成 4个三张牌, 1个二张牌, 如果能组成则存在胡的可能性, 记录下4个三张和对子
 
4. 如果上述存在胡牌的可能性,则进行番型比对
   由于断一门,断幺九比较简单,省略不讨论, 由于平胡复杂度比较高,实现起来难度大, 下面重点讨论平胡
    
 
    4.1 14张牌型确定后, 循环遍历14张牌,如果有风将牌,则肯定不是平胡, 循环过程中,找出最后一张牌
    4.2 判断4个三张牌中是否有坎子的情况,如果是,则找出有几个坎子,如果有三个坎子, 则判断这三个坎子能否组成三个顺子(如果能则执行规则4.3), 如果不能,则肯定不是平胡,不用继续下面的规则;
        如果有1,2,4个坎子,则肯定不是平胡,不用走下面的规则,    如果没有一个坎子,则执行规则4.3
    4.3 判断四组顺子牌中含有最后一张牌的组(记录A组), 如果最后一张牌在中间,则可能是卡单张的可能
        如果其它牌组跟A组牌不相同,但包含跟最后一张牌一样的牌则继续看规则4.4
    4.4 判断四组顺子牌中含有最后一张牌的组(记录A组), 如果最后一张牌是3或者7, 则可能是边三或者边七
        如果其它牌组跟A组牌不相同,但包含跟最后一张牌一样的牌并且此组牌的平均值不为最后一张的值,则执行规则4.5
    4.5 找出对子牌,判断对子牌中是否有最后一张,如果有最后一张,则判断其它四个牌组是否有跟最后一张牌一样的牌,
        如果有,则继续执行; 否则赞头
          
    如果能满足4.1并且4.2并且4.3并且4.4并且4.5  则为平胡

断一门

/**
     * 断一门
     * @param mahjongTiles
     * @return
     */
    public static boolean duanYiMen(MahjongTile[] mahjongTiles, MahjongTile[][] mahjongTileses){
         
        boolean hasWan   = false;
        boolean hasPie   = false;
        boolean hasStrip = false;
        boolean hasWind  = false;
        boolean hasMess  = false;
         
        for(MahjongTile tile : mahjongTiles){
            if(tile.isWan()){
                hasWan = true;
            }else if(tile.isPie()){
                hasPie = true;
            }else if(tile.isStrip()){
                hasStrip = true;
            }else if(tile.isWind()){
                hasWind = true;
                return false;
            }else if(tile.isMess()){
                hasMess = true;
                return false;
            }
        }
         
        if(hasWind == false && hasMess == false){
            int n = 0;
            if(hasWan == true){
                n++;
            }
            if(hasPie == true){
                n++;
            }
            if(hasStrip == true){
                n++;
            }
             
            if(n == 2){
                return true;
            }
        }
        return false;
    }