日麻十七步:关于如何从34张牌中取13张牌组成最大番数听牌

喜欢玩日本麻将的雀友都知道,麻将一般是3-4人玩的,但是如何连3个人都凑不齐呢?那么,2人玩法就应运而生了!具体玩法请移步至萌娘百科十七步查阅…
logo

关于如何从34张牌中取13张牌组成最大番数听牌

下面开始进入正题,正常情况下,如果要从34张牌中取13张,算了下排列组合,要进行:34!/13!/21!=927983760次运算,太恐怖了,一般程序猿应该不会干这种事吧。
没办法了,只好逐个拆解分析,只提取有效的结果进行比对…

一、听牌公式分析

日本麻将一般有的听牌模式有三大种类:
1、国士无双:十三张幺九牌+任意一种幺九牌
这个就简单了,一共14种情况,傻瓜式判断→_→

2、七对子:七种不同的对子
要七种,而且是不同的对子,因为是要听牌,所以组合是六种对子+第七种的一张牌,这种情况下,肯定要先将所有含有2张以上的牌记录下来,如果大于等于六种对子就进去分析,每次取其中6组对子,此时最大的判断次数是:17!/6!/11!=12376,勉强还能接受吧,实际情况下都是个位数的对子,所以应该会判断得更少。

3、一般情况:4组牌加1对
4组牌,可以是顺子,可以是刻子,,也是因为要听牌,所以最终的组合形式有:
4组顺子、刻子+1张牌:
(*32!/4!/28!+32!/3!/29!*11+32!/2!/30!11!/2!/9!+3211!/3!/8!+11!/4!/7!)34=4195940
3组顺子、刻子+2组对子:
(32!/3!/29!+32!/2!/30!11+3211!/2!/9!+11!/3!/8!)
(17!/2!/15!)=1678376
3组顺子、刻子+1组对子+1组缺一张的顺子:
(32!/3!/29!+32!/2!/30!11+3211!/2!/9!+11!/3!/8!)1733=6923301
(其中:顺子最大组合有:32,刻子最大组合有:11,对子的最大组合有:17,缺一张顺子的最大组合有:33)

综上所述,最大的判断次数为:12810007,好吧,还是很多,但这只是个极限数,大多情况下顺子+刻子的组合数量是不会超过个位数的,所以判断次数会远远小于这个数,安心地实践吧。

二、代码分析

首先,要进行组合,就要找出组合的元素先。

记录的元素有:所有存在的牌及对应的数量,所有的对子、刻子、顺子、缺一张的顺子

	/**
	 * 十七步算法:根据34张牌挑选出13张最优解牌谱
	 * --限定条件(确定:门清,可能:立直、一发、河底,无赤宝牌)
	 */
	public static boolean calcGame34ToResultScores(int calcWay,
			Game34Result game34Result, List<MjCard> cardNums,
			boolean isDealer, boolean isLiZhi, boolean isYiFa, boolean isFinalPick,
			MjWind groundWind, MjWind selfWind, 
			int lizhiCount, int roundCount,
			List<MjCard> indicators, List<MjCard> indicatorsIns) {
		// 用于计算的分值的元素
		int env = convert2JpnEnvironment(isLiZhi, false, false, isFinalPick,
				false, isYiFa, false, false, groundWind, selfWind, true); // 环境变量
		// 计算宝牌
		int[] doras = convertCard2TileDoras(indicators);
		// 计算里宝牌
		int[] doraIns = null;
		if (isLiZhi) {
			doraIns = convertCard2TileDoras(indicatorsIns);
		} else {
			doraIns = new int[indicatorsIns.size()];
			Arrays.fill(doraIns, -1);
		}
		game34Result.init(env, calcWay, doras, doraIns);
		// 记录所有牌,并分类
		Tile[] tileMap = new Tile[34]; // 记录所有存在的牌
		int[] tileCounts = new int[34];// 记录所有存在的牌对应的数量
        Arrays.fill(tileCounts, 0);
		List<Integer> pairTiles = new ArrayList<Integer>(); // 记录所有的对子
		List<Integer> pungTiles = new ArrayList<Integer>(); // 记录所有的刻子		
		long byteTile = 0; // 用二进制记录存在的牌
		for (MjCard card : cardNums) {
			Tile tile = convertCard2JpnTile(card, selfWind, false);
			tile.calDora(doras);
			tile.calInDora(doraIns);
			tileMap[tile.Value()] = tile;
			tileCounts[tile.Value()]++;
			byteTile |= (1L << tile.Value());
			if (tileCounts[tile.Value()] == 2) { // 对子
				pairTiles.add(tile.Value());
			} else if (tileCounts[tile.Value()] == 3) { // 刻子
				pungTiles.add(tile.Value());
			}
		}
		// 记录所有可用的点炮牌
		boolean[] canBombs = new boolean[34];
		Tile[] bombTiles = new Tile[34];
		Arrays.fill(canBombs, false);
		List<Integer> tileKeys = new ArrayList<Integer>(); // 记录所有存在的牌的索引
		List<Integer> junkoTiles = new ArrayList<Integer>(); // 记录所有的顺子
		List<Integer> junkoQbTiles = new ArrayList<Integer>(); // 记录所有缺边章的顺子,形如23缺1或4
		List<Integer> junkoQzTiles = new ArrayList<Integer>(); // 记录所有缺中章的顺子,形如13缺2
		long junkoByte = 0x3fe030180L; // 只含有8万、9万、8饼、9饼、8索、9索、东、南、西、北、白、发、中
		long junkoQbByte = 0x3fc020100L; // 只含有9万、9饼、9索、东、南、西、北、白、发、中
//		long junkoQzByte = junkoByte; // 只含有8万、9万、8饼、9饼、8索、9索、东、南、西、北、白、发、中
		for (int i = 0; i < tileCounts.length; i++) {
			if (tileCounts[i] < 4) {
				canBombs[i] = true;
				bombTiles[i] = new Tile(i, false, ramdomWind(selfWind));
				bombTiles[i].calDora(doras);
				bombTiles[i].calInDora(doraIns);
			}
			if (tileCounts[i] > 0) {
				tileKeys.add(i);
			}
			long tmpByte = 1L << i;
			if ((tmpByte & junkoByte) != tmpByte) {
				switch (tileCounts[i]) {
				case 4:
					if (tileCounts[i + 1] > 3 && tileCounts[i + 2] > 3) junkoTiles.add(i);
				case 3:
					if (tileCounts[i + 1] > 2 && tileCounts[i + 2] > 2) junkoTiles.add(i);
				case 2:
					if (tileCounts[i + 1] > 1 && tileCounts[i + 2] > 1) junkoTiles.add(i);
				case 1:
					if(tileCounts[i + 1] > 0 && tileCounts[i + 2] > 0) junkoTiles.add(i);
					if (tileCounts[i + 2] > 0) junkoQzTiles.add(i);
					break;
				case 0:
				default:
					break;
				}
			}
			if ((tmpByte & junkoQbByte) != tmpByte) {
				if (tileCounts[i] > 0 && tileCounts[i + 1] > 0) junkoQbTiles.add(i);
			}
		}
		// 判断特殊牌型(国士无双、七对子)
		// 1.国士无双
		calcGame34ForGuoShiWuShuang(game34Result, byteTile, tileMap, tileCounts, canBombs, bombTiles);
		// 2.七对子
		calcGame34ForQiDuiZi(game34Result, pairTiles, tileMap, tileKeys, canBombs, bombTiles);
		// 3.检测所有顺子、刻子的排列组合(单骑、听嵌章、双面听)
		calcGame34ForGroup(game34Result, junkoTiles, pungTiles, tileCounts, tileMap, tileKeys, 
				canBombs, bombTiles, pairTiles, junkoQbTiles, junkoQzTiles);
		return true;
	}

其中,Game34Result用于记录和保存结果

	public static class Game34Result {

		public int env; // 环境变量
		public int calcWay; // 计量方式:0:分值优先,1:番数优先
		public List<Tile> addedTiles; // 和牌(最高分时)
		public List<List<Tile>> handTiles; // 门清牌(最高分时)
		public List<Score> scores; // 存在多种结果时的分值
		public List<long[]> cmpNums; // 用于快速比较的数字,将14张牌转换成2个long数字
		public int[] doras; // 宝牌组
		public int[] doraIns; // 里宝牌组
		public Score maxScore; // 最大结果
		
		public int level; // 显示等级:1、一般情况;2、不听宝牌
		public Score level1Score; // 当显示2级结果时,不能超过的1级结果的上限
		public boolean isInit = false;
	}
接下来,就要进行分类判断

这里为了快速进行比对,使用一个long表示的二进制数,麻将有34种,所以要64位的数据
1、国士无双

	private static void calcGame34ForGuoShiWuShuang(Game34Result game34Result,
			long byteTile, Tile[] tileMap, int[] tileCounts,
			boolean[] canBombs, Tile[] bombTiles) {
		long gswsByte = 0x3fc060301L; // 只含有1万、9万、1饼、9饼、1索、9索、东、南、西、北、白、发、中
		if ((gswsByte & byteTile) == gswsByte) { // 国士无双十三面
			List<Tile> tmpHandTiles = new ArrayList<Tile>();
			for (int yaojiu : JpnSetting.YaoJiu) { // 首先添加13张幺九牌
				tmpHandTiles.add(tileMap[yaojiu]);
			}			
			for (int yaojiu : JpnSetting.YaoJiu) { // 再遍历所有可用的点炮牌
				if (!canBombs[yaojiu]) continue;
				Tile tmpAddedTile = bombTiles[yaojiu];
				// 取分值最大的结果
				game34Result.LogMaxScore(tmpAddedTile, tmpHandTiles, null);
			}
		} else { // 国士无双缺一张
			for (int yaojiu : JpnSetting.YaoJiu) {
				long tmpGswsByte = gswsByte ^ (1L << yaojiu);
				if ((tmpGswsByte & byteTile) == tmpGswsByte) {
					// 判断是否存在2只及以上的幺九牌
					boolean isExistYaojiuAbove2 = false;
					List<Tile> yaojiuAbove2 = new ArrayList<Tile>();
					for (int yaojiu2 : JpnSetting.YaoJiu) {
						if (tileCounts[yaojiu2] >= 2) {
							isExistYaojiuAbove2 = true;
							yaojiuAbove2.add(tileMap[yaojiu2]);
						}
					}
					if (!isExistYaojiuAbove2) break; // 不存在则退出
					List<Tile> tmpHandTiles = new ArrayList<Tile>();
					for (int yaojiu2 : JpnSetting.YaoJiu) { 
						if (tileMap[yaojiu2] == null) continue; // 无此牌,则跳过
						long keyByte = 1L << yaojiu2;
						if ((keyByte & tmpGswsByte) != keyByte) continue; // 如果不是缺一张的幺九牌,则跳过
						tmpHandTiles.add(tileMap[yaojiu2]);
					}
					Tile tmpAddedTile = bombTiles[yaojiu]; // 点炮牌唯一
					for (Tile yaojiuTile : yaojiuAbove2) { // 遍历存在2张的幺九牌
						List<Tile> tmpHandTiles2 = new ArrayList<Tile>(tmpHandTiles);
						tmpHandTiles2.add(yaojiuTile);
						// 取分值最大的结果
						game34Result.LogMaxScore(tmpAddedTile, tmpHandTiles2, null);
					}
					break; // 符合其中一种,另外8种情况可以直接排除
				}
			}
		}
	}

2、七对子

	private static void calcGame34ForQiDuiZi(Game34Result game34Result,
			List<Integer> pairTiles, Tile[] tileMap, List<Integer> tileKeys,
			boolean[] canBombs, Tile[] bombTiles) {
		if (pairTiles.size() >= 6) {
			for (int i = 0; i < (1 << pairTiles.size()); i++) { // 2的n次方则为所有组合的次数
				int countOfbyte1 = CountOfByte1(i);
				if (countOfbyte1 == 6) { // 取所有6组不同对子的组合
					long tmpByte = 0;
					List<Tile> tmpHandTiles = new ArrayList<Tile>();
					for (int j = 0; j < pairTiles.size(); j++) {
			            if (((i >> j) & 1) == 1) { // 二进制位为1则加入
			            	int value = pairTiles.get(j);
			            	tmpByte |= (1L << value);
			            	tmpHandTiles.add(tileMap[value]);
			            	tmpHandTiles.add(tileMap[value]);
			            }
					}
					// 遍历所有可用的点炮牌
					for (int key : tileKeys) { 
						if (!canBombs[key]) continue; // 如果有4张相同的牌,则跳过
						long keyByte = 1L << key;
						if ((keyByte & tmpByte) == keyByte) continue; // 如果是6对组合中的值,则跳过
						List<Tile> tmpKeyHandTiles = new ArrayList<Tile>(tmpHandTiles);
						tmpKeyHandTiles.add(tileMap[key]);
						Tile tmpAddedTile = bombTiles[key];
						// 取分值最大的结果
						game34Result.LogMaxScore(tmpAddedTile, tmpKeyHandTiles, null);
					}
				}
			}			
		}
	}

3、一般情况

	private static void calcGame34ForGroup(Game34Result game34Result,
			List<Integer> junkoTiles, List<Integer> pungTiles,
			int[] tileCounts, Tile[] tileMap, List<Integer> tileKeys,
			boolean[] canBombs, Tile[] bombTiles,
			List<Integer> pairTiles, List<Integer> junkoQbTiles, List<Integer> junkoQzTiles) {
		int junkoAndPungCount = junkoTiles.size() + pungTiles.size();
		if (junkoAndPungCount >= 3) {
			for (int i = 0; i < (1 << junkoAndPungCount); i++) { // 2的n次方则为所有组合的次数
				int countOfbyte1 = CountOfByte1(i);
				if (countOfbyte1 == 4 || countOfbyte1 == 3) { // 单骑找出4种组合, 听嵌章、双面听则找出3种
					boolean isOverload = false;
					int[] tmpTileCounts = tileCounts.clone();
					List<Tile> tmpHandTiles = new ArrayList<Tile>();
					List<Group> allGroups = new ArrayList<Group>();
					for (int j = 0; j < pungTiles.size(); j++) { // 从所有刻子中取组合
			            if (((i >> j) & 1) == 1) { // 二进制位为1则加入
			            	int value = pungTiles.get(j);
			            	if (tmpTileCounts[value] < 3) {isOverload = true; break;}
			            	tmpTileCounts[value] -= 3;
			            	tmpHandTiles.add(tileMap[value]);
			            	tmpHandTiles.add(tileMap[value]);
			            	tmpHandTiles.add(tileMap[value]);
			            	allGroups.add(new Groups.Pung(value, GroupState.MenQing));
			            }
					}
					if (isOverload) continue;
					for (int j = 0; j < junkoTiles.size(); j++) { // 从所有顺子中取组合
			            if (((i >> (j + pungTiles.size())) & 1) == 1) { // 二进制位为1则加入
			            	int value = junkoTiles.get(j);
			            	if (tmpTileCounts[value]-- <= 0) {isOverload = true; break;}
			            	tmpHandTiles.add(tileMap[value]);
			            	if (tmpTileCounts[value + 1]-- <= 0) {isOverload = true; break;}
			            	tmpHandTiles.add(tileMap[value + 1]);
			            	if (tmpTileCounts[value + 2]-- <= 0) {isOverload = true; break;}
			            	tmpHandTiles.add(tileMap[value + 2]);
			            	allGroups.add(new Groups.Junko(value, GroupState.MenQing));
			            }
					}
					if (isOverload) continue;
					if (countOfbyte1 == 4) { // 单骑
						// 遍历所有可用的点炮牌
						for (int key : tileKeys) {
							if (!canBombs[key]) continue; // 如果有4张相同的牌,则跳过
							if (tmpTileCounts[key] == 0) continue; // 如果是使用完的手牌,则跳过
							List<Group> tmpAllGroups = new ArrayList<Groups.Group>(allGroups);
							tmpAllGroups.add(new Groups.Pair(key, GroupState.HePai));
							List<Tile> tmp4BHandTiles = new ArrayList<Tile>(tmpHandTiles);
							tmp4BHandTiles.add(tileMap[key]);
							Tile tmpAddedTile = bombTiles[key];
							// 取分值最大的结果
							game34Result.LogMaxScore(tmpAddedTile, tmp4BHandTiles, tmpAllGroups);
						}
					} else if (countOfbyte1 == 3) { // 顺子听嵌章、顺子双面听、两对双面听
						for (int j = 0; j < pairTiles.size(); j++) { // 先取出1个对子
							int[] tmp2TileCounts = tmpTileCounts.clone();
							List<Tile> tmp2HandTiles = new ArrayList<Tile>(tmpHandTiles);
							List<Group> tmp2AllGroups = new ArrayList<Groups.Group>(allGroups);
							Tile tmpAddedTile;
							int value = pairTiles.get(j);
			            	if (tmp2TileCounts[value] < 2) continue;
			            	tmp2TileCounts[value] -= 2;
			            	tmp2HandTiles.add(tileMap[value]);
			            	tmp2HandTiles.add(tileMap[value]);
			            	tmp2AllGroups.add(new Groups.Pair(value, GroupState.MenQing));
			            	for (int k = 0; k < junkoQbTiles.size(); k++) { // 顺子双面听
			            		List<Tile> tmp3HandTiles = new ArrayList<Tile>(tmp2HandTiles);
								List<Group> tmp3AllGroups = new ArrayList<Groups.Group>(tmp2AllGroups);
			            		int QbValue = junkoQbTiles.get(k);
								if (tmp2TileCounts[QbValue] <= 0) continue;
								tmp3HandTiles.add(tileMap[QbValue]);
								if (tmp2TileCounts[QbValue + 1] <= 0) continue;
								tmp3HandTiles.add(tileMap[QbValue + 1]);
			            		int num = QbValue % 9;
								if (num == 0) { // 12听3
									if (!canBombs[QbValue + 2]) continue;
									tmpAddedTile = bombTiles[QbValue + 2];
									tmp3AllGroups.add(new Groups.Junko(QbValue, GroupState.HePai, 2));
									// 取分值最大的结果
									game34Result.LogMaxScore(tmpAddedTile, tmp3HandTiles, tmp3AllGroups);
								} else if (num == 8) { // 89听7
									if (!canBombs[QbValue - 1]) continue;
									tmpAddedTile = bombTiles[QbValue - 1];
									tmp3AllGroups.add(new Groups.Junko(QbValue - 1, GroupState.HePai, 0));
									// 取分值最大的结果
									game34Result.LogMaxScore(tmpAddedTile, tmp3HandTiles, tmp3AllGroups);
								} else { // 23听1、4
									if (canBombs[QbValue - 1]) {
										tmpAddedTile = bombTiles[QbValue - 1];
										List<Group> tmp4AllGroups = new ArrayList<Groups.Group>(tmp3AllGroups);
										tmp4AllGroups.add(new Groups.Junko(QbValue - 1, GroupState.HePai, 0));
										// 取分值最大的结果
										game34Result.LogMaxScore(tmpAddedTile, tmp3HandTiles, tmp4AllGroups);
									}
									if (canBombs[QbValue + 2]) {
										tmpAddedTile = bombTiles[QbValue + 2];
										List<Group> tmp4AllGroups = new ArrayList<Groups.Group>(tmp3AllGroups);
										tmp4AllGroups.add(new Groups.Junko(QbValue, GroupState.HePai, 2));
										// 取分值最大的结果
										game34Result.LogMaxScore(tmpAddedTile, tmp3HandTiles, tmp4AllGroups);
									}
								}
							}
			            	for (int k = 0; k < junkoQzTiles.size(); k++) { // 顺子听嵌章
			            		List<Tile> tmp3HandTiles = new ArrayList<Tile>(tmp2HandTiles);
								List<Group> tmp3AllGroups = new ArrayList<Groups.Group>(tmp2AllGroups);
			            		int QzValue = junkoQzTiles.get(k);
								if (tmp2TileCounts[QzValue] <= 0) continue;
								tmp3HandTiles.add(tileMap[QzValue]);
								if (tmp2TileCounts[QzValue + 2] <= 0) continue;
								tmp3HandTiles.add(tileMap[QzValue + 2]);
								if (!canBombs[QzValue + 1]) continue;
								tmpAddedTile = bombTiles[QzValue + 1];
								tmp3AllGroups.add(new Groups.Junko(QzValue, GroupState.HePai, 1));
								// 取分值最大的结果
								game34Result.LogMaxScore(tmpAddedTile, tmp3HandTiles, tmp3AllGroups);
			            	}
						}
						if (pairTiles.size() >= 2) { // 两对双面听
							for (int j = 0; j < (1 << pairTiles.size()); j++) { // 2的n次方则为所有组合的次数
								if (CountOfByte1(j) == 2) { // 取所有2组不同对子的组合
									List<Tile> tmp2HandTiles = new ArrayList<Tile>(tmpHandTiles);
									int[] tmpAddedKeys = new int[2];
									int index = 0;
									boolean canDone = true;
									for (int k = 0; k < pairTiles.size(); k++) {
							            if (((j >> k) & 1) == 1) { // 二进制位为1则加入
							            	int value = pairTiles.get(k);
							            	if (tmpTileCounts[value] < 2) {
							            		canDone = false;
							            		break;
							            	}
							            	tmp2HandTiles.add(tileMap[value]);
							            	tmp2HandTiles.add(tileMap[value]);
							            	tmpAddedKeys[index++] = value;
							            }
									}
									if (!canDone) continue;
									for (int key : tmpAddedKeys) {
										if (!canBombs[key]) continue; // 如果有4张相同的牌,则跳过
										List<Group> tmp2AllGroups = new ArrayList<Groups.Group>(allGroups);
										Tile tmpAddedTile = bombTiles[key];
										if (key == tmpAddedKeys[0]) {
											tmp2AllGroups.add(new Groups.Pung(tmpAddedKeys[0], GroupState.HePai));
											tmp2AllGroups.add(new Groups.Pair(tmpAddedKeys[1], GroupState.MenQing));
											game34Result.LogMaxScore(tmpAddedTile, tmp2HandTiles, tmp2AllGroups);
										} else if (key == tmpAddedKeys[1]) {
											tmp2AllGroups.add(new Groups.Pung(tmpAddedKeys[1], GroupState.HePai));
											tmp2AllGroups.add(new Groups.Pair(tmpAddedKeys[0], GroupState.MenQing));
											game34Result.LogMaxScore(tmpAddedTile, tmp2HandTiles, tmp2AllGroups);
										}
									}
								}
							}			
						}
					}
				} 
			}
		}
	}

所有步骤的最终都要进行LogMaxScore,即番数的分析和筛选,其中番数的分析请阅读我的上一篇文章日本麻将记点器,筛选的方式有两种:分值优先和番数优先。分值优先(calcWay=0)即只记录最大的相同分值的组合(例如:6、7番跳满为相同分值),番数优先(calcWay=1)即只记录最大番数的组合(这个不用讲了吧)。
另外,十七步里面最难听的牌应该就是宝牌了(我是死也不会打的!!!),当所有最大的结果只能听宝牌时,这是需要退而求其次,降低番数来听牌,所以我还设置了一个level,当level>1时,只需要设置一个组合上限,然后所有组合均小于该组合,就能得到次级结果。

	public void LogMaxScore(Tile tmpAddedTile, List<Tile> tmpHandTiles, 
				List<Group> allGroups) {
			// 先分析番数
			List<Tile> tmpAllTiles = new ArrayList<Tile>(tmpHandTiles);
			tmpAllTiles.add(tmpAddedTile);
			List<IGroups> gList = new ArrayList<IGroups>();
			if (allGroups != null) gList.add(new GroupCollection(allGroups));
			ITiles tiles = new TileCollection(tmpAllTiles, tmpAllTiles, tmpAddedTile);
			Score tmpScore = mGame.getScore(tiles, gList, env);	
			if (tmpScore == null || !tmpScore.hasYaku()) {
				return;
			}
			// 再做筛选比对
			if (level > 1) { // 当level大于1时,所有结果均需小于上级最大结果
				int levelCmp = calcWay == 0 ? ScoreSystem.compareForGame34(level1Score, tmpScore) :
					ScoreSystem.compare(level1Score, tmpScore);
				if (levelCmp <= 0) return;
			}
			long[] cmpNum = convert2CmpNums(tmpAllTiles, tmpAddedTile); // 使用两个long来记录牌谱
			if (maxScore == null) { // 当无组合时
				maxScore = tmpScore;
				scores.add(tmpScore);
				addedTiles.add(tmpAddedTile);
				handTiles.add(tmpHandTiles);
				cmpNums.add(cmpNum);
			} else {
				int cmp = calcWay == 0 ? ScoreSystem.compareForGame34(maxScore, tmpScore) :
					ScoreSystem.compare(maxScore, tmpScore); // 选择比对方式
				if (cmp <= 0) {
					if (cmp != 0) { // 如果遇到更大的结果,则先清空
						scores.clear();
						addedTiles.clear();
						handTiles.clear();
						cmpNums.clear();
					}
					int replaceIndex = -1;
					for (int i = 0; i < cmpNums.size(); i++) {
						long[] tmpNum = cmpNums.get(i);
						if (tmpNum[0] == cmpNum[0] && tmpNum[1] == cmpNum[1]) {
							// 就算是牌谱相同,但是不同的组合也会导致平和的缺失,此时要比较总番数来替换组合
							if (calcWay == 0 && tmpScore.FullYaku() == 0
									&& tmpScore.AllFanValue() > scores.get(i).AllFanValue()) {
								replaceIndex = i;
								break;
							}
							return;
						}
					}
					maxScore = tmpScore;
					if (replaceIndex >= 0) { // 当牌谱相同但组合不同时,则需要替换
						scores.set(replaceIndex, tmpScore);
						addedTiles.set(replaceIndex, tmpAddedTile);
						handTiles.set(replaceIndex, tmpHandTiles);
						cmpNums.set(replaceIndex, cmpNum);
					} else { // 相同结果,直接加入
						scores.add(tmpScore);
						addedTiles.add(tmpAddedTile);
						handTiles.add(tmpHandTiles);
						cmpNums.add(cmpNum);
					}
				}
			} 
		}

三、总结

经测试,输出结果大多在1-2s内,总体时间还是可以接受的。实践出真知,这只是我的初步分析而已,也需要大量的测试才能发现更多未知的bug,而这次只需要拉上你的一位好友就可以愉快地玩耍啦,当然如果各位大神还有其他想法的话欢迎赐教!
测试样例
附源代码地址:
https://github.com/WaSuper/Mahjong

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值