2026-01-01 07:54:30 +08:00
|
|
|
|
package taurus.util;
|
|
|
|
|
|
|
|
|
|
|
|
import com.game.Util;
|
|
|
|
|
|
|
|
|
|
|
|
import java.util.*;
|
|
|
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
|
|
|
|
|
|
public class ChangshaWinSplitCard {
|
|
|
|
|
|
|
|
|
|
|
|
// 统计牌数
|
|
|
|
|
|
public static int[][] countTiles(List<Integer> cardInHand) {
|
|
|
|
|
|
int[][] counts = new int[5][10]; // 类型×值
|
|
|
|
|
|
|
|
|
|
|
|
for (Integer card : cardInHand) {
|
|
|
|
|
|
if (card == 0) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
counts[card / 100 - 1][card % 100]++;
|
|
|
|
|
|
}
|
|
|
|
|
|
return counts;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 检测不带精湖
|
|
|
|
|
|
public static int checkNormalHu(List<Integer> cardInHand, Map<String, Object> map) {
|
|
|
|
|
|
String info = "";
|
|
|
|
|
|
int[][] counts = countTiles(cardInHand);
|
|
|
|
|
|
int group = 0;
|
|
|
|
|
|
// 尝试找将牌
|
|
|
|
|
|
for (int type = 0; type < 5; type++) {
|
|
|
|
|
|
int max = 9;
|
|
|
|
|
|
for (int value = 1; value <= max; value++) {
|
|
|
|
|
|
if (counts[type][value] >= 2 && (value == 2 || value == 5 || value == 8)) {
|
|
|
|
|
|
// 找到可能的将牌
|
|
|
|
|
|
counts[type][value] -= 2;
|
|
|
|
|
|
group = checkMelds(counts, cardInHand.size() / 3, map);
|
|
|
|
|
|
if (group == 0) { // 剩余12张牌需要组成4个顺子或刻子
|
|
|
|
|
|
counts[type][value] += 2;
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
counts[type][value] += 2;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
group = checkMelds(counts, (cardInHand.size() / 3) + 1, map);
|
2026-01-05 19:40:49 +08:00
|
|
|
|
|
2026-01-01 07:54:30 +08:00
|
|
|
|
if (group == 0) { // 剩余12张牌需要组成4个顺子或刻子
|
|
|
|
|
|
counts[type][value] += 2;
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-01-05 19:40:49 +08:00
|
|
|
|
|
2026-01-01 07:54:30 +08:00
|
|
|
|
List<Integer> cardResiue = (List<Integer>) map.get("cardResiue");
|
2026-01-05 19:40:49 +08:00
|
|
|
|
|
2026-01-01 07:54:30 +08:00
|
|
|
|
for (int i = 0; i < cardResiue.size() - 1; i++) {
|
|
|
|
|
|
if (cardResiue.get(i).equals(cardResiue.get(i + 1)) && (cardResiue.get(i) % 100 == 2 || cardResiue.get(i) % 100 == 5 || cardResiue.get(i) % 100 == 8)) {
|
|
|
|
|
|
cardResiue.remove(i);
|
|
|
|
|
|
cardResiue.remove(i);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-01-05 19:40:49 +08:00
|
|
|
|
|
|
|
|
|
|
haveBetween(cardResiue, cardInHand);
|
|
|
|
|
|
|
2026-01-01 07:54:30 +08:00
|
|
|
|
System.out.println("可以打出的牌:" + cardResiue);
|
|
|
|
|
|
System.out.println("总共差几手牌:" + map.get("remainingMelds"));
|
|
|
|
|
|
|
|
|
|
|
|
return group;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-05 19:40:49 +08:00
|
|
|
|
public static void haveBetween(List<Integer> cards, List<Integer> cardInHand) {
|
|
|
|
|
|
if (cards.size() <= 1 || cardInHand.size() <= 1) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 创建要删除的牌列表
|
|
|
|
|
|
Set<Integer> toRemove = new HashSet<>();
|
|
|
|
|
|
|
|
|
|
|
|
// 遍历手牌中的每一张牌
|
|
|
|
|
|
for (int handCard : cardInHand) {
|
|
|
|
|
|
// 检查cards中是否有与手牌形成靠章的牌
|
|
|
|
|
|
for (int card : cards) {
|
|
|
|
|
|
// 定义靠章规则:相同、相邻或隔一张
|
|
|
|
|
|
int diff = Math.abs(card - handCard);
|
|
|
|
|
|
if (diff == 1 || diff == 2) {
|
|
|
|
|
|
toRemove.add(card);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 只有当cards中还有多于1张牌时才执行删除
|
|
|
|
|
|
//if (toRemove.size()- cards.size() > 1){
|
|
|
|
|
|
if(cards.size()-toRemove.size()>0){
|
|
|
|
|
|
cards.removeAll(toRemove);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 可选:打印信息
|
|
|
|
|
|
if (!toRemove.isEmpty()) {
|
|
|
|
|
|
System.out.println("删除的牌: " + toRemove);
|
|
|
|
|
|
System.out.println("cards剩余: " + cards);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-01 07:54:30 +08:00
|
|
|
|
|
|
|
|
|
|
// 递归检查是否可以组成顺子或刻子
|
|
|
|
|
|
public static int checkMelds(int[][] counts, int remainingMelds, Map<String, Object> map) {
|
|
|
|
|
|
if (remainingMelds == 0) {
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 优先检查刻子
|
|
|
|
|
|
for (int type = 0; type < 5; type++) {
|
|
|
|
|
|
int max = 9;
|
|
|
|
|
|
for (int value = 1; value <= max; value++) {
|
|
|
|
|
|
if (counts[type][value] >= 3) {
|
|
|
|
|
|
counts[type][value] -= 3;
|
|
|
|
|
|
if (checkMelds(counts, remainingMelds - 1, map) == 0) {
|
|
|
|
|
|
counts[type][value] += 3;
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
counts[type][value] += 3;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查顺子
|
|
|
|
|
|
for (int type = 0; type < 5; type++) {
|
|
|
|
|
|
int max = 7;
|
|
|
|
|
|
|
|
|
|
|
|
for (int value = 1; value <= max; value++) {
|
|
|
|
|
|
if (counts[type][value] >= 1 && counts[type][value + 1] >= 1 && counts[type][value + 2] >= 1) {
|
|
|
|
|
|
|
|
|
|
|
|
counts[type][value]--;
|
|
|
|
|
|
counts[type][value + 1]--;
|
|
|
|
|
|
counts[type][value + 2]--;
|
|
|
|
|
|
|
|
|
|
|
|
if (checkMelds(counts, remainingMelds - 1, map) == 0) {
|
|
|
|
|
|
counts[type][value]++;
|
|
|
|
|
|
counts[type][value + 1]++;
|
|
|
|
|
|
counts[type][value + 2]++;
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
counts[type][value]++;
|
|
|
|
|
|
counts[type][value + 1]++;
|
|
|
|
|
|
counts[type][value + 2]++;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// 判断孤章
|
|
|
|
|
|
List<Integer> cardResiue = convertCountToCards(counts);
|
|
|
|
|
|
|
|
|
|
|
|
// System.out.println("只差几手牌" + remainingMelds + "," + cardResiue);
|
2026-01-05 19:40:49 +08:00
|
|
|
|
if (cardResiue.size()!=0) {
|
|
|
|
|
|
//优先保留连续张
|
|
|
|
|
|
removeConsecutivePairs1(cardResiue);
|
|
|
|
|
|
}
|
2026-01-01 07:54:30 +08:00
|
|
|
|
|
2026-01-05 19:40:49 +08:00
|
|
|
|
if (cardResiue.size()!=0) {
|
|
|
|
|
|
//第二优先保留对子
|
|
|
|
|
|
removeConsecutivePairsTwo(cardResiue);
|
|
|
|
|
|
}
|
2026-01-01 07:54:30 +08:00
|
|
|
|
//最后保留坎张
|
2026-01-05 19:40:49 +08:00
|
|
|
|
//如果坎张,为空则取边张
|
|
|
|
|
|
if (cardResiue.size()!=0) {
|
|
|
|
|
|
List<Integer> tmp3 = new ArrayList<>();
|
|
|
|
|
|
tmp3.addAll(cardResiue);
|
|
|
|
|
|
removeConsecutivePairs2(cardResiue);
|
|
|
|
|
|
if (cardResiue.size() == 0) {
|
|
|
|
|
|
//取边站
|
|
|
|
|
|
cardResiue = qubianzhang(tmp3);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-01-01 07:54:30 +08:00
|
|
|
|
|
|
|
|
|
|
if (remainingMelds == 0) {
|
|
|
|
|
|
map.put("remainingMelds", 0);
|
|
|
|
|
|
map.put("cardResiue", "已经胡牌");
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
if (map.get("remainingMelds") == null) {
|
|
|
|
|
|
map.put("remainingMelds", remainingMelds);
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
if (map.get("cardResiue") == null) {
|
|
|
|
|
|
map.put("cardResiue", cardResiue);
|
|
|
|
|
|
}
|
|
|
|
|
|
for (int i = 0; i < cardResiue.size() - 1; i++) {
|
|
|
|
|
|
if (cardResiue.get(i).equals(cardResiue.get(i + 1)) && (cardResiue.get(i) % 100 == 2 || cardResiue.get(i) % 100 == 5 || cardResiue.get(i) % 100 == 8)) {
|
|
|
|
|
|
cardResiue.remove(i);
|
|
|
|
|
|
cardResiue.remove(i);
|
|
|
|
|
|
if (remainingMelds >= 2) {
|
|
|
|
|
|
remainingMelds--;
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (Integer.parseInt(map.get("remainingMelds").toString()) >= remainingMelds && cardResiue.size() > 0) {
|
|
|
|
|
|
// 如果没有258做将则需要进入这里判断
|
|
|
|
|
|
if (remainingMelds == 1 && cardResiue.size() == 2) {
|
|
|
|
|
|
for (int i = 0; i < cardResiue.size(); i++) {
|
2026-01-05 19:40:49 +08:00
|
|
|
|
int diff = Math.abs(cardResiue.get(0) - cardResiue.get(1));
|
|
|
|
|
|
if (diff>1) {
|
|
|
|
|
|
map.put("remainingMelds", remainingMelds + 1);// 删除符合258做将的牌if (cardResiue.size() > 1) {cardResiue.remove(i);}map.put("cardResiue", cardResiue);
|
|
|
|
|
|
|
|
|
|
|
|
}else {
|
|
|
|
|
|
if (cardResiue.get(i) % 100 == 2 || cardResiue.get(i) % 100 == 5 || cardResiue.get(i) % 100 == 8) {
|
|
|
|
|
|
// 删除符合258做将的牌
|
|
|
|
|
|
if (cardResiue.size() > 1) {
|
|
|
|
|
|
cardResiue.remove(i);
|
|
|
|
|
|
}
|
|
|
|
|
|
map.put("remainingMelds", remainingMelds);
|
|
|
|
|
|
map.put("cardResiue", cardResiue);
|
2026-01-01 07:54:30 +08:00
|
|
|
|
|
2026-01-05 19:40:49 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
map.put("remainingMelds", remainingMelds);
|
|
|
|
|
|
map.put("cardResiue", cardResiue);
|
2026-01-01 07:54:30 +08:00
|
|
|
|
|
2026-01-05 19:40:49 +08:00
|
|
|
|
}
|
2026-01-01 07:54:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} else if (remainingMelds == 1) {
|
|
|
|
|
|
map.put("remainingMelds", remainingMelds);
|
|
|
|
|
|
map.put("cardResiue", cardResiue);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return remainingMelds;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static void removeConsecutivePairsTwo(List<Integer> cards) {
|
|
|
|
|
|
if (cards == null || cards.size() < 2) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
// 先排序
|
|
|
|
|
|
List<Integer> sorted = new ArrayList<>(cards);
|
|
|
|
|
|
Collections.sort(sorted);
|
|
|
|
|
|
|
|
|
|
|
|
List<Integer> result = new ArrayList<>();
|
|
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
|
|
|
|
while (i < sorted.size()) {
|
2026-01-05 19:40:49 +08:00
|
|
|
|
// boolean isleft = sorted.get(i) % 100 == 1 || sorted.get(i) % 100 == 8;
|
|
|
|
|
|
if (i < sorted.size() - 1 && sorted.get(i + 1) - sorted.get(i) == 0) {
|
2026-01-01 07:54:30 +08:00
|
|
|
|
// 跳过对子
|
|
|
|
|
|
i += 2;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 保留这张牌
|
|
|
|
|
|
result.add(sorted.get(i));
|
|
|
|
|
|
i++;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
cards.clear();
|
|
|
|
|
|
cards.addAll(result);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 辅助方法
|
|
|
|
|
|
public static void removeConsecutivePairs1(List<Integer> cards) {
|
|
|
|
|
|
if (cards == null || cards.size() < 2) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 先排序
|
|
|
|
|
|
List<Integer> sorted = new ArrayList<>(cards);
|
|
|
|
|
|
Collections.sort(sorted);
|
|
|
|
|
|
|
|
|
|
|
|
List<Integer> result = new ArrayList<>();
|
|
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
|
|
|
|
while (i < sorted.size()) {
|
|
|
|
|
|
boolean isleft = sorted.get(i) % 100 == 1 || sorted.get(i) % 100 == 8;
|
|
|
|
|
|
if (!isleft && i < sorted.size() - 1 && sorted.get(i + 1) - sorted.get(i) == 1) {
|
|
|
|
|
|
// 跳过这对连续牌
|
|
|
|
|
|
i += 2;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 保留这张牌
|
|
|
|
|
|
result.add(sorted.get(i));
|
|
|
|
|
|
i++;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
cards.clear();
|
|
|
|
|
|
cards.addAll(result);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-05 19:40:49 +08:00
|
|
|
|
public static List<Integer> qubianzhang(List<Integer> cards) {
|
|
|
|
|
|
List<Integer> result = new ArrayList<>();
|
|
|
|
|
|
for (int i = 0; i < cards.size(); i++) {
|
|
|
|
|
|
if (cards.get(i)%100 == 1||cards.get(i)%100 == 9) {
|
|
|
|
|
|
result.add(cards.get(i));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-01 07:54:30 +08:00
|
|
|
|
public static void removeConsecutivePairs2(List<Integer> cards) {
|
|
|
|
|
|
if (cards == null || cards.size() < 2) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 先排序
|
|
|
|
|
|
List<Integer> sorted = new ArrayList<>(cards);
|
|
|
|
|
|
Collections.sort(sorted);
|
|
|
|
|
|
|
|
|
|
|
|
// 使用布尔数组标记要删除的牌
|
|
|
|
|
|
boolean[] toRemove = new boolean[sorted.size()];
|
|
|
|
|
|
|
|
|
|
|
|
// 找出所有隔张(相差2的牌对,如1和3、2和4等)
|
|
|
|
|
|
for (int i = 0; i < sorted.size(); i++) {
|
|
|
|
|
|
if (!toRemove[i]) { // 如果这张牌还没被标记删除
|
|
|
|
|
|
for (int j = i + 1; j < sorted.size(); j++) {
|
|
|
|
|
|
if (!toRemove[j]) { // 如果这张牌还没被标记删除
|
|
|
|
|
|
int diff = sorted.get(j) - sorted.get(i);
|
|
|
|
|
|
|
|
|
|
|
|
if (diff == 2 && sorted.size()>2) {
|
|
|
|
|
|
// 找到隔张,标记删除
|
|
|
|
|
|
toRemove[i] = true;
|
|
|
|
|
|
toRemove[j] = true;
|
|
|
|
|
|
|
|
|
|
|
|
if (j + 1 < sorted.size()) {
|
|
|
|
|
|
if (sorted.get(j + 1) % 100 == 2 || sorted.get(j + 1) % 100 == 5
|
|
|
|
|
|
|| sorted.get(j + 1) % 100 == 6) {
|
|
|
|
|
|
toRemove[i] = false;
|
|
|
|
|
|
toRemove[j] = false;
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
break; // 找到一对就跳出内层循环
|
|
|
|
|
|
|
|
|
|
|
|
} else if (diff > 2) {
|
|
|
|
|
|
// 由于已排序,差值只会越来越大
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 构建结果列表(保留未标记删除的牌)
|
|
|
|
|
|
List<Integer> result = new ArrayList<>();
|
|
|
|
|
|
for (int i = 0; i < sorted.size(); i++) {
|
|
|
|
|
|
if (!toRemove[i]) {
|
|
|
|
|
|
result.add(sorted.get(i));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
cards.clear();
|
|
|
|
|
|
cards.addAll(result);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 从二维数组counts还原到手牌列表
|
|
|
|
|
|
private static List<Integer> convertCountToCards(int[][] counts) {
|
|
|
|
|
|
List<Integer> cardInHand = new ArrayList<>();
|
|
|
|
|
|
|
|
|
|
|
|
for (int suitIdx = 0; suitIdx < counts.length; suitIdx++) {
|
|
|
|
|
|
int suit = suitIdx + 1;
|
|
|
|
|
|
|
|
|
|
|
|
for (int value = 1; value < counts[suitIdx].length; value++) {
|
|
|
|
|
|
int count = counts[suitIdx][value];
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
|
|
int card = suit * 100 + value;
|
|
|
|
|
|
cardInHand.add(card);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return cardInHand;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static List<Integer> checktingpai(List<Integer> cardhand) {
|
|
|
|
|
|
List<Integer> tpcards = new ArrayList<>();
|
|
|
|
|
|
List<Integer> tmphc = cardhand;
|
|
|
|
|
|
for (int i = 0; i < cardhand.size(); i++) {
|
|
|
|
|
|
|
|
|
|
|
|
int tmpcard = tmphc.get(0);
|
|
|
|
|
|
tmphc.remove(0);
|
|
|
|
|
|
for (int j = 101; j <= 109; j++) {
|
|
|
|
|
|
WinCard win = new WinCard(tmphc, j);
|
|
|
|
|
|
if (win.tryWin()) {
|
|
|
|
|
|
if (!tpcards.contains(tmpcard)) {
|
|
|
|
|
|
tpcards.add(tmpcard);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
for (int j = 201; j <= 209; j++) {
|
|
|
|
|
|
WinCard win = new WinCard(tmphc, j);
|
|
|
|
|
|
if (win.tryWin()) {
|
|
|
|
|
|
if (!tpcards.contains(tmpcard)) {
|
|
|
|
|
|
tpcards.add(tmpcard);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
tmphc.add(tmpcard);
|
|
|
|
|
|
}
|
|
|
|
|
|
return tpcards;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-05 19:40:49 +08:00
|
|
|
|
|
|
|
|
|
|
public static boolean isJiangPai(int card){
|
|
|
|
|
|
if (card%100==2||card%100==5||card%100==8){
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static int checkduijiang(List<Integer> cardInHand,Map<String,List<Integer>> map) {
|
|
|
|
|
|
Map<Integer, Integer> countMap = new HashMap<>();
|
|
|
|
|
|
for (Integer item : cardInHand) {
|
|
|
|
|
|
countMap.put(item, countMap.getOrDefault(item, 0) + 1);
|
|
|
|
|
|
}
|
|
|
|
|
|
int jiangnum = 0;
|
|
|
|
|
|
for (int key : countMap.keySet()) {
|
|
|
|
|
|
if (isJiangPai(key)&&countMap.get(key)>=2){
|
|
|
|
|
|
jiangnum++;
|
|
|
|
|
|
List<Integer> tmpI = new ArrayList<>();
|
|
|
|
|
|
tmpI.add(key);
|
|
|
|
|
|
map.put("jiangcard", tmpI);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return jiangnum;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-01 07:54:30 +08:00
|
|
|
|
// 分析最优出牌
|
|
|
|
|
|
public static List<Integer> analyzeBestDiscard(List<Integer> cardInHand) {
|
|
|
|
|
|
|
|
|
|
|
|
//听牌
|
|
|
|
|
|
//返回要打的牌,打后可以听牌
|
|
|
|
|
|
List<Integer> checktingpai = checktingpai(cardInHand);
|
|
|
|
|
|
System.out.println("打出这种牌后可以听牌 " + checktingpai);
|
|
|
|
|
|
Map<String, Object> map = new HashMap<>();
|
2026-01-05 19:40:49 +08:00
|
|
|
|
Map<String, List<Integer>> jmap = new HashMap<>();//对将map
|
|
|
|
|
|
//先定将,再做分析
|
|
|
|
|
|
/* int jiangnum = checkduijiang(cardInHand,jmap);
|
|
|
|
|
|
System.out.println(jmap.get("jiangcard").get(0));
|
|
|
|
|
|
if (jiangnum==1){
|
|
|
|
|
|
//如果只有一对将
|
|
|
|
|
|
//去将 后判断方案
|
|
|
|
|
|
List<Integer> tmphc = new ArrayList<>();
|
|
|
|
|
|
tmphc.addAll(cardInHand);
|
|
|
|
|
|
Util.removeCard(tmphc,jmap.get("jiangcard").get(0),2);
|
|
|
|
|
|
Map<String, Object> tmap = new HashMap<>();
|
|
|
|
|
|
checkNormalHu(tmphc, tmap);
|
|
|
|
|
|
|
|
|
|
|
|
//不去将 判断
|
|
|
|
|
|
|
|
|
|
|
|
Map<String, Object> tmap2 = new HashMap<>();
|
|
|
|
|
|
System.out.println(cardInHand);
|
|
|
|
|
|
checkNormalHu(cardInHand, tmap2);
|
|
|
|
|
|
System.out.println(tmap2);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}else if(jiangnum>1){
|
|
|
|
|
|
|
|
|
|
|
|
}else{
|
|
|
|
|
|
|
|
|
|
|
|
}*/
|
|
|
|
|
|
|
2026-01-01 07:54:30 +08:00
|
|
|
|
checkNormalHu(cardInHand, map);
|
|
|
|
|
|
|
|
|
|
|
|
List<Integer> suggested = (List<Integer>) map.get("cardResiue");
|
|
|
|
|
|
|
|
|
|
|
|
//返回要打的牌,打后可以听牌
|
|
|
|
|
|
List<Integer> zuizhongchupai = new ArrayList<>();
|
|
|
|
|
|
//如果打出然后可以听牌,跟递归判断出来的牌一样,优先打出这张
|
|
|
|
|
|
if (checktingpai.size() > 0) {
|
|
|
|
|
|
for (Integer integer : checktingpai) {
|
|
|
|
|
|
for (Integer integer1 : suggested) {
|
|
|
|
|
|
if (integer.equals(integer1)) {
|
|
|
|
|
|
zuizhongchupai.add(integer);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//推荐打几个牌都能听牌,需要判断打哪个听的牌更多
|
|
|
|
|
|
if (checktingpai.size() > 1 && zuizhongchupai.size() == 0) {
|
|
|
|
|
|
zuizhongchupai.addAll(checktingpai);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//如果判断听牌和递归出来的牌不一样 优先以听牌为准
|
|
|
|
|
|
if (zuizhongchupai.size() == 0) {
|
|
|
|
|
|
zuizhongchupai.addAll(checktingpai);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//如果判断最后打出牌还未听牌,就用递归判断的牌
|
|
|
|
|
|
if (zuizhongchupai.size() == 0) {
|
|
|
|
|
|
zuizhongchupai.addAll(suggested);
|
|
|
|
|
|
}
|
|
|
|
|
|
System.out.println("zuizhongchupai" + zuizhongchupai);
|
|
|
|
|
|
if (zuizhongchupai.isEmpty()) {
|
|
|
|
|
|
return new ArrayList<>();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// //牌桌上出过的牌 未实现 这里听牌zuizhongchupai 对比 牌桌上出过的牌
|
|
|
|
|
|
// ChangShaSuanFaTest changShaSuanFaTest = new ChangShaSuanFaTest();
|
|
|
|
|
|
// List<Integer> chuguodepai = changShaSuanFaTest.chuguodepai();
|
|
|
|
|
|
// if (chuguodepai.size() > 0 && checktingpai.size() > 0) {
|
|
|
|
|
|
// System.out.println("递归内牌桌上出过的牌 ============ ===============" + chuguodepai);
|
|
|
|
|
|
// Map<Integer, Integer> tinCardCountMap = new HashMap<>();
|
|
|
|
|
|
//
|
|
|
|
|
|
// // 初始化听牌组中所有牌的出现次数为0
|
|
|
|
|
|
// for (Integer card : checktingpai) {
|
|
|
|
|
|
// tinCardCountMap.put(card, 0);
|
|
|
|
|
|
// }
|
|
|
|
|
|
//
|
|
|
|
|
|
// // 统计听牌组中每张牌在牌桌上的实际出现次数
|
|
|
|
|
|
// for (Integer card : checktingpai) {
|
|
|
|
|
|
// if (tinCardCountMap.containsKey(card)) {
|
|
|
|
|
|
// tinCardCountMap.put(card, tinCardCountMap.get(card) + 1);
|
|
|
|
|
|
// }
|
|
|
|
|
|
// }
|
|
|
|
|
|
//
|
|
|
|
|
|
// // 找到最大的出现次数
|
|
|
|
|
|
// Integer maxCount = tinCardCountMap.values().stream()
|
|
|
|
|
|
// .max(Integer::compareTo)
|
|
|
|
|
|
// .orElse(0);
|
|
|
|
|
|
//
|
|
|
|
|
|
// // 判断所有听牌是否都大于等于3次
|
|
|
|
|
|
// boolean allGreaterOrEqualThree = !tinCardCountMap.isEmpty() &&
|
|
|
|
|
|
// tinCardCountMap.values().stream().allMatch(count -> count >= 3);
|
|
|
|
|
|
//
|
|
|
|
|
|
// if (allGreaterOrEqualThree) {
|
|
|
|
|
|
// List<Integer> result = new ArrayList<>();
|
|
|
|
|
|
// // 如果所有听牌都出现大于等于3次,则随机出一张听牌
|
|
|
|
|
|
// Integer randomCard = checktingpai.get(new Random().nextInt(checktingpai.size()));
|
|
|
|
|
|
// System.out.println("递归内 -----所有听牌出现次数都>=3,随机出牌: " + randomCard);
|
|
|
|
|
|
// result.add(randomCard);
|
|
|
|
|
|
// return result;
|
|
|
|
|
|
// }
|
|
|
|
|
|
//
|
|
|
|
|
|
// if (maxCount > 0 && maxCount < 3) {
|
|
|
|
|
|
// // 出现次数大于0但小于3,也直接出次数最多的牌
|
|
|
|
|
|
// List<Integer> maxCards = tinCardCountMap.entrySet().stream()
|
|
|
|
|
|
// .filter(entry -> entry.getValue().equals(maxCount))
|
|
|
|
|
|
// .map(Map.Entry::getKey)
|
|
|
|
|
|
// .collect(Collectors.toList());
|
|
|
|
|
|
//
|
|
|
|
|
|
// if (!maxCards.isEmpty()) {
|
|
|
|
|
|
// List<Integer> result = new ArrayList<>();
|
|
|
|
|
|
// Integer maxCard = Collections.max(maxCards);
|
|
|
|
|
|
// result.add(maxCard);
|
|
|
|
|
|
// return result;
|
|
|
|
|
|
// }
|
|
|
|
|
|
//
|
|
|
|
|
|
// }
|
|
|
|
|
|
// }
|
|
|
|
|
|
return zuizhongchupai;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 计算听牌数量(简化版,只计算数量不具体分析是哪些牌)
|
|
|
|
|
|
private static int countTingCards(List<Integer> hand) {
|
|
|
|
|
|
int tingCount = 0;
|
|
|
|
|
|
|
|
|
|
|
|
// 只尝试添加万、筒、条牌(1-9)
|
|
|
|
|
|
for (int type = 1; type <= 3; type++) {
|
|
|
|
|
|
for (int value = 1; value <= 9; value++) {
|
|
|
|
|
|
int testCard = type * 100 + value;
|
|
|
|
|
|
|
|
|
|
|
|
// 模拟添加这张牌
|
|
|
|
|
|
List<Integer> testHand = new ArrayList<>(hand);
|
|
|
|
|
|
testHand.add(testCard);
|
|
|
|
|
|
Collections.sort(testHand);
|
|
|
|
|
|
|
|
|
|
|
|
// 检查是否能胡牌
|
|
|
|
|
|
Map<String, Object> testMap = new HashMap<>();
|
|
|
|
|
|
if (checkNormalHu(testHand, testMap) == 0) {
|
|
|
|
|
|
tingCount++;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 风牌和箭牌(可能性较小,但也要考虑)
|
|
|
|
|
|
for (int type = 4; type <= 5; type++) {
|
|
|
|
|
|
int max = (type == 4) ? 4 : 3;
|
|
|
|
|
|
for (int value = 1; value <= max; value++) {
|
|
|
|
|
|
int testCard = type * 100 + value;
|
|
|
|
|
|
|
|
|
|
|
|
List<Integer> testHand = new ArrayList<>(hand);
|
|
|
|
|
|
testHand.add(testCard);
|
|
|
|
|
|
Collections.sort(testHand);
|
|
|
|
|
|
|
|
|
|
|
|
Map<String, Object> testMap = new HashMap<>();
|
|
|
|
|
|
if (checkNormalHu(testHand, testMap) == 0) {
|
|
|
|
|
|
tingCount++;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return tingCount;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 或者使用更简洁的lambda写法(确保能进入)
|
|
|
|
|
|
// 或者更灵活的版本,可以指定优先级
|
|
|
|
|
|
public static Integer selectBestCardByPriority(List<Integer> cards) {
|
|
|
|
|
|
if (cards == null || cards.isEmpty()) {
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Integer bestCard = null;
|
|
|
|
|
|
int bestPriority = Integer.MAX_VALUE;
|
|
|
|
|
|
|
|
|
|
|
|
for (Integer card : cards) {
|
|
|
|
|
|
int value = card % 100;
|
|
|
|
|
|
int priority = getCardPriority(value);
|
|
|
|
|
|
|
|
|
|
|
|
if (priority < bestPriority) {
|
|
|
|
|
|
bestPriority = priority;
|
|
|
|
|
|
bestCard = card;
|
|
|
|
|
|
} else if (priority == bestPriority) {
|
|
|
|
|
|
// 优先级相同,比较牌值(小的优先)
|
|
|
|
|
|
if (bestCard == null || value < (bestCard % 100)) {
|
|
|
|
|
|
bestCard = card;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return bestCard;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 牌的优先级(数字越小优先级越高)
|
|
|
|
|
|
private static int getCardPriority(int value) {
|
|
|
|
|
|
if (value == 9) return 1;
|
|
|
|
|
|
if (value == 1) return 2;
|
|
|
|
|
|
if (value == 2 || value == 8) return 3; // 靠近边张
|
|
|
|
|
|
if (value >= 3 && value <= 7) return 4; // 中间牌
|
|
|
|
|
|
return 5; // 其他
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static Integer selectBestCardRemove258(List<Integer> cards) {
|
|
|
|
|
|
if (cards == null || cards.isEmpty()) {
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 如果只有一张牌,直接返回
|
|
|
|
|
|
if (cards.size() == 1) {
|
|
|
|
|
|
return cards.get(0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 创建非258牌的列表
|
|
|
|
|
|
List<Integer> non258Cards = new ArrayList<>();
|
|
|
|
|
|
List<Integer> is258Cards = new ArrayList<>();
|
|
|
|
|
|
|
|
|
|
|
|
// 分类:258牌和非258牌
|
|
|
|
|
|
for (Integer card : cards) {
|
|
|
|
|
|
if (is258Jiang(card)) {
|
|
|
|
|
|
is258Cards.add(card);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
non258Cards.add(card);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
System.out.println("258将牌: " + is258Cards);
|
|
|
|
|
|
System.out.println("非258牌: " + non258Cards);
|
|
|
|
|
|
|
|
|
|
|
|
// 优先从非258牌中选
|
|
|
|
|
|
if (!non258Cards.isEmpty()) {
|
|
|
|
|
|
return selectBestSingleCard(non258Cards);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 如果没有非258牌,从258牌中选
|
|
|
|
|
|
if (!is258Cards.isEmpty()) {
|
|
|
|
|
|
return selectBestSingleCard(is258Cards);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检测是否满足258做将的条件
|
|
|
|
|
|
private static boolean is258Jiang(int card) {
|
|
|
|
|
|
// 提取牌的类型和值
|
|
|
|
|
|
int type = card / 100; // 百位数:1-万,2-筒,3-条,4-风,5-箭
|
|
|
|
|
|
int value = card % 100; // 个位数:牌面值
|
|
|
|
|
|
|
|
|
|
|
|
// 只有万、筒、条有258做将的限制
|
|
|
|
|
|
if (type >= 1 && type <= 3) {
|
|
|
|
|
|
// 258做将:二万(102)、五万(105)、八万(108)
|
|
|
|
|
|
// 二筒(202)、五筒(205)、八筒(208)
|
|
|
|
|
|
// 二条(302)、五条(305)、八条(308)
|
|
|
|
|
|
return value == 2 || value == 5 || value == 8;
|
|
|
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 从候选牌中选出最优的一张牌(优先边张1或9)
|
|
|
|
|
|
public static Integer selectBestSingleCard(List<Integer> cards) {
|
|
|
|
|
|
if (cards == null || cards.isEmpty()) {
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 优先找边张(1或9)
|
|
|
|
|
|
for (Integer card : cards) {
|
|
|
|
|
|
int value = card % 100;
|
|
|
|
|
|
if (value == 1 || value == 9) {
|
|
|
|
|
|
return card;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 没有边张,找靠近边张的牌(2或8)- 注意这里2是258将牌,但我们已经去除了258
|
|
|
|
|
|
for (Integer card : cards) {
|
|
|
|
|
|
int value = card % 100;
|
|
|
|
|
|
if (value == 2 || value == 8) {
|
|
|
|
|
|
return card;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-05 19:40:49 +08:00
|
|
|
|
|
2026-01-01 07:54:30 +08:00
|
|
|
|
// 都没有,返回第一张
|
|
|
|
|
|
return cards.get(0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 主方法
|
|
|
|
|
|
public static void main(String[] args) {
|
|
|
|
|
|
// 测试用例:没有258将牌的情况
|
|
|
|
|
|
List<Integer> test1 = new ArrayList<Integer>();
|
|
|
|
|
|
// test1 = Arrays.asList(208, 204, 203, 202, 107, 106, 105, 103, 102, 101, 103);
|
|
|
|
|
|
// test1.add(208);
|
|
|
|
|
|
// test1.add(204);
|
|
|
|
|
|
// test1.add(203);
|
|
|
|
|
|
// test1.add(202);
|
|
|
|
|
|
// test1.add(107);
|
|
|
|
|
|
// test1.add(106);
|
|
|
|
|
|
// test1.add(105);
|
|
|
|
|
|
// test1.add(103);
|
|
|
|
|
|
// test1.add(102);
|
|
|
|
|
|
// test1.add(101);
|
|
|
|
|
|
// test1.add(101);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// test1.add(208);
|
|
|
|
|
|
// test1.add(207);
|
|
|
|
|
|
// test1.add(205);
|
|
|
|
|
|
// test1.add(204);
|
|
|
|
|
|
// test1.add(203);
|
|
|
|
|
|
// test1.add(202);
|
|
|
|
|
|
// test1.add(201);
|
|
|
|
|
|
// test1.add(109);
|
|
|
|
|
|
// test1.add(108);
|
|
|
|
|
|
// test1.add(106);
|
|
|
|
|
|
// test1.add(205);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// test1.add(205);
|
|
|
|
|
|
// test1.add(203);
|
|
|
|
|
|
// test1.add(202);
|
|
|
|
|
|
// test1.add(201);
|
|
|
|
|
|
// test1.add(204);
|
|
|
|
|
|
// test1.add(107);
|
|
|
|
|
|
// test1.add(106);
|
|
|
|
|
|
// test1.add(105);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// test1.add(201);
|
|
|
|
|
|
// test1.add(107);
|
|
|
|
|
|
// test1.add(107);
|
|
|
|
|
|
// test1.add(104);
|
|
|
|
|
|
|
|
|
|
|
|
// test1.add(101);
|
|
|
|
|
|
// test1.add(102);
|
|
|
|
|
|
// test1.add(103);
|
|
|
|
|
|
// test1.add(104);
|
|
|
|
|
|
// test1.add(109);
|
|
|
|
|
|
// test1.add(201);
|
|
|
|
|
|
// test1.add(204);
|
|
|
|
|
|
// test1.add(204);
|
|
|
|
|
|
// test1.add(206);
|
|
|
|
|
|
// test1.add(207);
|
|
|
|
|
|
// test1.add(207);
|
|
|
|
|
|
// test1.add(209);
|
|
|
|
|
|
// test1.add(209);
|
|
|
|
|
|
// test1.add(209);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
//
|
|
|
|
|
|
test1.add(206);
|
|
|
|
|
|
test1.add(205);
|
|
|
|
|
|
test1.add(204);
|
|
|
|
|
|
test1.add(203);
|
|
|
|
|
|
test1.add(202);
|
2026-01-05 19:40:49 +08:00
|
|
|
|
test1.add(209);
|
2026-01-01 07:54:30 +08:00
|
|
|
|
test1.add(201);
|
|
|
|
|
|
test1.add(108);
|
|
|
|
|
|
test1.add(107);
|
|
|
|
|
|
test1.add(106);
|
|
|
|
|
|
|
|
|
|
|
|
test1.add(104);
|
|
|
|
|
|
|
|
|
|
|
|
test1.add(103);
|
2026-01-05 19:40:49 +08:00
|
|
|
|
test1.add(102);
|
2026-01-01 07:54:30 +08:00
|
|
|
|
test1.add(101);
|
|
|
|
|
|
// test1.add(103);
|
|
|
|
|
|
// test1.add(102);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
System.out.println("手牌:" + test1);
|
|
|
|
|
|
List<Integer> bestDiscard = analyzeBestDiscard(test1);
|
|
|
|
|
|
// Integer integer = selectBestCardByPriority(bestDiscard);
|
|
|
|
|
|
Integer integer1 = selectBestCardRemove258(bestDiscard);
|
|
|
|
|
|
|
|
|
|
|
|
System.out.println("ww" +integer1);
|
|
|
|
|
|
// if (!bestDiscard.isEmpty()) {
|
|
|
|
|
|
// System.out.println("建议出牌:" + bestDiscard.get(0));
|
|
|
|
|
|
// } else {
|
|
|
|
|
|
// System.out.println("没有出牌建议");
|
|
|
|
|
|
// }
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|