package taurus.util; import com.game.Util; import java.util.*; import java.util.stream.Collectors; public class ChangshaWinSplitCard { // 统计牌数 public static int[][] countTiles(List 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 cardInHand, Map 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); if (group == 0) { // 剩余12张牌需要组成4个顺子或刻子 counts[type][value] += 2; return 0; } } } } List cardResiue = (List) map.get("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); break; } } haveBetween(cardResiue, cardInHand); System.out.println("可以打出的牌:" + cardResiue); System.out.println("总共差几手牌:" + map.get("remainingMelds")); return group; } public static void haveBetween(List cards, List cardInHand) { if (cards.size() <= 1 || cardInHand.size() <= 1) { return; } // 创建要删除的牌列表 Set 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); } } // 递归检查是否可以组成顺子或刻子 public static int checkMelds(int[][] counts, int remainingMelds, Map 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 cardResiue = convertCountToCards(counts); // System.out.println("只差几手牌" + remainingMelds + "," + cardResiue); if (cardResiue.size()!=0) { //优先保留连续张 removeConsecutivePairs1(cardResiue); } if (cardResiue.size()!=0) { //第二优先保留对子 removeConsecutivePairsTwo(cardResiue); } //最后保留坎张 //如果坎张,为空则取边张 if (cardResiue.size()!=0) { List tmp3 = new ArrayList<>(); tmp3.addAll(cardResiue); removeConsecutivePairs2(cardResiue); if (cardResiue.size() == 0) { //取边站 cardResiue = qubianzhang(tmp3); } } 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++) { 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); } else { map.put("remainingMelds", remainingMelds); map.put("cardResiue", cardResiue); } } } } else if (remainingMelds == 1) { map.put("remainingMelds", remainingMelds); map.put("cardResiue", cardResiue); } } return remainingMelds; } public static void removeConsecutivePairsTwo(List cards) { if (cards == null || cards.size() < 2) { return; } // 先排序 List sorted = new ArrayList<>(cards); Collections.sort(sorted); List result = new ArrayList<>(); int i = 0; while (i < sorted.size()) { // 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) { // 跳过对子 i += 2; } else { // 保留这张牌 result.add(sorted.get(i)); i++; } } cards.clear(); cards.addAll(result); } // 辅助方法 public static void removeConsecutivePairs1(List cards) { if (cards == null || cards.size() < 2) { return; } // 先排序 List sorted = new ArrayList<>(cards); Collections.sort(sorted); List 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); } public static List qubianzhang(List cards) { List 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; } public static void removeConsecutivePairs2(List cards) { if (cards == null || cards.size() < 2) { return; } // 先排序 List 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 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 convertCountToCards(int[][] counts) { List 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 checktingpai(List cardhand) { List tpcards = new ArrayList<>(); List 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; } 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 cardInHand,Map> map) { Map 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 tmpI = new ArrayList<>(); tmpI.add(key); map.put("jiangcard", tmpI); } } return jiangnum; } // 分析最优出牌 public static List analyzeBestDiscard(List cardInHand) { //听牌 //返回要打的牌,打后可以听牌 List checktingpai = checktingpai(cardInHand); System.out.println("打出这种牌后可以听牌 " + checktingpai); Map map = new HashMap<>(); Map> jmap = new HashMap<>();//对将map //先定将,再做分析 /* int jiangnum = checkduijiang(cardInHand,jmap); System.out.println(jmap.get("jiangcard").get(0)); if (jiangnum==1){ //如果只有一对将 //去将 后判断方案 List tmphc = new ArrayList<>(); tmphc.addAll(cardInHand); Util.removeCard(tmphc,jmap.get("jiangcard").get(0),2); Map tmap = new HashMap<>(); checkNormalHu(tmphc, tmap); //不去将 判断 Map tmap2 = new HashMap<>(); System.out.println(cardInHand); checkNormalHu(cardInHand, tmap2); System.out.println(tmap2); }else if(jiangnum>1){ }else{ }*/ checkNormalHu(cardInHand, map); List suggested = (List) map.get("cardResiue"); //返回要打的牌,打后可以听牌 List 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 chuguodepai = changShaSuanFaTest.chuguodepai(); // if (chuguodepai.size() > 0 && checktingpai.size() > 0) { // System.out.println("递归内牌桌上出过的牌 ============ ===============" + chuguodepai); // Map 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 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 maxCards = tinCardCountMap.entrySet().stream() // .filter(entry -> entry.getValue().equals(maxCount)) // .map(Map.Entry::getKey) // .collect(Collectors.toList()); // // if (!maxCards.isEmpty()) { // List result = new ArrayList<>(); // Integer maxCard = Collections.max(maxCards); // result.add(maxCard); // return result; // } // // } // } return zuizhongchupai; } // 计算听牌数量(简化版,只计算数量不具体分析是哪些牌) private static int countTingCards(List 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 testHand = new ArrayList<>(hand); testHand.add(testCard); Collections.sort(testHand); // 检查是否能胡牌 Map 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 testHand = new ArrayList<>(hand); testHand.add(testCard); Collections.sort(testHand); Map testMap = new HashMap<>(); if (checkNormalHu(testHand, testMap) == 0) { tingCount++; } } } return tingCount; } // 或者使用更简洁的lambda写法(确保能进入) // 或者更灵活的版本,可以指定优先级 public static Integer selectBestCardByPriority(List 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 cards) { if (cards == null || cards.isEmpty()) { return null; } // 如果只有一张牌,直接返回 if (cards.size() == 1) { return cards.get(0); } // 创建非258牌的列表 List non258Cards = new ArrayList<>(); List 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 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; } } // 都没有,返回第一张 return cards.get(0); } // 主方法 public static void main(String[] args) { // 测试用例:没有258将牌的情况 List test1 = new ArrayList(); // 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); test1.add(209); test1.add(201); test1.add(108); test1.add(107); test1.add(106); test1.add(104); test1.add(103); test1.add(102); test1.add(101); // test1.add(103); // test1.add(102); System.out.println("手牌:" + test1); List 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("没有出牌建议"); // } } }