package taurus.util; import com.game.Util; import com.taurus.core.entity.ITArray; import com.taurus.core.entity.ITObject; import com.taurus.core.entity.TArray; import com.taurus.core.entity.TObject; import com.taurus.core.util.Logger; import hunan.HandAnalysis; import hunan.HuNanChangSha; import org.w3c.dom.html.HTMLDOMImplementation; import java.util.*; import java.util.stream.Collectors; public class ChangShaSuanFaTest { public int drawnCards; //摸牌 public static List tinCards = new ArrayList<>(); public static boolean isTin = false; public static boolean isChi =false; public static boolean isPeng=false; public static List chuguodepai = new ArrayList<>(); // 长沙麻将特殊规则:二五八将 private static final Set JIANG_PAIS; static { Set jiangSet = new HashSet<>(); jiangSet.add(2); jiangSet.add(5); jiangSet.add(8); JIANG_PAIS = Collections.unmodifiableSet(jiangSet); } /** * 统一日志输出方法 */ private void logInfo(String message) { // 注释掉日志输出,减少测试时的信息干扰 // System.out.println(message); } private final static Logger log; static { log = Logger.getLogger(ChangShaSuanFaTest.class); } public String teshuXuanPai(List cardInhand, List pengCard, List chowGroup,List gangdepai){ String outcard = ""; //特殊牌型处理 //判断是否6对,是否快满足清一色 //判断清一色 //查询吃碰杠是否是同一色 List checkcpg = new ArrayList<>(); checkcpg.addAll(pengCard); checkcpg.addAll(chowGroup); checkcpg.addAll(gangdepai); boolean qys = false; if (checkcpg.size()>0){ int tmp1 = 0; int tmp2 = 0; for (int i=0;i10){ //落地已经有了不同色,所以可以进行 qys = true; } } Map> cardst2 = new HashMap<>(); collectionCardse(cardInhand,cardst2); //检测手牌 if (!qys){ //检测同色牌有多少张 for (Map.Entry> entry : cardst2.entrySet()) { List values = entry.getValue(); if (values.size() > 0 && values.size() <= 2){ //如果只有一张同色,那就打这一张 return entry.getValue().get(0).toString(); } } } }else{ //检测手牌 if (cardInhand.size()==14){ //检测同色牌有多少张 Map> cardst = new HashMap<>(); collectionCardse(cardInhand,cardst); for (Map.Entry> entry : cardst.entrySet()) { if (entry.getValue().size()==1){ //如果只有一张同色,那就打这一张 return entry.getValue().get(0).toString(); } } } } return null; } public void collectionCardse(List cardInhand,Map>cardst){ Map> se = new HashMap<>(); int wanzi = 0; int tongzi = 0; List wanzicards = new ArrayList<>(); List tongzicards = new ArrayList<>(); for (int i=0;i 听牌检测 -> 策略制定 -> 出牌选择 */ public String outCardSuanFa(List cardInhand, List pengCard, List chowGroup,List gangdepai, List resultList) { List handCards = new ArrayList<>(cardInhand); List pinghuhandCards = new ArrayList<>(cardInhand); List pinghuhandCards1 = new ArrayList<>(cardInhand); chuguodepai.addAll(resultList); log.info("cardInhand:"+cardInhand); log.info("pengCard:"+pengCard); log.info("chowGroup:"+chowGroup); log.info("gangdepai:"+gangdepai); log.info("resultList:"+resultList); handCards.addAll(chowGroup); handCards.addAll(pengCard); handCards.sort(Integer::compareTo); int i = countPengGroups(handCards, pengCard); //刻子的数量 int pisCardsCount = countPairs(handCards);//分析七小对 //将将胡 boolean jiangHu = isJiangHu(handCards); boolean isPengPengHu = hasThreeKeziAndTwoPairs(handCards, pengCard); //清一色碰碰胡 // boolean hasBigSuit = isAllSameSuit(handCards, pengCard); // 分析是否有可能的清一色花色 int hasBigSuit = checkDahu(handCards,chowGroup,pengCard,gangdepai); System.out.println("resultList22 +++++++++++++++++++++++================" + hasBigSuit); //特殊牌型处理 String tscard = teshuXuanPai(cardInhand, pengCard, chowGroup, gangdepai); if (tscard!=null){ System.out.printf("特殊牌型处理111 +++++++++++++++++++++++++++++++++++"); return tscard; } //循环去一张是否能大胡听牌 Map> afterDahuOp = quyizhangDahuTingPai(cardInhand,chowGroup,pengCard,gangdepai); System.out.println("afterDahuOp:"+afterDahuOp); if (afterDahuOp.size()>0){ //执行 int xuanzecard1 = 0; int caozuonum1 = 0; List tmpres = new ArrayList<>(); tmpres.addAll(resultList); tmpres.addAll(cardInhand); for (Map.Entry> entry : afterDahuOp.entrySet()) { if (caozuonum1==0){ caozuonum1 = getTingPainum(entry.getValue(),tmpres); xuanzecard1 = entry.getKey(); } if (caozuonum1 < getTingPainum(entry.getValue(),tmpres)){ caozuonum1 = getTingPainum(entry.getValue(),tmpres); xuanzecard1 = entry.getKey(); } } //判断是否绝听 if(caozuonum1>0){ System.out.printf("大胡222 +++++++++++++++++++++++++++++++++++"+caozuonum1); return String.valueOf(xuanzecard1); } } //循环去一张还能听多牌 Map> afterOp = quyizhangTingPai(cardInhand); System.out.println("afterOp:"+afterOp); if (afterOp.size()>0){ //执行 int xuanzecard = 0; int caozuonum = 0; List tmpres = new ArrayList<>(); tmpres.addAll(resultList); tmpres.addAll(cardInhand); for (Map.Entry> entry : afterOp.entrySet()) { if (caozuonum==0){ caozuonum = getTingPainum(entry.getValue(),tmpres); xuanzecard = entry.getKey(); } if (caozuonum < getTingPainum(entry.getValue(),tmpres)){ caozuonum = getTingPainum(entry.getValue(),tmpres); xuanzecard = entry.getKey(); } } if(caozuonum>0){ //绝听要换牌 System.out.printf("特殊牌型处理222 +++++++++++++++++++++++++++++++++++"); return String.valueOf(xuanzecard); } } //六对 if (pisCardsCount >= 6 && pengCard.size() == 0 && chowGroup.size() == 0) { System.out.println("七小对数量大于等于6+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); //单张牌 List danzhang = danzhang(handCards); isTin = true; Map danzhangCountMap = new HashMap<>(); for (Integer card2 : danzhang) { danzhangCountMap.put(card2, 0); tinCards.add(card2); } for (Integer card3 : resultList) { if (danzhangCountMap.containsKey(card3)) { danzhangCountMap.put(card3, danzhangCountMap.get(card3) + 1); } } // 找到最大的出现次数 Integer maxCount = danzhangCountMap.values().stream() .max(Integer::compareTo) .orElse(0); // 找出出现次数等于最大次数的牌 List maxCards = danzhangCountMap.entrySet().stream() .filter(entry -> entry.getValue().equals(maxCount)) .map(Map.Entry::getKey) .collect(Collectors.toList()); //102/106 // 找出maxCards中那些在handCards中出现3次的牌 List threeCardsFromMax = maxCards.stream() .filter(card3 -> Collections.frequency(handCards, card3) == 3) .collect(Collectors.toList()); if (threeCardsFromMax.size() > 0) { List zeroCountCards = threeCardsFromMax.stream() .filter(card3 -> danzhangCountMap.getOrDefault(card3, 0) == 0) .collect(Collectors.toList()); System.out.println("出现次数为0的牌: " + zeroCountCards); // 从maxCards中移除 if (!zeroCountCards.isEmpty()) { maxCards.removeAll(zeroCountCards); System.out.println("移除后maxCards: " + maxCards); } } System.out.println("maxCards中出现3次的牌: " + threeCardsFromMax); if (threeCardsFromMax.size() > 0) { return String.valueOf(threeCardsFromMax.get(0)); } Integer maxCard = maxCards.isEmpty() ? null : maxCards.get(0); System.out.println("maxCard++++++++++++++++++++++++++++++" + maxCard); return String.valueOf(maxCard); } // if (isTin) { // System.out.println(" "); // System.out.println("==============听牌牌组-=-==========" + tinCards); // System.out.println("=========================drawnCards +++++++++++++++++++++++================" + drawnCards); // List tin = new ArrayList<>(tinCards); // System.out.println(" "); // System.out.println("---------------tin--------------"+tin); // ai ai1 = new ai(); //// ChangshaMahjongAI ai = new ChangshaMahjongAI(); // PlayerState playerState = new PlayerState(); // playerState.handCards = pinghuhandCards; // // // for (int j = 0; j < chowGroup.size(); j += 3) { // List chigroup = new ArrayList<>(chowGroup.subList(j, Math.min(j + 3, chowGroup.size()))); // if (chigroup.size() == 3) { // playerState.addChiGroup(chigroup); // } // } // // // for (int h = 0; h < pengCard.size(); h += 3) { // List penggroup = new ArrayList<>(pengCard.subList(h, Math.min(h + 3, pengCard.size()))); // if (penggroup.size() == 3) { // playerState.addPongGroup(penggroup); // } // } // // // int bestDiscard = ai1.findBestDiscard(playerState); // // // if (bestDiscard != 0) { // if (ai1.isTinAi) { // isTin = true; // } // if (tin.size() chigroup = new ArrayList<>(chowGroup.subList(j, Math.min(j + 3, chowGroup.size()))); // if (chigroup.size() == 3) { // playerState.addChiGroup(chigroup); // } // } // // System.out.println("吃牌听牌playerState.chiGroups +++++++++++++++++++ " + playerState.chiGroups); // System.out.println("吃牌听牌最新 ai出牌--------------------================"); // for (int h = 0; h < pengCard.size(); h += 3) { // List penggroup = new ArrayList<>(pengCard.subList(h, Math.min(h + 3, pengCard.size()))); // if (penggroup.size() == 3) { // playerState.addPongGroup(penggroup); // } // } // // // int bestDiscard = ai1.findBestDiscard(playerState); // // // if (bestDiscard != 0) { // if (ai1.isTinAi) { // isTin = true; // isChi =false; // } // System.out.println("吃牌听牌出牌长麻 ++++++++++++++++++++++++++++++++++++++"); // log.info("吃牌听牌出牌长麻==============================="+bestDiscard); // return String.valueOf(bestDiscard); // } // } // // if (isPeng){ // System.out.println("-----------碰牌听牌中-------"); // ai ai1 = new ai(); //// ChangshaMahjongAI ai = new ChangshaMahjongAI(); // PlayerState playerState = new PlayerState(); // playerState.handCards = handCards; // System.out.println("碰牌听牌中chowGroup +++++++++++++++++++++++" + chowGroup); // System.out.println("碰牌听牌中pengCard +++++++++++++++++++++++" + pengCard); // // for (int j = 0; j < chowGroup.size(); j += 3) { // List chigroup = new ArrayList<>(chowGroup.subList(j, Math.min(j + 3, chowGroup.size()))); // if (chigroup.size() == 3) { // playerState.addChiGroup(chigroup); // } // } // // System.out.println("碰牌听牌中playerState.chiGroups +++++++++++++++++++ " + playerState.chiGroups); // System.out.println("碰牌听牌中最新 ai出牌--------------------================"); // for (int h = 0; h < pengCard.size(); h += 3) { // List penggroup = new ArrayList<>(pengCard.subList(h, Math.min(h + 3, pengCard.size()))); // if (penggroup.size() == 3) { // playerState.addPongGroup(penggroup); // } // } // // // int bestDiscard = ai1.findBestDiscard(playerState); // // // if (bestDiscard != 0) { // if (ai1.isTinAi) { // isTin = true; // isPeng =false; // } // System.out.println("碰牌听牌中出牌长麻 ++++++++++++++++++++++++++++++++++++++"); // log.info("碰牌听牌中出牌长麻==============================="+bestDiscard); // return String.valueOf(bestDiscard); // } // } //将将胡 if (jiangHu && chowGroup.size() == 0) { logInfo("将将胡"); int outcard = selectCardToDiscardJiangHu(pinghuhandCards); System.out.printf("特殊牌型处理444 +++++++++++++++++++++++++++++++++++"); return String.valueOf(outcard); } if (pisCardsCount >= 5 && pengCard.size() == 0 && chowGroup.size() == 0) { //大胡出牌逻辑 - 七小对 int outcard = selectCardToDiscardBig(handCards, pisCardsCount); System.out.printf("特殊牌型处理666 +++++++++++++++++++++++++++++++++++"); return String.valueOf(outcard); } System.out.println(hasBigSuit); if (hasBigSuit>0) { logInfo("执行清一色策略,尝试优化花色分布"); // 调用清一色特定出牌策略 int outcard = selectCardToDiscardForAllSameSuit(pinghuhandCards, chowGroup, pengCard); if (outcard != -1) { System.out.printf("特殊牌型处理555 +++++++++++++++++++++++++++++++++++"); return String.valueOf(outcard); } } //碰碰胡 List checktingpai1 = TinHuChi.checktingpai(cardInhand); if (i >= 3 && chowGroup.size() == 0 && checktingpai1.size() == 0) { if (i == 4) { Map> stringListMap = separateKeziAndRemaining(pinghuhandCards); List remaining = stringListMap.get("remaining"); Map danzhangCountMap = new HashMap<>(); //循环获取剩余的牌 for (Object remaining1 : remaining) { danzhangCountMap.put((Integer) remaining1, 0); } for (Integer card3 : resultList) { if (danzhangCountMap.containsKey(card3)) { danzhangCountMap.put(card3, danzhangCountMap.get(card3) + 1); } } Integer maxCount = danzhangCountMap.values().stream() .max(Integer::compareTo) .orElse(0); List maxCards = danzhangCountMap.entrySet().stream() .filter(entry -> entry.getValue().equals(maxCount)) .map(Map.Entry::getKey) .collect(Collectors.toList()); Integer maxCard = maxCards.isEmpty() ? null : maxCards.get(0); return String.valueOf(maxCard); } } //七小对清一色 List qixiaoduiqingyise = qixiaoduiqingyise(handCards); boolean isqingyiseqixiaodui = hasFourOrMorePairs(qixiaoduiqingyise); if (isqingyiseqixiaodui && pengCard.size() == 0 && chowGroup.size() == 0) { logInfo("执行清一色七小对策略,尝试优化七小对分布"); // 调用花色分析的清一色七小对出牌策略 String discardCard = selectCardToDiscardAllSameSuitQiXiaoDuiBySuit(handCards); if (discardCard != null) { System.out.printf("特殊牌型处理888 +++++++++++++++++++++++++++++++++++"); return discardCard; } } if (hasBigSuit>0 && i >= 3 && chowGroup.size() == 0) { logInfo("清一色碰碰胡"); String discardCard = selectCardToDiscardPengPeng(pinghuhandCards); if (discardCard != null) { System.out.printf("特殊牌型处理999 +++++++++++++++++++++++++++++++++++"); return discardCard; } } //调平胡递归 List integers = ChangshaWinSplitCard.analyzeBestDiscard(pinghuhandCards); System.out.println("integers:"+integers); if(integers.size()>0) { int integer = 0; integer = selectBestCardByPriority(integers); Map> mapduijiang = new HashMap<>(); int duijiangnum = checkduijiang(pinghuhandCards); if (integers.size() > 1 && duijiangnum == 0) { integer = selectBestCardRemove258(integers); } //判断是否可以开杠 int cardnum = 0; if (integer > 0) { cardnum = Util.cardNum(integer, cardInhand); } if (integers.size() > 0 && cardnum < 4) { System.out.println("平胡最新出牌策略=============================================== 666 " + integer); List guzhangc = new ArrayList<>(); for(int k : pinghuhandCards){ if(integer-1==k||integer+1==k||integer-2==k||integer+2==k){ //门子 guzhangc.add(integer); } } //没有孤章门子 if(guzhangc.size()==0) { return String.valueOf(integer); } //处理拆对子问题 //如果手牌没有对将则拆对子,等将下听 //1、判断是否有对将 int duijiangnum2 = checkduijiang(pinghuhandCards); //2拆对子 //获取对子 if(duijiangnum2==0) { // 1. 统计牌型 Map countMap = new HashMap<>(); for (int c : pinghuhandCards) { countMap.put(c, countMap.getOrDefault(c, 0) + 1); } List chaiduizi = new ArrayList<>(); for (Map.Entry entry : countMap.entrySet()) { if (entry.getValue() == 2) { if (entry.getKey()%100==2||entry.getKey()%100==5||entry.getKey()%100==8){ }else{ chaiduizi.add(entry.getKey()); } } } System.out.println("chaiduizi:" + chaiduizi); List kydongduizi = new ArrayList<>(); List tmps = new ArrayList<>(); tmps.addAll(chaiduizi); for (int c : chaiduizi) { if(c%100!=2&&c%100!=5&&c%100!=8){ for(int k : pinghuhandCards){ if(c-1==k||c+1==k){ //门子 kydongduizi.add(c); } } } } chaiduizi.removeAll(kydongduizi); System.out.println("kydongduizi:" + chaiduizi); if(chaiduizi.size()>0) { return String.valueOf(chaiduizi.get(0)); }else{ if (tmps.size() > 0) { for (int a : tmps) { if (a%100==9||a%100==1){ return String.valueOf(a); } } } } }else if(duijiangnum2==1){ int jiangcards = 0; // 1. 统计牌型 Map countMap = new HashMap<>(); for (int c : pinghuhandCards) { countMap.put(c, countMap.getOrDefault(c, 0) + 1); } List chaiduizi = new ArrayList<>(); for (Map.Entry entry : countMap.entrySet()) { if (entry.getValue() == 2) { if (entry.getKey()%100==2||entry.getKey()%100==5||entry.getKey()%100==8){ jiangcards = entry.getKey(); }else{ chaiduizi.add(entry.getKey()); } } } System.out.println("jiangcards:" + jiangcards); if (jiangcards > 0&&chaiduizi.size()<2) { // //去掉将判断孤章 List qudiaohou = new ArrayList<>(); qudiaohou.addAll(cardInhand); Util.removeCard(qudiaohou,jiangcards,2); Map map3 = new HashMap<>(); ChangshaWinSplitCard.checkNormalHu(qudiaohou, map3); List outs =(List) map3.get("cardResiue"); if (outs.size()>0){ return String.valueOf(outs.get(0)); } } //先过滤2,5,8 List kydongduizi = new ArrayList<>(); for (int c : chaiduizi) { for(int k : pinghuhandCards){ if(c-1==k||c+1==k){ //门子 if(!kydongduizi.contains(c)){ kydongduizi.add(c); } } } } if(kydongduizi.size()==chaiduizi.size()&&chaiduizi.size()>0) { return String.valueOf(chaiduizi.get(0)); } chaiduizi.removeAll(kydongduizi); if(chaiduizi.size()>0) { return String.valueOf(chaiduizi.get(0)); }else{ return String.valueOf(integer); } }else{ return String.valueOf(integer); } } } //先拆对子 //再拆门子 // ai ai1 = new ai(); //// ChangshaMahjongAI ai = new ChangshaMahjongAI(); // PlayerState playerState = new PlayerState(); // playerState.handCards = pinghuhandCards; // System.out.println("chowGroup +++++++++++++++++++++++" + chowGroup); // System.out.println("pengCard +++++++++++++++++++++++" + pengCard); // // for (int j = 0; j < chowGroup.size(); j += 3) { // List chigroup = new ArrayList<>(chowGroup.subList(j, Math.min(j + 3, chowGroup.size()))); // if (chigroup.size() == 3) { // playerState.addChiGroup(chigroup); // } // } // // System.out.println("playerState.chiGroups +++++++++++++++++++ " + playerState.chiGroups); // System.out.println("最新 ai出牌--------------------================"); // for (int h = 0; h < pengCard.size(); h += 3) { // List penggroup = new ArrayList<>(pengCard.subList(h, Math.min(h + 3, pengCard.size()))); // if (penggroup.size() == 3) { // playerState.addPongGroup(penggroup); // } // } //int bestDiscard = ai1.findBestDiscard(playerState); // System.out.println("bestDiscard:"+bestDiscard); /* if (bestDiscard != 0) { if (ai1.isTinAi) { isTin = true; } System.out.println("Ai出牌长麻 ++++++++++++++++++++++++++++++++++++++"); log.info("Ai出牌长麻==============================="); return String.valueOf(bestDiscard); }*/ // 1. 手牌分析 - 识别刻子、顺子、对子等牌型 HandAnalysis analysis = analyzeHand(pinghuhandCards1); //最终出的牌 int outcard = selectCardToDiscard(pinghuhandCards1, analysis); logInfo("\n===== 手牌分析结果 ====="); logInfo("按花色分组的牌: " + analysis.cardsBySuit); logInfo("已完成的刻子/顺子: " + analysis.completedMelds); logInfo("对子: " + analysis.pairs); logInfo("孤张牌: " + analysis.isolatedCards); logInfo("面子数量: " + analysis.meldCount); logInfo("对子数量: " + analysis.pairCount); logInfo("听牌状态: " + analysis.isTingPai); if (analysis.isTingPai) { logInfo("可胡牌: " + analysis.tingCards); } logInfo("向听数: " + analysis.shantenCount); logInfo("有龙七对潜力: " + analysis.hasLongQiDuiPotential); logInfo("有碰碰胡潜力: " + analysis.hasPengPengHu); logInfo("剩余需要分析的牌: " + analysis.remainingCards); //如果已经是听牌状态的情况 if (analysis.isTingPai) { System.out.println("已经是听牌状态----" + drawnCards); return String.valueOf(drawnCards); } if (isJiangPai(outcard)&&integers.size()>0){ return String.valueOf(integers.get(0)); } System.out.println("最终出的牌------" + outcard); return String.valueOf(outcard); } // 从候选牌中选出最优的一张牌,优先去除258将牌 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 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==5||value==8) { return 5; } if (value == 2 || value == 8) return 3; // 靠近边张 if (value >= 3 && value <= 7) return 4; // 中间牌 return 5; // 其他 } //听牌之后去分析出牌 private String isTinOutCard(List tinCards, List resultList) { // 统计听牌组中每张牌在牌桌上的出现次数 Map tinCardCountMap = new HashMap<>(); // 初始化听牌组中所有牌的出现次数为0 for (Integer card : tinCards) { tinCardCountMap.put(card, 0); } // 统计听牌组中每张牌在牌桌上的实际出现次数 for (Integer card : resultList) { 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) { isTin = false; // 如果所有听牌都出现大于等于3次,则随机出一张听牌 Integer randomCard = tinCards.get(new Random().nextInt(tinCards.size())); System.out.println("所有听牌出现次数都>=3,随机出牌: " + randomCard); tinCards.remove(randomCard); return "1"; } if (maxCount > 0 && maxCount < 3 && tinCards.size() > 1) { // 出现次数大于0但小于3,也直接出次数最多的牌 List maxCards = tinCardCountMap.entrySet().stream() .filter(entry -> entry.getValue().equals(maxCount)) .map(Map.Entry::getKey) .collect(Collectors.toList()); if (!maxCards.isEmpty()) { isTin = false; Integer maxCard = Collections.max(maxCards); System.out.println("出现次数大于0,直接出牌: " + maxCard); tinCards.remove(maxCard); return "1"; } } else { return String.valueOf(drawnCards); } // 如果所有听牌在牌桌上都没出现过(maxCount为0),或者需要调整听牌状态 if (maxCount == 0) { //将tinCards转成map格式 return String.valueOf(drawnCards); } // 返回一个默认值或处理逻辑(根据您的实际需求调整) return "0"; } private List danzhang(List handCards) { // 统计每张牌出现的次数 Map cardCountMap = new HashMap<>(); for (Integer card : handCards) { cardCountMap.put(card, cardCountMap.getOrDefault(card, 0) + 1); } System.out.println("统计结果: " + cardCountMap); List singleCards = new ArrayList<>(); // 遍历手牌,计算哪些是单张 for (Map.Entry entry : cardCountMap.entrySet()) { int card = entry.getKey(); int count = entry.getValue(); // 计算剩余单张的数量 int remainder = count % 2; // 取模2,奇数就有一个单张 if (remainder == 1) { singleCards.add(card); } } return singleCards; } private int selectCardToDiscardJiangHu(List handCards) { List tempHand = new ArrayList<>(handCards); // 1. 统计牌型 Map countMap = new HashMap<>(); for (int c : tempHand) { countMap.put(c, countMap.getOrDefault(c, 0) + 1); } // 2. 找出所有258对子(候选将牌) List jiangCandidates = new ArrayList<>(); for (Map.Entry entry : countMap.entrySet()) { if (entry.getValue() >= 2 && isCard(entry.getKey())) { jiangCandidates.add(entry.getKey()); } } // 3. 选择最优将牌(优先选择数量多的258对子) Integer bestJiang = selectBestJiang(jiangCandidates, countMap); // 4. 构建需要保留的牌(将牌 + 所有258牌) Set keepCards = new HashSet<>(); // 保留所有258牌(包括将牌) for (int c : tempHand) { if (is258Card(c)) { keepCards.add(c); } } // 5. 找出需要打出的牌(非258牌) List discardCandidates = new ArrayList<>(); for (int c : tempHand) { if (!isCard(c)) { discardCandidates.add(c); } } // 6. 如果有非258牌,优先打出 if (!discardCandidates.isEmpty()) { return discardCandidates.get(0); // 可以优化选择策略 } // 7. 如果全是258牌,但还没有将牌,需要拆牌做将 if (bestJiang == null) { return selectCardToMakeJiang(tempHand, countMap); } // 8. 如果全是258牌且有将牌,打出多余的258牌 return selectRedundant258Card(tempHand, countMap, bestJiang); } /** * 选择最优的将牌 */ private Integer selectBestJiang(List jiangCandidates, Map countMap) { if (jiangCandidates.isEmpty()) { return null; } // 策略:优先选择数量多的258对子(更容易碰成刻子) Integer bestJiang = null; int maxCount = 0; for (int candidate : jiangCandidates) { int count = countMap.get(candidate); if (count > maxCount) { maxCount = count; bestJiang = candidate; } } return bestJiang; } /** * 当没有将牌时,选择一张牌来促成将牌 */ private int selectCardToMakeJiang(List handCards, Map countMap) { // 策略:打出手牌中数量最少的258孤张 for (int c : handCards) { if (countMap.get(c) == 1) { return c; } } // 如果没有孤张,打出手牌中数量最少的258 int minCount = Integer.MAX_VALUE; int worstCard = handCards.get(0); for (int c : handCards) { int count = countMap.get(c); if (count < minCount) { minCount = count; worstCard = c; } } return worstCard; } /** * 选择多余的258牌打出 */ private int selectRedundant258Card(List handCards, Map countMap, int bestJiang) { // 策略:保留将牌,打出非将牌的258孤张 // 1. 先找非将牌的孤张 for (int c : handCards) { if (c != bestJiang && countMap.get(c) == 1) { return c; } } // 2. 找数量最少的非将牌 int minCount = Integer.MAX_VALUE; int worstCard = handCards.get(0); for (int c : handCards) { if (c != bestJiang) { int count = countMap.get(c); if (count < minCount) { minCount = count; worstCard = c; } } } // 3. 如果所有牌都是将牌,只能拆将牌(这种情况很少) if (worstCard == bestJiang) { // 选择一张将牌打出(保留另一张) return bestJiang; } return worstCard; } /** * 判断是否是258牌 */ private boolean isCard(int card) { int cardValue = card % 10; int cardType = card / 100; if (cardValue == 2 || cardValue == 5 || cardValue == 8) { return true; } return false; } /** * 判断手牌中是否有碰牌(刻子) * 刻子定义:三张花色相同且点数相同的牌 * 长沙麻将中碰牌需要满足至少三组刻子 */ /** * 统计手牌中的刻子数量 * 刻子定义:三张花色相同且点数相同的牌 * * @param handCards 手牌列表 * @return 刻子数量 */ private int countPengGroups(List handCards, List pengCard) { List handCards2 = new ArrayList<>(); handCards2.addAll(handCards); // handCards2.addAll(pengCard); System.out.println("碰碰胡 handCards2 ++++++++++++++++" + handCards2); // 按花色分组 Map> suitGroupMap = new HashMap<>(); for (Integer card : handCards2) { int suit = card / 100; suitGroupMap.computeIfAbsent(suit, k -> new ArrayList<>()).add(card); } logInfo("手牌花色分组: " + suitGroupMap.keySet().size() + " 种花色"); int keziCount = 0; // 对每个花色分别统计刻子 for (Map.Entry> entry : suitGroupMap.entrySet()) { int suit = entry.getKey(); List suitCards = entry.getValue(); // 统计该花色下每张牌的数量 Map valueCountMap = new HashMap<>(); for (Integer card : suitCards) { int value = card % 100; valueCountMap.put(value, valueCountMap.getOrDefault(value, 0) + 1); } // 找出该花色的刻子 for (Map.Entry valueEntry : valueCountMap.entrySet()) { if (valueEntry.getValue() >= 3) { keziCount++; int cardValue = suit * 100 + valueEntry.getKey(); logInfo("找到刻子: " + getCardName(cardValue) + " (花色:" + getSuitName(suit) + ", 点数:" + valueEntry.getKey() + ", 数量:" + valueEntry.getValue() + ")"); } } } logInfo("手牌中共找到 " + keziCount + " 组刻子"); return keziCount; } //分析碰碰胡是否听牌 public boolean hasThreeKeziAndTwoPairs(List handCards, List pengCard) { List handCards2 = new ArrayList<>(); handCards2.addAll(handCards); // handCards2.addAll(pengCard); // 统计每张牌出现的次数 Map cardCountMap = new HashMap<>(); for (Integer card : handCards2) { cardCountMap.put(card, cardCountMap.getOrDefault(card, 0) + 1); } int keziCount = 0; // 刻子数(三张相同) int pairCount = 0; // 对子数(两张相同) // 统计刻子和对子 for (int count : cardCountMap.values()) { if (count >= 3) { // 如果有3张或以上,可以算作一个刻子 keziCount++; // 如果有4张,剩余的1张不能算对子,但可能算单张 if (count >= 4) { // 4张可以看作1个刻子+1张单牌,或者如果有需要可以调整 // 这里暂时不额外处理 } } else if (count == 2) { pairCount++; } } // 检查是否有至少3个刻子和至少2个对子 return keziCount >= 3 && pairCount >= 2; } //拿出剩余牌 private static Map> separateKeziAndRemaining(List handCards) { List> keziList = new ArrayList<>(); Set remainingCardsSet = new HashSet<>(); // 使用Set去重 if (handCards == null || handCards.isEmpty()) { Map> result = new HashMap<>(); result.put("kezi", keziList); result.put("remaining", new ArrayList<>()); return result; } // 统计每张牌的数量 Map countMap = new HashMap<>(); for (Integer card : handCards) { countMap.put(card, countMap.getOrDefault(card, 0) + 1); } // 分离刻子 for (Map.Entry entry : countMap.entrySet()) { int card = entry.getKey(); int count = entry.getValue(); if (count >= 3) { // 添加刻子 List kezi = new ArrayList<>(); for (int i = 0; i < 3; i++) { kezi.add(card); } keziList.add(kezi); // 如果还有剩余,加入Set去重 if (count > 3) { remainingCardsSet.add(card); } } else { // 全部加入剩余牌Set remainingCardsSet.add(card); } } // 将Set转换为List List remainingCards = new ArrayList<>(remainingCardsSet); Map> result = new HashMap<>(); result.put("kezi", keziList); result.put("remaining", remainingCards); return result; } /** * 辅助方法:获取牌的名称 */ private String getCardName(int card) { int suit = card / 100; int value = card % 100; String suitName = getSuitName(suit); return suitName + value; } private boolean isJiangHu(List handCards) { // 统计手牌中258牌的数量 int count258 = 0; for (Integer card : handCards) { if (is258Card(card)) { count258++; } } // 判断258牌是否大于7张 return count258 >= 10; } /** * 判断一张牌是否是258牌 */ private static boolean is258Card(int card) { // 假设牌型编码规则: // 101-109: 一万到九万 // 201-209: 一筒到九筒 // 301-309: 一条到九条 // 获取牌的数字(个位数) int cardValue = card % 10; // 258牌的数字必须是2、5、8 if (cardValue == 2 || cardValue == 5 || cardValue == 8) { // 确保是万、筒、条(排除风牌等) int cardType = card / 100; return true; } return false; } /** * 七小对大胡策略出牌 * 当有5对以上时,按照七小对胡牌牌型进行出牌 */ public int selectCardToDiscardBig(List handCards, int pisCardsCount) { if (pisCardsCount >= 5) { logInfo("执行七小对大胡策略,当前对子数量: " + pisCardsCount); // 统计每张牌的数量 Map cardCounts = new HashMap<>(); for (int card : handCards) { cardCounts.put(card, cardCounts.getOrDefault(card, 0) + 1); } // 收集单张牌(数量为1的牌) List singleCards = new ArrayList<>(); for (int card : handCards) { if (cardCounts.get(card) == 1) { singleCards.add(card); } } // 如果有单张牌,优先打出这些牌 if (!singleCards.isEmpty()) { // 优先打出边张(1和9) for (int card : singleCards) { int value = card % 100; if (value == 1 || value == 9) { logInfo("打出七小对策略边张单牌: " + card); return card; } } // 其次打出中间不相关的牌(避开容易形成对子的中间牌) for (int card : singleCards) { int value = card % 100; if (value == 2 || value == 8) { logInfo("打出七小对策略边张单牌: " + card); return card; } } // 最后随便选一张单牌 logInfo("打出七小对策略单牌: " + singleCards.get(0)); return singleCards.get(0); } // 如果没有单张牌(所有牌都是对子或刻子),需要拆牌 // 优先拆刻子(保留对子) List tripleCards = new ArrayList<>(); for (int card : handCards) { if (cardCounts.get(card) >= 3) { tripleCards.add(card); // 只需要一张来代表这个刻子 break; } } if (!tripleCards.isEmpty()) { logInfo("七小对策略拆刻子: " + tripleCards.get(0)); return tripleCards.get(0); } // 如果所有都是对子,理论上已经是七小对听牌状态 // 但为了保险,随便选一张 logInfo("七小对策略,所有牌都是对子,随便打出一张: " + handCards.get(0)); return handCards.get(0); } // 不满足七小对条件时的默认返回 return handCards.get(0); } private String selectCardToDiscardPengPengHu(List handCards) { logInfo("开始执行碰碰胡出牌策略"); // 1. 复制手牌,避免修改原始数据 List remainingCards = new ArrayList<>(handCards); // 2. 找出所有刻子(三张相同的牌),不重复利用 List keziList = extractAllKezi(remainingCards); int keziCount = keziList.size() / 3; logInfo("找到刻子: " + keziCount + "组 - " + getKeziNames(keziList)); //去除刻子后的牌 List feiCandidates = findFeiJiangCandidates5(remainingCards); logInfo("去除刻子后的牌: " + (feiCandidates)); int i = selectFromFeiCandidates4(feiCandidates); return String.valueOf(i); } /** * 找出非将牌候选(除了258将牌和刻子之外的牌) */ private List findFeiJiangCandidates5(List cards) { Map cardCount = new HashMap<>(); List feiJiangCandidates = new ArrayList<>(); // 统计剩余牌的数量 for (Integer card : cards) { cardCount.put(card, cardCount.getOrDefault(card, 0) + 1); } for (Map.Entry entry : cardCount.entrySet()) { int card = entry.getKey(); int count = entry.getValue(); int value = card % 100; for (int i = 0; i < count; i++) { feiJiangCandidates.add(card); } } return feiJiangCandidates; } /** * 从非将牌候选中选择要打出的牌 * 优先打孤张,否则再拆牌 */ private int selectFromFeiCandidates4(List feiCandidates) { if (feiCandidates.isEmpty()) { return -1; // 或者抛出异常,根据实际情况处理 } // 1. 优先找出非对子的牌 Integer isolatedCard = findIsolatedCardInFei6(feiCandidates); logInfo("策略: 打出孤张牌 " + getCardName(isolatedCard)); return isolatedCard; } private Integer findIsolatedCardInFei6(List feiCandidates) { // 统计每张牌的出现次数 Map countMap = new HashMap<>(); for (int card : feiCandidates) { countMap.put(card, countMap.getOrDefault(card, 0) + 1); } // 寻找出现次数为 1 的牌(非对子) for (int card : feiCandidates) { if (countMap.get(card) == 1) { return card; } } // 如果没有非对子的牌,返回 null 或选择第一张牌等策略 return feiCandidates.get(0); } private String getKeziNames(List keziList) { if (keziList.isEmpty()) return "无"; Set keziNames = new HashSet<>(); for (int i = 0; i < keziList.size(); i += 3) { keziNames.add(getCardName(keziList.get(i))); } return String.join(", ", keziNames); } /** * 提取所有刻子(三张相同的牌),不重复利用 */ private List extractAllKezi(List cards) { List keziList = new ArrayList<>(); Map cardCount = new HashMap<>(); // 统计每张牌的数量 for (Integer card : cards) { cardCount.put(card, cardCount.getOrDefault(card, 0) + 1); } // 找出所有数量>=3的牌,提取刻子 List cardsToRemove = new ArrayList<>(); for (Map.Entry entry : cardCount.entrySet()) { int card = entry.getKey(); int count = entry.getValue(); if (count >= 3) { // 计算可以组成几个刻子(比如4张相同的牌可以组成1个刻子+1张单牌) int keziGroups = count / 3; for (int i = 0; i < keziGroups; i++) { // 添加3次相同的牌表示一个刻子 keziList.add(card); keziList.add(card); keziList.add(card); } // 标记需要从剩余牌中移除的牌(只移除刻子部分) for (int i = 0; i < keziGroups * 3; i++) { cardsToRemove.add(card); } } } // 从剩余牌中移除刻子 for (Integer card : cardsToRemove) { cards.remove(card); } return keziList; } /** * 最终出的牌 * 优化后的分析逻辑: * 1. 分析剩余牌的搭子和卡隆情况 * 2. 优先打出边张且不是将的牌 * 3. 如果没有,优先打边张1、2,其次2、8,最后随便打 */ public int selectCardToDiscard(List cardInhand, HandAnalysis analysis) { logInfo("\n===== 开始选择出牌策略 ====="); int discardCard = 0; // 如果analysis为null,自动分析手牌 if (analysis == null) { analysis = analyzeHand(cardInhand); } // 获取剩余牌 List remainingCards = new ArrayList<>(analysis.isolatedCards); logInfo("剩余需要分析的牌: " + remainingCards); // 识别搭子和卡隆 List> daAndKa = identifydaka(remainingCards); logInfo("识别到的搭子和卡隆: " + daAndKa); // 将搭子和卡隆中的牌收集起来 Set daka = new HashSet<>(); for (List card : daAndKa) { daka.addAll(card); } logInfo("搭子和卡隆中的牌: " + daka); // 找出所有可能的出牌候选,排除搭子和卡隆中的牌 Set candidates = new HashSet<>(remainingCards); candidates.removeAll(daka); logInfo("排除搭子和卡隆后的候选牌: " + candidates); // 1. 优先打出边张且不是将的牌 List ban = new ArrayList<>(); for (int card : candidates) { int rank = card % 100; // 且不是将牌 if ((rank != 5 && rank != 2 && rank != 8)) { ban.add(card); } } for (int card : candidates) { int rank = card % 100; // 边张是1或9,且不是将牌 if ((rank == 1 || rank == 9) && !analysis.usedInPairs.contains(card)) { ban.add(card); } } if (!ban.isEmpty()) { if (ban.get(0) != 5 && ban.get(0) != 2 && ban.get(0) != 8) { return ban.get(0); } } // 4. 如果以上情况都没有,打出剩余随便哪张 if (!candidates.isEmpty()) { discardCard = candidates.iterator().next(); logInfo("最后选择任意剩余牌: " + discardCard); if (discardCard != 5 && discardCard != 2 && discardCard != 8) { return discardCard; } } // 检查已完成的牌组中是否有带有将的牌 for (Integer meld : analysis.isolatedCards) { // 检查该牌组中是否有牌是将牌 if (analysis.usedInPairs.contains(meld)) { if (meld == 5 || meld == 2 || meld == 8) { return meld; } } } // 如果没有带有将的牌组,选择第一个牌组的第一张牌 if (!analysis.pairs.isEmpty()) { int pcard = 0; for (Integer pair : analysis.pairs) { int card = pair % 100; if (card != 5 && card != 2 && card != 8) { pcard = pair; } } if (pcard != 0) { return pcard; } else { return analysis.pairs.get(0); } } if (!analysis.remainingCards.isEmpty()) { for (Integer pair : analysis.remainingCards) { int card = pair % 100; if (card != 5 && card != 2 && card != 8) { return pair; } } } if (!analysis.completedMelds.isEmpty()) { List validMelds = new ArrayList<>(); for (List meld : analysis.completedMelds) { // 检查整个组合中是否包含要排除的牌 boolean containsExcluded = false; for (Integer card : meld) { int cardValue = card % 100; if (cardValue == 5 || cardValue == 2 || cardValue == 8) { containsExcluded = true; break; } } // 如果整个组合都不包含排除的牌,记录下来 if (!containsExcluded && !meld.isEmpty()) { validMelds.add(meld.get(0)); } } // 如果有有效的组合,返回第一个 if (!validMelds.isEmpty()) { return validMelds.get(0); } // 如果所有组合都包含排除牌,返回第一个组合的第一张牌作为备选 return analysis.completedMelds.get(0).get(0); } if (!analysis.remainingCards.isEmpty()) { for (Integer pair : analysis.remainingCards) { int card = pair % 100; if (card == 5 || card == 2 || card == 8) { return pair; } } } return discardCard; } /** * 识别搭子和卡隆 * 搭子:两张连续的牌,例如1-2,2-3等 * 卡隆:两张中间缺一张的牌,例如1-3(缺2),2-4(缺3)等 */ private List> identifydaka(List cards) { List> daka = new ArrayList<>(); // 按花色分组 Map> cardsBySuit = new HashMap<>(); for (int card : cards) { int suit = card / 100; cardsBySuit.computeIfAbsent(suit, k -> new ArrayList<>()).add(card); } // 记录已经使用过的牌 Set usedCards = new HashSet<>(); // 分析每个花色 for (List suitCards : cardsBySuit.values()) { Collections.sort(suitCards); // 优先找搭子(连续两张) for (int i = 0; i < suitCards.size() - 1; i++) { int card1 = suitCards.get(i); int card2 = suitCards.get(i + 1); if (usedCards.contains(card1) || usedCards.contains(card2)) { continue; // 牌已被使用 } int rank1 = card1 % 100; int rank2 = card2 % 100; // 搭子:连续两张牌 if (rank2 - rank1 == 1) { daka.add(Arrays.asList(card1, card2)); usedCards.add(card1); usedCards.add(card2); i++; // 跳过下一张牌 } } // 再找卡隆(中间差一张) for (int i = 0; i < suitCards.size() - 1; i++) { int card1 = suitCards.get(i); if (usedCards.contains(card1)) { continue; } for (int j = i + 1; j < suitCards.size(); j++) { int card2 = suitCards.get(j); if (usedCards.contains(card2)) { continue; } int rank1 = card1 % 100; int rank2 = card2 % 100; int rankDiff = rank2 - rank1; // 卡隆:中间差一张 if (rankDiff == 2) { daka.add(Arrays.asList(card1, card2)); usedCards.add(card1); usedCards.add(card2); break; } else if (rankDiff > 2) { break; } } } } return daka; } /** * 碰牌判断方法 * * @param opcard 要碰的牌 * @param handCards 当前手牌 * @return 是否允许碰牌 */ public boolean shouldPong(int opcard, List handCards) { // 1. 判断是否有至少两张相同的牌可以碰 int count = 0; for (int card : handCards) { if (card == opcard) { count++; } } if (count < 2) { return false; } // 2. 如果是清一色花色,只能碰相同花色的牌 if (isAllSameSuit1(handCards)) { Integer mainSuit = getMainSuit(handCards); int opcardSuit = opcard / 100; if (mainSuit != null && mainSuit != opcardSuit) { logInfo("清一色模式:不能碰不同花色的牌。目标牌花色:" + getSuitName(opcardSuit) + ", 主花色:" + getSuitName(mainSuit)); return false; } } // 2. 复制手牌并模拟去掉要碰的两张牌 List tempHand = new ArrayList<>(handCards); tempHand.remove(Integer.valueOf(opcard)); tempHand.remove(Integer.valueOf(opcard)); // 3. 检查该牌是否是听牌组中的牌 List tingCards = calculateTingCards(tempHand); if (tingCards.contains(opcard)) { return false; } // 4. 检查该牌是否是顺子的一部分 if (isPartOfSequence(opcard, handCards)) { return false; } // 5. 检查该牌是否是唯一的将牌 if (isOnlyPair(tempHand, opcard)) { return false; } // 其他情况允许碰牌 return true; } /** * 吃牌判断方法 * * @param opcard 要吃的牌 * @param handCards 当前手牌 * @return 是否允许吃牌 */ public boolean shouldChow(int opcard, List handCards) { logInfo("吃牌判断开始, 目标牌: " + opcard + ", 当前手牌: " + handCards); // 1. 如果是清一色花色,只能吃相同花色的牌 if (isAllSameSuit1(handCards)) { Integer mainSuit = getMainSuit(handCards); int opcardSuit = opcard / 100; if (mainSuit != null && mainSuit != opcardSuit) { logInfo("清一色模式:不能吃不同花色的牌。目标牌花色:" + getSuitName(opcardSuit) + ", 主花色:" + getSuitName(mainSuit)); return false; } } // 创建手牌的副本,避免修改原手牌 List handCopy = new ArrayList<>(handCards); // 分析手牌,识别刻子和顺子 HandAnalysis analysis = analyzeHand(handCopy); // 获取剩余牌(未用于刻子或顺子的牌) List remainingCards = analysis.remainingCards; // 检查剩余牌是否能与其他两张牌组成包含opcard的顺子 boolean canChow = canFormChow(opcard, remainingCards); logInfo("吃牌判断结束, 结果: " + canChow); return canChow; } /** * 检查是否能形成包含目标牌的顺子 * @param targetCard 目标牌 * @param handCards 手牌 * @return 是否能吃牌 */ /** * 判断是否应该杠牌(补牌) * * @param proposedCard 要杠的牌 * @param currentHand 当前手牌 * @param (1:暗杠, 2:明杠, 3:加杠, 4:补杠等) * @return 是否应该杠牌 */ public boolean shouldGang(int proposedCard, List currentHand) { logInfo("判断是否应该杠牌: " + proposedCard); // 1. 如果是清一色花色,只能杠相同花色的牌 if (isAllSameSuit1(currentHand)) { Integer mainSuit = getMainSuit(currentHand); int proposedCardSuit = proposedCard / 100; if (mainSuit != null && mainSuit != proposedCardSuit) { logInfo("清一色模式:不能杠不同花色的牌。目标牌花色:" + getSuitName(proposedCardSuit) + ", 主花色:" + getSuitName(mainSuit)); return false; } } // 基础杠牌条件:手牌中至少有3张相同的牌 int count = 0; for (int card : currentHand) { if (card == proposedCard) { count++; } } if (count < 3) { logInfo("手牌中没有足够的相同牌来杠"); return false; } // 这里可以添加更多杠牌决策逻辑,目前只实现清一色优化 return true; } private boolean canFormChow(int targetCard, List handCards) { // 检查是否是字牌(风牌或箭牌),字牌不能组成顺子 int suit = targetCard / 100; if (suit == 4 || suit == 5) { // 4是风牌,5是箭牌 return false; } // 获取目标牌的点数 int targetPoint = targetCard % 100; // 统计手牌中每张牌的数量 Map cardCounts = new HashMap<>(); for (int card : handCards) { cardCounts.put(card, cardCounts.getOrDefault(card, 0) + 1); } // 检查三种可能的顺子组合 // 1. targetCard-2, targetCard-1, targetCard if (targetPoint >= 3) { int card1 = suit * 100 + (targetPoint - 2); int card2 = suit * 100 + (targetPoint - 1); if (cardCounts.containsKey(card1) && cardCounts.containsKey(card2)) { return true; } } // 2. targetCard-1, targetCard, targetCard+1 if (targetPoint >= 2 && targetPoint <= 8) { int card1 = suit * 100 + (targetPoint - 1); int card2 = suit * 100 + (targetPoint + 1); if (cardCounts.containsKey(card1) && cardCounts.containsKey(card2)) { return true; } } // 3. targetCard, targetCard+1, targetCard+2 if (targetPoint <= 7) { int card1 = suit * 100 + (targetPoint + 1); int card2 = suit * 100 + (targetPoint + 2); if (cardCounts.containsKey(card1) && cardCounts.containsKey(card2)) { return true; } } return false; } /** * 计算听牌 * * @param handCards 手牌 * @return 听牌列表 */ private List calculateTingCards(List handCards) { List tingCards = new ArrayList<>(); Map cardCount = countCards(handCards); // 检查每种花色的牌 for (int suit = 1; suit <= 3; suit++) { // 1:万, 2:筒, 3:索 List sameSuitCards = new ArrayList<>(); for (int rank = 1; rank <= 9; rank++) { int card = suit * 100 + rank; int count = cardCount.getOrDefault(card, 0); for (int i = 0; i < count; i++) { sameSuitCards.add(rank); } } // 检查是否有搭子需要补牌 if (sameSuitCards.size() > 0) { // 简单检查:如果有单张牌,可能听它的相邻牌或相同牌 Map rankCount = new HashMap<>(); for (int rank : sameSuitCards) { rankCount.put(rank, rankCount.getOrDefault(rank, 0) + 1); } for (Map.Entry entry : rankCount.entrySet()) { int rank = entry.getKey(); int count = entry.getValue(); // 如果是单张或对子,可能需要补牌 if (count == 1 || count == 2) { // 添加当前牌(可能组成刻子) tingCards.add(suit * 100 + rank); // 添加相邻牌(可能组成顺子) if (rank > 1) { tingCards.add(suit * 100 + (rank - 1)); } if (rank < 9) { tingCards.add(suit * 100 + (rank + 1)); } } } } } return tingCards; } /** * 判断是否是顺子的一部分 * * @param card 要判断的牌 * @param handCards 手牌 * @return 是否是顺子的一部分 */ private boolean isPartOfSequence(int card, List handCards) { int suit = card / 100; int rank = card % 100; // 检查是否存在顺子 // 顺子是三个连续的牌,比如1-2-3,2-3-4等 boolean hasPrevPrev = handCards.contains(suit * 100 + rank - 2); boolean hasPrev = handCards.contains(suit * 100 + rank - 1); boolean hasNext = handCards.contains(suit * 100 + rank + 1); boolean hasNextNext = handCards.contains(suit * 100 + rank + 2); // 检查是否是顺子的中间牌或边牌 return (hasPrev && hasNext) || (hasPrevPrev && hasPrev) || (hasNext && hasNextNext); } /** * 判断是否是唯一的将牌 * * @param tempHand 去掉两张要碰的牌后的手牌 * @param opcard 要碰的牌 * @return 是否是唯一的将牌 */ public boolean isOnlyPair(List tempHand, int opcard) { Map cardCount = countCards(tempHand); int pairCount = 0; for (Map.Entry entry : cardCount.entrySet()) { if (entry.getValue() == 2) { pairCount++; } } // 如果去掉要碰的牌后没有对子,说明原来的牌是唯一的将牌 return pairCount == 0; } /** * 统计每张牌的数量 * * @param handCards 手牌 * @return 牌的数量映射 */ private Map countCards(List handCards) { Map cardCount = new HashMap<>(); for (int card : handCards) { cardCount.put(card, cardCount.getOrDefault(card, 0) + 1); } return cardCount; } //大胡分析七小对 public static int countPairs(List handCards) { // 1. 先根据花色和点数排序 List sortedCards = new ArrayList<>(handCards); sortedCards.sort((a, b) -> { int suitA = a / 100; int suitB = b / 100; int rankA = a % 100; int rankB = b % 100; // 先按花色排序,再按点数排序 if (suitA != suitB) { return suitA - suitB; } return rankA - rankB; }); // 2. 统计对子 int pairCount = 0; for (int i = 0; i < sortedCards.size() - 1; i++) { if (sortedCards.get(i).equals(sortedCards.get(i + 1))) { pairCount++; i++; // 跳过下一张 } } return pairCount; } //分析七小对清一色 public List qixiaoduiqingyise(List handCards) { // 合并所有牌 List allCards = new ArrayList<>(); allCards.addAll(handCards); // 按花色分组 List wanCards = new ArrayList<>(); // 万: 101-109 List tongCards = new ArrayList<>(); // 筒: 201-209 List tiaoCards = new ArrayList<>(); // 条: 301-309 for (Integer card : allCards) { if (card >= 101 && card <= 109) { wanCards.add(card); } else if (card >= 201 && card <= 209) { tongCards.add(card); } else if (card >= 301 && card <= 309) { tiaoCards.add(card); } } // 检查各花色数量 if (wanCards.size() >= 8) { Collections.sort(wanCards); logInfo("检测到清一色花色: 万, 数量: " + wanCards.size()); return wanCards; } if (tongCards.size() >= 8) { Collections.sort(tongCards); logInfo("检测到清一色花色: 筒, 数量: " + tongCards.size()); return tongCards; } if (tiaoCards.size() >= 8) { Collections.sort(tiaoCards); logInfo("检测到清一色花色: 条, 数量: " + tiaoCards.size()); return tiaoCards; } return new ArrayList<>(); } public boolean hasFourOrMorePairs(List qixiaoduiqingyise) { if (qixiaoduiqingyise == null || qixiaoduiqingyise.size() < 8) { return false; // 至少需要8张牌才能有4对 } // 对牌进行排序 List sortedCards = new ArrayList<>(qixiaoduiqingyise); Collections.sort(sortedCards); // 统计对子数量 int pairCount = 0; int i = 0; int n = sortedCards.size(); while (i < n - 1) { if (sortedCards.get(i).equals(sortedCards.get(i + 1))) { // 找到一对对子 pairCount++; i += 2; // 跳过这对对子 } else { i += 1; // 继续检查下一张牌 } } logInfo("清一色手牌中对子数量: " + pairCount); return pairCount >= 4; } //分析清一色 public boolean isAllSameSuit(List handCards, List pengCard) { // 统计各花色的牌数量 Map suitCountMap = new HashMap<>(); List handCards2 = new ArrayList<>(); handCards2.addAll(handCards); // handCards2.addAll(pengCard); for (Integer card : handCards2) { int suit = card / 100; // 获取花色,100=万,200=筒,300=条 suitCountMap.put(suit, suitCountMap.getOrDefault(suit, 0) + 1); } // 检查是否有花色的牌数量超过8张 for (Map.Entry entry : suitCountMap.entrySet()) { int suit = entry.getKey(); int count = entry.getValue(); if (count >= 10) { String suitName = getSuitName(suit); logInfo("检测到可能的清一色花色: " + suitName + ", 数量: " + count); return true; } } return false; } /** * 检测当前牌 是否符合清一色 * @param card * @param handCards * @param pengguopai * @param chipai * @param gangpai * @return */ public static boolean checkAllSameSuitAll(int card, List handCards, List pengguopai, List chipai, List gangpai) { //判断 peng,chi ,gang是否有不同色 //true 为可以 false 为不能 List cpg = new ArrayList<>(); cpg.addAll(pengguopai); cpg.addAll(chipai); cpg.addAll(gangpai); if (cpg.size()>0){ int tmp1 = 0; int tmp2 = 0; for (int i=0;i10){ //落地已经有了不同色,所以可以进行 return true; } } } //加入之后 cpg.add(card); if (cpg.size()>1){ int tmp3 = 0; int tmp4 = 0; for (int i=0;i10){ //牌 加入已经破坏手牌 return false; } } } cpg.clear(); //判断手牌 if(handCards.size()>3){ //手牌判断 // 统计各花色的牌数量 Map suitCountMap = new HashMap<>(); for (Integer cards : handCards) { int suit = cards / 100; // 获取花色,100=万,200=筒,300=条 suitCountMap.put(suit, suitCountMap.getOrDefault(suit, 0) + 1); } for (Map.Entry entry : suitCountMap.entrySet()) { int suit = entry.getKey(); int count = entry.getValue(); if (handCards.size()==4){ //手牌只有4张 if (count >= 2) { //当手牌已经剩下4张时候,可以进行 return true; } } if (count >= handCards.size()-3) { int tmpsuit = card / 100; if (suit == tmpsuit){ return true; } } } } return false; } //分析清一色 public boolean isAllSameSuit1(List handCards) { // 统计各花色的牌数量 Map suitCountMap = new HashMap<>(); for (Integer card : handCards) { int suit = card / 100; // 获取花色,100=万,200=筒,300=条 suitCountMap.put(suit, suitCountMap.getOrDefault(suit, 0) + 1); } for (Map.Entry entry : suitCountMap.entrySet()) { int suit = entry.getKey(); int count = entry.getValue(); if (count >= 11) { String suitName = getSuitName(suit); logInfo("检测到可能的清一色花色: " + suitName + ", 数量: " + count); return true; } } return false; } // 获取主要花色(数量最多且≥9张的花色) public Integer getMainSuit(List handCards) { Map suitCountMap = new HashMap<>(); for (Integer card : handCards) { int suit = card / 100; suitCountMap.put(suit, suitCountMap.getOrDefault(suit, 0) + 1); } Integer mainSuit = null; int maxCount = 0; for (Map.Entry entry : suitCountMap.entrySet()) { int suit = entry.getKey(); int count = entry.getValue(); if (count >= 10 && count > maxCount) { maxCount = count; mainSuit = suit; } } return mainSuit; } // 清一色特定出牌策略 /** * 基于花色分析的清一色七小对出牌策略 * 优先打出花色最少的牌,以优化七小对的形成 * * @param handCards 当前手牌 * @return 最优打出的牌 */ private String selectCardToDiscardAllSameSuitQiXiaoDuiBySuit(List handCards) { // 1. 分析每个花色的牌数量 Map suitCount = new HashMap<>(); Map> cardsBySuit = new HashMap<>(); for (int card : handCards) { int suit = card / 10; // 计算花色 suitCount.put(suit, suitCount.getOrDefault(suit, 0) + 1); // 按花色分组存储牌 cardsBySuit.computeIfAbsent(suit, k -> new ArrayList<>()).add(card); } logInfo("花色分布统计: " + suitCount); // 2. 找出花色数量最少的花色(排除主花色) Integer mainSuit = getMainSuit(handCards); Integer leastSuit = null; int minCount = Integer.MAX_VALUE; for (Map.Entry entry : suitCount.entrySet()) { int suit = entry.getKey(); int count = entry.getValue(); // 如果不是主花色,且数量更少 if ((mainSuit == null || suit != mainSuit) && count < minCount) { minCount = count; leastSuit = suit; } } // 3. 从最少花色中选择要打出的牌 if (leastSuit != null && cardsBySuit.containsKey(leastSuit)) { List leastSuitCards = cardsBySuit.get(leastSuit); logInfo("优先从最少花色 " + getSuitName(leastSuit) + " 中选择打出的牌: " + leastSuitCards); // 统计最少花色中各牌的出现次数 Map cardCountsInLeastSuit = new HashMap<>(); for (int card : leastSuitCards) { cardCountsInLeastSuit.put(card, cardCountsInLeastSuit.getOrDefault(card, 0) + 1); } // 优先打出单张牌(出现次数为1) for (Map.Entry entry : cardCountsInLeastSuit.entrySet()) { if (entry.getValue() == 1) { logInfo("选择打出最少花色中的单张牌: " + entry.getKey()); return String.valueOf(entry.getKey()); } } // 如果没有单张,优先打对子的,优先保留刻子或四张一样的 for (Map.Entry entry : cardCountsInLeastSuit.entrySet()) { if (entry.getValue() == 2) { return String.valueOf(entry.getKey()); } } } // 4. 如果没有找到合适的牌(可能所有牌都是同一花色),使用七小对大胡策略 logInfo("使用默认七小对大胡策略"); int outcard = selectCardToDiscardBig(handCards, countPairs(handCards)); return String.valueOf(outcard); } private String selectCardToDiscardPengPeng(List handCards) { logInfo("开始执行清一色碰碰胡出牌策略"); // 1. 分析每个花色的牌数量 Map suitCount = new HashMap<>(); Map> cardsBySuit = new HashMap<>(); for (int card : handCards) { int suit = card / 100; // 计算花色(修正为除以100) suitCount.put(suit, suitCount.getOrDefault(suit, 0) + 1); // 按花色分组存储牌 cardsBySuit.computeIfAbsent(suit, k -> new ArrayList<>()).add(card); } // 2. 找出主要花色(牌数量最多的花色) int mainSuit = findMainSuit(suitCount); logInfo("主要花色: " + getSuitName(mainSuit) + ", 数量: " + suitCount.get(mainSuit)); // 3. 如果有非主要花色的牌,优先打出 if (hasNonMainSuitCards(suitCount, mainSuit)) { logInfo("存在非主要花色牌,优先打出"); return discardNonMainSuitCard(handCards, mainSuit); } // 4. 所有牌都是主要花色,使用清一色碰碰胡策略 logInfo("所有牌都是主要花色,使用清一色碰碰胡策略"); return discardFromSameSuitPengPeng(handCards, mainSuit); } /** * 打出非主要花色的牌 */ private String discardNonMainSuitCard(List handCards, int mainSuit) { // 优先打出数量最少的非主要花色牌 List nonMainSuitCards = new ArrayList<>(); for (int card : handCards) { int suit = card / 100; if (suit != mainSuit) { nonMainSuitCards.add(card); } } // 按数量排序,优先打出手牌数量少的牌 Map cardCount = countCardOccurrences(nonMainSuitCards); nonMainSuitCards.sort((a, b) -> { int countA = cardCount.get(a); int countB = cardCount.get(b); return Integer.compare(countA, countB); }); int discardCard = nonMainSuitCards.get(0); logInfo("打出非主要花色牌: " + getCardName(discardCard)); return String.valueOf(discardCard); } /** * 统计每张牌的出现次数 */ private Map countCardOccurrences(List cards) { Map countMap = new HashMap<>(); for (int card : cards) { countMap.put(card, countMap.getOrDefault(card, 0) + 1); } return countMap; } /** * 找出主要花色(牌数量最多的花色) */ private int findMainSuit(Map suitCount) { int mainSuit = -1; int maxCount = 0; for (Map.Entry entry : suitCount.entrySet()) { if (entry.getValue() > maxCount) { maxCount = entry.getValue(); mainSuit = entry.getKey(); } } return mainSuit; } /** * 判断是否有非主要花色的牌 */ private boolean hasNonMainSuitCards(Map suitCount, int mainSuit) { for (Map.Entry entry : suitCount.entrySet()) { if (entry.getKey() != mainSuit && entry.getValue() > 0) { return true; } } return false; } /** * 从同花色牌中选择要打出的牌(清一色碰碰胡策略) */ private String discardFromSameSuitPengPeng(List handCards, int mainSuit) { // 统计每张牌的数量 Map cardCount = countCardOccurrences(handCards); // 找出刻子、将牌候选和剩余牌 List keziCards = new ArrayList<>(); List jiangCandidates = new ArrayList<>(); // 258将牌候选 List otherJiangCandidates = new ArrayList<>(); // 其他将牌候选 List remainingCards = new ArrayList<>(handCards); // 1. 先找出刻子(三张相同的牌) findAndRemoveKezi(remainingCards, cardCount, keziCards); findJiangCandidates(remainingCards, jiangCandidates, otherJiangCandidates); // 3. 选择要打出的牌 int discardCard = selectDiscardCardPengPeng(remainingCards, otherJiangCandidates, cardCount, handCards); logInfo("清一色碰碰胡策略打出: " + getCardName(discardCard)); return String.valueOf(discardCard); } /** * 找出将牌候选 */ private void findJiangCandidates(List cards, List jiangCandidates, List otherJiangCandidates) { Map cardCount = countCardOccurrences(cards); for (int card : cards) { int count = cardCount.get(card); if (count >= 2) { otherJiangCandidates.add(card); } else if (count == 1) { otherJiangCandidates.add(card); } } } /** * 选择要打出的牌(清一色碰碰胡策略) */ private int selectDiscardCardPengPeng(List remainingCards, List otherJiangCandidates, Map cardCount , List handCards) { // 如果还有剩余牌,优先从剩余牌中选择 if (!remainingCards.isEmpty()) { // 策略1: 优先打出孤张(没有相邻牌的牌) Integer isolatedCard = findIsolatedCard(remainingCards, cardCount); if (isolatedCard != null) { return isolatedCard; } // 策略2: 优先打出非258的牌 Integer non258Card = findNon258Card(remainingCards); if (non258Card != null) { return non258Card; } // 策略3: 打出手牌数量最少的牌 return findLeastCountCard(remainingCards, cardCount); } if (!otherJiangCandidates.isEmpty()) { return otherJiangCandidates.get(0); } // 默认情况(理论上不会执行到这里) logInfo("警告:无法选择出牌,使用默认策略"); return remainingCards.isEmpty() ? handCards.get(0) : remainingCards.get(0); } /** * 查找手牌数量最少的牌 */ private int findLeastCountCard(List cards, Map cardCount) { int minCount = Integer.MAX_VALUE; int resultCard = cards.get(0); for (int card : cards) { int count = cardCount.get(card); if (count < minCount) { minCount = count; resultCard = card; } } return resultCard; } /** * 查找非258的牌 */ private Integer findNon258Card(List cards) { for (int card : cards) { int value = card % 100; if (value != 2 && value != 5 && value != 8) { return card; } } return null; } public int selectCardToDiscardForAllSameSuit(List handCards, List chowGroup, List pengGroup) { Integer mainSuit = getMainSuit(handCards); if (mainSuit == null) { logInfo("未找到主要花色,使用默认策略"); return -1; } List chi = new ArrayList<>(); chi.addAll(chowGroup); //如果吃和碰的牌 包含主要花色的牌,放弃走清一色 if (chowGroup != null && pengGroup != null && chowGroup.size() > 0 && pengGroup.size() > 0) { chi.addAll(pengGroup); for (Integer integer : chi) { String str = integer.toString(); if (!str.isEmpty()) { int firstDigit = Character.getNumericValue(str.charAt(0)); if (!mainSuit.equals(firstDigit)) { return -1; } } } } // boolean allSameSuit = isAllSameSuit(handCards, mainSuit); String mainSuitName = getSuitName(mainSuit); logInfo("执行清一色策略,主要花色: " + mainSuitName); // 统计每张牌的数量 Map cardCountMap = new HashMap<>(); for (Integer card : handCards) { cardCountMap.put(card, cardCountMap.getOrDefault(card, 0) + 1); } // 收集非主要花色的牌 List nonMainSuitCards = new ArrayList<>(); for (Integer card : handCards) { int suit = card / 100; if (suit != mainSuit) { nonMainSuitCards.add(card); } } // 如果有非主要花色的牌,优先打出这些牌 if (!nonMainSuitCards.isEmpty()) { logInfo("有非主要花色牌,优先打出"); // 按优先级打出非主要花色牌 Integer cardToDiscard = selectNonMainSuitCard(nonMainSuitCards, cardCountMap); if (cardToDiscard != null) { return cardToDiscard; } } // 如果所有牌都是主要花色,使用清一色优化策略 logInfo("所有牌都是主要花色,使用清一色优化策略"); return selectCardFromSameSuit(handCards, cardCountMap, mainSuit); } public boolean isAllSameSuit(List handCards, Integer mainSuit) { // 提取主花色牌 List mainSuitCards = new ArrayList<>(); for (Integer card : handCards) { int suit = card / 100; if (suit == mainSuit) { mainSuitCards.add(card); } } Map>> stringListMap = analyzeMainSuitPatterns(mainSuitCards); List> sequences = stringListMap.get("sequences"); // 顺子列表 List> triplets = stringListMap.get("triplets"); // 刻子列表 List> pairs = stringListMap.get("pairs"); // 对子列表 // 获取数量 int sequenceCount = sequences.size(); int tripletCount = triplets.size(); // 判断顺子加上刻子的数量是否大于6 boolean isSequenceAndTripletGreaterThan6 = (sequenceCount + tripletCount) > 6; return isSequenceAndTripletGreaterThan6; } /** * 分析主花色牌组中的顺子、刻子、对子 * * @param mainSuitCards 主花色牌组 * @return 包含顺子、刻子、对子信息的Map */ private Map>> analyzeMainSuitPatterns(List mainSuitCards) { Map>> patterns = new HashMap<>(); patterns.put("sequences", new ArrayList<>()); // 顺子 patterns.put("triplets", new ArrayList<>()); // 刻子 patterns.put("pairs", new ArrayList<>()); // 对子 if (mainSuitCards == null || mainSuitCards.isEmpty()) { return patterns; } // 提取所有点数 List points = new ArrayList<>(); for (Integer card : mainSuitCards) { int point = card % 100; points.add(point); } Collections.sort(points); // 统计每个点数的数量 Map pointCountMap = new HashMap<>(); for (Integer point : points) { pointCountMap.put(point, pointCountMap.getOrDefault(point, 0) + 1); } // 找出刻子(三张相同的牌) for (Map.Entry entry : pointCountMap.entrySet()) { if (entry.getValue() >= 3) { List triplet = new ArrayList<>(); for (int i = 0; i < 3; i++) { triplet.add(entry.getKey()); } patterns.get("triplets").add(triplet); } } // 找出对子 for (Map.Entry entry : pointCountMap.entrySet()) { if (entry.getValue() >= 2) { List pair = new ArrayList<>(); for (int i = 0; i < 2; i++) { pair.add(entry.getKey()); } patterns.get("pairs").add(pair); } } // 找出顺子(需要从剩余牌中找) // 先复制一份牌用于顺子检测(避免刻子/对子使用的牌影响顺子检测) List remainingPoints = new ArrayList<>(points); // 移除刻子使用的牌(如果有的话) for (List triplet : patterns.get("triplets")) { int point = triplet.get(0); for (int i = 0; i < 3; i++) { remainingPoints.remove((Integer) point); } } // 检查顺子 for (int start = 1; start <= 7; start++) { boolean canFormSequence = true; List tempPoints = new ArrayList<>(remainingPoints); List sequence = new ArrayList<>(); // 检查是否有连续的3个点数 for (int i = 0; i < 3; i++) { int currentPoint = start + i; if (!tempPoints.remove((Integer) currentPoint)) { canFormSequence = false; break; } sequence.add(currentPoint); } if (canFormSequence) { patterns.get("sequences").add(sequence); // 从剩余牌中移除这三个点数 for (int i = 0; i < 3; i++) { remainingPoints.remove((Integer) (start + i)); } } } return patterns; } /** * 选择非主要花色牌打出 */ private Integer selectNonMainSuitCard(List nonMainSuitCards, Map cardCountMap) { // 1. 优先打出手牌中数量最少的单张牌 List singleCards = new ArrayList<>(); for (Integer card : nonMainSuitCards) { if (cardCountMap.get(card) == 1) { singleCards.add(card); } } if (!singleCards.isEmpty()) { // 在单张牌中优先打边张(1和9) Integer edgeCard = findEdgeCard(singleCards); if (edgeCard != null) { logInfo("打出非主要花色的边张单牌: " + edgeCard); return edgeCard; } // 没有边张就打任意单张 logInfo("打出非主要花色的单张牌: " + singleCards.get(0)); return singleCards.get(0); } // 2. 拆数量最少的对子 for (Integer card : nonMainSuitCards) { if (cardCountMap.get(card) == 2) { logInfo("拆非主要花色的对子: " + card); return card; } } // 3. 最后随便选一张非主要花色的牌 logInfo("打出非主要花色的牌: " + nonMainSuitCards.get(0)); return nonMainSuitCards.get(0); } /** * 从同花色牌中选择要打出的牌 */ private int selectCardFromSameSuit(List handCards, Map cardCountMap, int mainSuit) { // 分离出刻子、顺子、将牌和剩余牌 List kezi = new ArrayList<>(); List shunzi = new ArrayList<>(); List jiang = new ArrayList<>(); List remainingCards = new ArrayList<>(handCards); // 先找出刻子(三张相同的牌) findAndRemoveKezi(remainingCards, cardCountMap, kezi); // 再找出顺子 findAndRemoveShunzi(remainingCards, mainSuit, shunzi); // 找出将牌(258对子) // findAndRemoveJiang(remainingCards, jiang); // 如果还有剩余牌,从剩余牌中选择要打出的牌 if (!remainingCards.isEmpty()) { return selectFromRemainingCards(remainingCards, cardCountMap); } // 如果没有剩余牌,说明牌型很好,考虑拆散一组来优化 return selectCardFromFormedGroups(handCards, cardCountMap, mainSuit); } /** * 找出并移除刻子 */ private void findAndRemoveKezi(List cards, Map cardCountMap, List kezi) { // 创建临时副本避免修改遍历中的列表 List tempCards = new ArrayList<>(cards); Set processedCards = new HashSet<>(); // 记录已处理的牌 for (Integer card : tempCards) { // 如果这张牌已经处理过,或者数量不足3张,跳过 if (processedCards.contains(card) || cardCountMap.get(card) < 3) { continue; } // 找到刻子,从原始cards列表中移除3张相同的牌 int removeCount = 0; Iterator iterator = cards.iterator(); while (iterator.hasNext() && removeCount < 3) { Integer currentCard = iterator.next(); if (currentCard.equals(card)) { iterator.remove(); removeCount++; } } // 将刻子添加到结果列表(3次) kezi.add(card); kezi.add(card); kezi.add(card); // 标记这张牌已处理 processedCards.add(card); logInfo("找到刻子: " + getCardName(card) + " (数量: " + cardCountMap.get(card) + ")"); } } /** * 找出并移除顺子 */ private void findAndRemoveShunzi(List cards, int mainSuit, List shunzi) { List sortedCards = new ArrayList<>(cards); Collections.sort(sortedCards); for (int i = 0; i <= sortedCards.size() - 3; i++) { int card1 = sortedCards.get(i); int card2 = sortedCards.get(i + 1); int card3 = sortedCards.get(i + 2); // 检查是否构成顺子 if (isShunzi(card1, card2, card3, mainSuit)) { // 移除顺子 cards.remove((Integer) card1); cards.remove((Integer) card2); cards.remove((Integer) card3); shunzi.add(card1); shunzi.add(card2); shunzi.add(card3); i += 2; // 跳过已处理的牌 } } } /** * 判断三张牌是否构成顺子 */ private boolean isShunzi(int card1, int card2, int card3, int mainSuit) { int value1 = card1 % 100; int value2 = card2 % 100; int value3 = card3 % 100; return value2 == value1 + 1 && value3 == value2 + 1; } /** * 找出并移除将牌(258对子) */ private void findAndRemoveJiang(List cards, List jiang) { Map tempCount = new HashMap<>(); for (Integer card : cards) { tempCount.put(card, tempCount.getOrDefault(card, 0) + 1); } // 优先找258对子作为将牌 for (Integer card : cards) { int value = card % 100; if ((value == 2 || value == 5 || value == 8) && tempCount.get(card) >= 2) { // 找到将牌 jiang.add(card); jiang.add(card); cards.remove(card); cards.remove(card); return; } } // 如果没有258对子,找任意对子作为将牌 for (Integer card : cards) { if (tempCount.get(card) >= 2) { jiang.add(card); jiang.add(card); cards.remove(card); cards.remove(card); return; } } } /** * 从剩余牌中选择要打出的牌 */ private int selectFromRemainingCards(List remainingCards, Map cardCountMap) { // 优先打出孤张(没有相邻牌的牌) Integer isolatedCard = findIsolatedCard(remainingCards, cardCountMap); if (isolatedCard != null) { logInfo("打出孤张: " + isolatedCard); return isolatedCard; } // 其次打出手牌数量最多的牌(保留单张和刻子胚子) Integer mostCountCard = findMostCountCard(remainingCards, cardCountMap); if (mostCountCard != null) { logInfo("打出手牌数量多的牌: " + mostCountCard); return mostCountCard; } // 最后打边张(1和9) Integer edgeCard = findEdgeCard(remainingCards); if (edgeCard != null) { logInfo("打出边张: " + edgeCard); return edgeCard; } // 默认打出第一张 logInfo("打出默认牌: " + remainingCards.get(0)); return remainingCards.get(0); } /** * 从已组成的牌组中选择要拆打的牌 */ private int selectCardFromFormedGroups(List handCards, Map cardCountMap, int mainSuit) { // 这里可以实现更复杂的策略,比如拆散效率最低的顺子或刻子 // 简单实现:打出手牌数量最少的牌 int minCount = Integer.MAX_VALUE; Integer cardToDiscard = null; for (Integer card : handCards) { int count = cardCountMap.get(card); if (count < minCount) { minCount = count; cardToDiscard = card; } } logInfo("从已组成牌组中打出: " + cardToDiscard); return cardToDiscard; } /** * 查找孤张(没有相邻牌的牌) */ private Integer findIsolatedCard(List cards, Map cardCountMap) { // 统计每张牌的出现次数 Map countMap = new HashMap<>(); for (int card : cards) { countMap.put(card, countMap.getOrDefault(card, 0) + 1); } // 寻找出现次数为 1 的牌(非对子) for (int card : cards) { if (countMap.get(card) == 1) { return card; } } // 如果没有非对子的牌,返回 null 或选择第一张牌等策略 return cards.get(0); } /** * 查找手牌数量最多的牌 */ private Integer findMostCountCard(List cards, Map cardCountMap) { int maxCount = 0; Integer result = null; for (Integer card : cards) { int count = cardCountMap.get(card); if (count > maxCount) { maxCount = count; result = card; } } return result; } /** * 查找边张(1和9) */ private Integer findEdgeCard(List cards) { for (Integer card : cards) { int value = card % 100; if (value == 1 || value == 9) { return card; } } return null; } /** * handCards :摸牌后的手牌 * 分析手牌,将顺子,刻子,将 另存 */ public HandAnalysis analyzeHand(List handCards) { logInfo("\n===== 开始手牌分析 ====="); logInfo("待分析手牌: " + handCards); // 创建分析结果对象 HandAnalysis analysis = new HandAnalysis(); // 1. 统计每张牌的数量并按花色分组 countCardsAndGroupBySuit(handCards, analysis); // 2. 识别刻子(三张相同) identifyTriplets(analysis); // 4. 识别顺子(三张连续,仅分析剩余的牌) identifySequences(analysis); // 3. 优先识别对子(两张相同) identifyPairs(analysis); // 5. 识别孤张牌 identifyIsolatedCards(analysis); // 6. 检测听牌状态 detectTingPai(analysis); return analysis; } /** * 统计每张牌的数量并按花色分组 */ private void countCardsAndGroupBySuit(List handCards, HandAnalysis analysis) { Map counts = new HashMap<>(); Map> bySuit = new HashMap<>(); // 初始化花色分组(1:万, 2:筒, 3:条) bySuit.put(1, new ArrayList<>()); bySuit.put(2, new ArrayList<>()); bySuit.put(3, new ArrayList<>()); // 统计数量并按花色分组 for (int card : handCards) { counts.put(card, counts.getOrDefault(card, 0) + 1); int suit = card / 100; if (bySuit.containsKey(suit)) { bySuit.get(suit).add(card); } } // 对每个花色的牌进行排序 for (List suitCards : bySuit.values()) { suitCards.sort(Integer::compareTo); } analysis.cardCounts = counts; analysis.cardsBySuit = bySuit; analysis.remainingCards = new ArrayList<>(handCards); analysis.remainingCards.sort(Integer::compareTo); } /** * 识别刻子(三张相同的牌) */ private void identifyTriplets(HandAnalysis analysis) { List cardsToRemove = new ArrayList<>(); for (Map.Entry entry : analysis.cardCounts.entrySet()) { int card = entry.getKey(); int count = entry.getValue(); // 如果某张牌有3张或以上,识别为刻子 if (count >= 3) { // 添加刻子 List triplet = Arrays.asList(card, card, card); analysis.completedMelds.add(triplet); analysis.meldCount++; // 标记为已使用 for (int i = 0; i < 3; i++) { analysis.usedInMelds.add(card); } // 记录需要从剩余牌中移除的牌 for (int i = 0; i < 3; i++) { cardsToRemove.add(card); } } } // 从剩余牌中移除已识别为刻子的牌 for (int card : cardsToRemove) { analysis.remainingCards.remove(Integer.valueOf(card)); } } /** * 识别顺子(三张连续的牌,同一花色) */ private void identifySequences(HandAnalysis analysis) { // 按花色分析顺子,只使用剩余的牌 Map> remainingCardsBySuit = new HashMap<>(); for (int card : analysis.remainingCards) { if (!analysis.usedInMelds.contains(card)) { int suit = card / 100; remainingCardsBySuit.computeIfAbsent(suit, k -> new ArrayList<>()).add(card); } } for (Map.Entry> entry : remainingCardsBySuit.entrySet()) { int suit = entry.getKey(); List suitCards = entry.getValue(); if (suitCards.size() < 3) continue; suitCards.sort(Integer::compareTo); // 创建牌的计数映射,用于跟踪每张牌的数量 Map cardCount = new HashMap<>(); for (int card : suitCards) { cardCount.put(card, cardCount.getOrDefault(card, 0) + 1); } // 遍历所有可能的顺子起始点 for (int startCard : suitCards) { int secondCard = startCard + 1; int thirdCard = startCard + 2; // 检查是否同一花色且连续 if (secondCard / 100 == suit && thirdCard / 100 == suit && cardCount.getOrDefault(startCard, 0) > 0 && cardCount.getOrDefault(secondCard, 0) > 0 && cardCount.getOrDefault(thirdCard, 0) > 0) { // 添加顺子 List sequence = Arrays.asList(startCard, secondCard, thirdCard); analysis.completedMelds.add(sequence); analysis.meldCount++; // 从计数中移除(每张牌只用一次) cardCount.put(startCard, cardCount.get(startCard) - 1); cardCount.put(secondCard, cardCount.get(secondCard) - 1); cardCount.put(thirdCard, cardCount.get(thirdCard) - 1); // 更新剩余牌和已用牌 analysis.remainingCards.remove(Integer.valueOf(startCard)); analysis.remainingCards.remove(Integer.valueOf(secondCard)); analysis.remainingCards.remove(Integer.valueOf(thirdCard)); analysis.usedInMelds.add(startCard); analysis.usedInMelds.add(secondCard); analysis.usedInMelds.add(thirdCard); } } } } /** * 识别对子(两张相同的牌) */ private void identifyPairs(HandAnalysis analysis) { // 统计剩余牌中每张牌的数量 Map remainingCounts = new HashMap<>(); for (int card : analysis.remainingCards) { if (!analysis.usedInMelds.contains(card)) { remainingCounts.put(card, remainingCounts.getOrDefault(card, 0) + 1); } } // 识别对子 for (Map.Entry entry : remainingCounts.entrySet()) { int card = entry.getKey(); int count = entry.getValue(); if (count >= 2) { analysis.pairs.add(card); analysis.pairCount++; // 标记为已使用 analysis.usedInPairs.add(card); // 将对子的牌从剩余牌中移除 for (int i = 0; i < 2; i++) { analysis.remainingCards.remove(Integer.valueOf(card)); } // 检查是否为特殊对子(最后一位数字为2、5、8) int lastDigit = card % 10; if (lastDigit == 2 || lastDigit == 5 || lastDigit == 8) { int suit = card / 100; String suitName = getSuitName(suit); logInfo("识别到特殊对子: " + card + " (" + suitName + lastDigit + ")"); } } } // 检测是否有碰碰胡潜力(如果大部分牌都是刻子或对子) if (analysis.meldCount >= 3 && analysis.pairCount >= 1) { analysis.hasPengPengHu = true; } } /** * 获取花色名称 */ private String getSuitName(int suit) { switch (suit) { case 1: return "万"; case 2: return "筒"; case 3: return "条"; default: return "未知"; } } /** * 识别孤张牌(未组成任何牌型的单张牌) */ private void identifyIsolatedCards(HandAnalysis analysis) { // 统计剩余牌中每张牌的数量 Map remainingCounts = new HashMap<>(); for (int card : analysis.remainingCards) { remainingCounts.put(card, remainingCounts.getOrDefault(card, 0) + 1); } // 识别孤张牌 for (Map.Entry entry : remainingCounts.entrySet()) { int card = entry.getKey(); int count = entry.getValue(); // 如果牌未用于刻子或顺子,且不是对子,则为孤张 if (!analysis.usedInPairs.contains(card)) { for (int i = 0; i < count; i++) { analysis.isolatedCards.add(card); } } } // 检测是否有龙七对潜力(如果有多个对子和孤张) if (analysis.pairCount >= 4) { analysis.hasLongQiDuiPotential = true; } } /** * 检测听牌状态 */ private void detectTingPai(HandAnalysis analysis) { // 创建完整手牌列表 List fullHand = new ArrayList<>(analysis.remainingCards); for (int card : analysis.usedInMelds) { fullHand.add(card); } for (int card : analysis.usedInPairs) { fullHand.add(card); fullHand.add(card); } int totalCards = fullHand.size(); // 对于14张牌的情况,需要去掉一张牌后检查是否为和牌牌型 if (totalCards == 14) { // 尝试去掉每张牌,检查是否可以和牌 for (int card : new HashSet<>(fullHand)) { List tempHand = new ArrayList<>(fullHand); tempHand.remove(Integer.valueOf(card)); if (canHu(tempHand, analysis)) { analysis.tingCards.add(card); } } } // 对于13张牌的情况,检查摸到哪些牌可以和牌 else if (totalCards == 13) { // 尝试添加每张可能的牌,检查是否可以和牌 for (int suit = 1; suit <= 3; suit++) { // 1:万, 2:筒, 3:条 for (int rank = 1; rank <= 9; rank++) { int card = suit * 100 + rank; // 检查这张牌是否已经在手中有4张(麻将中每种牌最多4张) int count = 0; for (int c : fullHand) { if (c == card) { count++; } } if (count >= 4) continue; List tempHand = new ArrayList<>(fullHand); tempHand.add(card); if (canHu(tempHand, analysis)) { analysis.tingCards.add(card); } } } } // 如果有可胡的牌,则为听牌状态 analysis.isTingPai = !analysis.tingCards.isEmpty(); // 计算向听数 calculateShanten(analysis); } /** * 判断手牌是否符合和牌条件(基本牌型:四组合一对将) */ private boolean canHu(List hand, HandAnalysis analysis) { // 统计牌的数量 Map tempCounts = new HashMap<>(); for (int card : hand) { tempCounts.put(card, tempCounts.getOrDefault(card, 0) + 1); } // 尝试找出一对将牌 for (Map.Entry entry : tempCounts.entrySet()) { int card = entry.getKey(); int count = entry.getValue(); if (count >= 2) { // 假设这对牌作为将牌 Map copyCounts = new HashMap<>(tempCounts); copyCounts.put(card, count - 2); // 检查剩余的牌是否可以组成四组合(刻子或顺子) if (checkAllMelds(copyCounts)) { return true; } } } return false; } /** * 检查剩余的牌是否可以全部组成刻子或顺子 */ private boolean checkAllMelds(Map counts) { // 创建剩余牌的列表(排除数量为0的牌) List remainingCards = new ArrayList<>(); for (Map.Entry entry : counts.entrySet()) { int card = entry.getKey(); int count = entry.getValue(); for (int i = 0; i < count; i++) { remainingCards.add(card); } } // 排序以便分析 remainingCards.sort(Integer::compareTo); // 递归检查是否可以组成全部的刻子或顺子 return checkMeldsRecursive(remainingCards); } /** * 递归检查是否可以组成刻子或顺子 */ private boolean checkMeldsRecursive(List cards) { if (cards.isEmpty()) { return true; // 所有牌都已组成合法牌型 } int firstCard = cards.get(0); // 尝试组成刻子(三张相同) if (cards.size() >= 3 && firstCard == cards.get(1) && firstCard == cards.get(2)) { List newCards = new ArrayList<>(cards.subList(3, cards.size())); if (checkMeldsRecursive(newCards)) { return true; } } // 尝试组成顺子(三张连续) if (cards.size() >= 3) { int secondCard = firstCard + 1; int thirdCard = firstCard + 2; // 确保是同一花色 int suit = firstCard / 100; if (suit == secondCard / 100 && suit == thirdCard / 100) { List tempCards = new ArrayList<>(cards); tempCards.remove(Integer.valueOf(firstCard)); // 查找第二张牌 boolean foundSecond = false; for (int i = 0; i < tempCards.size(); i++) { if (tempCards.get(i) == secondCard) { tempCards.remove(i); foundSecond = true; break; } } if (foundSecond) { // 查找第三张牌 boolean foundThird = false; for (int i = 0; i < tempCards.size(); i++) { if (tempCards.get(i) == thirdCard) { tempCards.remove(i); foundThird = true; break; } } if (foundThird && checkMeldsRecursive(tempCards)) { return true; } } } } return false; } /** * 计算向听数(距离听牌还有几张牌) * 优化的向听数计算算法,考虑了面子、对子和搭子的组合 */ private void calculateShanten(HandAnalysis analysis) { // 如果已经听牌,向听数为0 if (analysis.isTingPai) { analysis.shantenCount = 0; return; } // 创建完整手牌列表 List fullHand = new ArrayList<>(analysis.remainingCards); for (int card : analysis.usedInMelds) { fullHand.add(card); } for (int card : analysis.usedInPairs) { fullHand.add(card); fullHand.add(card); } int totalCards = fullHand.size(); if (totalCards != 13 && totalCards != 14) { analysis.shantenCount = Integer.MAX_VALUE; return; } // 统计每种牌的数量 Map counts = new HashMap<>(); for (int card : fullHand) { counts.put(card, counts.getOrDefault(card, 0) + 1); } // 计算最小向听数 int minShanten = Integer.MAX_VALUE; // 尝试每种可能的将牌(对子) Set possiblePairs = new HashSet<>(counts.keySet()); // 添加没有对子的情况(需要摸一张牌形成对子) possiblePairs.add(-1); for (int pairCard : possiblePairs) { Map tempCounts = new HashMap<>(counts); int pairShanten = 0; // 如果选择了具体的将牌 if (pairCard != -1) { int pairCount = tempCounts.get(pairCard); if (pairCount >= 2) { tempCounts.put(pairCard, pairCount - 2); } else { // 需要摸一张牌形成对子 pairShanten = 1; tempCounts.put(pairCard, tempCounts.getOrDefault(pairCard, 0) + 1 - 2); } } else { // 需要摸一张牌形成对子 pairShanten = 1; } // 计算剩余牌的向听数 int meldShanten = calculateMeldShanten(tempCounts); int totalShanten = pairShanten + meldShanten; // 更新最小向听数 if (totalShanten < minShanten) { minShanten = totalShanten; } } // 确保向听数不为负数 analysis.shantenCount = Math.max(0, minShanten); } /** * 计算剩余牌(排除将牌后)的面子向听数 */ private int calculateMeldShanten(Map counts) { // 创建剩余牌的列表 List cards = new ArrayList<>(); for (Map.Entry entry : counts.entrySet()) { int card = entry.getKey(); int count = entry.getValue(); for (int i = 0; i < count; i++) { cards.add(card); } } if (cards.isEmpty()) { return 0; } // 按花色和数值排序 cards.sort(Comparator.comparingInt(card -> { int suit = card / 100; int rank = card % 100; return suit * 10 + rank; })); // 使用动态规划计算最小向听数 return calculateMeldShantenRecursive(cards, 0); } /** * 递归计算面子向听数 */ private int calculateMeldShantenRecursive(List cards, int index) { if (index >= cards.size()) { return 0; } int minShanten = Integer.MAX_VALUE; int currentCard = cards.get(index); // 尝试作为刻子 if (index + 2 < cards.size() && currentCard == cards.get(index + 1) && currentCard == cards.get(index + 2)) { int shanten = calculateMeldShantenRecursive(cards, index + 3); if (shanten < minShanten) { minShanten = shanten; } } else if (index + 1 < cards.size() && currentCard == cards.get(index + 1)) { // 有对子,可以考虑刻子(需要摸一张) int shanten = 1 + calculateMeldShantenRecursive(cards, index + 2); if (shanten < minShanten) { minShanten = shanten; } } else { // 单张,可以考虑刻子(需要摸两张) int shanten = 2 + calculateMeldShantenRecursive(cards, index + 1); if (shanten < minShanten) { minShanten = shanten; } } // 尝试作为顺子 if (index + 2 < cards.size()) { int secondCard = currentCard + 1; int thirdCard = currentCard + 2; // 检查是否同一花色 int suit = currentCard / 100; if (secondCard / 100 == suit && thirdCard / 100 == suit) { // 检查后两张牌是否存在 boolean hasSecond = false; boolean hasThird = false; int secondIndex = -1; int thirdIndex = -1; for (int i = index + 1; i < cards.size(); i++) { if (cards.get(i) == secondCard && !hasSecond) { hasSecond = true; secondIndex = i; } else if (cards.get(i) == thirdCard && !hasThird) { hasThird = true; thirdIndex = i; } } if (hasSecond && hasThird) { // 创建新的牌列表,移除组成顺子的三张牌 List newCards = new ArrayList<>(); for (int i = 0; i < cards.size(); i++) { if (i != index && i != secondIndex && i != thirdIndex) { newCards.add(cards.get(i)); } } int shanten = calculateMeldShantenRecursive(newCards, 0); if (shanten < minShanten) { minShanten = shanten; } } else { // 缺少牌,需要计算所需的牌数 int needed = 0; if (!hasSecond) needed++; if (!hasThird) needed++; // 创建新的牌列表,移除已有的牌 List newCards = new ArrayList<>(); for (int i = 0; i < cards.size(); i++) { if (i != index) { if (hasSecond && i == secondIndex) continue; if (hasThird && i == thirdIndex) continue; newCards.add(cards.get(i)); } } int shanten = needed + calculateMeldShantenRecursive(newCards, 0); if (shanten < minShanten) { minShanten = shanten; } } } } else if (index + 1 < cards.size()) { // 只有两张连续牌,需要一张牌组成顺子 int nextCard = cards.get(index + 1); if (nextCard - currentCard == 1 && (currentCard / 100) == (nextCard / 100)) { List newCards = new ArrayList<>(cards); newCards.remove(index); newCards.remove(index); // 移除第二张牌 int shanten = 1 + calculateMeldShantenRecursive(newCards, 0); if (shanten < minShanten) { minShanten = shanten; } } } return minShanten; } public static int analyzeHandCards(List handCards) { if (handCards == null || handCards.size() != 14) { System.out.println("错误:手牌必须是14张"); return 0; } System.out.println("当前手牌(14张): " + convertToReadable(handCards)); System.out.println("======================================"); // 2. 分析手牌结构 List sortedHand = new ArrayList<>(handCards); Collections.sort(sortedHand); System.out.println("\n=== 手牌结构分析 ==="); // 找出所有刻子 List> kezis = findKezis(sortedHand); System.out.println("刻子(" + kezis.size() + "组):"); for (List kezi : kezis) { System.out.println(" " + convertToReadable(kezi)); } // 找出所有顺子 List> shunzis = findShunzis(sortedHand); System.out.println("\n顺子(" + shunzis.size() + "组):"); for (List shunzi : shunzis) { System.out.println(" " + convertToReadable(shunzi)); } // 找出所有258将牌 List jiangCandidates = findJiangCandidates(sortedHand); System.out.println("\n258将牌候选(" + jiangCandidates.size() + "张):"); System.out.println(" " + convertToReadable(jiangCandidates)); if (kezis.size()>0 || shunzis.size()>0 || jiangCandidates.size()>0){ return kezis.size()+shunzis.size(); } return 0; } /** * 找出所有刻子 */ private static List> findKezis(List handCards) { List> kezis = new ArrayList<>(); List temp = new ArrayList<>(handCards); while (!temp.isEmpty()) { int card = temp.get(0); int count = Collections.frequency(temp, card); if (count >= 3) { List kezi = new ArrayList<>(); kezi.add(card); kezi.add(card); kezi.add(card); kezis.add(kezi); // 移除这3张牌 for (int i = 0; i < 3; i++) { temp.remove(Integer.valueOf(card)); } } else { temp.remove(Integer.valueOf(card)); } } return kezis; } /** * 找出所有顺子 */ private static List> findShunzis(List handCards) { List> shunzis = new ArrayList<>(); List temp = new ArrayList<>(handCards); // 先移除已找到的刻子 List> kezis = findKezis(handCards); for (List kezi : kezis) { for (int i = 0; i < 3; i++) { temp.remove(Integer.valueOf(kezi.get(0))); } } // 找顺子 Collections.sort(temp); for (int i = 0; i < temp.size(); i++) { int card1 = temp.get(i); int type = getCardType(card1); int value = getCardValue(card1); if (type <= 3 && value <= 7) { int card2 = card1 + 1; int card3 = card1 + 2; if (temp.contains(card2) && temp.contains(card3)) { List shunzi = new ArrayList<>(); shunzi.add(card1); shunzi.add(card2); shunzi.add(card3); shunzis.add(shunzi); // 移除这3张牌 temp.remove(Integer.valueOf(card1)); temp.remove(Integer.valueOf(card2)); temp.remove(Integer.valueOf(card3)); i--; // 因为移除了元素 } } } return shunzis; } /** * 找出258将牌候选 */ private static List findJiangCandidates(List handCards) { List jiangs = new ArrayList<>(); for (int card : handCards) { int value = getCardValue(card); if (JIANG_PAIS.contains(value)) { jiangs.add(card); } } return jiangs; } /** * 获取牌值 */ private static int getCardValue(int card) { return card % 10; } /** * 获取牌类型 */ private static int getCardType(int card) { return card / 100; } /** * 格式化单张牌 */ private static String formatCard(int card) { int type = getCardType(card); int value = getCardValue(card); switch (type) { case 1: return value + "万"; case 2: return value + "筒"; case 3: return value + "条"; default: return "字牌" + value; } } /** * 转换为可读格式 */ private static String convertToReadable(List cards) { if (cards == null || cards.isEmpty()) { return "空"; } List cardStrs = new ArrayList<>(); for (int card : cards) { cardStrs.add(formatCard(card)); } return String.join(" ", cardStrs); } public List chuguodepai() { return chuguodepai; } 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 countMap = new HashMap<>(); for (Integer item : cardInHand) { countMap.put(item, countMap.getOrDefault(item, 0) + 1); } System.out.println("countMap: " + countMap); int jiangnum = 0; for (int key : countMap.keySet()) { if (isJiangPai(key) && countMap.get(key) == 2) { jiangnum++; } } return jiangnum; } public static int checkChiTingAction(int card,List changShaCardInhand){ //判断吃之后能否下听 List> lists = new ArrayList<>(); lists.addAll(TinHuChi.checkChi(changShaCardInhand, card)); int index = 0; int flag = 0; for (List list : lists) { List temphand = new ArrayList<>(); temphand.addAll(changShaCardInhand); temphand.add(card); Util.removeCard(temphand, list.get(0), 1); Util.removeCard(temphand, list.get(1), 1); Util.removeCard(temphand, list.get(2), 1); List checktingpai1 = TinHuChi.checktingpai(temphand); if (checktingpai1.size() > 0) { flag = index + 1; } index++; System.out.println("checktingpai1 吃牌 " + checktingpai1); } // TODO: 2026/1/1 // 1.需要补充 没听牌也可以吃 但是吃之后要比吃之前的手牌强 也就是可听数量变多 // 2.听牌后也能吃牌,要比吃之前的牌强 // 3.碰也一样要加 //吃之后不下听 if (flag>0){ return flag; } return 0; } public static int checkCanChiAction(int card,List changShaCardInhand){ //吃之后也不能听 Map map = new HashMap<>(); Map map2 = new HashMap<>(); //吃之前的逻辑 List> lists1 = TinHuChi.checkChi(changShaCardInhand, card); int jiangnum = checkduijiang(changShaCardInhand); List tmpChangSch = new ArrayList<>(); tmpChangSch.addAll(changShaCardInhand); tmpChangSch.add(card); ChangshaWinSplitCard.checkNormalHu(tmpChangSch, map); System.out.println("checkNormalHu" + map.get("cardResiue")); System.out.println("checktingpai1" + lists1); int index1 = 0; int flag1 = 0; for (List list : lists1) { List temphand = new ArrayList<>(); temphand.addAll(tmpChangSch); Util.removeCard(temphand, list.get(0), 1); Util.removeCard(temphand, list.get(1), 1); Util.removeCard(temphand, list.get(2), 1); ChangshaWinSplitCard.checkNormalHu(temphand, map2); //判断两个map是否找到更优 System.out.println("map1:" + Integer.parseInt(map.get("remainingMelds").toString())); System.out.println("map2:" + Integer.parseInt(map2.get("remainingMelds").toString())); if (Integer.parseInt(map2.get("remainingMelds").toString()) < Integer.parseInt(map.get("remainingMelds").toString())) { flag1 = index1 + 1; } else if (Integer.parseInt(map2.get("remainingMelds").toString()) == Integer.parseInt(map.get("remainingMelds").toString())) { int size2 = ((List) map2.get("cardResiue")).size(); System.out.println("size2" + size2); int size1 = ((List) map.get("cardResiue")).size(); System.out.println("size1" + size1); if (size2 < size1) { flag1 = index1 + 1; } //如果手里没有将,则可以吃 if (jiangnum > 0) { int chihoujiangnum = checkduijiang(temphand); if (chihoujiangnum > 0) { //吃之后还有将 flag1 = index1 + 1; } } else { System.out.println("没队将"); //孤章1张 差一手 不是将 而且card不是将 if (Integer.parseInt(map.get("remainingMelds").toString()) == 1 && size1 == 1 && !isJiangPai(((List) map.get("cardResiue")).get(0)) && !isJiangPai(card)) { //吃 break; } flag1 = index1 + 1; // break; } } index1++; } if (flag1 > 0) { return flag1; } return 0; } /** * * @param handcards * @param chowGrop * @param pengGrop * @param gangGrop * @return * 1,清一色,2,7小对,3,碰碰胡,4,将将胡,5,门清 */ public static int checkDahu(List handcards,List chowGrop,ListpengGrop,ListgangGrop){ //检测是否可以是大胡 //1、青一色 boolean qys = checkHandsQingYiSe(handcards,chowGrop,pengGrop,gangGrop); if (qys){ return 1; } //2、7小对 int cpairs = countPairs(handcards); if (cpairs>4){ return 2; } boolean pph = false; //3、碰碰胡 if (pengGrop.size()==3&&chowGrop.size()==0){ if (cpairs>3){ return 3; } }else if (pengGrop.size()==6&&chowGrop.size()==0){ if (cpairs>2){ return 3; } }else if (pengGrop.size()==9&&chowGrop.size()==0){ if (cpairs>1){ return 3; } } //4、将将胡 boolean jjh = false; if (pengGrop.size() == 0){ int count258 = 0; for (Integer card : handcards) { if (is258Card(card)) { count258++; } } if (count258>=9){ return 4; } } if (pengGrop.size() == 3){ //碰都是将 boolean tmpj = isJiangPai(pengGrop.get(0)); if (tmpj){ int count258 = 0; for (Integer card : handcards) { if (is258Card(card)) { count258++; } } if (count258>6){ return 4; } } } if (pengGrop.size() == 6){ //碰都是将 boolean tmpj2 = isJiangPai(pengGrop.get(0)); if (tmpj2){ int count258 = 0; for (Integer card : handcards) { if (is258Card(card)) { count258++; } } if (count258>3){ return 4; } } } return 0; } public static boolean checkHandsQingYiSe(List handcards,List chowGrop,ListpengGrop,ListgangGrop){ //true 为可以 false 为不能 List cpg = new ArrayList<>(); cpg.addAll(chowGrop); cpg.addAll(pengGrop); cpg.addAll(gangGrop); if (cpg.size()>0){ int tmp1 = 0; int tmp2 = 0; for (int i=0;i10){ //落地已经有了不同色,所以可以进行 return false; } } } Map suitCountMap = new HashMap<>(); for (Integer card : handcards) { int suit = card / 100; // 获取花色,100=万,200=筒,300=条 suitCountMap.put(suit, suitCountMap.getOrDefault(suit, 0) + 1); } for (Map.Entry entry : suitCountMap.entrySet()) { int suit = entry.getKey(); int count = entry.getValue(); if (handcards.size()==13){ if (count >= 10) { return true; } }else if (handcards.size()==10){ if (count >= 7) { List cpg2 = new ArrayList<>(); cpg2.addAll(chowGrop); cpg2.addAll(pengGrop); cpg2.addAll(gangGrop); if (cpg2.size()>0){ if(cpg2.get(0)/100!=suit){ return false; } } return true; } } else if (handcards.size()==7) { if (count >= 4) { List cpg3 = new ArrayList<>(); cpg3.addAll(chowGrop); cpg3.addAll(pengGrop); cpg3.addAll(gangGrop); if (cpg3.size()>0){ if(cpg3.get(0)/100!=suit){ return false; } } return true; } }else if (handcards.size()==4) { if (count >= 2) { List cpg3 = new ArrayList<>(); cpg3.addAll(chowGrop); cpg3.addAll(pengGrop); cpg3.addAll(gangGrop); if (cpg3.size()>0){ if(cpg3.get(0)/100!=suit){ return false; } } return true; } } } return false; } /** * 通过结果计算最优分数并返回对应的选择id * @param map * @return */ public static int suanfen(Mapmap){ System.out.println("suanfen"+map); if (map.size()==0){ return 0; } Map fell = new HashMap<>(); for (Map.Entry entry : map.entrySet()) { int tmp = 0; /*if (entry.getValue().getBoolean("isTing")){ tmp= tmp+30; }*/ if (entry.getValue().getBoolean("xiaoDahu")){ tmp= tmp-20; } /* if (entry.getValue().getInt("isDaHu")>0){ tmp= tmp-10; }*/ if (entry.getValue().getBoolean("xiaoJiang")){ tmp= tmp-13; } if (entry.getValue().getInt("tingNum")!=0){ tmp= tmp+entry.getValue().getInt("tingNum")*4; } if (entry.getValue().getBoolean("sanTing")){ tmp= tmp-50; } if (entry.getValue().getBoolean("canTing")&&!entry.getValue().getBoolean("isTing")){ //从不能听牌到听牌, tmp= tmp+20; } if (entry.getValue().getInt("teshu")>0){ tmp= tmp+entry.getValue().getInt("teshu")*1; } if (entry.getValue().getInt("lastHands")>0){ tmp= tmp+entry.getValue().getInt("lastHands")*5; } if (entry.getValue().getInt("guzhang")>0){ tmp= tmp+entry.getValue().getInt("guzhang")*1; } System.out.println("tmp:"+tmp); fell.put(entry.getKey(), tmp); } //找分数最高的id int dangqianfen = 0; int dangqianid = 0; for (Map.Entry entry : fell.entrySet()) { if (dangqianid==0){ dangqianid = entry.getKey(); dangqianfen = entry.getValue(); } if (entry.getValue()>dangqianfen){ dangqianfen = entry.getValue(); dangqianid = entry.getKey(); } } if (dangqianfen>4){ return dangqianid; }else{ dangqianid = 0; } return dangqianid; } /** * 检测碰之后的结果 * @param beforelisten * @param card * @param opcard * @param yupanhandcard * @param beforeIsDahu * @param allSeeCard * @param chowGroup * @param pengGrop * @param gangGrop * @return * * boolean isTing 是否听牌 * * boolean canTing 能否听牌 * * boolean sanTing 是否散听 * * int tingNum 听多少张 * * int isDaHu 是否大胡 * * boolean xiaoDahu 是否消掉大胡 * * * * int guzhang 有多少孤章 相差 * * int lastHands 差多少手 相差 * * boolean xiaoJiang 是否把将去没了 * int teshu 特殊加分减分 * * * int id tiplist id * * List opcard * * int weight tiplist weight * * int card tiplist card * * int type tiplist type * */ public static ITObject pingguPeng(boolean beforelisten, int card, ITArray opcard, List yupanhandcard, int beforeIsDahu, List allSeeCard ,ListchowGroup,ListpengGrop,ListgangGrop,ListchangShachuguopai){ ITObject chiob = new TObject(); chiob.putBoolean("isTing",beforelisten); chiob.putInt("isDaHu",beforeIsDahu); List checkCards = new ArrayList(); checkCards.addAll(yupanhandcard); System.out.println("checkCards"+checkCards); //去掉三张牌是否还能听牌 List npeng = new ArrayList<>(); for (int i=0;i=1){ int afterdj = checkduijiang(checkCards); System.out.println(checkCards); if (afterdj==0){ chiob.putBoolean("xiaoJiang",true); }else{ chiob.putBoolean("xiaoJiang",false); } }else{ chiob.putBoolean("xiaoJiang",false); } //循环去三张还能听牌 Map> afterdaHuOp = quyizhangDahuTingPai(checkCards,chowGroup,npeng,gangGrop); if (afterdaHuOp.size()>0){ chiob.putBoolean("canTing", true); chiob.putBoolean("sanTing", false); }else { Map> afterOp = quyizhangTingPai(checkCards); if (afterOp.size() == 0 && beforelisten) { chiob.putBoolean("sanTing", true); chiob.putBoolean("canTing", false); } if (afterOp.size() > 0) { chiob.putBoolean("canTing", true); chiob.putBoolean("sanTing", false); } else { chiob.putBoolean("canTing", false); chiob.putBoolean("sanTing", false); } } //循环之后还能是大胡吗?1,清一色,2,7小对,3,碰碰胡,4,将将胡,5,门清 if(beforeIsDahu>0){ if (beforeIsDahu==2){ chiob.putBoolean("xiaoDahu",true); chiob.putInt("teshu",0); } if (beforeIsDahu==3){ //碰碰胡 chiob.putBoolean("xiaoDahu",false); chiob.putInt("teshu",10); }else if (beforeIsDahu==4){ //判断被碰的牌是否将 if(!isJiangPai(card)){ chiob.putBoolean("xiaoDahu",true); }else{ chiob.putBoolean("xiaoDahu",false); chiob.putInt("teshu",10); } }else if(beforeIsDahu==5){ //门清 chiob.putBoolean("xiaoDahu",true); chiob.putInt("teshu",0); }else{ //毁掉清一色 List tmpchi = new ArrayList<>(); List chi = new ArrayList<>(); //新new 吃的牌 todo tmpchi.addAll(chowGroup); chi.addAll(chowGroup); ListpongGroup = new ArrayList<>(); List gangdepai = new ArrayList<>(); gangdepai.addAll(gangGrop); pongGroup.add(card); for (int i=0;i map = new HashMap<>(); List tmpChangSch = new ArrayList<>(); tmpChangSch.addAll(yupanhandcard); ChangshaWinSplitCard.checkNormalHu(tmpChangSch, map); System.out.println("map:"+map); //操作后,去一张牌,分析求最优 Map map2 = new HashMap<>(); System.out.println(checkCards); Map> xiatingList = new HashMap<>(); List peng = new ArrayList<>(); //新new peng.add(card); peng.add(card); peng.add(card); map2 = quyizhangChayou(checkCards,chowGroup,peng,gangGrop,xiatingList); Util.removeCard(peng,card,3); peng.add(card); peng.add(card); System.out.println("map2:"+map2); System.out.println("xiatingList:"+xiatingList); //比对 if (map2.size()>0){ //可以下听 chiob.putInt("guzhang",((List)map.get("cardResiue")).size()-((List)map2.get("cardResiue")).size()); chiob.putInt("lastHands",Integer.parseInt(map.get("remainingMelds").toString())-Integer.parseInt(map.get("remainingMelds").toString())); }else{ chiob.putInt("guzhang",((List)map.get("cardResiue")).size()); chiob.putInt("lastHands",Integer.parseInt(map.get("remainingMelds").toString())); } //听多张问题: List caozuoqian = handscardshifoutingpai(yupanhandcard,chowGroup,pengGrop,gangGrop); int caozuoqiannum = 0;//操作之前听牌张数 List allpai = new ArrayList<>(); allpai.addAll(yupanhandcard); allpai.addAll(changShachuguopai); if (caozuoqian.size()>0){ caozuoqiannum = getTingPainum(caozuoqian,allpai); } int caozuohounum = 0; if (xiatingList.size()>0){ //获取能听牌数最多的 for (Map.Entry> entry : xiatingList.entrySet()) { if (caozuohounum==0){ caozuohounum = getTingPainum(entry.getValue(),allpai); } if (caozuohounum < getTingPainum(entry.getValue(),allpai)){ caozuohounum = getTingPainum(entry.getValue(),allpai); } } } chiob.putInt("tingNum",caozuohounum - caozuoqiannum); //先评分 int tmp = 0; /*if (entry.getValue().getBoolean("isTing")){ tmp= tmp+30; }*/ if (chiob.getBoolean("xiaoDahu")){ tmp= tmp-20; } /* if (entry.getValue().getInt("isDaHu")>0){ tmp= tmp-10; }*/ if (chiob.getBoolean("xiaoJiang")){ tmp= tmp-13; } if (chiob.getInt("tingNum")>0){ tmp= tmp+chiob.getInt("tingNum")*4; } if (chiob.getBoolean("sanTing")){ tmp= tmp-50; } if (chiob.getBoolean("canTing")&&!chiob.getBoolean("isTing")){ //从不能听牌到听牌, tmp= tmp+25; } if (chiob.getInt("teshu")>0){ tmp= tmp+chiob.getInt("teshu")*1; } if (chiob.getInt("lastHands")>0){ tmp= tmp+chiob.getInt("lastHands")*5; } if (chiob.getInt("guzhang")>0){ tmp= tmp+chiob.getInt("guzhang")*2; } //如果评分大于0,且小于5 if (tmp>0&&tmp<=5){ //去掉将时候 //判断是否有对将 if(card%100==9||card%100==1){ chiob.putInt("teshu",5); } } //检测是否有4对 int duizi = countPairs(yupanhandcard); System.out.println("duizi:"+duizi); System.out.println(pengGrop); if (duizi==4&&pengGrop.size()>=0){ chiob.putInt("teshu",5); } if (duizi==3&&pengGrop.size()>=2){ chiob.putInt("teshu",8); } System.out.println(pengGrop.size()); if (duizi>=2&&pengGrop.size()>=6){ chiob.putInt("teshu",12); } System.out.println("peng:"+chiob); return chiob; } /** * * @param beforelisten * @param card * @param opcard * @param yupanhandcard * @param beforeIsDahu * @param allSeeCard * @return * boolean isTing 是否听牌 * boolean canTing 能否听牌 * boolean sanTing 是否散听 * int tingNum 听多少张 * int isDaHu 是否大胡 * boolean xiaoDahu 是否消掉大胡 * * int guzhang 有多少孤章 相差 * int lastHands 差多少手 相差 * boolean xiaoJiang 是否把将去没了 * * int id tiplist id * List opcard * int weight tiplist weight * int card tiplist card * int type tiplist type */ public static ITObject pingguChi(boolean beforelisten, int card, ITArray opcard, List yupanhandcard, int beforeIsDahu, List allSeeCard ,ListchowGroup,ListpengGrop,ListgangGrop,ListchangShachuguopai){ ITObject chiob = new TObject(); chiob.putBoolean("isTing",beforelisten); chiob.putInt("isDaHu",beforeIsDahu); chiob.putInt("teshu",0); List checkCards = new ArrayList(); checkCards.addAll(yupanhandcard); System.out.println("运行之前:"+checkCards); //去掉三张牌是否还能听牌 List nchi = new ArrayList(); for (int i=0;i=1){ int afterdj = checkduijiang(checkCards); if (afterdj==0){ chiob.putBoolean("xiaoJiang",true); }else{ chiob.putBoolean("xiaoJiang",false); } }else{ chiob.putBoolean("xiaoJiang",false); } Map> afterdaHuOp = quyizhangDahuTingPai(checkCards,nchi,pengGrop,gangGrop); if (afterdaHuOp.size()>0){ chiob.putBoolean("canTing",true); chiob.putBoolean("sanTing",false); }else { //循环去一张还能听牌 Map> afterOp = quyizhangTingPai(checkCards); if (afterOp.size() == 0 && beforelisten) { chiob.putBoolean("sanTing", true); chiob.putBoolean("canTing", false); } if (afterOp.size() > 0) { chiob.putBoolean("canTing", true); chiob.putBoolean("sanTing", false); } else { chiob.putBoolean("canTing", false); chiob.putBoolean("sanTing", false); } } //循环之后还能是大胡吗?1,清一色,2,7小对,3,碰碰胡,4,将将胡,5,门清 if(beforeIsDahu>0){ if (beforeIsDahu>1){ chiob.putBoolean("xiaoDahu",true); }else{ //毁掉清一色 List tmpchi = new ArrayList<>(); tmpchi.addAll(chowGroup); ListpongGroup = new ArrayList<>(); List gangdepai = new ArrayList<>(); List chi = new ArrayList<>(); // 新new chi.add(card); chi.addAll(chowGroup); for (int i=0;i map = new HashMap<>(); List tmpChangSch = new ArrayList<>(); tmpChangSch.addAll(yupanhandcard); ChangshaWinSplitCard.checkNormalHu(tmpChangSch, map); System.out.println("map:"+map); //操作后,去一张牌,分析求最优 Map map2 = new HashMap<>(); System.out.println(checkCards); Map> xiatingList = new HashMap<>(); List chi = new ArrayList<>(); //新new chi.addAll(chowGroup); chi.add(card); for (int i=0;i0){ //可以下听 chiob.putInt("guzhang",((List)map.get("cardResiue")).size()-((List)map2.get("cardResiue")).size()); chiob.putInt("lastHands",Integer.parseInt(map.get("remainingMelds").toString())-Integer.parseInt(map2.get("remainingMelds").toString())); }else{ chiob.putInt("guzhang",((List)map.get("cardResiue")).size()); chiob.putInt("lastHands",Integer.parseInt(map.get("remainingMelds").toString())); } //听多张问题: List caozuoqian = handscardshifoutingpai(yupanhandcard,chowGroup,pengGrop,gangGrop); int caozuoqiannum = 0;//操作之前听牌张数 List allpai = new ArrayList<>(); allpai.addAll(yupanhandcard); allpai.addAll(changShachuguopai); if (caozuoqian.size()>0){ caozuoqiannum = getTingPainum(caozuoqian,allpai); } int caozuohounum = 0; if (xiatingList.size()>0){ //获取能听牌数最多的 for (Map.Entry> entry : xiatingList.entrySet()) { if (caozuohounum==0){ caozuohounum = getTingPainum(entry.getValue(),allpai); } if (caozuohounum < getTingPainum(entry.getValue(),allpai)){ caozuohounum = getTingPainum(entry.getValue(),allpai); } } } chiob.putInt("tingNum",caozuohounum - caozuoqiannum); System.out.println("chiob:"+chiob); return chiob; } public static boolean shiFouJieTing(List tingList,List changShachuguopai ){ return false; } public static int getTingPainum(List caozq, List changShachuguopai){ int num = 0; for (int i=0;i handscardshifoutingpai(List cardhand,ListchowGroup,List pongGroup,List gangdepai) { List tpcards = new ArrayList<>(); List tmphc = new ArrayList<>(); tmphc.addAll(cardhand); for (int j = 101; j <= 109; j++) { WinCard win = new WinCard(tmphc, j); if (win.tryWin()) { if (!tpcards.contains(j)) { tpcards.add(j); } } Paixing px = new Paixing(); List pxc = new ArrayList<>(); pxc.addAll(tmphc); //pxc.add(j); if(px.tingCheck(pxc,chowGroup,pongGroup,gangdepai)){ if (!tpcards.contains(j)) { tpcards.add(j); } } } for (int k = 201; k<= 209; k++) { WinCard win = new WinCard(tmphc, k); if (win.tryWin()) { if (!tpcards.contains(k)) { tpcards.add(k); } } Paixing px = new Paixing(); List pxc2 = new ArrayList<>(); pxc2.addAll(tmphc); // pxc2.add(k); if(px.tingCheck(pxc2,chowGroup,pongGroup,gangdepai)){ if (!tpcards.contains(k)) { tpcards.add(k); } } } //如果没有则检测大胡 return tpcards; } public static Map> quyizhangDahuTingPai(List cardhand,List chowGroup,List pongGroup,List gangdepai){ Map> map = new HashMap<>(); List tmphc = new ArrayList<>(); tmphc.addAll(cardhand); // List tmphc = cardhand; //问题111 for (int i = 0; i < cardhand.size(); i++) { int tmpcard = tmphc.get(0); tmphc.remove(0); List huziting = new ArrayList<>(); for (int j = 101; j <= 109; j++) { Paixing px = new Paixing(); List pxc = new ArrayList<>(); pxc.add(j); pxc.addAll(tmphc); if(px.tingCheck(pxc,chowGroup,pongGroup,gangdepai)){ if (!huziting.contains(j)) { huziting.add(j); } } } for (int k = 201; k <= 209; k++) { Paixing px2 = new Paixing(); List pxc2 = new ArrayList<>(); pxc2.add(k); pxc2.addAll(tmphc); if(px2.tingCheck(pxc2,chowGroup,pongGroup,gangdepai)){ if (!huziting.contains(k)) { huziting.add(k); } } } if (huziting.size()>0){ map.put(tmpcard,huziting); } tmphc.add(tmpcard); } return map; } public static Map> quyizhangTingPai(List cardhand) { Map> quxiatingmap = new HashMap<>(); List tmphc = new ArrayList<>(); tmphc.addAll(cardhand); // List tmphc = cardhand; //问题111 for (int i = 0; i < cardhand.size(); i++) { int tmpcard = tmphc.get(0); tmphc.remove(0); List huziting = new ArrayList<>(); for (int j = 101; j <= 109; j++) { WinCard win = new WinCard(tmphc, j); if (win.tryWin()) { if (!huziting.contains(j)) { huziting.add(j); } } } for (int k = 201; k <= 209; k++) { WinCard win = new WinCard(tmphc, k); if (win.tryWin()) { if (!huziting.contains(k)) { huziting.add(k); } } } if (huziting.size()>0){ quxiatingmap.put(tmpcard,huziting); } tmphc.add(tmpcard); } return quxiatingmap; } public static Map quyizhangChayou(List cardhand,ListchowGroup,List pongGroup,List gangdepai,Map> xiatingList) { Map map = new HashMap<>(); Map> bigMap = new HashMap<>(); List tmphc = new ArrayList<>(); tmphc.addAll(cardhand); System.out.println("tmphc1:"+tmphc); for (int i = 0; i < cardhand.size(); i++) { int tmpcard = tmphc.get(0); tmphc.remove(0); Map tmap = new HashMap<>(); int res = ChangshaWinSplitCard.checkNormalHu(tmphc,tmap); bigMap.put(tmpcard,tmap); //检查是否能下听 List tmxt = new ArrayList<>(); tmxt = handscardshifoutingpai(tmphc,chowGroup,pongGroup,gangdepai); if (tmxt.size()>0){ xiatingList.put(tmpcard,tmxt); } tmphc.add(tmpcard); } System.out.println(bigMap); System.out.println("吃之后"); //遍历找最优解 if (bigMap.size()==0){ return map; } Map bestmap = gaiBestCardInMap(bigMap); return bestmap; } private static Map gaiBestCardInMap(Map> map){ Map tmpmap = new HashMap<>(); int bestcard = 0; for (Map.Entry> entry : map.entrySet()) { if (tmpmap.isEmpty()){ tmpmap = entry.getValue(); bestcard = entry.getKey(); }else{ int tmpr = Integer.parseInt( tmpmap.get("remainingMelds").toString()); int entr = Integer.parseInt( entry.getValue().get("remainingMelds").toString()); if (tmpr>entr){ tmpmap = entry.getValue(); }else if (tmpr==entr){ List tl = (List)tmpmap.get("cardResiue"); List enl = (List)entry.getValue().get("cardResiue"); if (tl.size()>enl.size()){ tmpmap = entry.getValue(); } if(tl.size()==enl.size()){ //寻找是否有边张 for (int i=0;i test1 = new ArrayList(); test1.add(205); test1.add(204); test1.add(204); test1.add(203); test1.add(202); test1.add(201); test1.add(108); test1.add(107); test1.add(103); test1.add(103); // test1.add(106); //test1.add(104); // test1.add(103); int lg = countPairs(test1); System.out.println(lg); //int card = 206; //int flag = checkChiTingAction(card,test1); //System.out.println(flag); //测试吃: List allcard = new ArrayList<>(); allcard.add(105); allcard.add(101); allcard.add(209); allcard.add(209); allcard.add(104); allcard.add(104); allcard.add(104); //allcard.add(104); /*ITArray opcard = TArray.newInstance(); opcard.addInt(209); opcard.addInt(208);*/ List chipai = new ArrayList<>(); chipai.add(207); chipai.add(208); chipai.add(209); // chipai.add(101); // chipai.add(102); // chipai.add(103); // int card = 202; ListchangShachuguopai = new ArrayList<>(); //changShachuguopai.add(101); changShachuguopai.add(202); changShachuguopai.add(201); changShachuguopai.add(203); changShachuguopai.add(105); changShachuguopai.add(105); changShachuguopai.add(105); changShachuguopai.addAll(allcard); List gangguopai = new ArrayList<>(); // gangguopai.add(108);0 int card = 103; ListpengCard = new ArrayList<>(); /*pengCard.add(203); pengCard.add(203); pengCard.add(203); pengCard.add(101); pengCard.add(101); pengCard.add(101);*/ //出牌求优 ChangShaSuanFaTest ct = new ChangShaSuanFaTest(); test1.add(card); String outcard = ct.outCardSuanFa(test1,pengCard,chipai,gangguopai,changShachuguopai); System.out.println("outcard:"+outcard); // pingguChi(false,card,opcard,test1,0,allcard,chipai,changShachuguopai); //测试碰 // int card = 102; ITArray opcardpeng = TArray.newInstance(); opcardpeng.addInt(203); List pengpai = new ArrayList<>(); List gangpai = new ArrayList<>(); //pingguPeng(true,card,opcardpeng,test1,0,allcard,chipai,pengpai,gangpai,changShachuguopai); /* //Listtf = handscardshifoutingpai(test1); // System.out.println(tf); //Map map = new HashMap<>(); //map = quyizhangChayou(test1); // System.out.println(map); /*Map> maps = new HashMap<>(); maps = quyizhangTingPai(test1); System.out.println(maps);*/ // test1.add(202); // test1.add(209); // test1.add(201); // test1.add(208); // test1.add(207); // test1.add(209); // test1.add(104); // test1.add(203); // test1.add(202); // test1.add(201); // test1.add(103); // test1.add(102); /* int card = 201; List pengguopai = new ArrayList<>(); pengguopai.add(204); pengguopai.add(204); pengguopai.add(204); List chipai = new ArrayList<>(); chipai.add(203); chipai.add(202); chipai.add(201); List gangguopai = new ArrayList<>(); gangguopai.add(206); gangguopai.add(206); gangguopai.add(206); gangguopai.add(206); System.out.println("手牌:" + test1); boolean c = checkAllSameSuitAll(card,test1,pengguopai,chipai,gangguopai); System.out.println(c); */ // if (!bestDiscard.isEmpty()) { // System.out.println("建议出牌:" + bestDiscard.get(0)); // } else { // System.out.println("没有出牌建议"); // } } }