最近一个棋牌游戏项目中涉及对麻将胡牌的判定,网上搜了搜虽然看到一些算法,但是感觉都不尽如人意,一般麻将的胡牌为1对和4组三张牌的连牌,所以在网上搜到的算法往往都死死的为了这个目的来实现,而且多数没有考虑到对百塔牌的支持,下面贴上代码:
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package main;
import java.util.Arrays;
/**
* 单张麻将牌
*
* @author 奔跑 QQ:361817468
*/
public class MahjongTile
{
public static int MAHJONG_TILE_TYPE_TEN_THOUSAND = 1;
public static int MAHJONG_TILE_TYPE_PIE = 2;
public static int MAHJONG_TILE_TYPE_STRIP = 3;
public static int MAHJONG_TILE_TYPE_WIND = 4;
public static int MAHJONG_TILE_TYPE_MESS = 5;
public static int MAHJONG_TILE_TYPE_FLOWER = 6;
/**
* 标准麻将的各种牌的名称,该名称为一个三维数组,第一维为各套独立的名称
* 第二维为每套名称中的不同类别,例如万和桶九属于不同类型的牌
* 第三维维具体的名称
*/
public final static String[][][] STANDARD_MAHJONG_NAMES = {
new String[][]{
{"一万","二万","三万","四万","五万","六万","七万","八万","九万"},
{"一桶","二桶","三桶","四桶","五桶","六桶","七桶","八桶","九桶"},
{"一条","二条","三条","四条","五条","六条","七条","八条","九条"},
{"东风","南风","西风","北风"},
{"红中","发财","白板"},
{"春","夏","秋","冬","梅","兰","竹","菊"}
},
new String[][]{
{"一万","二万","三万","四万","五万","六万","七万","八万","九万"},
{"一饼","二饼","三饼","四饼","五饼","六饼","七饼","八饼","九饼"},
{"一条","二条","三条","四条","五条","六条","七条","八条","九条"},
{"东风","南风","西风","北风"},
{"红中","发财","白板"},
{"春","夏","秋","冬","梅","兰","竹","菊"}
}
};
private final int type;
private final int typeId;
private final int uniqueId;
public MahjongTile(String name) throws MahjongTileInitWrongTypeAndTypeIdException, MahjongTileInitWrongNameException
{
for (String[][] standardMahjongName : STANDARD_MAHJONG_NAMES)
{
for (int j = 0; j < standardMahjongName.length; j++)
{
for (int k = 0; k < standardMahjongName[j].length; k++)
{
if (standardMahjongName[j][k].equals(name))
{
this.type = j + 1;
this.typeId = k + 1;
this.uniqueId = computeUniqueId(type, typeId);
return;
}
}
}
}
throw new MahjongTileInitWrongNameException(name);
}
public MahjongTile(int type, int typeId) throws MahjongTileInitWrongTypeAndTypeIdException
{
this.uniqueId = computeUniqueId(type, typeId);
this.type = type;
this.typeId = typeId;
}
private void initCheck(int type, int typeId) throws MahjongTileInitWrongTypeAndTypeIdException
{
if (STANDARD_MAHJONG_NAMES[0].length < type || type < 1)
{
throw new MahjongTileInitWrongTypeAndTypeIdException(type, typeId, true);
}
else if (STANDARD_MAHJONG_NAMES[0][type - 1].length < typeId || typeId < 1)
{
throw new MahjongTileInitWrongTypeAndTypeIdException(type, typeId, false);
}
}
private int computeUniqueId(int type, int typeId) throws MahjongTileInitWrongTypeAndTypeIdException
{
initCheck(type, typeId);
if (type == MAHJONG_TILE_TYPE_TEN_THOUSAND)
{
return typeId;
}
else if (type == MAHJONG_TILE_TYPE_PIE)
{
return typeId + 9;
}
else if (type == MAHJONG_TILE_TYPE_STRIP)
{
return typeId + 18;
}
else if (type == MAHJONG_TILE_TYPE_WIND)
{
return typeId + 27;
}
else if (type == MAHJONG_TILE_TYPE_MESS)
{
return typeId + 31;
}
else
{
return typeId + 34;
}
}
public int getType()
{
return type;
}
public int getTypeId()
{
return typeId;
}
public int getUniqueId()
{
return typeId;
}
public boolean isCanTwo(MahjongTile mahjongTile)
{
if (isCanAny() || mahjongTile.isCanAny())
{
return true;
}
else
{
return uniqueId == mahjongTile.uniqueId;
}
}
private boolean isIdLink(int id1, int id2, int id3)
{
int[] ids =
{
id1, id2, id3
};
Arrays.sort(ids);
if (ids[2] - ids[1] != 1)
{
return false;
}
else if (ids[1] - ids[0] != 1)
{
return false;
}
return true;
}
public boolean isCanThree(MahjongTile mahjongTileOne, MahjongTile mahjongTileTwo)
{
if (type == mahjongTileOne.type && type == mahjongTileTwo.type)
{
if (typeId == mahjongTileOne.typeId && typeId == mahjongTileTwo.typeId)
{
return true;
}
else if (isIdLink(typeId, mahjongTileOne.typeId, mahjongTileTwo.typeId) && type != MAHJONG_TILE_TYPE_WIND && type != MAHJONG_TILE_TYPE_MESS && type != MAHJONG_TILE_TYPE_FLOWER)
{
return true;
}
}
if (isCanAny())
{
if (mahjongTileOne.isCanAny() || mahjongTileTwo.isCanAny())
{
return true;
}
else if (Math.abs(mahjongTileOne.typeId - mahjongTileTwo.typeId) <= 2 && mahjongTileOne.type == mahjongTileTwo.type)
{
return true;
}
}
else if (mahjongTileOne.isCanAny())
{
if (isCanAny() || mahjongTileTwo.isCanAny())
{
return true;
}
else if (Math.abs(typeId - mahjongTileTwo.typeId) <= 2 && type == mahjongTileTwo.type)
{
return true;
}
}
else if (mahjongTileTwo.isCanAny())
{
if (mahjongTileOne.isCanAny() || isCanAny())
{
return true;
}
else if ((Math.abs(typeId - mahjongTileOne.typeId) <= 2) && type == mahjongTileOne.type)
{
return true;
}
}
return false;
}
public boolean isCanAny()
{
if (type == 1 && typeId == 9)
{
return true;
}
return false;
}
@Override
public String toString()
{
String name = STANDARD_MAHJONG_NAMES[0][type - 1][typeId - 1];
if (isCanAny())
{
name = name + "(百搭)";
}
return name;
}
}
下面再贴两个无关紧要的异常类:
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package main;
/**
*
* @author 奔跑 QQ:361817468
*/
public class MahjongTileInitWrongNameException extends Exception
{
private final String wrongName;
public MahjongTileInitWrongNameException(String wrongName)
{
this.wrongName = wrongName;
}
public String getWrongName()
{
return wrongName;
}
public String[][][] standardMahjongNames()
{
return MahjongTile.STANDARD_MAHJONG_NAMES;
}
}
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package main;
/**
*
* @author 奔跑 QQ:361817468
*/
public class MahjongTileInitWrongTypeAndTypeIdException extends Exception
{
private final int type;
private final int typeId;
private final boolean isTypeWrong;
public MahjongTileInitWrongTypeAndTypeIdException(int type,int typeId,boolean isTypeWrong)
{
this.type = type;
this.typeId = typeId;
this.isTypeWrong = isTypeWrong;
}
public int type()
{
return type;
}
public int typeId()
{
return typeId;
}
public boolean isTypeWrong()
{
return isTypeWrong;
}
}
下面贴上核心工具类:
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package main;
/**
*
* @author 奔跑 QQ:361817468
*/
public class MahjongStaticTool
{
private static MahjongTile[] removeSomeMahjongTiles(MahjongTile[] mahjongTiles, int[] indexs)
{
int lenNew = mahjongTiles.length - indexs.length;
if (lenNew > 0)
{
MahjongTile[] mahjongTilesNew = new MahjongTile[lenNew];
int index = 0;
for (int i = 0; i < mahjongTiles.length; i++)
{
boolean isAppend = true;
for (int j = 0; j < indexs.length; j++)
{
if (i == indexs[j])
{
isAppend = false;
break;
}
}
if (isAppend)
{
mahjongTilesNew[index] = mahjongTiles[i];
index++;
}
}
return mahjongTilesNew;
}
return null;
}
//从数组长度为arrayLen的整形数组中任意抽取两个元素,把所有可能的组合的索引列成一个二位数组返回出来
private static int[][] siphonTwoIndexs(int arrayLen)
{
int len = (arrayLen * (arrayLen - 1)) / 2;
if (len > 0)
{
int[][] indexs = new int[len][2];
int index = 0;
for (int i = 0; i < arrayLen; i++)
{
for (int j = (i + 1); j < arrayLen; j++)
{
indexs[index][0] = i;
indexs[index][1] = j;
index++;
}
}
return indexs;
}
else
{
return null;
}
}
//从数组长度为arrayLen的整形数组中任意抽取两个元素,把所有可能的组合的索引列成一个二位数组返回出来
private static int[][] siphonThreeIndexs(int arrayLen)
{
int len = (arrayLen * (arrayLen - 1) * (arrayLen - 2)) / 6;
if (len > 0)
{
int[][] indexs = new int[len][3];
int index = 0;
for (int i = 0; i < arrayLen; i++)
{
for (int j = (i + 1); j < arrayLen; j++)
{
for (int k = (j + 1); k < arrayLen; k++)
{
indexs[index][0] = i;
indexs[index][1] = j;
indexs[index][2] = k;
index++;
}
}
}
return indexs;
}
else
{
return null;
}
}
private static MahjongTile[][] appendSomeMahjongTiles(MahjongTile[][] saveMahjongTileses, MahjongTile[] mahjongTiles)
{
if (saveMahjongTileses == null)
{
MahjongTile[][] mahjongTilesesReturn = new MahjongTile[1][];
mahjongTilesesReturn[0] = mahjongTiles;
return mahjongTilesesReturn;
}
else
{
MahjongTile[][] mahjongTilesesReturn = new MahjongTile[saveMahjongTileses.length + 1][];
System.arraycopy(saveMahjongTileses, 0, mahjongTilesesReturn, 0, saveMahjongTileses.length);
mahjongTilesesReturn[saveMahjongTileses.length] = mahjongTiles;
return mahjongTilesesReturn;
}
}
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);
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 (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;
}
public static void main(String[] args) throws MahjongTileInitWrongTypeAndTypeIdException, MahjongTileInitWrongNameException
{
MahjongTile[] mahjongTiles = new MahjongTile[]
{
new MahjongTile(1, 9),
new MahjongTile(1, 1),
new MahjongTile(1, 1),
new MahjongTile(1, 2),
new MahjongTile(1, 3),
new MahjongTile(1, 2),
new MahjongTile(1, 3),
new MahjongTile(1, 4),
new MahjongTile(1, 2),
new MahjongTile(1, 3),
new MahjongTile(1, 4),
new MahjongTile("九万"),
new MahjongTile(2, 7),
new MahjongTile(2, 8),
};
System.out.println("检查所有下列牌:");
for (int i = 0; i < mahjongTiles.length; i++)
{
if (i != 0)
{
System.out.print(",");
}
System.out.print(mahjongTiles[i]);
}
System.out.println("");
MahjongTile[][] mahjongTileses = tryCombination(mahjongTiles, 1, 4);
if (mahjongTileses != null)
{
System.out.println("检查通过!");
System.out.println("组合结果如下:");
int twoIndex = 1;
int threeIndex = 1;
for (MahjongTile[] mahjongTilesRow : mahjongTileses)
{
if (mahjongTilesRow.length == 2)
{
System.out.print("第"+twoIndex+"对组合:");
for (int j = 0; j < mahjongTilesRow.length; j++)
{
if (j != 0)
{
System.out.print(",");
}
System.out.print(mahjongTilesRow[j]);
}
System.out.println("");
twoIndex ++;
}
else if (mahjongTilesRow.length == 3)
{
System.out.print("第"+threeIndex+"趟组合:");
for (int j = 0; j < mahjongTilesRow.length; j++)
{
if (j != 0)
{
System.out.print(",");
}
System.out.print(mahjongTilesRow[j]);
}
System.out.println("");
threeIndex ++;
}
}
}
else
{
System.out.println("检查未通过!");
}
}
}
在该类的public static void main(String[] args)方法下有判断胡牌的实例代码。
原创文章,转载请注明原地址