|
马上注册,结交更多好友,享用更多功能^_^
您需要 登录 才可以下载或查看,没有账号?立即注册
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();
- }
- }
复制代码 |
|