没有其他牌类型可出,才能出炸弹

master
zhouwei 2026-02-09 18:59:20 +08:00
parent 2acbd22bad
commit 376332412b
1 changed files with 153 additions and 43 deletions

View File

@ -211,15 +211,17 @@ public class test_smart {
List<CardObj> responseCards = findMatchingResponse(handCards, type, minCard, len); List<CardObj> responseCards = findMatchingResponse(handCards, type, minCard, len);
if (responseCards.isEmpty()) { if (responseCards.isEmpty()) {
// 核心规则:要得起必须出!没有对应牌型时必须出炸弹 // 保守策略:只有在特定条件下才使用炸弹
if (shouldUseBombConservatively(handCards, type, minCard, seatRemainHistory, huNanPaoDeKuai)) {
List<CardObj> bomb = findSmallestBomb(handCards); List<CardObj> bomb = findSmallestBomb(handCards);
if (!bomb.isEmpty()) { if (!bomb.isEmpty()) {
System.out.println("[强制规则] 无法用对应牌型响应,必须出炸弹"); System.out.println("[保守策略] 满足炸弹使用条件,出炸弹");
return CardUtil.toTArray(bomb); return CardUtil.toTArray(bomb);
} }
}
// 理论上不应该到达这里(有炸弹就必须出) // 无合适牌型且不满足炸弹使用条件时的兜底策略
System.out.println("[警告] 无匹配牌型且无炸弹,违反游戏规则"); System.out.println("[策略] 无匹配牌型且不满足炸弹使用条件");
return emergencyOutCard(handCards); return emergencyOutCard(handCards);
} }
@ -657,16 +659,9 @@ public class test_smart {
List<CardObj> bestCombo = new ArrayList<>(); List<CardObj> bestCombo = new ArrayList<>();
int bestScore = 0; int bestScore = 0;
// 评估各种组合牌型的价值 // 评估各种组合牌型的价值(炸弹不再是最高优先级)
// 1. 炸弹(最高价值) // 1. 飞机带牌
List<CardObj> bomb = findSmallestBomb(handCards);
if (!bomb.isEmpty()) {
System.out.println("[残局评估] 发现炸弹,价值: 100");
return bomb; // 炸弹直接使用
}
// 2. 飞机带牌
List<CardObj> airplane = findSmallestAirplaneWithCards(handCards); List<CardObj> airplane = findSmallestAirplaneWithCards(handCards);
if (!airplane.isEmpty() && airplane.size() > bestScore) { if (!airplane.isEmpty() && airplane.size() > bestScore) {
bestCombo = airplane; bestCombo = airplane;
@ -674,7 +669,7 @@ public class test_smart {
System.out.println("[残局评估] 发现飞机,价值: " + airplane.size()); System.out.println("[残局评估] 发现飞机,价值: " + airplane.size());
} }
// 3. 四带二 // 2. 四带二
List<CardObj> fourWithTwo = findSmallestFourWithTwo(handCards); List<CardObj> fourWithTwo = findSmallestFourWithTwo(handCards);
if (!fourWithTwo.isEmpty() && fourWithTwo.size() > bestScore) { if (!fourWithTwo.isEmpty() && fourWithTwo.size() > bestScore) {
bestCombo = fourWithTwo; bestCombo = fourWithTwo;
@ -682,7 +677,7 @@ public class test_smart {
System.out.println("[残局评估] 发现四带二,价值: " + fourWithTwo.size()); System.out.println("[残局评估] 发现四带二,价值: " + fourWithTwo.size());
} }
// 4. 三带二 // 3. 三带二
List<CardObj> trioWithTwo = findSmallestTrioWithTwo(handCards); List<CardObj> trioWithTwo = findSmallestTrioWithTwo(handCards);
if (!trioWithTwo.isEmpty() && trioWithTwo.size() > bestScore) { if (!trioWithTwo.isEmpty() && trioWithTwo.size() > bestScore) {
bestCombo = trioWithTwo; bestCombo = trioWithTwo;
@ -690,7 +685,7 @@ public class test_smart {
System.out.println("[残局评估] 发现三带二,价值: " + trioWithTwo.size()); System.out.println("[残局评估] 发现三带二,价值: " + trioWithTwo.size());
} }
// 5. 连对 // 4. 连对
List<CardObj> consecutivePair = findSmallestConsecutivePair(handCards); List<CardObj> consecutivePair = findSmallestConsecutivePair(handCards);
if (!consecutivePair.isEmpty() && consecutivePair.size() > bestScore) { if (!consecutivePair.isEmpty() && consecutivePair.size() > bestScore) {
bestCombo = consecutivePair; bestCombo = consecutivePair;
@ -698,15 +693,7 @@ public class test_smart {
System.out.println("[残局评估] 发现连对,价值: " + consecutivePair.size()); System.out.println("[残局评估] 发现连对,价值: " + consecutivePair.size());
} }
// 6. 对子 // 5. 顺子
List<CardObj> pair = findSmallestPair(handCards);
if (!pair.isEmpty() && pair.size() > bestScore) {
bestCombo = pair;
bestScore = pair.size();
System.out.println("[残局评估] 发现对子,价值: " + pair.size());
}
// 7. 顺子
List<CardObj> straight = findSmallestStraight(handCards); List<CardObj> straight = findSmallestStraight(handCards);
if (!straight.isEmpty() && straight.size() > bestScore) { if (!straight.isEmpty() && straight.size() > bestScore) {
bestCombo = straight; bestCombo = straight;
@ -714,7 +701,7 @@ public class test_smart {
System.out.println("[残局评估] 发现顺子,价值: " + straight.size()); System.out.println("[残局评估] 发现顺子,价值: " + straight.size());
} }
// 8. 三张 // 6. 三张
List<CardObj> trio = findSmallestTrio(handCards); List<CardObj> trio = findSmallestTrio(handCards);
if (!trio.isEmpty() && trio.size() > bestScore) { if (!trio.isEmpty() && trio.size() > bestScore) {
bestCombo = trio; bestCombo = trio;
@ -722,6 +709,22 @@ public class test_smart {
System.out.println("[残局评估] 发现三张,价值: " + trio.size()); System.out.println("[残局评估] 发现三张,价值: " + trio.size());
} }
// 7. 对子
List<CardObj> pair = findSmallestPair(handCards);
if (!pair.isEmpty() && pair.size() > bestScore) {
bestCombo = pair;
bestScore = pair.size();
System.out.println("[残局评估] 发现对子,价值: " + pair.size());
}
// 8. 炸弹(仅在完全没有其他组合时考虑)
List<CardObj> bomb = findSmallestBomb(handCards);
if (!bomb.isEmpty() && bestScore == 0) { // 仅在无其他组合时才考虑
System.out.println("[残局评估] 无其他组合牌型,考虑使用炸弹");
return bomb;
}
if (bestScore > 0) { if (bestScore > 0) {
System.out.println("[残局决策] 选择最佳组合: " + bestCombo.stream().map(c -> getCardName(c.cardMod)).collect(Collectors.joining(","))); System.out.println("[残局决策] 选择最佳组合: " + bestCombo.stream().map(c -> getCardName(c.cardMod)).collect(Collectors.joining(",")));
} else { } else {
@ -738,7 +741,7 @@ public class test_smart {
List<CardObj> bestCombo = new ArrayList<>(); List<CardObj> bestCombo = new ArrayList<>();
int bestScore = 0; int bestScore = 0;
// 按价值优先级评估组合牌型 // 按价值优先级评估组合牌型(炸弹优先级降低)
// 1. 飞机带牌(最高优先级) // 1. 飞机带牌(最高优先级)
List<CardObj> airplane = findSmallestAirplaneWithCards(handCards); List<CardObj> airplane = findSmallestAirplaneWithCards(handCards);
@ -747,14 +750,7 @@ public class test_smart {
return airplane; return airplane;
} }
// 2. 炸弹 // 2. 四带二
List<CardObj> bomb = findSmallestBomb(handCards);
if (!bomb.isEmpty()) {
System.out.println("[组合优先] 发现炸弹: " + bomb.stream().map(c -> getCardName(c.cardMod)).collect(Collectors.joining("")));
return bomb;
}
// 3. 四带二
List<CardObj> fourWithTwo = findSmallestFourWithTwo(handCards); List<CardObj> fourWithTwo = findSmallestFourWithTwo(handCards);
if (!fourWithTwo.isEmpty() && fourWithTwo.size() > bestScore) { if (!fourWithTwo.isEmpty() && fourWithTwo.size() > bestScore) {
bestCombo = fourWithTwo; bestCombo = fourWithTwo;
@ -762,7 +758,7 @@ public class test_smart {
System.out.println("[组合优先] 发现四带二: " + fourWithTwo.stream().map(c -> getCardName(c.cardMod)).collect(Collectors.joining(""))); System.out.println("[组合优先] 发现四带二: " + fourWithTwo.stream().map(c -> getCardName(c.cardMod)).collect(Collectors.joining("")));
} }
// 4. 三带二 // 3. 三带二
List<CardObj> trioWithTwo = findSmallestTrioWithTwo(handCards); List<CardObj> trioWithTwo = findSmallestTrioWithTwo(handCards);
if (!trioWithTwo.isEmpty() && trioWithTwo.size() > bestScore) { if (!trioWithTwo.isEmpty() && trioWithTwo.size() > bestScore) {
bestCombo = trioWithTwo; bestCombo = trioWithTwo;
@ -770,7 +766,7 @@ public class test_smart {
System.out.println("[组合优先] 发现三带二: " + trioWithTwo.stream().map(c -> getCardName(c.cardMod)).collect(Collectors.joining(""))); System.out.println("[组合优先] 发现三带二: " + trioWithTwo.stream().map(c -> getCardName(c.cardMod)).collect(Collectors.joining("")));
} }
// 5. 连对 // 4. 连对
List<CardObj> consecutivePair = findSmallestConsecutivePair(handCards); List<CardObj> consecutivePair = findSmallestConsecutivePair(handCards);
if (!consecutivePair.isEmpty() && consecutivePair.size() > bestScore) { if (!consecutivePair.isEmpty() && consecutivePair.size() > bestScore) {
bestCombo = consecutivePair; bestCombo = consecutivePair;
@ -778,7 +774,7 @@ public class test_smart {
System.out.println("[组合优先] 发现连对: " + consecutivePair.stream().map(c -> getCardName(c.cardMod)).collect(Collectors.joining(""))); System.out.println("[组合优先] 发现连对: " + consecutivePair.stream().map(c -> getCardName(c.cardMod)).collect(Collectors.joining("")));
} }
// 6. 顺子 // 5. 顺子
List<CardObj> straight = findSmallestStraight(handCards); List<CardObj> straight = findSmallestStraight(handCards);
if (!straight.isEmpty() && straight.size() > bestScore) { if (!straight.isEmpty() && straight.size() > bestScore) {
bestCombo = straight; bestCombo = straight;
@ -786,7 +782,7 @@ public class test_smart {
System.out.println("[组合优先] 发现顺子: " + straight.stream().map(c -> getCardName(c.cardMod)).collect(Collectors.joining(""))); System.out.println("[组合优先] 发现顺子: " + straight.stream().map(c -> getCardName(c.cardMod)).collect(Collectors.joining("")));
} }
// 7. 三张 // 6. 三张
List<CardObj> trio = findSmallestTrio(handCards); List<CardObj> trio = findSmallestTrio(handCards);
if (!trio.isEmpty() && trio.size() > bestScore) { if (!trio.isEmpty() && trio.size() > bestScore) {
bestCombo = trio; bestCombo = trio;
@ -794,7 +790,7 @@ public class test_smart {
System.out.println("[组合优先] 发现三张: " + trio.stream().map(c -> getCardName(c.cardMod)).collect(Collectors.joining(""))); System.out.println("[组合优先] 发现三张: " + trio.stream().map(c -> getCardName(c.cardMod)).collect(Collectors.joining("")));
} }
// 8. 对子 // 7. 对子
List<CardObj> pair = findSmallestPair(handCards); List<CardObj> pair = findSmallestPair(handCards);
if (!pair.isEmpty() && pair.size() > bestScore) { if (!pair.isEmpty() && pair.size() > bestScore) {
bestCombo = pair; bestCombo = pair;
@ -802,6 +798,14 @@ public class test_smart {
System.out.println("[组合优先] 发现对子: " + pair.stream().map(c -> getCardName(c.cardMod)).collect(Collectors.joining(""))); System.out.println("[组合优先] 发现对子: " + pair.stream().map(c -> getCardName(c.cardMod)).collect(Collectors.joining("")));
} }
// 8. 炸弹(最低优先级,仅在没有其他组合时考虑)
List<CardObj> bomb = findSmallestBomb(handCards);
if (!bomb.isEmpty() && bestScore == 0) { // 只有在没有任何其他组合时才考虑炸弹
System.out.println("[组合优先] 无其他组合牌型,考虑使用炸弹");
return bomb;
}
if (bestScore > 0) { if (bestScore > 0) {
System.out.println("[组合优先] 选择最佳组合: " + bestCombo.stream().map(c -> getCardName(c.cardMod)).collect(Collectors.joining(""))); System.out.println("[组合优先] 选择最佳组合: " + bestCombo.stream().map(c -> getCardName(c.cardMod)).collect(Collectors.joining("")));
} else { } else {
@ -2321,6 +2325,78 @@ public class test_smart {
return reportedType; return reportedType;
} }
/**
* 使
* 使
* 1.
* 2. 2
* 3. 3
* 4. A6
* 5.
*/
private static boolean shouldUseBombConservatively(
List<CardObj> handCards,
int opponentType,
int opponentMinCard,
Map<Integer, List<Integer>> seatRemainHistory,
HuNanPaoDeKuai huNanPaoDeKuai) {
System.out.println("[炸弹策略] 分析是否满足炸弹使用条件");
// 条件1对手只剩一张牌
if (checkIfAnyOpponentHasOneCard(seatRemainHistory, huNanPaoDeKuai != null ? huNanPaoDeKuai.seat : 0)) {
System.out.println("[炸弹策略] ✓ 对手只剩一张牌,使用炸弹");
return true;
}
// 条件2对手出2
if (opponentMinCard == CARD_2) {
System.out.println("[炸弹策略] ✓ 对手出2使用炸弹");
return true;
}
// 条件3自身手牌≤3张
if (handCards.size() <= 3) {
System.out.println("[炸弹策略] ✓ 手牌很少(" + handCards.size() + "张),使用炸弹");
return true;
}
// 条件4对手出A且手牌≤6张
if (opponentMinCard == CARD_A && handCards.size() <= 6) {
System.out.println("[炸弹策略] ✓ 对手出A且手牌较少(" + handCards.size() + "张),使用炸弹");
return true;
}
// 条件5其他极端情况最后手段
// 比如对手出大牌且自己即将输掉
if (isExtremeLosingSituation(handCards, opponentType, opponentMinCard)) {
System.out.println("[炸弹策略] ✓ 极端劣势局面,使用炸弹");
return true;
}
System.out.println("[炸弹策略] ✗ 不满足炸弹使用条件,保留炸弹");
return false;
}
/** 判断是否处于极端劣势局面 */
private static boolean isExtremeLosingSituation(List<CardObj> handCards, int opponentType, int opponentMinCard) {
// 如果对手出的是大牌Q及以上且我们没有更好的应对方式
if (opponentMinCard >= CARD_Q) {
// 检查是否还有其他组合牌型
HandAnalysis analysis = analyzeHandComposition(handCards);
int comboTypes = (analysis.pairCount > 0 ? 1 : 0) +
(analysis.trioCount > 0 ? 1 : 0) +
(analysis.straightCount > 0 ? 1 : 0) +
(analysis.consecutivePairCount > 0 ? 1 : 0);
// 如果几乎没有组合牌型,且对手出大牌,可以考虑使用炸弹
if (comboTypes <= 1 && handCards.size() <= 8) {
return true;
}
}
return false;
}
/** 智能分析实际牌型 - 真正的AI核心 */ /** 智能分析实际牌型 - 真正的AI核心 */
private static int analyzeActualCardType(List<CardObj> cards) { private static int analyzeActualCardType(List<CardObj> cards) {
if (cards == null || cards.isEmpty()) return TYPE_SINGLE; if (cards == null || cards.isEmpty()) return TYPE_SINGLE;
@ -2473,4 +2549,38 @@ public class test_smart {
String cardNames = cards.stream().map(c -> getCardName(c.cardMod)).collect(Collectors.joining(",")); String cardNames = cards.stream().map(c -> getCardName(c.cardMod)).collect(Collectors.joining(","));
return typeName + "[" + cardNames + "]"; return typeName + "[" + cardNames + "]";
} }
// ====================== 测试方法 ======================
/** 测试炸弹使用保守策略 */
public static void testBombConservativeStrategy() {
System.out.println("\n========== 炸弹使用保守策略测试 ==========");
// 模拟不同场景
testBombScenario("对手剩1张牌", 15, TYPE_SINGLE, CARD_7, true);
testBombScenario("对手出2", 10, TYPE_SINGLE, CARD_2, true);
testBombScenario("手牌≤3张", 3, TYPE_PAIR, CARD_8, true);
testBombScenario("对手出A且手牌≤6张", 5, TYPE_SINGLE, CARD_A, true);
testBombScenario("正常情况-不应使用炸弹", 12, TYPE_PAIR, CARD_9, false);
}
private static void testBombScenario(String scenario, int handSize, int opponentType, int opponentMinCard, boolean shouldUseBomb) {
System.out.println("\n测试场景: " + scenario);
System.out.println("手牌数: " + handSize + ", 对手类型: " + getCardTypeName(opponentType) + ", 最小牌: " + getCardName(opponentMinCard));
// 创建模拟手牌
List<CardObj> mockHand = new ArrayList<>();
for (int i = 0; i < handSize; i++) {
mockHand.add(new CardObj(CARD_3 + (i % 13))); // 简单模拟
}
boolean result = shouldUseBombConservatively(mockHand, opponentType, opponentMinCard, null, null);
System.out.println("策略判断: " + (result ? "使用炸弹" : "保留炸弹"));
if (result == shouldUseBomb) {
System.out.println("✅ 策略判断正确");
} else {
System.out.println("❌ 策略判断错误,期望: " + (shouldUseBomb ? "使用" : "保留") + "炸弹");
}
}
} }