package taurus.util; import hunan.HuNanChangSha; import java.util.*; public class TinHuGang { /** * 判断是否能杠牌(检查杠牌后是否立即听牌) */ public static boolean canGang(List handCards, int card, boolean isMingGang) { int type = card / 100; int value = card % 100; System.out.println("\n检查" + (isMingGang ? "明" : "暗") + "杠" + value + getTypeName(type) + ":"); // 检查是否符合杠牌条件 int count = Collections.frequency(handCards, card); if (isMingGang) { // 明杠:手牌中需要有三张相同的牌 if (count < 3) { System.out.println(" 手牌中没有三张相同的" + value + getTypeName(type) + ",不能明杠"); return false; } System.out.println(" 发现三张" + value + getTypeName(type) + ",可以明杠"); } else { // 暗杠:手牌中需要有四张相同的牌 if (count < 4) { System.out.println(" 手牌中没有四张相同的" + value + getTypeName(type) + ",不能暗杠"); return false; } System.out.println(" 发现四张" + value + getTypeName(type) + ",可以暗杠"); } // 检查杠牌后是否立即听牌(杠牌后手牌本身就是听牌状态) return canTingImmediatelyAfterGang(handCards, card, isMingGang); } /** * 检查杠牌后是否能立即听牌 */ private static boolean canTingImmediatelyAfterGang(List handCards, int gangCard, boolean isMingGang) { int type = gangCard / 100; int value = gangCard % 100; System.out.println("\n模拟" + (isMingGang ? "明" : "暗") + "杠" + value + getTypeName(type) + ":"); // 1. 模拟杠牌:移除手牌中的牌 List afterGang = new ArrayList<>(handCards); if (isMingGang) { // 明杠:移除手牌中的三张牌 for (int i = 0; i < 3; i++) { afterGang.remove(Integer.valueOf(gangCard)); } // 杠牌后手牌数:13张 → 10张 } else { // 暗杠:移除手牌中的四张牌 for (int i = 0; i < 4; i++) { afterGang.remove(Integer.valueOf(gangCard)); } // 暗杠后需要补牌,但这里先不处理 } Collections.sort(afterGang); System.out.println(" 杠后手牌(" + afterGang.size() + "张): " + convertToReadable(afterGang)); // 2. 检查是否需要258将 boolean needs258 = !checkSuitCount(afterGang); if (needs258) { System.out.println(" 花色牌数不足10张,需要258做将"); } // 3. 检查杠牌后手牌本身是否就是听牌状态 // 注意:杠牌后手牌数是10张,这10张牌本身应该是听牌状态 // 也就是说,随便摸一张牌(任何牌)都能胡牌 System.out.println("\n 检查杠后手牌是否听牌:"); if (afterGang.size() != 10) { System.out.println(" 手牌数不是10张,不符合听牌条件"); return false; } // 检查这10张牌是否听牌 boolean canTing = checkIfHandIsTingPai(afterGang, needs258); if (canTing) { System.out.println(" ✓ 杠后手牌是听牌状态!"); // 显示听哪些牌 Set tingCards = getTingCards(afterGang, needs258); if (!tingCards.isEmpty()) { System.out.print(" 听" + tingCards.size() + "张牌: "); List tingStrs = new ArrayList<>(); for (int tingCard : tingCards) { tingStrs.add((tingCard%100) + getTypeName(tingCard/100)); } System.out.println(String.join(", ", tingStrs)); } return true; } else { System.out.println(" ✗ 杠后手牌不是听牌状态"); return false; } } /** * 检查手牌是否处于听牌状态 */ private static boolean checkIfHandIsTingPai(List hand, boolean needs258) { if (hand.size() != 10) { return false; } // 听牌状态:再摸任何一张牌都能胡牌 // 我们需要检查是否至少有一张牌能让这手牌胡牌 Set allCards = getAllCards(); int tingCount = 0; for (int testCard : allCards) { List tempHand = new ArrayList<>(hand); tempHand.add(testCard); if (canHu(tempHand, needs258)) { tingCount++; } } System.out.println(" 可胡" + tingCount + "张牌"); return tingCount > 0; } /** * 获取听哪些牌 */ private static Set getTingCards(List hand, boolean needs258) { Set tingCards = new HashSet<>(); if (hand.size() != 10) { return tingCards; } Set allCards = getAllCards(); for (int testCard : allCards) { List tempHand = new ArrayList<>(hand); tempHand.add(testCard); if (canHu(tempHand, needs258)) { tingCards.add(testCard); } } return tingCards; } /** * 检查手牌是否能胡牌 */ private static boolean canHu(List handCards, boolean needs258) { // 手牌排序 List sorted = new ArrayList<>(handCards); Collections.sort(sorted); // 胡牌时手牌数必须是3n+2 if (sorted.size() != 11 && sorted.size() != 14) { return false; } // 检查七对子 if (checkQiDuiZi(sorted)) { // 七对子不需要258将 return true; } // 如果需要258将,检查普通胡牌(必须有258将) if (needs258) { return checkNormalHuWith258(sorted); } else { // 不需要258将,检查普通胡牌 return checkNormalHu(sorted); } } /** * 检查七对子 */ private static boolean checkQiDuiZi(List handCards) { if (handCards.size() != 14) return false; Map countMap = new HashMap<>(); for (int card : handCards) { countMap.put(card, countMap.getOrDefault(card, 0) + 1); } // 检查是否都是对子 for (int count : countMap.values()) { if (count != 2) { return false; } } return true; } /** * 检查普通胡牌(必须有258将) */ private static boolean checkNormalHuWith258(List handCards) { // 尝试每种258作为将牌 for (int card : handCards) { int value = card % 100; // 检查是否是258 if (value == 2 || value == 5 || value == 8) { // 检查是否有至少2张相同的牌做将 int count = Collections.frequency(handCards, card); if (count >= 2) { // 移除将牌 List remaining = new ArrayList<>(handCards); for (int i = 0; i < 2; i++) { remaining.remove(Integer.valueOf(card)); } // 检查剩余牌是否能组成顺子/刻子 if (canGroup(remaining)) { return true; } } } } return false; } /** * 检查普通胡牌(不需要258将) */ private static boolean checkNormalHu(List handCards) { return checkNormalHuRecursive(new ArrayList<>(handCards), false); } private static boolean checkNormalHuRecursive(List handCards, boolean hasJiang) { if (handCards.isEmpty()) { return true; // 所有牌都分组成功 } Collections.sort(handCards); // 统计第一张牌的数量 int firstCard = handCards.get(0); int count = Collections.frequency(handCards, firstCard); // 尝试作为刻子(三张相同) if (count >= 3) { List remaining = new ArrayList<>(handCards); for (int i = 0; i < 3; i++) { remaining.remove(Integer.valueOf(firstCard)); } if (checkNormalHuRecursive(remaining, hasJiang)) { return true; } } // 尝试作为顺子(三张连续) int type = firstCard / 100; int value = firstCard % 100; if (type < 4 && value <= 7) { // 字牌和8、9不能组成顺子 int second = type * 100 + (value + 1); int third = type * 100 + (value + 2); if (handCards.contains(second) && handCards.contains(third)) { List remaining = new ArrayList<>(handCards); remaining.remove(Integer.valueOf(firstCard)); remaining.remove(Integer.valueOf(second)); remaining.remove(Integer.valueOf(third)); if (checkNormalHuRecursive(remaining, hasJiang)) { return true; } } } // 尝试作为将(对子)- 只能有一个将 if (!hasJiang && count >= 2) { List remaining = new ArrayList<>(handCards); remaining.remove(Integer.valueOf(firstCard)); remaining.remove(Integer.valueOf(firstCard)); if (checkNormalHuRecursive(remaining, true)) { return true; } } return false; } /** * 检查牌是否能组成顺子或刻子 */ private static boolean canGroup(List cards) { if (cards.isEmpty()) { return true; // 所有牌都分组成功 } List sorted = new ArrayList<>(cards); Collections.sort(sorted); int firstCard = sorted.get(0); int count = Collections.frequency(sorted, firstCard); // 尝试作为刻子(三张相同) if (count >= 3) { List remaining = new ArrayList<>(sorted); for (int i = 0; i < 3; i++) { remaining.remove(Integer.valueOf(firstCard)); } if (canGroup(remaining)) { return true; } } // 尝试作为顺子(三张连续) int type = firstCard / 100; int value = firstCard % 100; if (type < 4 && value <= 7) { // 字牌和8、9不能组成顺子 int second = type * 100 + (value + 1); int third = type * 100 + (value + 2); if (sorted.contains(second) && sorted.contains(third)) { List remaining = new ArrayList<>(sorted); remaining.remove(Integer.valueOf(firstCard)); remaining.remove(Integer.valueOf(second)); remaining.remove(Integer.valueOf(third)); if (canGroup(remaining)) { return true; } } } return false; } /** * 检查手牌花色牌数是否>=10张 */ private static boolean checkSuitCount(List hand) { Map suitCount = new HashMap<>(); for (int card : hand) { int type = card / 100; if (type < 4) { suitCount.put(type, suitCount.getOrDefault(type, 0) + 1); } } for (int count : suitCount.values()) { if (count >= 10) { return true; } } return false; } /** * 获取所有麻将牌 */ private static Set getAllCards() { Set allCards = new HashSet<>(); for (int type = 1; type <= 3; type++) { for (int value = 1; value <= 9; value++) { allCards.add(type * 100 + value); } } return allCards; } /** * 转换为可读格式 */ private static String convertToReadable(List cards) { StringBuilder sb = new StringBuilder(); for (int card : cards) { int type = card / 100; int value = card % 100; sb.append(value).append(getTypeName(type)).append(" "); } return sb.toString(); } /** * 获取牌型名称 */ private static String getTypeName(int type) { switch(type) { case 1: return "万"; case 2: return "筒"; case 3: return "条"; default: return "字"; } } }