feyerobot/libs/robot_common/src/main/java/taurus/util/TinHuGang.java

406 lines
13 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package taurus.util;
import hunan.HuNanChangSha;
import java.util.*;
public class TinHuGang {
/**
* 判断是否能杠牌(检查杠牌后是否立即听牌)
*/
public static boolean canGang(List<Integer> 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<Integer> handCards, int gangCard, boolean isMingGang) {
int type = gangCard / 100;
int value = gangCard % 100;
System.out.println("\n模拟" + (isMingGang ? "明" : "暗") + "杠" + value + getTypeName(type) + ":");
// 1. 模拟杠牌:移除手牌中的牌
List<Integer> 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<Integer> tingCards = getTingCards(afterGang, needs258);
if (!tingCards.isEmpty()) {
System.out.print(" 听" + tingCards.size() + "张牌: ");
List<String> 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<Integer> hand, boolean needs258) {
if (hand.size() != 10) {
return false;
}
// 听牌状态:再摸任何一张牌都能胡牌
// 我们需要检查是否至少有一张牌能让这手牌胡牌
Set<Integer> allCards = getAllCards();
int tingCount = 0;
for (int testCard : allCards) {
List<Integer> tempHand = new ArrayList<>(hand);
tempHand.add(testCard);
if (canHu(tempHand, needs258)) {
tingCount++;
}
}
System.out.println(" 可胡" + tingCount + "张牌");
return tingCount > 0;
}
/**
* 获取听哪些牌
*/
private static Set<Integer> getTingCards(List<Integer> hand, boolean needs258) {
Set<Integer> tingCards = new HashSet<>();
if (hand.size() != 10) {
return tingCards;
}
Set<Integer> allCards = getAllCards();
for (int testCard : allCards) {
List<Integer> tempHand = new ArrayList<>(hand);
tempHand.add(testCard);
if (canHu(tempHand, needs258)) {
tingCards.add(testCard);
}
}
return tingCards;
}
/**
* 检查手牌是否能胡牌
*/
private static boolean canHu(List<Integer> handCards, boolean needs258) {
// 手牌排序
List<Integer> 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<Integer> handCards) {
if (handCards.size() != 14) return false;
Map<Integer, Integer> 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<Integer> 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<Integer> 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<Integer> handCards) {
return checkNormalHuRecursive(new ArrayList<>(handCards), false);
}
private static boolean checkNormalHuRecursive(List<Integer> 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<Integer> 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<Integer> 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<Integer> 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<Integer> cards) {
if (cards.isEmpty()) {
return true; // 所有牌都分组成功
}
List<Integer> sorted = new ArrayList<>(cards);
Collections.sort(sorted);
int firstCard = sorted.get(0);
int count = Collections.frequency(sorted, firstCard);
// 尝试作为刻子(三张相同)
if (count >= 3) {
List<Integer> 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<Integer> 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<Integer> hand) {
Map<Integer, Integer> 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<Integer> getAllCards() {
Set<Integer> 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<Integer> 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 "字";
}
}
}