麻将胡牌算法
包含内容: 源码,全套工具
作者QQ549710689
视频中的源码可以点击【下载实例】进行下载, 环境配置: Eclipse+Maven+JDK1.7
环境下载地址: http://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; }
断幺九
/** * 断幺九 * @param mahjongTiles * @return */ public static boolean duanYaoJiu(MahjongTile[] mahjongTiles, MahjongTile[][] mahjongTileses){ boolean hasWind = false; boolean hasMess = false; boolean has1 = false; boolean has9 = false; for(MahjongTile tile : mahjongTiles){ if(tile.isWind()){ hasWind = true; return false; }else if(tile.isMess()){ hasMess = true; return false; } if((tile.isWan() || tile.isPie() || tile.isStrip()) && tile.getTypeId() == 1){ has1 = true; return false; } if((tile.isWan() || tile.isPie() || tile.isStrip()) && tile.getTypeId() == 9){ has9 = true; return false; } } if(hasWind == false && hasMess == false){ if(has1 == false && has9 == false){ return true; } } return false; }
平胡(规则最为复杂)
/** * 平胡 * @param mahjongTiles * @return */ public static boolean pingHu(MahjongTile[] mahjongTiles, MahjongTile[][] mahjongTileses){ boolean hasWind = false; //是否风牌 boolean hasMess = false; // 是否将牌 boolean hasThreeSame = false; //是否是刻字 boolean hasKarTile = false; //是否卡单张 boolean hasEdgeThreeAndSeven = false; //是否边三边七 boolean hasZanTou = true; //是否有赞头 MahjongTile lastTile = null; //判断有没有风将牌 for(MahjongTile tile : mahjongTiles){ if(tile.isWind()){ hasWind = true; return false; }else if(tile.isMess()){ hasMess = true; return false; } if(tile.isLast()){ lastTile = tile; } } //判断是否有三张一样的(带赖子) //MahjongTile[][] mahjongTileses = tryCombination(mahjongTiles, 1, 4); int nThree = 0; int firstGroupAvg = 0; int secondGroupAvg = 0; int thirdGroupAvg = 0; Map<Integer, List<MahjongTile>> mapMajong = new HashMap<Integer, List<MahjongTile>>(); for (MahjongTile[] mahjongTilesRow : mahjongTileses){ if (mahjongTilesRow.length == 3) { int firstTileUnniqueId = mahjongTilesRow[0].getUniqueId(); int secondTileUnniqueId = mahjongTilesRow[1].getUniqueId(); int thirdTileUnniqueId = mahjongTilesRow[2].getUniqueId(); boolean bSame = false; if((firstTileUnniqueId == secondTileUnniqueId) && (firstTileUnniqueId == thirdTileUnniqueId) && (secondTileUnniqueId == thirdTileUnniqueId)){ nThree++; bSame = true; List<MahjongTile> lstMahjongTile = new ArrayList<MahjongTile>(); lstMahjongTile.add(mahjongTilesRow[0]); lstMahjongTile.add(mahjongTilesRow[1]); lstMahjongTile.add(mahjongTilesRow[2]); mapMajong.put(firstTileUnniqueId, lstMahjongTile); } if(bSame){ if(nThree == 1){ firstGroupAvg = (firstTileUnniqueId + secondTileUnniqueId + thirdTileUnniqueId) / 3; }else if(nThree == 2){ secondGroupAvg = (firstTileUnniqueId + secondTileUnniqueId + thirdTileUnniqueId) / 3; }else if (nThree == 3){ thirdGroupAvg = (firstTileUnniqueId + secondTileUnniqueId + thirdTileUnniqueId) / 3; } } } } if(nThree == 1 || nThree == 2 || nThree == 4){ return false; }else if (nThree == 3){ if(!MahjongTile.isIdLink(firstGroupAvg, secondGroupAvg, thirdGroupAvg)){ return false; }else{ //重新组合牌 int localIndex = 0; for (MahjongTile[] mahjongTilesRow : mahjongTileses){ if (mahjongTilesRow.length == 3){ int firstTileUnniqueId = mahjongTilesRow[0].getUniqueId(); int secondTileUnniqueId = mahjongTilesRow[1].getUniqueId(); int thirdTileUnniqueId = mahjongTilesRow[2].getUniqueId(); if((firstTileUnniqueId == secondTileUnniqueId) && (firstTileUnniqueId == thirdTileUnniqueId) && (secondTileUnniqueId == thirdTileUnniqueId)){ mahjongTilesRow[0] = mapMajong.get(firstGroupAvg).get(localIndex); mahjongTilesRow[1] = mapMajong.get(secondGroupAvg).get(localIndex); mahjongTilesRow[2] = mapMajong.get(thirdGroupAvg).get(localIndex); localIndex++; } } } } } //判断是否卡单张, 手上有4和6,抓到5就是卡单张(因为这种牌型只可能是顺子) for (MahjongTile[] mahjongTilesRow : mahjongTileses) { int nTotal = 0; if (mahjongTilesRow.length == 3) { int firstTileUnniqueId = mahjongTilesRow[0].getUniqueId(); int secondTileUnniqueId = mahjongTilesRow[1].getUniqueId(); int thirdTileUnniqueId = mahjongTilesRow[2].getUniqueId(); boolean bPossible = false; //表明最后一张是中间值 if(mahjongTilesRow[0].isLast()){ if((secondTileUnniqueId + thirdTileUnniqueId)/2 == firstTileUnniqueId){ nTotal = firstTileUnniqueId + secondTileUnniqueId + thirdTileUnniqueId; //return false; bPossible = true; } } if(mahjongTilesRow[1].isLast()){ if((firstTileUnniqueId + thirdTileUnniqueId)/2 == secondTileUnniqueId){ nTotal = firstTileUnniqueId + secondTileUnniqueId + thirdTileUnniqueId; //return false; bPossible = true; } } if(mahjongTilesRow[2].isLast()){ if((firstTileUnniqueId + secondTileUnniqueId)/2 == thirdTileUnniqueId){ nTotal = firstTileUnniqueId + secondTileUnniqueId + thirdTileUnniqueId; //return false; bPossible = true; } } //可能出现卡单张 if(bPossible){ boolean isReplaceable = false; int nSame = 0; for(MahjongTile[] TilesIn : mahjongTileses){ if (TilesIn.length == 3){ int nInTotal = TilesIn[0].getUniqueId() + TilesIn[1].getUniqueId() + TilesIn[2].getUniqueId(); //与卡单张相同牌型 if(nInTotal == nTotal){ nSame++; if(nSame >= 2){ } }else{ //与卡单张表不同的牌,但跟最后一张一样 if((TilesIn[0].getUniqueId() == lastTile.getUniqueId()) ||(TilesIn[1].getUniqueId() == lastTile.getUniqueId()) || (TilesIn[2].getUniqueId() == lastTile.getUniqueId())){ //要求同时不能再边三或者边七 /* if(lastTile.getTypeId() != 7 || lastTile.getTypeId() != 3){ isReplaceable = true; }*/ isReplaceable = true; } } } } if(isReplaceable == false){ return false; } } } } //是否边三边七 for (MahjongTile[] mahjongTilesRow : mahjongTileses) { if (mahjongTilesRow.length == 3) { int firstTypeId = mahjongTilesRow[0].getTypeId(); int secondTypeId = mahjongTilesRow[1].getTypeId(); int thirdTypeId = mahjongTilesRow[2].getTypeId(); int sumTypeId = firstTypeId + secondTypeId + thirdTypeId; //判断是 123, 789 int nTotal = 0; if(sumTypeId == 6 || sumTypeId == 24){ int firstTileUnniqueId = mahjongTilesRow[0].getUniqueId(); int secondTileUnniqueId = mahjongTilesRow[1].getUniqueId(); int thirdTileUnniqueId = mahjongTilesRow[2].getUniqueId(); boolean bPossible = false; if((mahjongTilesRow[0].isLast()) && (mahjongTilesRow[0].getTypeId()==3 || mahjongTilesRow[0].getTypeId()==7)){ nTotal = firstTileUnniqueId + secondTileUnniqueId + thirdTileUnniqueId; bPossible = true; //return false; } if((mahjongTilesRow[1].isLast()) && (mahjongTilesRow[1].getTypeId()==3 || mahjongTilesRow[1].getTypeId()==7)){ nTotal = firstTileUnniqueId + secondTileUnniqueId + thirdTileUnniqueId; bPossible = true; //return false; } if((mahjongTilesRow[2].isLast()) && (mahjongTilesRow[2].getTypeId()==3 || mahjongTilesRow[2].getTypeId()==7)){ nTotal = firstTileUnniqueId + secondTileUnniqueId + thirdTileUnniqueId; bPossible = true; //return false; } //可能出现边三边七 if(bPossible){ boolean isReplaceable = false; for(MahjongTile[] TilesIn : mahjongTileses){ if (TilesIn.length == 3){ int nInTotal = TilesIn[0].getUniqueId() + TilesIn[1].getUniqueId() + TilesIn[2].getUniqueId(); //与卡单张相同牌型 if(nInTotal == nTotal){ }else{ //与卡单张表不同的牌,但跟最后一张一样 if((TilesIn[0].getUniqueId() == lastTile.getUniqueId()) ||(TilesIn[1].getUniqueId() == lastTile.getUniqueId()) || (TilesIn[2].getUniqueId() == lastTile.getUniqueId())){ if(nInTotal/3 != lastTile.getUniqueId()){ isReplaceable = true; } //isReplaceable = true; } } } } if(isReplaceable == false){ return false; } } } } } //是否赞头 for (MahjongTile[] mahjongTilesRow : mahjongTileses) { if (mahjongTilesRow.length == 2) { if(mahjongTilesRow[0].isLast() || mahjongTilesRow[1].isLast()){ for (MahjongTile[] mahjongTilesInner : mahjongTileses) { if (mahjongTilesInner.length == 3) { int firstTileUnniqueId = mahjongTilesInner[0].getUniqueId(); int secondTileUnniqueId = mahjongTilesInner[1].getUniqueId(); int thirdTileUnniqueId = mahjongTilesInner[2].getUniqueId(); //找到一对,则不是赞头, 可以用这个变量替换出来 int averate = (firstTileUnniqueId + secondTileUnniqueId + thirdTileUnniqueId)/3; if(((averate + 1) == mahjongTilesRow[1].getUniqueId()) || ((averate - 1) == mahjongTilesRow[1].getUniqueId()) ){ hasZanTou = false; break; } } } }else{ hasZanTou = false; break; } } } if(hasZanTou == true){ return false; } //没有 东南西北中发白 if(hasWind || hasMess || hasThreeSame || hasKarTile || hasEdgeThreeAndSeven || hasZanTou){ return false; } return true; }