|
马上注册,结交更多好友,享用更多功能^_^
您需要 登录 才可以下载或查看,没有账号?立即注册
x
本帖最后由 zxcv8556723 于 2017-10-3 09:12 编辑
先上效果图:
算法思路:
首先对传入的牌编号数组进行排序(整牌),防止超过4张相同牌型,无效牌编号的出现
然后根据3n+2的公式对牌组进行拆解,如果拆解最后没有剩余,即满足和牌规定
如果有剩余,且不能继续拆解时,将牌组还原,向后偏移到下一个牌组下标,继续重新拆解,直到拆解到牌组的最后
3n+2公式不满足时,判断固定和牌牌型:国士无双,七对子
判断国士无双采用去对牌中的一张,然后判断剩余牌是否都是边牌杂牌,如果有不是边牌杂牌,直接不满足
判断七对子采用收集七个对子的单牌数组,然后对数组进行重复判断,不重复即满足
最后返回最终结果
枚举起手的牌,并且判断和牌,返回可以胡牌的String,没有和牌返回0
寻找和牌使用枚举,先加入一张牌,然后排序整理,最后带入和牌判断的方法判断
通过判断后记录下来,全部枚举完毕后返回记录下的数组,则是全部的和牌可能
牌组编号转换对照:
(0表示空,没有牌)
东 1
西 2
南 3
北 4
中 5
白 6
发 7
万 11-19
筒 21-29
索 31-39
源码:(该源码完全是我基于解析算法写出,没有进行进一步的优化,也没有进行所有和牌的验证,可能性太多了,仅供交流分享)package com.zdg.mahjong;
public class Rule {
public String get_HuPai(String paiZu) {
String result = null;
int[] intTemp = null;
for (int i = 1; i < 40; i++) {
if (!(i == 8 || i == 9 || i == 10 || i == 20 || i == 30)) {
intTemp = zhengLi(paiZu + "," + tr2(i));
if (isHuPai(intTemp)) {
result = result + "," + tr2(i);
}
}
}
//===================打印排序后的牌组===================
System.out.print("待和牌:");
for (int n = 0; n < intTemp.length - 1; n++) {
System.out.print(tr2(intTemp[n]));
if (n != intTemp.length - 2) {
System.out.print(",");
}
}
System.out.println();
//===================打印排序后的牌组===================
if (result == null) {
//没有找到和牌
return "0";
} else {
//去除第一个逗号
return result.substring(5);
}
}
public boolean is_HuPai(String paiZu) {
int[] intTemp = zhengLi(paiZu);
//===================打印排序后的牌组===================
System.out.print("待测牌:");
for (int n = 0; n < intTemp.length; n++) {
System.out.print(tr2(intTemp[n]));
if (n != intTemp.length - 1) {
System.out.print(",");
}
}
System.out.println();
//===================打印排序后的牌组===================
return isHuPai(intTemp);
}
private int[] zhengLi(String paiZu) {
String[] strTemp = paiZu.split(",");
int[] intTemp = new int[strTemp.length];
//防止牌组超过14张,或者传入空牌组
if (strTemp.length > 14 | strTemp.length == 0) {
return null;
} else {
//String转int,便于后续使用
for (int i = 0; i < strTemp.length; i++) {
//牌组文字编号转换
intTemp[i] = tr1(strTemp[i]);
if (intTemp[i] == 0) {
//牌组不在编号范围内
return null;
}
}
//对牌组进行排序,从小到大
intTemp = xuanzePaiXu(intTemp);
//同一种牌不超4张牌检测
for (int anIntTemp : intTemp) {
if (is4(intTemp, anIntTemp)) {
} else {
return null;
}
}
return intTemp;
}
}
private boolean isHuPai(int[] paiZu) {
/*
如果是和牌,返回true
*/
int[] tempPaiZu = paiZu;//tempPaiZu是用来拆解的牌组
/*
3n+2和牌牌型
大致过程:去除将牌;剩余牌组去除刻牌;剩余牌组去除连续牌
*/
for (int a = 0; a < tempPaiZu.length - 1; a++) {
int temp1 = tempPaiZu[a];
int temp1Type = tempPaiZu[a] / 10;
if (tempPaiZu[a + 1] == temp1 & tempPaiZu[a + 1] / 10 == temp1Type) {
//找到将牌,删除,继续删除刻牌
tempPaiZu = delArr(tempPaiZu, a);
tempPaiZu = delArr(tempPaiZu, a);
if (tempPaiZu.length % 3 != 0) {
//相公牌,永不和牌
return false;
} else {
if (tempPaiZu.length == 0) {
//和牌类型:一对牌,只有两张
return true;
} else {
//去除刻牌和链牌,最多四组,四层for循环
/*
刻 刻 刻 刻
刻 刻 刻 链
刻 刻 链 刻
刻 刻 链 链
刻 链 刻 刻
刻 链 刻 链
刻 链 链 刻
刻 链 链 链
链 刻 刻 刻
链 刻 刻 链
链 刻 链 刻
链 刻 链 链
链 链 刻 刻
链 链 刻 链
链 链 链 刻
链 链 链 链
*/
int[] backPaiZu = tempPaiZu;//备份牌组
int[] result;
for (int n1=0;n1<2;n1++){
for (int n2=0;n2<2;n2++){
for (int n3=0;n3<2;n3++){
for (int n4=0;n4<2;n4++){
result = fenjiePaiZu(fenjiePaiZu(fenjiePaiZu(fenjiePaiZu(tempPaiZu, n1), n2), n3), n4);
if (result[0] == -1) {
return true;
} else if (result[0] == 0) {
tempPaiZu= backPaiZu;
}else {
return false;
}
}
}
}
}
}
//还原将牌
tempPaiZu = addArr(tempPaiZu,temp1);
tempPaiZu = addArr(tempPaiZu,temp1);
tempPaiZu=xuanzePaiXu(tempPaiZu);
}
}
}
//3n+2匹配失败
tempPaiZu = paiZu;
/*
国士无双和牌牌型
国士无双牌组只有一个对牌,其余牌全部为除对牌以外的杂牌和三花边牌,且不能重复
*/
//去除对牌的一张单牌
if (tempPaiZu.length == 14) {
for (int a = 1; a < 14; a++) {
if (tempPaiZu[a] == tempPaiZu[a - 1]) {
if (tempPaiZu[a] / 10 == 0 | tempPaiZu[a] % 10 == 1 | tempPaiZu[a] % 10 == 9) {
tempPaiZu = delArr(tempPaiZu, a);
break;
}
}
}
}
boolean isGuoShiWuShuang = false;
if (tempPaiZu.length == 13) {
isGuoShiWuShuang = true;
for (int a = 0; a < 13; a++) {
//边牌杂牌检测
if (!(tempPaiZu[a] / 10 == 0 | tempPaiZu[a] % 10 == 1 | tempPaiZu[a] % 10 == 9)) {
isGuoShiWuShuang = false;
break;
}
}
}
if (isGuoShiWuShuang) {
return true;
}
//国士无双匹配失败,还原牌组
tempPaiZu = paiZu;
/*
七对子和牌牌型
*/
boolean isQiDuiZi = false;
if (tempPaiZu.length == 14) {
int[] tempQiDuiZi = new int[7];
for (int i = 0; i < tempQiDuiZi.length; i++) {
//判断都为对牌,牌型不重复
if ((tempPaiZu[2 * i] == tempPaiZu[2 * i + 1]) & (tempPaiZu[2 * i] / 10 == tempPaiZu[2 * i + 1] / 10)) {
tempQiDuiZi[i] = tempPaiZu[2 * i];
isQiDuiZi = true;
} else {
isQiDuiZi = false;
break;
}
}
if (isQiDuiZi) {
for (int i = 1; i < 7; i++) {
//七个对子牌组没有重复
if (tempQiDuiZi[i - 1] == tempQiDuiZi[i]) {
isQiDuiZi = false;
break;
} else {
isQiDuiZi = true;
}
}
}
if (isQiDuiZi) {
return true;
}
}
/*
//七对子匹配失败,还原数组
tempPaiZu = paiZu;
*/
//所有可能都没有匹配
return false;
}
private static int[] fenjiePaiZu(int[] tempPaiZu, int type) {
/*
type==>0删除刻牌
type==>1删除链牌
fenjiePaiZu==>返回tempPaiZu,表示删除成功
fenjiePaiZu==>返回{-1},表示和牌成功
fenjiePaiZu==>返回{0},表示分解失败
*/
if (tempPaiZu[0] == -1) {
return new int[]{-1};
}
if (tempPaiZu[0] == 0) {
return new int[]{0};
}
if (type == 0) {
for (int b = 0; b < tempPaiZu.length - 2; b++) {
int temp2 = tempPaiZu[b];
int temp2Type = tempPaiZu[b] / 10;
if (tempPaiZu[b + 1] == temp2 & tempPaiZu[b + 1] / 10 == temp2Type &
tempPaiZu[b + 2] == temp2 & tempPaiZu[b + 2] / 10 == temp2Type) {
//删除刻牌
tempPaiZu = delArr(tempPaiZu, b);
tempPaiZu = delArr(tempPaiZu, b);
tempPaiZu = delArr(tempPaiZu, b);
if (tempPaiZu.length == 0) {
//和牌成立
return new int[]{-1};
}
//删除刻牌成功
return tempPaiZu;
}
}
} else if (type == 1) {
for (int b = 0; b < tempPaiZu.length - 2; b++) {
int temp2 = tempPaiZu[b];
int temp2Type = tempPaiZu[b] / 10;
for (int c = b + 1; c < tempPaiZu.length - 1; c++) {
int temp3 = tempPaiZu[c];
int temp3Type = tempPaiZu[c] / 10;
if (temp2 == temp3 - 1 & temp2Type != 0 & temp3Type != 0) {
for (int d = c + 1; d < tempPaiZu.length; d++) {
int temp4 = tempPaiZu[d];
int temp4Type = tempPaiZu[d] / 10;
if (temp3 == temp4 - 1 & temp2 == temp3 - 1 & temp4Type != 0
) {
//删除链牌
tempPaiZu = delArr(tempPaiZu, d);
tempPaiZu = delArr(tempPaiZu, c);
tempPaiZu = delArr(tempPaiZu, b);
//拆解完毕,没有剩余和牌成功
if (tempPaiZu.length == 0) {
//和牌成立
return new int[]{-1};
}
//删除刻牌成功
return tempPaiZu;
}
}
}
}
}
}
//删除失败
return new int[]{0};
}
/*
辅助方法
*/
private static int[] xuanzePaiXu(int[] arr) {
/**
* 选择排序 <br>
* 从第2-n个元素中找出最小的元素,与第1个比较交换,<br>
* 从第3-n个元素中找出最小的元素,与第2个比较交换<br>
* O(n*n)
*/
int temp;
int loc = 0;
for (int i = 0; i < arr.length - 1; i++) {
temp = arr[i];
for (int j = i + 1; j < arr.length; j++) {
if (arr[j] < temp) {
// 从小到大
temp = arr[j];
loc = j;
}
}
if (temp != arr[i]) {
// temp是最值
arr[loc] = arr[i];
arr[i] = temp;
} // 相等不需要交换
}
return arr;
}
private static boolean is4(int[] arr, int jiance) {
/*
检测的牌超过4张返回false
*/
int count = 0;
for (int i = 0; i < arr.length; i++) {
if (jiance == arr[i]) {
count++;
}
}
if (count > 4) {
return false;
} else {
return true;
}
}
//定长数组的长度调整
private static int[] delArr(int[] arr, int loc) {
int[] temp = new int[arr.length - 1];
System.arraycopy(arr, 0, temp, 0, loc);
System.arraycopy(arr, loc + 1, temp, loc, arr.length - loc - 1);
return temp;
}
private static int[] addArr(int[] arr, int add) {
int[] temp = new int[arr.length + 1];
System.arraycopy(arr, 0, temp, 0, arr.length);
temp[temp.length - 1] = add;
return temp;
}
//牌组编号互转:String转int
private static int tr1(String a) {
switch (a) {
case "东风":
return 1;
case "西风":
return 2;
case "南风":
return 3;
case "北风":
return 4;
case "红中":
return 5;
case "白板":
return 6;
case "发财":
return 7;
case "一万":
return 11;
case "二万":
return 12;
case "三万":
return 13;
case "四万":
return 14;
case "五万":
return 15;
case "六万":
return 16;
case "七万":
return 17;
case "八万":
return 18;
case "九万":
return 19;
case "一筒":
return 21;
case "二筒":
return 22;
case "三筒":
return 23;
case "四筒":
return 24;
case "五筒":
return 25;
case "六筒":
return 26;
case "七筒":
return 27;
case "八筒":
return 28;
case "九筒":
return 29;
case "一索":
return 31;
case "二索":
return 32;
case "三索":
return 33;
case "四索":
return 34;
case "五索":
return 35;
case "六索":
return 36;
case "七索":
return 37;
case "八索":
return 38;
case "九索":
return 39;
default:
return 0;
}
}
//牌组编号互转:int转String
private static String tr2(int a) {
switch (a) {
case 1:
return "东风";
case 2:
return "西风";
case 3:
return "南风";
case 4:
return "北风";
case 5:
return "红中";
case 6:
return "白板";
case 7:
return "发财";
case 11:
return "一万";
case 12:
return "二万";
case 13:
return "三万";
case 14:
return "四万";
case 15:
return "五万";
case 16:
return "六万";
case 17:
return "七万";
case 18:
return "八万";
case 19:
return "九万";
case 21:
return "一筒";
case 22:
return "二筒";
case 23:
return "三筒";
case 24:
return "四筒";
case 25:
return "五筒";
case 26:
return "六筒";
case 27:
return "七筒";
case 28:
return "八筒";
case 29:
return "九筒";
case 31:
return "一索";
case 32:
return "二索";
case 33:
return "三索";
case 34:
return "四索";
case 35:
return "五索";
case 36:
return "六索";
case 37:
return "七索";
case 38:
return "八索";
case 39:
return "九索";
default:
return "0";
}
}
}
main方法调用示范package com.zdg.mahjong;
public class Test {
public static void main(String[] args) {
Rule mj = new Rule();
String result;
boolean isWin;
String pai;
System.out.println("国士无双14测试1");
pai = "一万,九万,一筒,九筒,一索,九索,东风,西风,南风,北风,红中,白板,发财,一万";
isWin = mj.is_HuPai(pai);
System.out.println("判断结果:" + isWin);
System.out.println();
System.out.println("10和8测试");
pai = "三万,三万,三万,四万,五万,六万,七万,八万,八万,八万";
result = mj.get_HuPai(pai);
System.out.println("和牌有:" + result);
System.out.println();
}
}
|
|