diff --git a/robots/majiang/robot_mj_cs/src/main/java/robot/mj/Config.java b/robots/majiang/robot_mj_cs/src/main/java/robot/mj/Config.java index 8f18e2d..e808312 100644 --- a/robots/majiang/robot_mj_cs/src/main/java/robot/mj/Config.java +++ b/robots/majiang/robot_mj_cs/src/main/java/robot/mj/Config.java @@ -25,4 +25,26 @@ public class Config { */ public static final String JOIN_ROOM_CS = "1002"; + /** Web组加入房间协议 */ + public static final String WEB_GROUP_JOIN_ROOM = "225"; + + /** Web组主动重连协议 */ + public static final String WEB_GROUP_ACTIVE_RECONNECT = "226"; + + //==================== 游戏服务器配置 ==================== + /** 游戏服务器主机地址 */ + public static final String GAME_SERVER_HOST = "127.0.0.1"; + + /** 游戏服务器端口 */ + public static final String GAME_SERVER_PORT = "6311"; + + /** 默认密码 */ + public static final String DEFAULT_PASSWORD = "123456"; + + /** 默认PID */ + public static final String DEFAULT_PID = "10"; + + /** 默认群组ID */ + public static final String DEFAULT_GROUP_ID = "762479"; + } \ No newline at end of file diff --git a/robots/majiang/robot_mj_cs/src/main/java/robot/mj/EXGameController.java b/robots/majiang/robot_mj_cs/src/main/java/robot/mj/EXGameController.java index fc52a3a..4b5e6af 100644 --- a/robots/majiang/robot_mj_cs/src/main/java/robot/mj/EXGameController.java +++ b/robots/majiang/robot_mj_cs/src/main/java/robot/mj/EXGameController.java @@ -219,7 +219,7 @@ public class EXGameController extends GameController { /** * 接收来自web_group的加入房间协议 */ - @ActionKey(value = "225", validate = GameInterceptor.NOT_PLAYER) + @ActionKey(value = Config.WEB_GROUP_JOIN_ROOM, validate = GameInterceptor.NOT_PLAYER) public void webGroup(Session session, ITObject params, int gid) { int robotId = params.getInt("robotid"); String roomId = params.getString("roomid"); @@ -245,7 +245,7 @@ public class EXGameController extends GameController { /** * 接收来自web_group的主动重连协议 */ - @ActionKey(value = "226", validate = GameInterceptor.NOT_PLAYER) + @ActionKey(value = Config.WEB_GROUP_ACTIVE_RECONNECT, validate = GameInterceptor.NOT_PLAYER) public void webGroupActive(Session session, ITObject params, int gid) { int robotId = params.getInt("robotid"); String roomId = params.getString("roomid"); @@ -363,11 +363,11 @@ public class EXGameController extends GameController { if (robotUser ==null) { RobotUser robotUserCopy = new RobotUser(); robotUserCopy.setRobotId(robotId); - robotUserCopy.setPassword("123456"); - robotUserCopy.setGameHost("127.0.0.1"); - robotUserCopy.setGamePort("6311"); - robotUserCopy.setRobotGroupid("330800"); - robotUserCopy.setRobotPid("10"); + robotUserCopy.setPassword(Config.DEFAULT_PASSWORD); + robotUserCopy.setGameHost(Config.GAME_SERVER_HOST); + robotUserCopy.setGamePort(Config.GAME_SERVER_PORT); + robotUserCopy.setRobotGroupid(Config.DEFAULT_GROUP_ID); + robotUserCopy.setRobotPid(Config.DEFAULT_PID); return robotUserCopy; } return robotRoomMapping.get(robotId); diff --git a/robots/majiang/robot_mj_cs/src/main/java/robot/mj/EXMainServer.java b/robots/majiang/robot_mj_cs/src/main/java/robot/mj/EXMainServer.java index b6a2df2..23c8955 100644 --- a/robots/majiang/robot_mj_cs/src/main/java/robot/mj/EXMainServer.java +++ b/robots/majiang/robot_mj_cs/src/main/java/robot/mj/EXMainServer.java @@ -35,18 +35,18 @@ public class EXMainServer extends MainServer{ //startConnectionCheckScheduler(); //测试 Jedis jedis2 = Redis.use("group1_db2").getJedis(); - String robotskey = "g{"+762479+"}:play:"+10; + String robotskey = "g{"+Config.DEFAULT_GROUP_ID+"}:play:"+Config.DEFAULT_PID; Map maprobot = jedis2.hgetAll(robotskey); for(Map.Entry entry : maprobot.entrySet()) { System.out.println(entry.getKey() + ":" + entry.getValue()); //是否创建 RobotUser robotUser = new RobotUser(); robotUser.setRobotId(entry.getKey()); - robotUser.setPassword("123456"); - robotUser.setGameHost("127.0.0.1"); - robotUser.setGamePort("6311"); - robotUser.setRobotGroupid("762479"); - robotUser.setRobotPid("10"); + robotUser.setPassword(Config.DEFAULT_PASSWORD); + robotUser.setGameHost(Config.GAME_SERVER_HOST); + robotUser.setGamePort(Config.GAME_SERVER_PORT); + robotUser.setRobotGroupid(Config.DEFAULT_GROUP_ID); + robotUser.setRobotPid(Config.DEFAULT_PID); robotRoomMapping.put(entry.getKey(), robotUser); } diff --git a/robots/majiang/robot_mj_cs/src/main/java/robot/mj/RobotConnectionManager.java b/robots/majiang/robot_mj_cs/src/main/java/robot/mj/RobotConnectionManager.java index 31b63c7..8178c3a 100644 --- a/robots/majiang/robot_mj_cs/src/main/java/robot/mj/RobotConnectionManager.java +++ b/robots/majiang/robot_mj_cs/src/main/java/robot/mj/RobotConnectionManager.java @@ -34,8 +34,8 @@ public class RobotConnectionManager { private static final Map huNanChangShaInstances = new ConcurrentHashMap<>(); private final EXGameController exGameController; - private final String host="127.0.0.1"; - private final int port=6311; + private final String host= Config.GAME_SERVER_HOST; + private final int port= Integer.parseInt(Config.GAME_SERVER_PORT); /*长沙麻将游戏算法相关 start*/ private final Map>> playerOutcardsMapByConn = new ConcurrentHashMap<>(); diff --git a/robots/majiang/robot_mj_cs/src/main/java/taurus/util/ChangshaWinSplitCard.java b/robots/majiang/robot_mj_cs/src/main/java/taurus/util/ChangshaWinSplitCard.java index f4f2a6f..5b1ad45 100644 --- a/robots/majiang/robot_mj_cs/src/main/java/taurus/util/ChangshaWinSplitCard.java +++ b/robots/majiang/robot_mj_cs/src/main/java/taurus/util/ChangshaWinSplitCard.java @@ -8,20 +8,28 @@ public class ChangshaWinSplitCard { public static int[][] countTiles(List cardInHand) { int[][] counts = new int[5][10]; // 类型×值 + //预计算牌型和牌值 避免重复计算 for (Integer card : cardInHand) { if (card == 0) { continue; } - counts[card / 100 - 1][card % 100]++; + int suitIndex = card / 100 - 1; // 牌型索引 + int cardValue = card % 100; // 牌面值 + counts[suitIndex][cardValue]++; } return counts; } // 检测不带精湖 public static int checkNormalHu(List cardInHand, Map map) { + if (cardInHand == null || cardInHand.isEmpty()) { + throw new IllegalArgumentException("手牌不能为空"); + } + String info = ""; int[][] counts = countTiles(cardInHand); int group = 0; + int handSizeDiv3 = cardInHand.size() / 3;//缓存计算结果 避免重复计算 // 尝试找将牌 for (int type = 0; type < 5; type++) { int max = 9; @@ -29,14 +37,14 @@ public class ChangshaWinSplitCard { if (counts[type][value] >= 2 && (value == 2 || value == 5 || value == 8)) { // 找到可能的将牌 counts[type][value] -= 2; - group = checkMelds(counts, cardInHand.size() / 3, map); + group = checkMelds(counts, handSizeDiv3, map); if (group == 0) { // 剩余12张牌需要组成4个顺子或刻子 counts[type][value] += 2; return 0; } counts[type][value] += 2; } else { - group = checkMelds(counts, (cardInHand.size() / 3) + 1, map); + group = checkMelds(counts, handSizeDiv3 + 1, map); if (group == 0) { // 剩余12张牌需要组成4个顺子或刻子 counts[type][value] += 2; @@ -47,13 +55,20 @@ public class ChangshaWinSplitCard { } List cardResiue = (List) map.get("cardResiue"); - - for (int i = 0; i < cardResiue.size() - 1; i++) { - if (cardResiue.get(i).equals(cardResiue.get(i + 1)) - && (cardResiue.get(i) % 100 == 2 || cardResiue.get(i) % 100 == 5 || cardResiue.get(i) % 100 == 8)) { - cardResiue.remove(i); - cardResiue.remove(i); - break; + + //空指针检查 + if (cardResiue != null && cardResiue.size() > 1) { + for (int i = 0; i < cardResiue.size() - 1; i++) { + Integer currentCard = cardResiue.get(i); + Integer nextCard = cardResiue.get(i + 1); + + //缓存牌值计算结果 + int currentValue = currentCard % 100; + if (currentCard.equals(nextCard) && (currentValue == 2 || currentValue == 5 || currentValue == 8)) { + cardResiue.remove(i); + cardResiue.remove(i); + break; + } } } @@ -77,11 +92,15 @@ public class ChangshaWinSplitCard { for (int value = 1; value <= max; value++) { if (counts[type][value] >= 3) { counts[type][value] -= 3; - if (checkMelds(counts, remainingMelds - 1, map) == 0) { + try { + if (checkMelds(counts, remainingMelds - 1, map) == 0) { + counts[type][value] += 3; + return 0; + } + } finally { + //确保状态回滚 counts[type][value] += 3; - return 0; } - counts[type][value] += 3; } } } @@ -97,103 +116,83 @@ public class ChangshaWinSplitCard { counts[type][value + 1]--; counts[type][value + 2]--; - if (checkMelds(counts, remainingMelds - 1, map) == 0) { + try { + if (checkMelds(counts, remainingMelds - 1, map) == 0) { + return 0; + } + } finally { + //确保状态回滚 counts[type][value]++; counts[type][value + 1]++; counts[type][value + 2]++; - return 0; } - - counts[type][value]++; - counts[type][value + 1]++; - counts[type][value + 2]++; } } } // 判断孤章 List cardResiue = convertCountToCards(counts); - // System.out.println("只差几手牌" + remainingMelds + "," + cardResiue); - if (cardResiue.size() != 0) { + if (!cardResiue.isEmpty()) { // 优先保留连续张 removeConsecutivePairs1(cardResiue); } - if (cardResiue.size() != 0) { + if (!cardResiue.isEmpty()) { // 第二优先保留对子 removeConsecutivePairsTwo(cardResiue); } + // 最后保留坎张 // 如果坎张,为空则取边张 - if (cardResiue.size() != 0) { - List tmp3 = new ArrayList<>(); - tmp3.addAll(cardResiue); + if (!cardResiue.isEmpty()) { + List tmp3 = new ArrayList<>(cardResiue); //使用构造函数替代addAll removeConsecutivePairs2(cardResiue); - if (cardResiue.size() == 0) { + if (cardResiue.isEmpty()) { // 取边站 cardResiue = qubianzhang(tmp3); } } + //合并重复的条件判断 if (remainingMelds == 0) { map.put("remainingMelds", 0); map.put("cardResiue", "已经胡牌"); - - } - if (map.get("remainingMelds") == null) { + } else if (map.get("remainingMelds") == null) { map.put("remainingMelds", remainingMelds); - } if (map.get("cardResiue") == null) { map.put("cardResiue", cardResiue); } - for (int i = 0; i < cardResiue.size() - 1; i++) { - if (cardResiue.get(i).equals(cardResiue.get(i + 1)) - && (cardResiue.get(i) % 100 == 2 || cardResiue.get(i) % 100 == 5 || cardResiue.get(i) % 100 == 8)) { - cardResiue.remove(i); - cardResiue.remove(i); - if (remainingMelds >= 2) { - remainingMelds--; + //提取重复逻辑为私有方法 添加空指针检查 + if (cardResiue != null && cardResiue.size() > 1) { + for (int i = 0; i < cardResiue.size() - 1; i++) { + Integer currentCard = cardResiue.get(i); + Integer nextCard = cardResiue.get(i + 1); + + int currentValue = currentCard % 100; + if (currentCard.equals(nextCard) && is258JiangValue(currentValue)) { + cardResiue.remove(i); + cardResiue.remove(i); + if (remainingMelds >= 2) { + remainingMelds--; + } + break; } - break; } } - if (Integer.parseInt(map.get("remainingMelds").toString()) >= remainingMelds && cardResiue.size() > 0) { + //添加类型安全检查和空指针防护 + Object remainingMeldsObj = map.get("remainingMelds"); + if (remainingMeldsObj != null && + Integer.parseInt(remainingMeldsObj.toString()) >= remainingMelds && + !cardResiue.isEmpty()) { // 如果没有258做将则需要进入这里判断 if (remainingMelds == 1 && cardResiue.size() == 2) { - for (int i = 0; i < cardResiue.size(); i++) { - int diff = Math.abs(cardResiue.get(0) - cardResiue.get(1)); - if (diff > 1) { - map.put("remainingMelds", remainingMelds + 1);// 删除符合258做将的牌 - if (cardResiue.size() > 1) { - cardResiue.remove(i); - } - map.put("cardResiue", cardResiue); - - } else { - if (cardResiue.get(i) % 100 == 2 || cardResiue.get(i) % 100 == 5 - || cardResiue.get(i) % 100 == 8) { - // 删除符合258做将的牌 - if (cardResiue.size() > 1) { - cardResiue.remove(i); - } - map.put("remainingMelds", remainingMelds); - map.put("cardResiue", cardResiue); - - } else { - map.put("remainingMelds", remainingMelds); - map.put("cardResiue", cardResiue); - - } - } - } - + processSpecialCase(cardResiue, remainingMelds, map); } else { map.put("remainingMelds", remainingMelds); map.put("cardResiue", cardResiue); } - } return remainingMelds; @@ -203,22 +202,20 @@ public class ChangshaWinSplitCard { if (cards == null || cards.size() < 2) { return; } - // 先排序 - List sorted = new ArrayList<>(cards); - Collections.sort(sorted); - + + //使用TreeSet自动排序并去重 + Set uniqueCards = new TreeSet<>(cards); List result = new ArrayList<>(); - int i = 0; - - while (i < sorted.size()) { - // boolean isleft = sorted.get(i) % 100 == 1 || sorted.get(i) % 100 == 8; - if (i < sorted.size() - 1 && sorted.get(i + 1) - sorted.get(i) == 0) { - // 跳过对子 - i += 2; - } else { - // 保留这张牌 - result.add(sorted.get(i)); - i++; + + //过滤掉成对的牌 + Map cardCount = new HashMap<>(); + for (Integer card : cards) { + cardCount.put(card, cardCount.getOrDefault(card, 0) + 1); + } + + for (Integer card : uniqueCards) { + if (cardCount.get(card) == 1) { + result.add(card); } } @@ -232,16 +229,17 @@ public class ChangshaWinSplitCard { return; } - // 先排序 - List sorted = new ArrayList<>(cards); - Collections.sort(sorted); - + //使用TreeSet自动排序 + List sorted = new ArrayList<>(new TreeSet<>(cards)); List result = new ArrayList<>(); int i = 0; while (i < sorted.size()) { - boolean isleft = sorted.get(i) % 100 == 1 || sorted.get(i) % 100 == 8; - if (!isleft && i < sorted.size() - 1 && sorted.get(i + 1) - sorted.get(i) == 1) { + //缓存计算结果 + int currentValue = sorted.get(i) % 100; + boolean isEdgeCard = currentValue == 1 || currentValue == 8; + + if (!isEdgeCard && i < sorted.size() - 1 && sorted.get(i + 1) - sorted.get(i) == 1) { // 跳过这对连续牌 i += 2; } else { @@ -256,10 +254,16 @@ public class ChangshaWinSplitCard { } public static List qubianzhang(List cards) { + if (cards == null || cards.isEmpty()) { + return new ArrayList<>(); + } + List result = new ArrayList<>(); - for (int i = 0; i < cards.size(); i++) { - if (cards.get(i) % 100 == 1 || cards.get(i) % 100 == 9) { - result.add(cards.get(i)); + //使用增强for循环提高可读性 + for (Integer card : cards) { + int value = card % 100; + if (value == 1 || value == 9) { + result.add(card); } } return result; @@ -270,49 +274,52 @@ public class ChangshaWinSplitCard { return; } - // 先排序 - List sorted = new ArrayList<>(cards); - Collections.sort(sorted); - - // 使用布尔数组标记要删除的牌 - boolean[] toRemove = new boolean[sorted.size()]; + //使用TreeSet自动排序 + List sorted = new ArrayList<>(new TreeSet<>(cards)); + + //使用Set替代布尔数组 + Set cardsToRemove = new HashSet<>(); // 找出所有隔张(相差2的牌对,如1和3、2和4等) for (int i = 0; i < sorted.size(); i++) { - if (!toRemove[i]) { // 如果这张牌还没被标记删除 - for (int j = i + 1; j < sorted.size(); j++) { - if (!toRemove[j]) { // 如果这张牌还没被标记删除 - int diff = sorted.get(j) - sorted.get(i); + Integer currentCard = sorted.get(i); + if (cardsToRemove.contains(currentCard)) { + continue; //如果这张牌已被标记删除 跳过 + } + + for (int j = i + 1; j < sorted.size(); j++) { + Integer nextCard = sorted.get(j); + if (cardsToRemove.contains(nextCard)) { + continue; //如果这张牌已被标记删除 跳过 + } + + int diff = nextCard - currentCard; + + if (diff == 2 && sorted.size() > 2) { + //找到隔张 标记删除 + cardsToRemove.add(currentCard); + cardsToRemove.add(nextCard); - if (diff == 2 && sorted.size() > 2) { - // 找到隔张,标记删除 - toRemove[i] = true; - toRemove[j] = true; - - if (j + 1 < sorted.size()) { - if (sorted.get(j + 1) % 100 == 2 || sorted.get(j + 1) % 100 == 5 - || sorted.get(j + 1) % 100 == 6) { - toRemove[i] = false; - toRemove[j] = false; - - } - } - break; // 找到一对就跳出内层循环 - - } else if (diff > 2) { - // 由于已排序,差值只会越来越大 - break; + //提前检查边界条件 + if (j + 1 < sorted.size()) { + int nextNextValue = sorted.get(j + 1) % 100; + if (nextNextValue == 2 || nextNextValue == 5 || nextNextValue == 6) { + cardsToRemove.remove(currentCard); + cardsToRemove.remove(nextCard); } } + break;//找到一对就跳出内层循环 + } else if (diff > 2) { + break; } } } // 构建结果列表(保留未标记删除的牌) List result = new ArrayList<>(); - for (int i = 0; i < sorted.size(); i++) { - if (!toRemove[i]) { - result.add(sorted.get(i)); + for (Integer card : sorted) { + if (!cardsToRemove.contains(card)) { + result.add(card); } } @@ -321,21 +328,35 @@ public class ChangshaWinSplitCard { } public static void haveBetween(List cards, List cardInHand) { - if (cards.size() <= 1 || cardInHand.size() <= 1) { + //添加空指针检查 + if (cards == null || cardInHand == null || + cards.size() <= 1 || cardInHand.size() <= 1) { return; } // 创建要删除的牌列表 Set toRemove = new HashSet<>(); - // 遍历手牌中的每一张牌 + //缓存手牌的牌型信息,避免重复计算 + Map handCardTypes = new HashMap<>(); for (int handCard : cardInHand) { - // 检查cards中是否有与手牌形成靠章的牌 - for (int card : cards) { - // 定义靠章规则:相同、相邻或隔一张 - int diff = Math.abs(card - handCard); - if (diff == 1 || diff == 2) { - toRemove.add(card); + handCardTypes.put(handCard, handCard / 100); + } + + //遍历cards中的每一张牌 + for (int card : cards) { + int cardType = card / 100; + + //检查是否有与手牌形成靠章的牌 + for (int handCard : cardInHand) { + //先检查牌型是否相同 再计算差值 + if (handCardTypes.get(handCard) == cardType) { + //定义靠章规则:相同、相邻或隔一张 + int diff = Math.abs(card - handCard); + if (diff == 1 || diff == 2) { + toRemove.add(card); + break;//找到一个匹配就可以跳出内层循环 + } } } } @@ -343,33 +364,33 @@ public class ChangshaWinSplitCard { // 只有当cards中还有多于1张牌时才执行删除 if (cards.size() - toRemove.size() > 0) { cards.removeAll(toRemove); - } - // 可选:打印信息 - if (!toRemove.isEmpty()) { - System.out.println("删除的牌: " + toRemove); - System.out.println("cards剩余: " + cards); - } + System.out.println("删除的牌: " + toRemove); + System.out.println("cards剩余: " + cards); } // 从二维数组counts还原到手牌列表 private static List convertCountToCards(int[][] counts) { + if (counts == null) { + return new ArrayList<>(); + } + List cardInHand = new ArrayList<>(); // counts数组结构:[类型索引][牌值] // 类型索引: 0=万, 1=筒, 2=条, 3=风, 4=箭 (根据您的编码规则) // 牌值: 1-9表示牌面值 - for (int suitIdx = 0; suitIdx < counts.length; suitIdx++) { + for (int suitIdx = 0; suitIdx < counts.length && suitIdx < 5; suitIdx++) { int suit = suitIdx + 1; // 还原百位数 (1,2,3,4,5) - for (int value = 1; value < counts[suitIdx].length; value++) { + for (int value = 1; value < counts[suitIdx].length && value <= 9; value++) { int count = counts[suitIdx][value]; - + + int baseCard = suit * 100 + value; for (int i = 0; i < count; i++) { - int card = suit * 100 + value; - cardInHand.add(card); + cardInHand.add(baseCard); } } } @@ -378,28 +399,31 @@ public class ChangshaWinSplitCard { } public static List checktingpai(List cardhand) { + //添加参数验证 + if (cardhand == null || cardhand.isEmpty()) { + return new ArrayList<>(); + } + List tpcards = new ArrayList<>(); - List tmphc = cardhand; + List tmphc = new ArrayList<>(cardhand); + + //缓存可能的胡牌列表 + List candidateCards = new ArrayList<>(); + for (int j = 101; j <= 109; j++) candidateCards.add(j); + for (int j = 201; j <= 209; j++) candidateCards.add(j); + for (int i = 0; i < cardhand.size(); i++) { - - int tmpcard = tmphc.get(0); - tmphc.remove(0); - for (int j = 101; j <= 109; j++) { - WinCard win = new WinCard(tmphc, j); - if (win.tryWin()) { - if (!tpcards.contains(tmpcard)) { - tpcards.add(tmpcard); - } - } - } - for (int j = 201; j <= 209; j++) { - WinCard win = new WinCard(tmphc, j); - if (win.tryWin()) { - if (!tpcards.contains(tmpcard)) { - tpcards.add(tmpcard); - } + Integer tmpcard = tmphc.remove(0); //使用泛型避免强制转换 + + //批量检查候选牌 + for (Integer candidate : candidateCards) { + WinCard win = new WinCard(tmphc, candidate); + if (win.tryWin() && !tpcards.contains(tmpcard)) { + tpcards.add(tmpcard); + break;//找到一个就可以跳出循环 } } + tmphc.add(tmpcard); } return tpcards; @@ -407,6 +431,9 @@ public class ChangshaWinSplitCard { // 分析最优出牌 public static List analyzeBestDiscard(List cardInHand) { + if (cardInHand == null || cardInHand.isEmpty()) { + return new ArrayList<>(); + } // 听牌 // 返回要打的牌,打后可以听牌 @@ -414,120 +441,36 @@ public class ChangshaWinSplitCard { System.out.println("打出这种牌后可以听牌 " + checktingpai); Map map = new HashMap<>(); Map> jmap = new HashMap<>();// 对将map - // 先定将,再做分析 - /* - * int jiangnum = checkduijiang(cardInHand,jmap); - * System.out.println(jmap.get("jiangcard").get(0)); if (jiangnum==1){ //如果只有一对将 - * //去将 后判断方案 List tmphc = new ArrayList<>(); tmphc.addAll(cardInHand); - * Util.removeCard(tmphc,jmap.get("jiangcard").get(0),2); Map - * tmap = new HashMap<>(); checkNormalHu(tmphc, tmap); - * - * //不去将 判断 - * - * Map tmap2 = new HashMap<>(); System.out.println(cardInHand); - * checkNormalHu(cardInHand, tmap2); System.out.println(tmap2); - * - * - * - * - * - * }else if(jiangnum>1){ - * - * }else{ - * - * } - */ - checkNormalHu(cardInHand, map); List suggested = (List) map.get("cardResiue"); + + if (suggested == null) { + suggested = new ArrayList<>(); + } // 返回要打的牌,打后可以听牌 List zuizhongchupai = new ArrayList<>(); - // 如果打出然后可以听牌,跟递归判断出来的牌一样,优先打出这张 - if (checktingpai.size() > 0) { - for (Integer integer : checktingpai) { - for (Integer integer1 : suggested) { - if (integer.equals(integer1)) { - zuizhongchupai.add(integer); - } - } - } - } + + //提取交集逻辑为独立方法 + findIntersectionCards(checktingpai, suggested, zuizhongchupai); // 推荐打几个牌都能听牌,需要判断打哪个听的牌更多 - if (checktingpai.size() > 1 && zuizhongchupai.size() == 0) { + if (checktingpai.size() > 1 && zuizhongchupai.isEmpty()) { zuizhongchupai.addAll(checktingpai); } // 如果判断听牌和递归出来的牌不一样 优先以听牌为准 - if (zuizhongchupai.size() == 0) { + if (zuizhongchupai.isEmpty()) { zuizhongchupai.addAll(checktingpai); } // 如果判断最后打出牌还未听牌,就用递归判断的牌 - if (zuizhongchupai.size() == 0) { + if (zuizhongchupai.isEmpty()) { zuizhongchupai.addAll(suggested); } System.out.println("zuizhongchupai" + zuizhongchupai); - if (zuizhongchupai.isEmpty()) { - return new ArrayList<>(); - } - -// //牌桌上出过的牌 未实现 这里听牌zuizhongchupai 对比 牌桌上出过的牌 -// ChangShaSuanFaTest changShaSuanFaTest = new ChangShaSuanFaTest(); -// List chuguodepai = changShaSuanFaTest.chuguodepai(); -// if (chuguodepai.size() > 0 && checktingpai.size() > 0) { -// System.out.println("递归内牌桌上出过的牌 ============ ===============" + chuguodepai); -// Map tinCardCountMap = new HashMap<>(); -// -// // 初始化听牌组中所有牌的出现次数为0 -// for (Integer card : checktingpai) { -// tinCardCountMap.put(card, 0); -// } -// -// // 统计听牌组中每张牌在牌桌上的实际出现次数 -// for (Integer card : checktingpai) { -// if (tinCardCountMap.containsKey(card)) { -// tinCardCountMap.put(card, tinCardCountMap.get(card) + 1); -// } -// } -// -// // 找到最大的出现次数 -// Integer maxCount = tinCardCountMap.values().stream() -// .max(Integer::compareTo) -// .orElse(0); -// -// // 判断所有听牌是否都大于等于3次 -// boolean allGreaterOrEqualThree = !tinCardCountMap.isEmpty() && -// tinCardCountMap.values().stream().allMatch(count -> count >= 3); -// -// if (allGreaterOrEqualThree) { -// List result = new ArrayList<>(); -// // 如果所有听牌都出现大于等于3次,则随机出一张听牌 -// Integer randomCard = checktingpai.get(new Random().nextInt(checktingpai.size())); -// System.out.println("递归内 -----所有听牌出现次数都>=3,随机出牌: " + randomCard); -// result.add(randomCard); -// return result; -// } -// -// if (maxCount > 0 && maxCount < 3) { -// // 出现次数大于0但小于3,也直接出次数最多的牌 -// List maxCards = tinCardCountMap.entrySet().stream() -// .filter(entry -> entry.getValue().equals(maxCount)) -// .map(Map.Entry::getKey) -// .collect(Collectors.toList()); -// -// if (!maxCards.isEmpty()) { -// List result = new ArrayList<>(); -// Integer maxCard = Collections.max(maxCards); -// result.add(maxCard); -// return result; -// } -// -// } -// } - return zuizhongchupai; + return zuizhongchupai.isEmpty() ? new ArrayList<>() : zuizhongchupai; } public static Integer selectBestCardRemove258(List cards) { @@ -557,14 +500,12 @@ public class ChangshaWinSplitCard { System.out.println("非258牌: " + non258Cards); //判断是否手牌有其他对将 - // 优先从非258牌中选 if (!non258Cards.isEmpty()) { return selectBestSingleCard(non258Cards); } // 如果没有非258牌,从258牌中选 - if (!is258Cards.isEmpty()) { return selectBestSingleCard(is258Cards); } @@ -587,31 +528,67 @@ public class ChangshaWinSplitCard { } return false; } + + //提取258将值判断逻辑 + private static boolean is258JiangValue(int value) { + return value == 2 || value == 5 || value == 8; + } + + //提取特殊处理逻辑 + private static void processSpecialCase(List cardResiue, int remainingMelds, Map map) { + int diff = Math.abs(cardResiue.get(0) - cardResiue.get(1)); + if (diff > 1) { + map.put("remainingMelds", remainingMelds + 1); + if (cardResiue.size() > 1) { + cardResiue.remove(0); + } + map.put("cardResiue", cardResiue); + } else { + int cardValue = cardResiue.get(0) % 100; + if (is258JiangValue(cardValue)) { + //删除符合258做将的牌 + if (cardResiue.size() > 1) { + cardResiue.remove(0); + } + map.put("remainingMelds", remainingMelds); + } else { + map.put("remainingMelds", remainingMelds); + } + map.put("cardResiue", cardResiue); + } + } + + //提取交集查找逻辑 + private static void findIntersectionCards(List checktingpai, List suggested, List result) { + if (checktingpai.isEmpty()) return; + + for (Integer card : checktingpai) { + if (suggested.contains(card)) { + result.add(card); + } + } + } // 从候选牌中选出最优的一张牌(优先边张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); + + //使用Stream API简化查找逻辑 + return cards.stream() + .filter(card -> { + int value = card % 100; + return value == 1 || value == 9; + }) + .findFirst() + .orElseGet(() -> cards.stream() + .filter(card -> { + int value = card % 100; + return value == 2 || value == 8; + }) + .findFirst() + .orElse(cards.get(0))); } // 主方法 diff --git a/robots/majiang/robot_mj_cs/src/main/java/taurus/util/TingPaiChecker.java b/robots/majiang/robot_mj_cs/src/main/java/taurus/util/TingPaiChecker.java index cb6ddbc..24148df 100644 --- a/robots/majiang/robot_mj_cs/src/main/java/taurus/util/TingPaiChecker.java +++ b/robots/majiang/robot_mj_cs/src/main/java/taurus/util/TingPaiChecker.java @@ -5,14 +5,15 @@ import java.util.*; public class TingPaiChecker { // 长沙麻将特殊规则:二五八将 - private static final Set JIANG_PAIS; - - static { + private static final Set JIANG_PAIS = createJiangPaisSet(); + + //提取静态初始化逻辑 + private static Set createJiangPaisSet() { Set jiangSet = new HashSet<>(); jiangSet.add(2); jiangSet.add(5); jiangSet.add(8); - JIANG_PAIS = Collections.unmodifiableSet(jiangSet); + return Collections.unmodifiableSet(jiangSet); } /** @@ -20,9 +21,6 @@ public class TingPaiChecker { * @param handCards 手牌列表(13张或14张) * @return TingResult 听牌结果对象 */ - /** - * 判断手牌是否听牌(主要入口方法) - */ public static TingResult checkTingPai(List handCards) { TingResult result = new TingResult(); @@ -36,22 +34,13 @@ public class TingPaiChecker { System.out.println("手牌: " + convertToReadable(handCards)); // === 新增:处理四张相同牌的情况 === - // 统计每种牌的数量 - Map countMap = new HashMap<>(); - for (int card : handCards) { - countMap.put(card, countMap.getOrDefault(card, 0) + 1); - } + //提取统计逻辑为独立方法 + Map countMap = countCards(handCards); // 找出所有四张相同的牌 - List cardsWithFour = new ArrayList<>(); - for (Map.Entry entry : countMap.entrySet()) { - if (entry.getValue() == 4) { - cardsWithFour.add(entry.getKey()); - } - } + List cardsWithFour = findCardsWithCount(countMap, 4); - -// 如果有四张相同的牌,去掉一张(选择其中一种处理) + // 如果有四张相同的牌,去掉一张(选择其中一种处理) List processedHandCards = new ArrayList<>(handCards); if (!cardsWithFour.isEmpty()) { // 只处理第一种四张相同的牌(如果有多个,实际麻将中不会出现这种情况) @@ -95,30 +84,51 @@ public class TingPaiChecker { System.out.println("听牌数: " + tingCards.size() + "张"); // 按胡牌类型分组显示 - Map> groupedByType = new HashMap<>(); - for (TingCardInfo info : tingCards) { - groupedByType.computeIfAbsent(info.huType, k -> new ArrayList<>()).add(info); - } - - for (Map.Entry> entry : groupedByType.entrySet()) { - System.out.print(" " + entry.getKey() + "听牌: "); - List cardStrs = new ArrayList<>(); - for (TingCardInfo info : entry.getValue()) { - cardStrs.add(formatCard(info.card)); - } - System.out.println(String.join(" ", cardStrs)); - } + displayTingCardsByType(tingCards); } else { result.setTingPai(false); result.setMessage("未听牌"); + System.out.println("✗ 未听牌"); } return result; } - /** - * 检查所有胡牌类型 - */ + + private static Map countCards(List handCards) { + Map countMap = new HashMap<>(); + for (int card : handCards) { + countMap.put(card, countMap.getOrDefault(card, 0) + 1); + } + return countMap; + } + + private static List findCardsWithCount(Map countMap, int targetCount) { + List result = new ArrayList<>(); + for (Map.Entry entry : countMap.entrySet()) { + if (entry.getValue() == targetCount) { + result.add(entry.getKey()); + } + } + return result; + } + + private static void displayTingCardsByType(List tingCards) { + Map> groupedByType = new HashMap<>(); + for (TingCardInfo info : tingCards) { + groupedByType.computeIfAbsent(info.huType, k -> new ArrayList<>()).add(info); + } + + for (Map.Entry> entry : groupedByType.entrySet()) { + System.out.print(" " + entry.getKey() + "听牌: "); + List cardStrs = new ArrayList<>(); + for (TingCardInfo info : entry.getValue()) { + cardStrs.add(formatCard(info.card)); + } + System.out.println(String.join(" ", cardStrs)); + } + } + /** * 检查所有胡牌类型 */ diff --git a/robots/majiang/robot_mj_cs/src/main/java/taurus/util/WinCard.java b/robots/majiang/robot_mj_cs/src/main/java/taurus/util/WinCard.java index c2f631b..19afd90 100644 --- a/robots/majiang/robot_mj_cs/src/main/java/taurus/util/WinCard.java +++ b/robots/majiang/robot_mj_cs/src/main/java/taurus/util/WinCard.java @@ -1,6 +1,7 @@ package taurus.util; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Stack; @@ -34,7 +35,8 @@ public class WinCard { } private boolean tryShunzi(int card) { - if (card % 100 > 7) { + int cardValue = card % 100; + if (cardValue > 7) { return false; } @@ -42,10 +44,7 @@ public class WinCard { Util.removeCard(this.cardList, card, 1); Util.removeCard(this.cardList, card + 1, 1); Util.removeCard(this.cardList, card + 2, 1); - List cardGroup = new ArrayList(); - cardGroup.add(card); - cardGroup.add(card + 1); - cardGroup.add(card + 2); + List cardGroup = Arrays.asList(card, card + 1, card + 2); this.push(cardGroup); return true; } @@ -54,11 +53,7 @@ public class WinCard { private boolean tryKezi(int card) { if (Util.checkCardAndRomve(card, this.cardList, 3)) { -// CardUtil.removeCard(this.cardList, card, 3); - List cardGroup = new ArrayList(); - cardGroup.add(card); - cardGroup.add(card); - cardGroup.add(card); + List cardGroup = Arrays.asList(card, card, card); this.push(cardGroup); return true; } @@ -69,16 +64,14 @@ public class WinCard { if (this.pair_count > 0) { return false; } - if (jiang) { - if (!(card % 100 == 2 || card % 100 == 5 || card % 100 == 8)) { - return false; - } + + int cardValue = card % 100; + if (jiang && !(cardValue == 2 || cardValue == 5 || cardValue == 8)) { + return false; } + if (Util.checkCardAndRomve(card, this.cardList, 2)) { -// CardUtil.removeCard(this.cardList, card, 2); - List cardGroup = new ArrayList(); - cardGroup.add(card); - cardGroup.add(card); + List cardGroup = Arrays.asList(card, card); this.push(cardGroup); this.pair_count = 1; return true; @@ -88,11 +81,7 @@ public class WinCard { private boolean tryKezi1Zhong(int card) { if (this.zhong_count >= 1 && Util.checkCardAndRomve(card, this.cardList, 2)) { -// CardUtil.removeCard(this.cardList, card, 2); - List cardGroup = new ArrayList(); - cardGroup.add(card); - cardGroup.add(card); - cardGroup.add(this.zhongid); + List cardGroup = Arrays.asList(card, card, this.zhongid); this.zhong_count -= 1; this.push(cardGroup); return true; @@ -101,7 +90,8 @@ public class WinCard { } private boolean tryShunzi1Zhong(int card) { - if (card % 100 > 8) { + int cardValue = card % 100; + if (cardValue > 8) { return false; } @@ -113,10 +103,7 @@ public class WinCard { Util.removeCard(cardList, card, 1); Util.removeCard(cardList, card + 1, 1); this.zhong_count -= 1; - List cardGroup = new ArrayList(); - cardGroup.add(card); - cardGroup.add(card + 1); - cardGroup.add(this.zhongid); + List cardGroup = Arrays.asList(card, card + 1, this.zhongid); this.push(cardGroup); return true; } @@ -125,10 +112,7 @@ public class WinCard { Util.removeCard(cardList, card, 1); Util.removeCard(cardList, card + 2, 1); this.zhong_count -= 1; - List cardGroup = new ArrayList(); - cardGroup.add(card); - cardGroup.add(this.zhongid); - cardGroup.add(card + 2); + List cardGroup = Arrays.asList(card, this.zhongid, card + 2); this.push(cardGroup); return true; } @@ -139,19 +123,18 @@ public class WinCard { if (this.pair_count > 0) { return false; } - if (jiang) { - if (!(card % 100 == 2 || card % 100 == 5 || card % 100 == 8)) { - return false; - } + + int cardValue = card % 100; + if (jiang && !(cardValue == 2 || cardValue == 5 || cardValue == 8)) { + return false; } + if (this.zhong_count < 1) { return false; } Util.removeCard(cardList, card, 1); - List cardGroup = new ArrayList(); - cardGroup.add(card); - cardGroup.add(this.zhongid); + List cardGroup = Arrays.asList(card, this.zhongid); this.push(cardGroup); this.zhong_count -= 1; this.pair_count = 1; diff --git a/robots/majiang/robot_mj_cs/src/main/java/taurus/util/ai.java b/robots/majiang/robot_mj_cs/src/main/java/taurus/util/ai.java index 1d5bf5b..91f5304 100644 --- a/robots/majiang/robot_mj_cs/src/main/java/taurus/util/ai.java +++ b/robots/majiang/robot_mj_cs/src/main/java/taurus/util/ai.java @@ -14,8 +14,9 @@ public class ai { private static final Set JIANG_PAIS; public boolean isTinAi = false; public boolean isTingpai = false; - private Map scoreCache = new HashMap(); - private Map> tingCache = new HashMap(); + private Map scoreCache = new HashMap<>(); + private Map> tingCache = new HashMap<>(); + public static void main(String[] args) { System.out.println("=== 长沙麻将测试(含碰杠吃牌) ==="); @@ -28,6 +29,10 @@ public class ai { } public int findBestDiscard(PlayerState state) { + //参数验证 + if (state == null || state.handCards == null) { + throw new IllegalArgumentException("玩家状态和手牌不能为空"); + } System.out.println("\n=== 长沙麻将AI出牌分析 ==="); System.out.println("手牌(" + state.handCards.size() + "张): " + this.formatCards(state.handCards)); int pongGroups = state.getPongGroupCount(); @@ -37,10 +42,10 @@ public class ai { this.scoreCache.clear(); this.tingCache.clear(); long startTime = System.currentTimeMillis(); - Map results = new HashMap(); - Set uniqueCards = new HashSet(state.handCards); + Map results = new HashMap<>(); + Set uniqueCards = new HashSet<>(state.handCards); if (state.handCards.size() == 1) { - int onlyCard = (Integer)state.handCards.get(0); + int onlyCard = state.handCards.get(0); System.out.println("只剩一张牌,必须出: " + this.formatCard(onlyCard)); return onlyCard; } else { @@ -80,19 +85,23 @@ public class ai { long endTime = System.currentTimeMillis(); System.out.printf("分析耗时: %dms%n", endTime - startTime); if (bestCard != -1) { - DiscardResult bestResult = (DiscardResult)results.get(bestCard); + DiscardResult bestResult = results.get(bestCard); System.out.println("\n\ud83c\udfaf AI推荐出牌: " + this.formatCard(bestCard)); if (!bestResult.tingCards.isEmpty()) { this.isTinAi = true; this.isTingpai = true; System.out.println("✅ 听牌,可听 " + bestResult.tingCards.size() + " 张牌:"); System.out.println("----牌组----" + bestResult.tingCards); - if (bestResult.tingCards.size() > 0) { + + //添加同步控制 + synchronized (ChangShaSuanFaTest.tinCards) { ChangShaSuanFaTest.tinCards.clear(); ChangShaSuanFaTest.tinCards.addAll(bestResult.tingCards); } - List tingCardsFormatted = (List)bestResult.tingCards.stream().map(this::formatCard).collect(Collectors.toList()); + List tingCardsFormatted = bestResult.tingCards.stream() + .map(this::formatCard) + .collect(Collectors.toList()); System.out.println(" " + String.join(" ", tingCardsFormatted)); }