From 14e81d42abd2519a979a544b861b35c24246d622 Mon Sep 17 00:00:00 2001 From: zhouwei <849588297@qq.com> Date: Fri, 30 Jan 2026 06:47:13 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9C=BA=E5=99=A8=E4=BA=BA=E8=BF=9E=E6=8E=A501?= =?UTF-8?q?30?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/group/MainServer.java | 4 +- .../java/com/group/robot/RobotManager.java | 74 +- .../robot/handler/RobotConnectionHandler.java | 207 +- .../group/robot/matcher/RoomWanfaMatcher.java | 228 +-- .../main/java/com/game/GameController.java | 8 + .../src/main/java/com/game/MainServer.java | 3 +- .../src/main/java/com/game/data/Player.java | 16 +- .../src/main/java/com/game/data/Room.java | 28 +- .../src/main/java/hunan/HuNanChangSha.java | 1 - .../src/main/java/hunan/JiQiRens.java | 43 +- .../main/java/extend/mj/EXGameController.java | 5 + .../src/main/java/robot/mj/Config.java | 55 +- .../main/java/robot/mj/EXGameController.java | 160 +- .../src/main/java/robot/mj/EXMainServer.java | 33 +- .../java/robot/mj/GameServerConnector.java | 114 -- .../java/robot/mj/RobotConnectionManager.java | 821 ++++---- .../java/robot/mj/handler/HuNanChangSha.java | 1732 +++++++++++++++++ .../mj/handler/HuNanChangShaHandler.java | 582 ------ .../src/main/java/taurus/util/TinHuChi.java | 18 +- .../src/main/java/taurus/util/TinHuGang.java | 404 ++++ .../src/main/java/taurus/util/TinHuPeng.java | 425 ++++ .../src/main/java/taurus/util/ai.java | 1197 +++++------- 22 files changed, 4126 insertions(+), 2032 deletions(-) delete mode 100644 robots/majiang/robot_mj_cs/src/main/java/robot/mj/GameServerConnector.java create mode 100644 robots/majiang/robot_mj_cs/src/main/java/robot/mj/handler/HuNanChangSha.java delete mode 100644 robots/majiang/robot_mj_cs/src/main/java/robot/mj/handler/HuNanChangShaHandler.java create mode 100644 robots/majiang/robot_mj_cs/src/main/java/taurus/util/TinHuGang.java create mode 100644 robots/majiang/robot_mj_cs/src/main/java/taurus/util/TinHuPeng.java diff --git a/game_web/robot_mgr/src/main/java/com/group/MainServer.java b/game_web/robot_mgr/src/main/java/com/group/MainServer.java index b0f478d..43ab0cd 100644 --- a/game_web/robot_mgr/src/main/java/com/group/MainServer.java +++ b/game_web/robot_mgr/src/main/java/com/group/MainServer.java @@ -116,8 +116,8 @@ public class MainServer extends Extension { } Map gameRobotConfig = new HashMap<>(); - gameRobotConfig.put(10, 5); - gameRobotConfig.put(22, 3); + gameRobotConfig.put(10, 1); + gameRobotConfig.put(22, 0); int robotIndex = 0; //长沙麻将机器人 diff --git a/game_web/robot_mgr/src/main/java/com/group/robot/RobotManager.java b/game_web/robot_mgr/src/main/java/com/group/robot/RobotManager.java index f95edde..5e3485e 100644 --- a/game_web/robot_mgr/src/main/java/com/group/robot/RobotManager.java +++ b/game_web/robot_mgr/src/main/java/com/group/robot/RobotManager.java @@ -45,7 +45,7 @@ public class RobotManager { private final Map wanfaRobotIndex = new ConcurrentHashMap<>(); //玩法房间轮询器 - private final Map wanfaRoomPollers = new ConcurrentHashMap<>(); + private final Map wanfaRoomPollers = new ConcurrentHashMap<>(); //定时任务调度器 private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2); @@ -135,19 +135,20 @@ public class RobotManager { robot.setToken(tokenList.get(0)); } else { //如果没有token 则执行正常登录 - loginResult = accountBusiness.idPasswordLogin(robot.getRobotId(), robot.getPassword()); + loginResult = accountBusiness.idPasswordLogin(robot.getRobotId(), "123456"); robot.setToken(loginResult.getString("token")); robot.setSession(accountBusiness.getSession()); } } else { log.debug("机器人 {} 执行常规登录", robot.getRobotId()); //执行正常登录流程 - loginResult = accountBusiness.idPasswordLogin(robot.getRobotId(), robot.getPassword()); + loginResult = accountBusiness.idPasswordLogin(robot.getRobotId(), "123456"); robot.setToken(loginResult.getString("token")); robot.setSession(accountBusiness.getSession()); } robot.setOnline(true); robot.setWanfaId(gameId); + robot.setGroupId(-1); robot.setLastActiveTime(System.currentTimeMillis()); HashMap robotMap = new HashMap<>(); @@ -226,14 +227,15 @@ public class RobotManager { RoomWanfaMatcher poller = getWanfaRoomPoller(groupId, wanfaId); if (!poller.isRunning()) { poller.startPolling(); - log.info("为玩法ID {} 启动房间轮询器", wanfaId); + log.info("为群组 {} 玩法ID {} 启动房间轮询器", groupId, wanfaId); } } else { //如果没有配置机器人 停止轮询器 - RoomWanfaMatcher poller = wanfaRoomPollers.get(wanfaId); + String key = groupId + ":" + wanfaId; + RoomWanfaMatcher poller = wanfaRoomPollers.get(key); if (poller != null) { poller.stopPolling(); - log.info("为玩法ID {} 停止房间轮询器", wanfaId); + log.info("为群组 {} 玩法ID {} 停止房间轮询器", groupId, wanfaId); } } } @@ -274,14 +276,18 @@ public class RobotManager { int disconnectedCount = 0; for (int i = 0; i < allWanfaRobots.size() && disconnectedCount < toDisconnect; i++) { RobotInfo robot = allWanfaRobots.get(i); - //不在使用中且已连接则断开连接 + //不在使用断开连接 if (!robot.isUsing() && robot.isConnected()) { - RobotManagerInterface handler = getGameHandler(robot.getWanfaId()); - if (handler != null) { - handler.disconnectRobot(robot); - log.debug("仅断开机器人 {} TCP连接,保持注册状态", robot.getRobotId()); + synchronized(robot) { + if (!robot.isUsing() && robot.isConnected()) { + RobotManagerInterface handler = getGameHandler(robot.getWanfaId()); + if (handler != null) { + handler.disconnectRobot(robot); + log.debug("断开机器人 {} TCP连接,玩法ID: {}", robot.getRobotId(), wanfaId); + } + disconnectedCount++; + } } - disconnectedCount++; } } if (disconnectedCount > 0) { @@ -316,8 +322,16 @@ public class RobotManager { if (!robot.isConnected() && !robot.isUsing() && reconnectedCount < (requiredCount - currentConnected)) { RobotManagerInterface handler = getGameHandler(robot.getWanfaId()); if (handler != null) { - handler.connectRobot(robot); - log.debug("重新连接玩法 {} 的机器人: {}", wanfaId, robot.getRobotId()); + if (shouldConnectRobot(robot)) { + synchronized(robot) { + if (!robot.isConnected() && !robot.isUsing()) { + handler.connectRobot(robot); + log.debug("重新连接玩法 {} 的机器人: {}", wanfaId, robot.getRobotId()); + } + } + } else { + log.debug("玩法 {} 的leftover_robot为0,跳过连接机器人: {}", wanfaId, robot.getRobotId()); + } reconnectedCount++; } } @@ -329,9 +343,10 @@ public class RobotManager { * 获取玩法房间轮询器 */ public RoomWanfaMatcher getWanfaRoomPoller(int groupId, int wanfaId) { - return wanfaRoomPollers.computeIfAbsent(wanfaId, k -> { + String key = groupId + ":" + wanfaId; + return wanfaRoomPollers.computeIfAbsent(key, k -> { RoomWanfaMatcher poller = new RoomWanfaMatcher(this, groupId, wanfaId); - log.info("创建玩法ID {} 的房间轮询器", wanfaId); + log.info("创建群组 {} 玩法ID {} 的房间轮询器", groupId, wanfaId); return poller; }); } @@ -495,7 +510,32 @@ public class RobotManager { public void releaseRobot(int robotId) { RobotInfo robot = connectedRobots.get(robotId); if (robot != null) { - robot.setUsing(false); + synchronized(robot) { + robot.setUsing(false); + } + } + } + + /** + * 根据玩法的leftover_robot数量检查是否连接机器人 + */ + public boolean shouldConnectRobot(RobotInfo robot) { + if (robot.getGroupId() == -1) { + return true; + } + + try (Jedis jedis11 = Redis.use("group1_db11").getJedis()) { + String playKey = "g{" + robot.getGroupId() + "}:play:" + robot.getWanfaId(); + String leftoverRobotStr = jedis11.hget(playKey, "leftover_robot"); + + if (leftoverRobotStr != null) { + int leftoverRobot = Integer.parseInt(leftoverRobotStr); + return leftoverRobot > 0; + } + + return false; + } catch (Exception e) { + return false; } } } \ No newline at end of file diff --git a/game_web/robot_mgr/src/main/java/com/group/robot/handler/RobotConnectionHandler.java b/game_web/robot_mgr/src/main/java/com/group/robot/handler/RobotConnectionHandler.java index 0eba756..4aeab99 100644 --- a/game_web/robot_mgr/src/main/java/com/group/robot/handler/RobotConnectionHandler.java +++ b/game_web/robot_mgr/src/main/java/com/group/robot/handler/RobotConnectionHandler.java @@ -1,20 +1,22 @@ package com.group.robot.handler; import java.util.Map; +import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; import com.group.Protocol; import com.group.robot.info.RobotInfo; import com.group.robot.RobotManager; -import com.group.robot.info.RoomInfo; -import com.group.robot.matcher.RoomWanfaMatcher; import com.taurus.core.entity.TObject; import com.taurus.core.plugin.redis.Redis; -import com.taurus.core.util.ICallback; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import taurus.client.MessageResponse; import taurus.client.SocketCode; import taurus.client.TaurusClient; import taurus.client.Message; @@ -35,10 +37,18 @@ public class RobotConnectionHandler { //机器人ID到账户信息的映射 private final Map robotAccounts = new ConcurrentHashMap<>(); + + //机器人ID到连接ID的映射,用于标识每个机器人连接的唯一性 + private final Map robotConnectionIds = new ConcurrentHashMap<>(); //机器人管理器引用 private final RobotManager robotManager; + //心跳和重连 + private final Map> heartbeatTasks = new ConcurrentHashMap<>(); + private final Map robotServerAddresses = new ConcurrentHashMap<>(); + private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(10); + /** * 机器人账户信息 */ @@ -96,31 +106,68 @@ public class RobotConnectionHandler { * 连接机器人到游戏服务器 */ public void connectRobot(RobotInfo robot) { + //玩法leftover_robot大于0连接机器人 + if (!robotManager.shouldConnectRobot(robot)) { + return; + } + //异步执行连接逻辑 CompletableFuture.runAsync(() -> { try { + //连接前再次检查leftover_robot数量 防止并发 + if (!robotManager.shouldConnectRobot(robot)) { + return; + } + log.info("开始连接机器人 {} 到游戏服务器,玩法ID: {}", robot.getRobotId(), robot.getWanfaId()); //TCP客户端连接 - TaurusClient client = new TaurusClient(getGameServerAddress(robot.getWanfaId()), "game", TaurusClient.ConnectionProtocol.Tcp); + String serverAddress = getGameServerAddress(robot.getWanfaId()); + TaurusClient client = new TaurusClient(serverAddress, "game", TaurusClient.ConnectionProtocol.Tcp); client.connect(); + //生成唯一的connecId + String connecId = generateConnectionId(robot.getRobotId()); + + //先清理可能存在的旧连接 + TaurusClient oldClient = robotClients.remove(robot.getRobotId()); + if (oldClient != null && oldClient.isConnected()) { + oldClient.killConnection(); + } + //保存客户端连接 robotClients.put(robot.getRobotId(), client); + robotServerAddresses.put(robot.getRobotId(), serverAddress); + //保存connecId + robotConnectionIds.put(robot.getRobotId(), connecId); //设置事件监听器 setupInitializationEventListeners(client, robot); //连接成功后发送初始化协议 sendInitializationProtocol(client, robot); - log.info("机器人 {} 成功连接到游戏服务器", robot.getRobotId()); + + //设置机器人连接状态 + robot.setConnected(true); + log.info("机器人 {} 成功连接到游戏服务器, ConnectionId: {}", robot.getRobotId(), connecId); + + //启动心跳 + startHeartTask(robot.getRobotId(), client, robot.getWanfaId()); } catch (Exception e) { log.error("连接机器人到游戏服务器时发生异常: " + robot.getRobotId(), e); - //清理已经建立的连接 + robot.setConnected(false); disconnectRobot(robot.getRobotId()); } }); } + /** + * 生成唯一的连接ID + */ + private String generateConnectionId(int robotId) { + String uuid = UUID.randomUUID().toString().replace("-", "").substring(0, 16); + return robotId + "_" + uuid; + } + /** * 设置初始化阶段的事件监听器 */ @@ -132,12 +179,14 @@ public class RobotConnectionHandler { SocketCode code = (SocketCode) event.getParameter("code"); if (code == SocketCode.Connect) { log.info("机器人 {} 连接游戏服务器成功", robot.getRobotId()); + robot.setConnected(true); } else { log.warn("机器人 {} 连接游戏服务器失败: {}", robot.getRobotId(), code); + robot.setConnected(false); } } }); - + client.addEventListener(TaurusClient.NetClientEvent.OnEvent, new IEventListener() { @Override public void handleEvent(Event event) { @@ -161,6 +210,77 @@ public class RobotConnectionHandler { }); } + /** + * 启动心跳任务 + */ + private void startHeartTask(int robotId, TaurusClient client, int wanfaId) { + stopHeartTask(robotId); + + ScheduledFuture heartbeatTask = scheduler.scheduleWithFixedDelay(() -> { + try { + if (client != null && client.isConnected()) { + //发送心跳包 + ITObject heartbeatParam = new TObject(); + heartbeatParam.putString("type", "heartbeat"); + heartbeatParam.putInt("robot_id", robotId); + heartbeatParam.putInt("wanfa_id", wanfaId); + heartbeatParam.putString("connecId", robotConnectionIds.get(robotId)); + heartbeatParam.putLong("timestamp", System.currentTimeMillis()); + + client.send("ping", heartbeatParam, response -> { + if (response != null) { + log.debug("机器人 {} 心跳响应成功", robotId); + } else { + log.warn("机器人 {} 心跳响应失败", robotId); + } + }); + } else { + log.warn("机器人 {} 连接已断开,无法发送心跳", robotId); + //连接断开 尝试重连 + RobotInfo robot = robotManager.getConnectedRobot(robotId); + if (robot != null) { + scheduleReconnection(robot); + } + } + } catch (Exception e) { + log.error("发送心跳时发生异常,机器人: " + robotId, e); + } + }, 30, 30, TimeUnit.SECONDS);//30秒发送一次心跳 + + heartbeatTasks.put(robotId, heartbeatTask); + log.info("已启动机器人 {} 的心跳任务", robotId); + } + + /** + * 停止心跳任务 + */ + private void stopHeartTask(int robotId) { + ScheduledFuture task = heartbeatTasks.remove(robotId); + if (task != null && !task.isCancelled()) { + robotConnectionIds.remove(robotId); + task.cancel(false); + log.info("已停止机器人 {} 的心跳任务", robotId); + } + } + + /** + * 计划重连任务 + */ + private void scheduleReconnection(RobotInfo robot) { + scheduler.schedule(() -> { + try { + log.info("开始重连机器人: {}", robot.getRobotId()); + //清理可能存在的旧连接 + robot.setConnected(false); + disconnectRobot(robot.getRobotId()); + //重新连接 + connectRobot(robot); + } catch (Exception e) { + log.error("重连机器人失败: {}", robot.getRobotId(), e); + } + }, 2, TimeUnit.SECONDS); + } + /** * 根据游戏ID获取服务器地址 */ @@ -168,14 +288,14 @@ public class RobotConnectionHandler { //根据玩法ID返回对应的服务器地址 switch (wanfaId) { case 10: //长沙麻将 - return "127.0.0.1:8701"; + return "192.168.0.32:8701"; case 22: //红中麻将 return "8.138.242.190:6421"; case 66: //跑得快 return "8.138.242.190:6841"; default: log.warn("未知的玩法ID: {}, 使用默认服务器地址", wanfaId); - return "127.0.0.1:6311"; //默认地址 + return "192.168.0.32:6311"; //默认地址 } } @@ -185,10 +305,11 @@ public class RobotConnectionHandler { public void disconnectRobot(int robotId) { try { log.info("清理机器人 {} 的连接资源", robotId); + stopHeartTask(robotId); //移除机器人TCP客户端连接 TaurusClient client = robotClients.remove(robotId); if (client != null) { - client.clearResponse(); + client.killConnection(); log.debug("已清理机器人 {} 的客户端连接", robotId); } //移除机器人信息 @@ -220,6 +341,8 @@ public class RobotConnectionHandler { if (client != null) { //发送离开房间协议 ITObject param = new TObject(); + param.putString("robotId", String.valueOf(robot.getRobotId())); + param.putString("connecId", robotConnectionIds.get(robot.getRobotId())); // 添加connecId client.send("2005", param, response -> { log.debug("机器人 {} 发送离开房间请求", robot.getRobotId()); }); @@ -281,6 +404,9 @@ public class RobotConnectionHandler { } + /** + * 发送加入房间消息给指定机器人 + */ public void sendJoinRoomMessage(RobotInfo robot) { TaurusClient client = robotClients.get(robot.getRobotId()); @@ -291,6 +417,8 @@ public class RobotConnectionHandler { ITObject readyParam = new TObject(); readyParam.putString("session", robot.getSession() + "," + robot.getToken()); + readyParam.putString("robotId", String.valueOf(robot.getRobotId())); + readyParam.putString("connecId", robotConnectionIds.get(robot.getRobotId())); client.send("2002", readyParam, response -> { log.debug("机器人 {} 发送加入房间请求响应: {}", robot.getRobotId(), response); }); @@ -309,11 +437,62 @@ public class RobotConnectionHandler { ITObject readyParam = new TObject(); readyParam.putString("session", robot.getSession() + "," + robot.getToken()); + readyParam.putString("robotId", String.valueOf(robot.getRobotId())); + readyParam.putString("connecId", robotConnectionIds.get(robot.getRobotId())); client.send("2003", readyParam, response -> { log.debug("机器人 {} 发送准备请求响应: {}", robot.getRobotId(), response); }); } + /** + * 同步发送准备消息并等待响应 + */ + public boolean sendReadyMessageSync(RobotInfo robot) { + CompletableFuture future = new CompletableFuture<>(); + TaurusClient client = robotClients.get(robot.getRobotId()); + if (client == null) { + log.warn("机器人 {} 没有有效的TCP连接,无法发送准备消息", robot.getRobotId()); + return false; + } + + ITObject readyParam = new TObject(); + readyParam.putString("session", robot.getSession() + "," + robot.getToken()); + readyParam.putString("robotId", String.valueOf(robot.getRobotId())); + readyParam.putString("connecId", robotConnectionIds.get(robot.getRobotId())); + + client.send("2003", readyParam, response -> { + if (response != null && response.returnCode == 0) { + future.complete(true); + } else { + future.complete(false); + } + }); + try { + return future.get(15, TimeUnit.SECONDS); + } catch (Exception e) { + log.error("发送准备消息时发生异常", e); + return false; + } + } + + public void sendReadyMessageAsync(RobotInfo robot, Consumer callback) { + TaurusClient client = robotClients.get(robot.getRobotId()); + if (client == null) { + callback.accept(false); + return; + } + + ITObject readyParam = new TObject(); + readyParam.putString("session", robot.getSession() + "," + robot.getToken()); + readyParam.putString("robotId", String.valueOf(robot.getRobotId())); + readyParam.putString("connecId", robotConnectionIds.get(robot.getRobotId())); + + client.send("2003", readyParam, response -> { + boolean success = response != null && response.returnCode == 0; + callback.accept(success); + }); + } + /** * 回调接口 */ @@ -332,9 +511,8 @@ public class RobotConnectionHandler { */ public void sendCreateRoom(RobotInfo robot, int groupId, int wanfaId) { TaurusClient client = robotClients.get(robot.getRobotId()); - if (!client.isConnected()) { - log.warn("机器人 {} 的TCP连接未激活,无法发送创建房间请求", robot.getRobotId()); - robotClients.remove(robot.getRobotId()); + if (client == null ) { + connectRobot(robot); return; } @@ -357,5 +535,4 @@ public class RobotConnectionHandler { } }); } - } \ No newline at end of file diff --git a/game_web/robot_mgr/src/main/java/com/group/robot/matcher/RoomWanfaMatcher.java b/game_web/robot_mgr/src/main/java/com/group/robot/matcher/RoomWanfaMatcher.java index 561d17f..97b270e 100644 --- a/game_web/robot_mgr/src/main/java/com/group/robot/matcher/RoomWanfaMatcher.java +++ b/game_web/robot_mgr/src/main/java/com/group/robot/matcher/RoomWanfaMatcher.java @@ -76,7 +76,7 @@ public class RoomWanfaMatcher { } isRunning = true; - log.info("开始为玩法ID {} 启动房间轮询", wanfaId); + log.info("开始为群组 {} 玩法ID {} 启动房间轮询", groupId, wanfaId); //10秒轮询一次 scheduler.scheduleWithFixedDelay(this::pollRooms, 0, 10, TimeUnit.SECONDS); @@ -93,96 +93,88 @@ public class RoomWanfaMatcher { } try (Jedis jedis0 = Redis.use().getJedis(); Jedis jedis11 = Redis.use("group1_db11").getJedis()) { - Set roomIds = jedis0.keys("room:*"); - if (roomIds.isEmpty()) { - createRobotWanfaRoomTCP(groupId, this.wanfaId); - } - - int groupId = -1; + String playKey = "g{" + groupId + "}:play:" + wanfaId; + String leftoverRobotStr = jedis11.hget(playKey, "leftover_robot"); - for (String roomId : roomIds) { - int wanfaId = Integer.parseInt(jedis0.hget(roomId, "gpid")); - if (wanfaId == this.wanfaId) { - groupId = Integer.parseInt(jedis0.hget(roomId, "group")); - break; + int leftoverRobot = Integer.parseInt(leftoverRobotStr); + + //根据leftover_robot数量管理机器人连接状态 + manageRobotConnections(leftoverRobot); + + if (leftoverRobot > 0) { + //查询当前群组和玩法的房间 + Set roomIds = jedis0.keys("room:*"); + + if (roomIds.isEmpty()) { + createRobotWanfaRoomTCP(groupId, this.wanfaId); + return; } - } + + //满人房间 + int fullRooms = 0; + //所有房间 + int totalRooms = 0; + List roomsList = new ArrayList<>(); - if (groupId != -1) { - //检查该玩法是否配置了机器人 - String playKey = "g{" + groupId + "}:play:" + this.wanfaId; - int leftoverRobot = Integer.parseInt(jedis11.hget(playKey, "leftover_robot")); + //统计所有当前群组和玩法的2人房间 + for (String roomId : roomIds) { + int currentWanfaId = Integer.parseInt(jedis0.hget(roomId, "gpid")); + int currentGroup = Integer.parseInt(jedis0.hget(roomId, "group")); + int maxPlayers = Integer.parseInt(jedis0.hget(roomId, "maxPlayers")); - //根据leftover_robot数量管理机器人连接状态 - manageRobotConnections(leftoverRobot); + //处理当前群组和玩法maxPlayers为2的房间 + if (maxPlayers == 2 && currentWanfaId == this.wanfaId && currentGroup == this.groupId) { + totalRooms++; - if (leftoverRobot > 0) { - //满人房间 - int fullRooms = 0; - //所有房间 - int totalRooms = 0; - List roomsList = new ArrayList<>(); - - //统计所有2人房间 - for (String roomId : roomIds) { - int currentWanfaId = Integer.parseInt(jedis0.hget(roomId, "gpid")); - int status = Integer.parseInt(jedis0.hget(roomId, "status")); - int maxPlayers = Integer.parseInt(jedis0.hget(roomId, "maxPlayers")); - - //处理当前玩法maxPlayers为2的房间 - if (maxPlayers == 2 && currentWanfaId == this.wanfaId && status == 0) { - totalRooms++; - - String playersStr = jedis0.hget(roomId, "players"); - int currentPlayers = 0; - if (!playersStr.equals("[]")) { - String playersClean = playersStr.substring(1, playersStr.length() - 1); - if (!playersClean.isEmpty()) { - String[] playerIds = playersClean.split(","); - currentPlayers = playerIds.length; - } - } - if (currentPlayers == maxPlayers) { - fullRooms++; - } else { - roomsList.add(roomId); + String playersStr = jedis0.hget(roomId, "players"); + int currentPlayers = 0; + if (!playersStr.equals("[]")) { + String playersClean = playersStr.substring(1, playersStr.length() - 1); + if (!playersClean.isEmpty()) { + String[] playerIds = playersClean.split(","); + currentPlayers = playerIds.length; } } + if (currentPlayers == maxPlayers) { + fullRooms++; + } else { + roomsList.add(roomId); + } } + } - //该玩法机器人房间满了 创建新房间 - if (fullRooms == totalRooms && totalRooms > 0) { - createRobotWanfaRoomTCP(groupId, this.wanfaId); - } + //todo该玩法机器人房间满了 创建新房间 + /*if (fullRooms == totalRooms && totalRooms > 0) { + createRobotWanfaRoomTCP(groupId, this.wanfaId); + return; + }*/ - for (String roomId : roomsList) { - int group = Integer.parseInt(jedis0.hget(roomId, "group")); - int status = Integer.parseInt(jedis0.hget(roomId, "status")); + for (String roomId : roomsList) { + int status = Integer.parseInt(jedis0.hget(roomId, "status")); - //处理未开始的陪打房间 - if (status == 0) { - String playersStr = jedis0.hget(roomId, "players"); - RobotInfo robot = new RobotInfo(); - robot.setWanfaId(this.wanfaId); - robot.setGroupId(group); - robot.setRoomId(roomId); - robot.setRobotId(-1); + //处理未开始的陪打房间 + if (status == 0) { + String playersStr = jedis0.hget(roomId, "players"); + RobotInfo robot = new RobotInfo(); + robot.setWanfaId(this.wanfaId); + robot.setGroupId(this.groupId); + robot.setRoomId(roomId); + robot.setRobotId(-1); - //房间没人 加入房间 - if (playersStr.equals("[]")) { - //房间是否正在被加入 + //房间没人 加入房间 + if (playersStr.equals("[]")) { + //房间是否正在被加入 + if (!processingRooms.contains(roomId)) { + processingRooms.add(roomId); + robotJoinRoom(robot, true); + } + } else { + //房间有人 不是机器人则加入房间 + Integer robotInRoom = playerIsRobotRedis(playersStr); + if (robotInRoom == null) { if (!processingRooms.contains(roomId)) { processingRooms.add(roomId); - robotJoinRoom(robot, true); - } - } else { - //房间有人 不是机器人则加入房间 - Integer robotInRoom = playerIsRobotRedis(playersStr); - if (robotInRoom == null) { - if (!processingRooms.contains(roomId)) { - processingRooms.add(roomId); - robotJoinRoom(robot, false); - } + robotJoinRoom(robot, false); } } } @@ -191,7 +183,7 @@ public class RoomWanfaMatcher { } } } catch (Throwable t) { - log.error("轮询玩法ID {} 的房间时发生严重错误", wanfaId, t); + log.error("轮询群组 {} 玩法ID {} 的房间时发生严重错误", groupId, wanfaId, t); } } @@ -213,31 +205,51 @@ public class RoomWanfaMatcher { } //加入房间 - GroupRoomBusiness.joinRoom(robotInfo.getGroupId(), robotInfo.getRoomId(), robotInfo.getSession(), null); - Thread.sleep(5000); - robotConnectionHandler.sendJoinRoomMessage(robotInfo); + try { + GroupRoomBusiness.joinRoom(robotInfo.getGroupId(), robotInfo.getRoomId(), robotInfo.getSession(), null); + Thread.sleep(5000); + robotConnectionHandler.sendJoinRoomMessage(robotInfo); + Thread.sleep(1000); + }catch (Exception e) { + log.error("加入房间失败", e); + processingRooms.remove(roomId); + return; + } //准备 - Thread.sleep(1000); - robotConnectionHandler.sendReadyMessage(robotInfo); + RobotInfo finalRobotInfo = robotInfo; + robotConnectionHandler.sendReadyMessageAsync(robotInfo, readySuccess -> { + if (readySuccess) { + roomToRobotMap.put(roomId, finalRobotInfo.getRobotId()); + updateRobotStatusRedis(finalRobotInfo); + if (isRobot) { + //6秒没有玩家加入 则退出房间 + robotConnectionHandler.readyTimeRobotExit(finalRobotInfo, joinRoomLock); + } + } else { + System.out.println("机器人准备失败"); + } + if (roomId != null) { + processingRooms.remove(roomId); + } + }); + /*boolean readySuccess = robotConnectionHandler.sendReadyMessageSync(robotInfo); + if (readySuccess) { + //记录机器人和房间的关联 + roomToRobotMap.put(roomId, robotInfo.getRobotId()); - //记录机器人和房间的关联 - roomToRobotMap.put(roomId, robotInfo.getRobotId()); - - if (isRobot) { - updateRobotStatusRedis(robotInfo, isRobot); - //6秒没有玩家加入 则退出房间 - robotConnectionHandler.readyTimeRobotExit(robotInfo, joinRoomLock); + updateRobotStatusRedis(robotInfo); + + if (isRobot) { + //6秒没有玩家加入 则退出房间 + robotConnectionHandler.readyTimeRobotExit(robotInfo, joinRoomLock); + } } else { - updateRobotStatusRedis(robotInfo, isRobot); - } + //robotConnectionHandler.disconnectRobot(robotInfo.getRobotId()); + }*/ } catch (Exception e) { if (robotInfo != null) { - robotConnectionHandler.disconnectRobot(robotInfo.getRobotId()); - } - } finally { - if (roomId != null) { - processingRooms.remove(roomId); + //robotConnectionHandler.disconnectRobot(robotInfo.getRobotId()); } } } @@ -309,10 +321,10 @@ public class RoomWanfaMatcher { private void manageRobotConnections(int currentLeftoverRobot) { if (lastLeftoverRobot != currentLeftoverRobot) { if (currentLeftoverRobot <= 0 && lastLeftoverRobot > 0) { - //leftover_robot变为0 断开该玩法未连接的机器人 + //leftover_robot变为0 断开该玩法与robot_mj_cs的连接 robotManager.disconnectExcessRobots(wanfaId, 0); } else if (currentLeftoverRobot > 0 && lastLeftoverRobot <= 0) { - //leftover_robot变为正数 重新连接机器人 + //leftover_robot变为正数 重新连接机器人到robot_mj_cs robotManager.connectRequiredRobots(wanfaId, currentLeftoverRobot); } lastLeftoverRobot = currentLeftoverRobot; @@ -341,28 +353,24 @@ public class RoomWanfaMatcher { } /** - * 机器人加入真人房间 + * 更新机器人状态 - 减少leftover_robot计数并更新机器人start状态 */ - private void updateRobotStatusRedis(RobotInfo robot, boolean isRobot) { + private void updateRobotStatusRedis(RobotInfo robot) { try (Jedis jedis11 = Redis.use("group1_db11").getJedis(); Jedis jedis2 = Redis.use("group1_db2").getJedis()) { String playKey = "g{" + robot.getGroupId() + "}:play:" + robot.getWanfaId(); + jedis11.hincrBy(playKey, "leftover_robot", -1); + + String gRobotKey = "{grobot}:" + robot.getRobotId(); + jedis2.hset(gRobotKey, "start", "1"); - if (!isRobot) { - jedis11.hincrBy(playKey, "leftover_robot", -1); - - String gRobotKey = "{grobot}:" + robot.getRobotId(); - jedis2.hset(gRobotKey, "start", "1"); - - log.debug("机器人 {} 加入非机器人房间,减少leftover_robot并更新redis2状态,群组={}, 玩法={}", robot.getRobotId(), robot.getGroupId(), robot.getWanfaId()); - } } catch (Exception e) { log.error("更新机器人状态时异常,机器人={}, 群组={}, 玩法={}", robot.getRobotId(), robot.getGroupId(), robot.getWanfaId(), e); } } /** - * 通知房间处理完成,从processingRooms集合中移除指定房间 + * 通知房间处理完成 从processingRooms集合中移除指定房间 */ public void roomProcessingFinish(String roomId) { processingRooms.remove(roomId); diff --git a/libs/game_common/src/main/java/com/game/GameController.java b/libs/game_common/src/main/java/com/game/GameController.java index 2acda6f..6ab1f87 100644 --- a/libs/game_common/src/main/java/com/game/GameController.java +++ b/libs/game_common/src/main/java/com/game/GameController.java @@ -257,6 +257,8 @@ public class GameController implements IController{ AccountBean acc = AccountCache.getAccount(session_key); int playerid = acc.id; + Global.logger.info("joinRoom - 正在处理加入请求-PlayerId: {}, Session: {}, Sender: {}", playerid, session_key, sender); + boolean full = owner.playerMapBySeat.size() >= owner.maxPlayers; boolean reload = owner.status == Constant.ROOM_STATUS_PLAYING; @@ -269,6 +271,7 @@ public class GameController implements IController{ boolean new_player = false; if (owner.playerMapById.containsKey(playerid)) { player = owner.playerMapById.get(playerid); + Global.logger.info("joinRoom - 已找到现有玩家-PlayerId: {}, Player: {}, isConnect: {}", playerid, player, player.isConnect); if (player.isReload) { player.isReload = false; player.stateMachine.changeState(Global.getState(PlayerInitState.class)); @@ -280,6 +283,7 @@ public class GameController implements IController{ if (full) { sender.setHashId(null); delRoomSeat(session_key, owner.room_key); + Global.logger.warn("joinRoom - 房间已满,拒绝玩家: {}", playerid); return ErrorCode.ROOM_CLOSE; } // 检测是否打开GPS 、IP检测 @@ -318,6 +322,7 @@ public class GameController implements IController{ } } player = MainServer.instance.newPlayer(playerid, owner, session_key); + Global.logger.info("joinRoom - 已创建新玩家-PlayerId: {}, Player: {}", playerid, player); player.hp.cur_hp = cur_hp; owner.addPlayer(player, false, onseat); @@ -333,6 +338,9 @@ public class GameController implements IController{ if (StringUtil.isNotEmpty(gps_pos)) player.gps_pos = gps_pos; + Global.logger.info("joinRoom - 播放器设置完成-PlayerId: {}, Nick: {}, isConnect: {}, Sender: {}", + player.playerid, player.nick, player.isConnect, player.sender); + ITObject data = new TObject(); if (reload) { data.putTObject("reloadInfo", owner.getReloadInfo(player)); diff --git a/libs/game_common/src/main/java/com/game/MainServer.java b/libs/game_common/src/main/java/com/game/MainServer.java index 1b02d41..ea39a5c 100644 --- a/libs/game_common/src/main/java/com/game/MainServer.java +++ b/libs/game_common/src/main/java/com/game/MainServer.java @@ -114,7 +114,8 @@ public abstract class MainServer extends Extension implements IEventListener{ * @param recipients 客户端session列表 */ public void sendEvent(String cmdName, ITObject params, List recipients) { - TPServer.me().getController().sendEvent(cmdName, params, recipients); + TPServer.me().getController().sendEv + ent(cmdName, params, recipients); } /** diff --git a/libs/game_common/src/main/java/com/game/data/Player.java b/libs/game_common/src/main/java/com/game/data/Player.java index bb467de..e90e441 100644 --- a/libs/game_common/src/main/java/com/game/data/Player.java +++ b/libs/game_common/src/main/java/com/game/data/Player.java @@ -191,23 +191,28 @@ public class Player { public void setSender(Session sender) { // 服务器从崩溃中重启 if (sender == null) { + Global.logger.error("setSender - 为Player设置空会话: {}, PlayerId: {}, Nick: {}, changing isConnect to false", + this.toString(), this.playerid, this.nick); this.isReload = true; this.isConnect = false; this.stateMachine.changeState(Global.getState(PlayerReloadState.class)); return; } if (this.isConnect && this.sender != null && this.sender != sender) { + Global.logger.warn("setSender - 替换Player的现有会话: {}, PlayerId: {}, Old sender: {}, New sender: {}", + this.toString(), this.playerid, this.sender, sender); Global.sessionMgr.deleteSession(this.sender); TPServer.me().getController().disconnect(this.sender); } // 已经连接 this.sender = sender; Global.sessionMgr.putPlayer(sender, this); - this.isConnect = true; + this.isConnect = true; // 这里设置连接状态为 true + Global.logger.info("setSender - 已成功为玩家设置会话: {}, PlayerId: {}, Nick: {}, isConnect: true", + this.toString(), this.playerid, this.nick); // if (this.stateMachine.curState == null || this.isReload) { // this.stateMachine.changeState(Global.getState(PlayerInitState.class)); this.isReload = false; - // } } /** @@ -416,8 +421,13 @@ public class Player { * @param param */ public void sendEvent(String cmd, ITObject param) { - if (!this.isConnect) + if (!this.isConnect) { + Global.logger.error("sendEvent failed - Player: {}, PlayerId: {}, Nick: {}, isConnect: false, Cmd: {}", + this.toString(), this.playerid, this.nick, cmd); return; + } + Global.logger.info("sendEvent success - Player: {}, PlayerId: {}, Nick: {}, isConnect: true, Cmd: {}", + this.toString(), this.playerid, this.nick, cmd); MainServer.instance.sendEvent(cmd, param, this.sender); } diff --git a/libs/game_common/src/main/java/com/game/data/Room.java b/libs/game_common/src/main/java/com/game/data/Room.java index ef910f6..4338345 100644 --- a/libs/game_common/src/main/java/com/game/data/Room.java +++ b/libs/game_common/src/main/java/com/game/data/Room.java @@ -721,9 +721,13 @@ public class Room implements Runnable { * @param reload */ public void addPlayer(Player player, boolean reload, boolean onseat) { + Global.logger.info("addPlayer-将玩家添加到房间-房间: {}, PlayerId: {}, Nick: {}, Reload: {}, OnSeat: {}", + this.roomid, player.playerid, player.nick, reload, onseat); + if (!reload) { if (onseat) { player.seat = this.getSeat(); + Global.logger.info("addPlayer-分配的座位: {} to player: {}", player.seat, player.playerid); } Redis.use().hset(player.session_id, "seat", player.seat + ""); if (groupId > 0) { @@ -740,10 +744,14 @@ public class Room implements Runnable { } if (onseat) { this.playerMapBySeat.put(player.seat, player); + Global.logger.info("addPlayer-将玩家添加到座位映射-座位 - Seat: {}, Player: {}", player.seat, player.playerid); } else { this.playerMapBySpectator.put(player.playerid, player); + Global.logger.info("addPlayer-将玩家添加到观众映射中 - PlayerId: {}", player.playerid); } this.playerMapById.put(player.playerid, player); + Global.logger.info("addPlayer-将玩家添加到ID映射中-房间中的玩家总数: {}, Player: {}", + this.playerMapById.size(), player.playerid); if (!reload && onseat) { updateRedisMap(); @@ -894,15 +902,33 @@ public class Room implements Runnable { if (!isActive) return; List list = new ArrayList(); + Global.logger.info("broadCastToClient-向房间中的玩家广播cmd: {}, to players in room: {}, total players: {}", + cmd, this.roomid, this.playerMapById.size()); + + int skippedPlayers = 0; + int addedPlayers = 0; + for (Entry entry : this.playerMapById.entrySet()) { Player player = entry.getValue(); if (player.playerid == withOutPlayerid) { + Global.logger.debug("broadCastToClient - 由于withOutPlayelid而跳过玩家: {}", player.playerid); continue; } - if (!player.isConnect) + if (!player.isConnect) { + Global.logger.warn("broadCastToClient - 跳过玩家(未连接): {}, PlayerId: {}, Nick: {}, isConnect: {}", + player.toString(), player.playerid, player.nick, player.isConnect); + skippedPlayers++; continue; + } + Global.logger.info("broadCastToClient - 将玩家添加到广播列表: {}, PlayerId: {}, Nick: {}, isConnect: {}", + player.toString(), player.playerid, player.nick, player.isConnect); list.add(player.sender); + addedPlayers++; } + + Global.logger.info("broadCastToClient - 最终广播列表大小: {}, Added: {}, Skipped: {}, Command: {}", + list.size(), addedPlayers, skippedPlayers, cmd); + MainServer.instance.sendEvent(cmd, param, list); } diff --git a/libs/robot_common_bak/src/main/java/hunan/HuNanChangSha.java b/libs/robot_common_bak/src/main/java/hunan/HuNanChangSha.java index b9b3243..1471dfb 100644 --- a/libs/robot_common_bak/src/main/java/hunan/HuNanChangSha.java +++ b/libs/robot_common_bak/src/main/java/hunan/HuNanChangSha.java @@ -10,7 +10,6 @@ import taurus.client.Message; import taurus.client.TaurusClient; import taurus.util.CardUtil; import taurus.util.ChangShaSuanFaTest; -import taurus.util.HongZhongSuanFaTest; import java.sql.SQLException; import java.util.*; diff --git a/libs/robot_common_bak/src/main/java/hunan/JiQiRens.java b/libs/robot_common_bak/src/main/java/hunan/JiQiRens.java index c9b56a0..cb619d5 100644 --- a/libs/robot_common_bak/src/main/java/hunan/JiQiRens.java +++ b/libs/robot_common_bak/src/main/java/hunan/JiQiRens.java @@ -750,7 +750,8 @@ public class JiQiRens { System.out.println(e); } } - } else if ("108".equalsIgnoreCase(wanfaId)) {//转转麻将 + } + else if ("108".equalsIgnoreCase(wanfaId)) {//转转麻将 if ("811".equalsIgnoreCase(command)) {//初始化收手牌 huNanZhuanZhuan.cardInHead(command, message, client); } else if ("812".equalsIgnoreCase(command)) {//出牌广播 @@ -779,21 +780,27 @@ public class JiQiRens { System.out.println(e); } } - } else if ("10".equalsIgnoreCase(wanfaId)) { + } + else if ("10".equalsIgnoreCase(wanfaId)) { //长沙麻将 System.out.println("11111-----------command" + command); if ("811".equalsIgnoreCase(command)) {//初始化收手牌 huNanChangSha.cardInHead(command, message, client); - } else if ("812".equalsIgnoreCase(command)) {//出牌广播 + } + else if ("812".equalsIgnoreCase(command)) {//出牌广播 HuNanChangSha.drawCard(command, message); - } else if ("819".equalsIgnoreCase(command)) {//摸牌 + } + else if ("819".equalsIgnoreCase(command)) {//摸牌 huNanChangSha.getCard(command, message); - } else if ("813".equalsIgnoreCase(command)) {//出牌提示 + } + else if ("813".equalsIgnoreCase(command)) {//出牌提示 System.out.println("出牌++++++++++++++++++++++++"); huNanChangSha.outCard(client); - } else if ("814".equalsIgnoreCase(command)) {//放招提示 + } + else if ("814".equalsIgnoreCase(command)) {//放招提示 huNanChangSha.actionCard(param, client); - } else if ("2009".equalsIgnoreCase(command)) { + } + else if ("2009".equalsIgnoreCase(command)) { for (int i = 0; i < 7; i++) { sleepTime(1000); if (i == 6) { @@ -872,7 +879,8 @@ public class JiQiRens { } } - } else if ("817".equalsIgnoreCase(command)) {//结算 + } + else if ("817".equalsIgnoreCase(command)) {//结算 huNanChangSha.getChangShaCardInhand().clear(); huNanChangSha.getChuGuoCardInhand().clear(); huNanChangSha.getpongGroup().clear(); @@ -928,11 +936,15 @@ public class JiQiRens { ready(); //写定时器 - } else if ("815".equalsIgnoreCase(command)) { //服务器通知客户端有玩家执行了操作 + } + else if ("815".equalsIgnoreCase(command)) { //服务器通知客户端有玩家执行了操作 huNanChangSha.shanchuchuguopai(param); - } else if ("820".equalsIgnoreCase(command)) {//换牌提示 + } + else if ("820".equalsIgnoreCase(command)) {//换牌提示 huNanChangSha.changePlayer(command, message); - } else if ("2008".equalsIgnoreCase(command)) { //解散房间时候恢复机器人账号可以使用 + } + else if ("2008".equalsIgnoreCase(command)) { + //解散房间时候恢复机器人账号可以使用 // Jedis jedis11s = Redis.use("group1_db11").getJedis(); // String key = "g{" + groupId + "}:play:" + pid; // @@ -971,21 +983,24 @@ public class JiQiRens { // } catch (SQLException e) { // System.out.println(e); // } - } else if ("825".equalsIgnoreCase(command)) { + } + else if ("825".equalsIgnoreCase(command)) { ITObject params = TObject.newInstance(); params.putInt("qi", 0); params.putInt("id", 1); client.send("612", params, response -> { System.out.println("操作成功: " + response.returnCode); }); - } else if ("822".equalsIgnoreCase(command)) { + } + else if ("822".equalsIgnoreCase(command)) { ITObject params = TObject.newInstance(); params.putInt("qi", 0); params.putInt("id", 1); client.send("612", params, response -> { System.out.println("操作成功: " + response.returnCode); }); - } else if ("835".equalsIgnoreCase(command)) { //听牌天听 + } + else if ("835".equalsIgnoreCase(command)) { //听牌天听 ITObject params = TObject.newInstance(); params.putInt("qi", 0); params.putInt("id", 1); diff --git a/majiang/changsha/game_mj_cs/src/main/java/extend/mj/EXGameController.java b/majiang/changsha/game_mj_cs/src/main/java/extend/mj/EXGameController.java index edee3b2..2e857b7 100644 --- a/majiang/changsha/game_mj_cs/src/main/java/extend/mj/EXGameController.java +++ b/majiang/changsha/game_mj_cs/src/main/java/extend/mj/EXGameController.java @@ -158,6 +158,11 @@ public class EXGameController extends GameController { owner.getRoom().currenDiscardSeat = owner.seat; ITObject param = new TObject(); param.putBoolean("auto",owner.autoOutCard); + + // 添加调试日志,检查连接状态 + Global.logger.info("discardTipEvent - Player: {}, PlayerId: {}, Seat: {}, isConnect: {}, Sender: {}", + owner.nick, owner.playerid, owner.seat, owner.isConnect, owner.sender); + owner.sendEvent(Config.GAME_EVT_DISCARD_TIP, param); } 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 badd843..edf1406 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 @@ -37,14 +37,14 @@ public class Config { public static final int NIAO_TYPE_DOUBLE = 1; public static final int NIAO_TYPE_CS2NIAO = 2; - + public static final String ROOM_CONFIG_QS_JTYN = "two_pair"; public static final String ROOM_CONFIG_NO_JIANG = "no_jiang"; public static final String ROOM_CONFIG_NATIVE_HU = "native_hu"; - + public static final String ROOM_CONFIG_FOUR_WIN = "four_win"; @@ -98,36 +98,33 @@ public class Config { public static final String GAME_EVT_TING = "836"; - public static final String CREATE_ROOM_ROBOT = "create_room_for_robot"; - public static final String INIT_CONNECTION = "init_connection"; + // 初始化连接协议 + public static final String INIT_CONNECTION = "init_connection"; - /** - * 加入房间 - robot_mgr to robot_mj_cs 的内部协议号 - */ - public static final String JOIN_ROOM = "2002"; + // 创建房间机器人协议 + public static final String CREATE_ROOM_ROBOT = "create_room_for_robot"; - /** - * 发送准备 - robot_mgr to robot_mj_cs 的内部协议号 - */ - public static final String GAME_READY = "2003"; + // 加入房间协议 + public static final String JOIN_ROOM = "2002"; - /** - * 退出房间 - robot_mgr to robot_mj_cs 的内部协议号 - */ - public static final String EXIT_ROOM = "2005"; - - /** - * 发送准备 - robot_mj_cs to game_mj_cs 的协议号 - */ - public static final String GAME_READY_CS = "1003"; + // 加入房间CS协议 + public static final String JOIN_ROOM_CS = "1002"; - /** - * 加入房间 - robot_mgr to game_mj_cs 的内部协议号 - */ - public static final String JOIN_ROOM_CS = "1002"; + // 游戏准备协议 + public static final String GAME_READY = "2003"; - /** - * 退出房间 - robot_mgr to game_mj_cs 的内部协议号 - */ - public static final String EXIT_ROOM_CS = "1005"; + // 游戏准备CS协议 + public static final String GAME_READY_CS = "1003"; + + // 退出房间协议 + public static final String EXIT_ROOM = "2004"; + + // 退出房间CS协议 + public static final String EXIT_ROOM_CS = "1004"; + + // 其他可能的游戏相关协议 + public static final String GAME_CHAT = "1006"; + + // 心跳协议 + public static final String HEARTBEAT = "9000"; } \ 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 04e10e4..4992dbe 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 @@ -5,12 +5,16 @@ import com.robot.GameInterceptor; import com.robot.MainServer; import com.taurus.core.entity.ITObject; import com.taurus.core.entity.TObject; +import com.taurus.core.events.Event; +import com.taurus.core.events.IEventListener; import com.taurus.core.routes.ActionKey; import com.taurus.core.util.ICallback; +import com.taurus.core.util.StringUtil; import com.taurus.permanent.data.Session; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import robot.mj.handler.HuNanChangShaHandler; +import robot.mj.handler.HuNanChangSha; +import taurus.client.Message; import taurus.client.MessageResponse; import taurus.client.TaurusClient; @@ -19,6 +23,8 @@ import taurus.client.TaurusClient; */ public class EXGameController extends GameController { private static final Logger log = LoggerFactory.getLogger(EXGameController.class); + + private static RobotConnectionManager connectionManager = new RobotConnectionManager(); public EXGameController() { super(); @@ -35,12 +41,13 @@ public class EXGameController extends GameController { String type = params.getString("type"); String client = params.getString("client"); + String sessionToken = params.getString("session"); log.info("客户端类型: {}, 客户端标识: {}", type, client); - // 验证参数 + //验证参数 if ("manager_connection".equals(type) && "robot_mgr".equals(client)) { - // 返回成功响应 + //返回成功响应 ITObject response = TObject.newInstance(); response.putString("status", "success"); response.putString("message", "初始化成功"); @@ -53,7 +60,6 @@ public class EXGameController extends GameController { ITObject response = TObject.newInstance(); response.putString("status", "failed"); response.putString("message", "参数验证失败"); - MainServer.instance.sendResponse(gid, 1, response, session); } } catch (Exception e) { @@ -73,8 +79,6 @@ public class EXGameController extends GameController { @ActionKey(value = Config.CREATE_ROOM_ROBOT, validate = GameInterceptor.NOT_PLAYER) public void createRoomForRobot(Session session, ITObject params, int gid) { try { - log.info("收到robot_mgr的创建房间请求,Session: {}, GID: {}, 参数: {}", session, gid, params); - int reqGroupId = params.getInt("groupId"); int wanfaId = params.getInt("wanfaId"); int robotId = params.getInt("robotId"); @@ -97,17 +101,9 @@ public class EXGameController extends GameController { MainServer.instance.sendResponse(gid, 0, response, session); } else { log.error("创建房间失败,群组ID: {}, 玩法ID: {}", reqGroupId, wanfaId); - ITObject errorResponse = TObject.newInstance(); - errorResponse.putInt("error", 1); - errorResponse.putString("message", "创建房间失败"); - MainServer.instance.sendResponse(gid, 1, errorResponse, session); } } catch (Exception e) { log.error("处理创建房间请求时发生错误", e); - ITObject errorResponse = TObject.newInstance(); - errorResponse.putInt("error", 1); - errorResponse.putString("message", "服务器内部错误: " + e.getMessage()); - MainServer.instance.sendResponse(gid, 1, errorResponse, session); } } @@ -117,32 +113,40 @@ public class EXGameController extends GameController { @ActionKey(value = Config.JOIN_ROOM, validate = GameInterceptor.NOT_PLAYER) public void joinRoom(Session session, ITObject params, int gid) { try { - log.info("收到robot_mgr的机器人准备请求,Session: {}, GID: {}, 参数: {}", session, gid, params); + System.out.println("收到加入房间请求,Session: {}, GID: {}, 参数: {}"); - //在收到加入房间请求 建立到长沙麻将游戏服务器的连接 - TaurusClient client = GameServerConnector.getCsMjGameServerConnection(); + String connecId = params.getString("connecId"); + TaurusClient client = null;// getCsMjGameServerConnection(connecId); + client = new TaurusClient("8.134.76.43" + ":" + "6311", "game", TaurusClient.ConnectionProtocol.Tcp); + client.connect(); if (client == null) { - log.error("无法连接到长沙麻将游戏服务器"); - ITObject errorResponse = TObject.newInstance(); - errorResponse.putInt("error", 1); - errorResponse.putString("message", "无法连接到游戏服务器"); - MainServer.instance.sendResponse(gid, 1, errorResponse, session); return; } - - - //发送准备请求 + + //设置session和token + String sessionToken = params.getString("session"); + if (sessionToken != null && sessionToken.contains(",")) { + String[] sessionParts = sessionToken.split(","); + if (sessionParts.length >= 2) { + String actualSessionFromParams = sessionParts[0]; + String token = sessionParts[1]; + connectionManager.setSessionAndToken(actualSessionFromParams, token); + } + } + + //发送加入房间请求 client.send(Config.JOIN_ROOM_CS, params, response -> { - System.out.println("进入房间成功: " + response.returnCode); + System.out.println("csmj OnEvent " + response.returnCode); + log.info("加入房间请求发送结果: returnCode={}", response.returnCode); + }); + client.addEventListener(TaurusClient.NetClientEvent.OnEvent, new IEventListener() { + @Override + public void handleEvent(Event event) { + System.out.println("csmj OnEvent 能监听:jefe " ); + } }); - - } catch (Exception e) { - e.printStackTrace(); - ITObject errorResponse = TObject.newInstance(); - errorResponse.putInt("error", 1); - errorResponse.putString("message", "服务器内部错误: " + e.getMessage()); - MainServer.instance.sendResponse(gid, 1, errorResponse, session); + log.error("处理加入房间请求时发生错误", e); } } @@ -152,79 +156,69 @@ public class EXGameController extends GameController { @ActionKey(value = Config.GAME_READY, validate = GameInterceptor.NOT_PLAYER) public void robotReadyRoom(Session session, ITObject params, int gid) { try { - log.info("收到robot_mgr的机器人准备请求,Session: {}, GID: {}, 参数: {}", session, gid, params); - - //在收到准备请求 建立到长沙麻将游戏服务器的连接 - TaurusClient client = GameServerConnector.getCsMjGameServerConnection(); + String connecId = params.getString("connecId"); + TaurusClient client = getCsMjGameServerConnection(connecId); + System.out.println("session: " + session); + System.out.println("client: " + client); if (client == null) { - log.error("无法连接到长沙麻将游戏服务器"); - ITObject errorResponse = TObject.newInstance(); - errorResponse.putInt("error", 1); - errorResponse.putString("message", "无法连接到游戏服务器"); - MainServer.instance.sendResponse(gid, 1, errorResponse, session); return; } + params.del("connecId"); + client.send(Config.GAME_READY_CS, params, null); - - //发送准备请求 - client.send(Config.GAME_READY_CS, params, new ICallback() { - @Override - public void action(MessageResponse readyResponse) { - log.info("机器人准备请求发送结果: returnCode={}", readyResponse.returnCode); - - if (readyResponse.returnCode == 0) { - log.info("机器人准备成功"); - - ITObject successResponse = TObject.newInstance(); - successResponse.putString("status", "success"); - successResponse.putString("message", "机器人准备成功"); - MainServer.instance.sendResponse(gid, 0, successResponse, session); - } else { - log.error("机器人准备失败,返回码: {}", readyResponse.returnCode); - - ITObject errorResponse = TObject.newInstance(); - errorResponse.putInt("error", 1); - errorResponse.putString("message", "机器人准备失败"); - MainServer.instance.sendResponse(gid, 1, errorResponse, session); - } - } - }); - - + ITObject paramsReq = TObject.newInstance(); + paramsReq.putString("status", "success"); + MainServer.instance.sendResponse(gid, 0, paramsReq, session); } catch (Exception e) { - e.printStackTrace(); log.error("处理机器人准备请求时发生错误", e); - ITObject errorResponse = TObject.newInstance(); - errorResponse.putInt("error", 1); - errorResponse.putString("message", "服务器内部错误: " + e.getMessage()); - MainServer.instance.sendResponse(gid, 1, errorResponse, session); } } /** * 接收来自robot_mgr的退出房间协议 + * 断开与game_mj_cs的TCP连接 */ @ActionKey(value = Config.EXIT_ROOM, validate = GameInterceptor.NOT_PLAYER) public void exitRoom(Session session, ITObject params, int gid) { try { - log.info("收到robot_mgr的机器人准备请求,Session: {}, GID: {}, 参数: {}", session, gid, params); - - TaurusClient client = GameServerConnector.getCsMjGameServerConnection(); + String connecId = params.getString("connecId"); + TaurusClient client = getCsMjGameServerConnection(connecId); + + if (client == null) { + return; + } + //发送退出房间请求 client.send(Config.EXIT_ROOM_CS, params, response -> { - System.out.println("退出房间成功: " + response.returnCode); }); - + //断开与游戏服务器的连接 + //connectionManager.disconnectFromGameServer(connecId); } catch (Exception e) { - e.printStackTrace(); - ITObject errorResponse = TObject.newInstance(); - errorResponse.putInt("error", 1); - errorResponse.putString("message", "服务器内部错误: " + e.getMessage()); - MainServer.instance.sendResponse(gid, 1, errorResponse, session); + log.error("处理退出房间请求时发生错误", e); + String connecId = params.getString("connecId"); + connectionManager.disconnectFromGameServer(connecId); } } + /** + * 根据机器人ID和连接ID获取长沙麻将游戏服务器连接 + */ + public static TaurusClient getCsMjGameServerConnection(String connecId) { + TaurusClient taurusClient = connectionManager.getGameServerConnection(connecId); + if (taurusClient != null) { + return taurusClient; + } + + boolean connected = connectionManager.connectToGameServer(connecId); + if (connected) { + taurusClient = connectionManager.getGameServerConnection(connecId); + if (taurusClient != null) { + return taurusClient; + } + } + return null; + } } \ No newline at end of file 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 9b90ba7..911d65e 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 @@ -1,6 +1,11 @@ package robot.mj; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; import com.robot.GameController; import com.robot.Global; @@ -14,7 +19,7 @@ import com.taurus.permanent.core.TPEvents; import com.taurus.permanent.data.Session; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import robot.mj.handler.HuNanChangShaHandler; +import robot.mj.handler.HuNanChangSha; /** * 长沙麻将机器人主服务器 @@ -27,7 +32,11 @@ public class EXMainServer extends MainServer{ private static RobotConnectionManager robotConnectionManager; //长沙麻将AI处理器 - private static HuNanChangShaHandler huNanChangShaHandler; + private static HuNanChangSha huNanChangSha; + + //心跳和重连 + private final Map> serverHeartbeatTasks = new ConcurrentHashMap<>(); + private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(10); @Override public void onStart() { @@ -41,24 +50,26 @@ public class EXMainServer extends MainServer{ initializeComponents(); //添加会话断开事件监听器 - TPServer.me().getEventManager().addEventListener(TPEvents.EVENT_SESSION_DISCONNECT, new IEventListener() { + /*TPServer.me().getEventManager().addEventListener(TPEvents.EVENT_SESSION_DISCONNECT, new IEventListener() { @Override public void handleEvent(Event event) { Session session = (Session) event.getParameter(TPEvents.PARAM_SESSION); - log.info("会话断开: {}", session.getId()); + String sessionId = String.valueOf(session.getId()); + log.info("会话断开: {}", sessionId); + System.out.println("robot_mj_cs会话断开: " + sessionId); + Global.sessionMgr.disconnect(session); } - }); + });*/ } /** * 初始化组件 */ private void initializeComponents() { - robotConnectionManager = RobotConnectionManager.getInstance(); - + robotConnectionManager = new RobotConnectionManager(); //长沙麻将AI处理器 - huNanChangShaHandler = new HuNanChangShaHandler(); + huNanChangSha = new HuNanChangSha(); log.info("长沙麻将机器人服务器组件初始化完成"); } @@ -68,14 +79,12 @@ public class EXMainServer extends MainServer{ return new EXRoom(roomid, redis_room_map); } - @Override - public Player newPlayer(int playerid, Room room, String session_id) { - return new EXPlayer(playerid, room, session_id); + public Player newPlayer(int i, Room room, String s) { + return new EXPlayer(i, room, s); } - @Override protected GameController newController() { return new EXGameController(); } diff --git a/robots/majiang/robot_mj_cs/src/main/java/robot/mj/GameServerConnector.java b/robots/majiang/robot_mj_cs/src/main/java/robot/mj/GameServerConnector.java deleted file mode 100644 index 3ba4834..0000000 --- a/robots/majiang/robot_mj_cs/src/main/java/robot/mj/GameServerConnector.java +++ /dev/null @@ -1,114 +0,0 @@ -package robot.mj; - -import com.taurus.core.util.ICallback; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import taurus.client.TaurusClient; -import taurus.client.Message; -import taurus.client.SocketCode; -import taurus.client.MessageResponse; -import com.taurus.core.entity.ITObject; -import com.taurus.core.entity.TObject; -import com.taurus.core.events.Event; -import com.taurus.core.events.IEventListener; - -/** - * 游戏服务器连接器 - 管理到长沙麻将游戏服务器的连接 - */ -public class GameServerConnector { - private static final Logger log = LoggerFactory.getLogger(GameServerConnector.class); - - //长沙麻将服务器地址 - private static final String CS_MJ_SERVER_ADDRESS = "8.134.76.43:6311"; - - //长沙麻将服务器连接 - private static volatile TaurusClient csMjConnection; - - /** - * 获取长沙麻将游戏服务器连接 - */ - public static TaurusClient getCsMjGameServerConnection() { - if (csMjConnection != null && csMjConnection.isConnected()) { - return csMjConnection; - } - - synchronized (GameServerConnector.class) { - if (csMjConnection != null) { - return csMjConnection; - } - - csMjConnection = createConnection(CS_MJ_SERVER_ADDRESS, "长沙麻将"); - if (csMjConnection != null) { - log.info("成功创建到长沙麻将游戏服务器的连接: {}", CS_MJ_SERVER_ADDRESS); - } else { - log.error("无法连接到长沙麻将游戏服务器: {}", CS_MJ_SERVER_ADDRESS); - return null; - } - } - - return csMjConnection; - } - - /** - * 创建到游戏服务器的连接 - */ - private static TaurusClient createConnection(String serverAddress, String gameName) { - try { - log.info("正在连接到{}服务器: {}", gameName, serverAddress); - - TaurusClient client = new TaurusClient(serverAddress, "game", TaurusClient.ConnectionProtocol.Tcp); - - //添加连接事件监听器 - client.addEventListener(TaurusClient.NetClientEvent.Connect, new IEventListener() { - @Override - public void handleEvent(Event event) { - SocketCode code = (SocketCode) event.getParameter("code"); - if (code == SocketCode.Connect) { - log.info("成功连接到{}服务器: {}", gameName, serverAddress); - } else { - log.warn("连接到{}服务器失败: {},错误码: {}", gameName, serverAddress, code); - } - } - }); - - //添加消息接收事件监听器 - client.addEventListener(TaurusClient.NetClientEvent.OnEvent, new IEventListener() { - @Override - public void handleEvent(Event event) { - Message message = (Message) event.getParameter("msg"); - if (message != null) { - log.debug("从{}服务器收到消息: 命令={},参数={}", gameName, message.command, message.param); - } - } - }); - - client.connect(); - Thread.sleep(100); - - return client; - } catch (Exception e) { - log.error("连接到{}服务器时发生错误: {}", gameName, serverAddress, e); - return null; - } - } - - /** - * 发送协议到长沙麻将游戏服务器 - */ - public static void sendProtocolToCsMj(String protocol, ITObject params, ICallback responseCallback) { - TaurusClient connection = getCsMjGameServerConnection(); - if (connection != null) { - connection.send(protocol, params, responseCallback); - } else { - log.error("无法发送协议到长沙麻将游戏服务器,连接不可用"); - // 在回调中返回错误 - if (responseCallback != null) { - MessageResponse errorResponse = new MessageResponse(); - errorResponse.returnCode = 1; - errorResponse.messageData = new taurus.client.Message(); - errorResponse.messageData.param = new com.taurus.core.entity.TObject(); - responseCallback.action(errorResponse); - } - } - } -} \ No newline at end of file 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 cfd8c0c..8590359 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 @@ -4,6 +4,7 @@ import com.taurus.core.entity.ITArray; import com.taurus.core.entity.ITObject; import com.taurus.core.entity.TObject; import com.taurus.core.events.Event; +import com.taurus.core.events.IEventListener; import com.taurus.core.plugin.redis.Redis; import com.taurus.core.plugin.database.DataBase; import com.taurus.core.util.Logger; @@ -11,9 +12,16 @@ import com.taurus.core.util.StringUtil; import taurus.client.Message; import taurus.client.TaurusClient; import redis.clients.jedis.Jedis; -import robot.mj.handler.HuNanChangShaHandler; +import robot.mj.handler.HuNanChangSha; +import taurus.client.business.GroupRoomBusiness; +import taurus.util.ChangShaSuanFaTest; +import taurus.util.TinHuChi; +import java.sql.SQLException; import java.util.*; +import java.util.concurrent.*; + +import static java.lang.Thread.sleep; /** * 机器人连接管理器 - 管理与游戏服务器的连接 @@ -21,60 +29,106 @@ import java.util.*; public class RobotConnectionManager { private Logger log = Logger.getLogger(RobotConnectionManager.class); - private static RobotConnectionManager instance; - private Map gameClients; + private Map gameClients; //存储全局的长沙麻将AI处理器 - private HuNanChangShaHandler huNanChangShaHandler; - - private RobotConnectionManager() { + private HuNanChangSha huNanChangSha; + + private final String host="8.134.76.43"; + private final int port=6311; + + private TaurusClient client = null; + + private Map> playerOutcardsMap = new HashMap<>(); + + private Map> playerchisMap = new HashMap<>(); + + private Map> playerpengsMap = new HashMap<>(); + + private Map> playermingsMap = new HashMap<>(); + + private Map> playerzisMap = new HashMap<>(); + + // 全局变量定义 + private int groupId = 0; + private int pid = 0; + private int playerId = 0; + private String playKey = ""; + private Map count = new HashMap(); + + + public RobotConnectionManager() { gameClients = new HashMap<>(); - huNanChangShaHandler = new HuNanChangShaHandler(); + huNanChangSha = new HuNanChangSha(); } - - public static synchronized RobotConnectionManager getInstance() { - if (instance == null) { - instance = new RobotConnectionManager(); - } - return instance; + + /** + * 获取所有session列表 + */ + public Set getAllSessions() { + return new HashSet<>(gameClients.keySet()); } - + + /** + * 设置会话和令牌 + */ + public void setSessionAndToken(String session, String token) { + huNanChangSha.session = session; + huNanChangSha.token = token; + } + /** * 连接到长沙麻将游戏服务器 */ - public boolean connectToGameServer(String host, int port, int playerId, int groupId, String roomId) { + public boolean connectToGameServer(String connecId) { try { - //检查是否已经连接到该房间 - if (gameClients.containsKey(roomId)) { - TaurusClient existingClient = gameClients.get(roomId); + //检查是否已经连接 + if (gameClients.containsKey(connecId)) { + TaurusClient existingClient = gameClients.get(connecId); if (existingClient.isConnected()) { - log.info("房间 " + roomId + " 已经连接到游戏服务器: " + host + ":" + port); return true; } else { - //如果连接已断开 先移除旧的客户端 - gameClients.remove(roomId); + //连接断开 移除旧的客户端 + disconnectFromGameServer(connecId); } } - + //创建Taurus客户端 - TaurusClient client = new TaurusClient(host + ":" + port, "game", TaurusClient.ConnectionProtocol.Tcp); - - //连接到游戏服务器 + client = new TaurusClient(host + ":" + port, "game", TaurusClient.ConnectionProtocol.Tcp); client.connect(); - - //添加事件监听器处理网络消息 - client.addEventListener(TaurusClient.NetClientEvent.OnEvent, new com.taurus.core.events.IEventListener() { + + + + System.out.println("csmj OnEvent " + client.getSession()); + /// GroupRoomBusiness.joinRoom(383709, "room:592473", "{user}:101555", null); + //Thread.sleep(5000); + System.out.println("csmj OnEvent2 " + client.getSession()); + + + + + System.out.println("client.getSession()1111" +client.getSession()); + System.out.println("client.getId()1111" +client.getId()); + //添加事件监听器处理网络消息和连接断开 + client.addEventListener(TaurusClient.NetClientEvent.OnEvent, new IEventListener() { @Override public void handleEvent(Event event) { + System.out.println("csmj OnEvent 能监听: " ); + //获取 msg Message message = (Message) event.getParameter("msg"); if (message == null) { + System.out.println("在OnEvent中收到空消息"); return; } ITObject param = message.param; //回调协议号 String command = message.command; - + System.out.println("csmj OnEvent msg: " + command); + + // 添加更详细的日志 + System.out.println("收到协议命令: " + command + " params: " + param); + //根据玩法ID处理不同的回调 if (StringUtil.isNotEmpty(command)) { //直接处理协议 @@ -82,284 +136,250 @@ public class RobotConnectionManager { } } }); - + //添加连接状态监听器 - client.addEventListener(TaurusClient.NetClientEvent.Connect, new com.taurus.core.events.IEventListener() { + client.addEventListener(TaurusClient.NetClientEvent.Connect, new IEventListener() { @Override public void handleEvent(Event event) { - log.info("长沙麻将AI机器人连接成功到游戏服务器: " + host + ":" + port + ",房间: " + roomId); - - //通知robot_mgr连接成功 - notifyConnectionStatus(roomId, playerId + "", host, port, "connected"); + System.out.println("Connect"); + System.out.println("csmj Connect connecId: " + connecId); } }); - - //添加断开连接监听器 - client.addEventListener(TaurusClient.NetClientEvent.OnEvent, new com.taurus.core.events.IEventListener() { - @Override - public void handleEvent(com.taurus.core.events.Event event) { - log.info("长沙麻将AI机器人连接断开,房间: " + roomId); - //从连接列表中移除断开的客户端 - for (Map.Entry entry : gameClients.entrySet()) { - if (entry.getValue() == client) { - gameClients.remove(entry.getKey()); - log.info("已从连接列表中移除断开的连接: " + entry.getKey()); - - //通知robot_mgr连接断开 - notifyConnectionStatus(entry.getKey(), playerId + "", "", 0, "disconnected"); - break; - } - } - } - }); - - HuNanChangShaHandler.playerId = playerId; - - gameClients.put(roomId, client); - log.info("长沙麻将AI机器人成功连接到游戏服务器: " + host + ":" + port + ",房间: " + roomId + ",玩家: " + playerId); - + + ITObject readyParam = new TObject(); + readyParam.putString("session","{user}:101555,c739ec6c3e93e9655e507397f78d857f"); + readyParam.putString("pos", "10,10"); + //readyParam.putString("connecId", "1"); + //pos + client.send(Config.JOIN_ROOM_CS, readyParam, response -> { + System.out.println("csmj OnEvent " + response.returnCode); + log.info("加入房间请求发送结果: returnCode={}", response.returnCode); + }); + + readyParam.del("connecId"); + sleep(5000); + client.send(Config.GAME_READY_CS, readyParam, null); + + + System.out.println(client); + System.out.println("client.getSession()2222" +client.getSession()); + System.out.println("client.getId()2222" +client.getId()); + gameClients.put(connecId, client); + System.out.println("gameClients" + gameClients.get(connecId)); + return true; } catch (Exception e) { - e.printStackTrace(); + log.error("连接到游戏服务器时发生异常", e); return false; } } - + /** - * 通知robot_mgr连接状态 + * 断开与游戏服务器的连接 */ - private void notifyConnectionStatus(String roomId, String robotId, String host, int port, String status) { - try { - Jedis jedis = Redis.use("group1_db11").getJedis(); + public void disconnectFromGameServer(String compositeKey) { + TaurusClient client = gameClients.remove(compositeKey); + if (client != null && client.isConnected()) { try { - String notificationKey = "robot_connection_notify:" + roomId + ":" + robotId; - Map notification = new HashMap<>(); - notification.put("status", status); - if (!host.isEmpty()) { - notification.put("game_server", host + ":" + port); - } - notification.put("room_id", roomId); - notification.put("robot_id", robotId); - notification.put("robot_server", "robot_mj_cs"); - notification.put("timestamp", String.valueOf(System.currentTimeMillis())); - - jedis.hmset(notificationKey, notification); - jedis.expire(notificationKey, 300); //5分钟过期 - - } finally { - jedis.close(); + client.killConnection(); + } catch (Exception e) { + log.error("断开客户端连接时发生异常", e); } - } catch (Exception e) { - e.printStackTrace(); } } - + + public static void main(String[] args) { + RobotConnectionManager rb=new RobotConnectionManager(); + boolean i = rb.connectToGameServer("1"); + System.out.println(i); + } + + /** + * 根据connecId获取游戏服务器连接 + */ + public TaurusClient getGameServerConnection(String connecId) { + TaurusClient client = gameClients.get(connecId); + + if (client == null) { + System.out.println("Attempting to reconnect: " + connecId); + // 尝试重新连接 + connectToGameServer(connecId); + client = gameClients.get(connecId); + } + + return client; + } + /** * 处理接收到的游戏协议 */ private void handleProtocol(String command, taurus.client.Message message, TaurusClient client) { ITObject param = message.param; - - switch (command) { - case "811": //初始化手牌 - handleInitCards(command, message, client); - break; - case "812": //出牌广播 - handleDiscardBroadcast(command, message); - break; - case "819": //摸牌 - handleDrawCard(command, message); - break; - case "813": //出牌提示 - handleDiscardPrompt(client); - break; - case "814": //操作提示(吃碰杠胡) - handleActionPrompt(param, client); - break; - case "817": //结算 - handleSettlement(param, client); - break; - case "815": //服务器通知客户端有玩家执行了操作 - handleServerNotification(param); - break; - case "820": //换牌提示 - handleChangeCardTip(command, message); - break; - case "825": - case "822": - case "835": //听牌相关 - handleTingPai(command, client); - break; - case "2008": //解散房间 - handleRoomDisband(); - break; - case "2009": //其他操作 - 房间检查和踢人逻辑 - handleRoomCheckAndKick(param, client); - break; - case "838": //补杠事件 - handleBuGang(param, client); - break; - case "816": //抢杠胡 - handleQiangGangHu(param, client); - break; - case "821": //吃牌 - handleChiCard(param, client); - break; - case "823": //杠后补牌 - handleGangBuPai(param, client); - break; - default: - handleUnknownCommand(command, param, client); - break; - } - } - - /** - * 处理初始化手牌协议 - */ - private void handleInitCards(String command, taurus.client.Message message, TaurusClient client) { - huNanChangShaHandler.cardInHead(command, message, client); - } - - /** - * 处理出牌广播协议 - */ - private void handleDiscardBroadcast(String command, taurus.client.Message message) { - HuNanChangShaHandler.drawCard(command, message); - } - - /** - * 处理摸牌协议 - */ - private void handleDrawCard(String command, taurus.client.Message message) { - huNanChangShaHandler.getCard(command, message); - } - - /** - * 处理出牌提示协议 - */ - private void handleDiscardPrompt(TaurusClient client) { - huNanChangShaHandler.outCard(client, new HashMap<>(), new HashMap<>(), new HashMap<>(), new HashMap<>(), new HashMap<>()); - } - - /** - * 处理操作提示协议(吃碰杠胡) - */ - private void handleActionPrompt(com.taurus.core.entity.ITObject param, TaurusClient client) { - huNanChangShaHandler.actionCard(param, client); - } - - /** - * 处理结算协议 - */ - private void handleSettlement(com.taurus.core.entity.ITObject param, TaurusClient client) { + try (Jedis jedis0 = Redis.use().getJedis()){ + switch (command) { + case "811": //初始化手牌 + huNanChangSha.cardInHead(command, message, client); + break; + case "812": //出牌广播 + ITArray outcard_map = param.getTArray("outcard_map"); + ITArray opchicards = param.getTArray("opchicards"); + ITArray oppengcards = param.getTArray("oppengcards"); + ITArray opmingcards = param.getTArray("opmingcards"); + ITArray opzicards = param.getTArray("opzicards"); - huNanChangShaHandler.getChangShaCardInhand().clear(); - huNanChangShaHandler.getChuGuoCardInhand().clear(); - huNanChangShaHandler.getPongGroup().clear(); - huNanChangShaHandler.getChowGroup().clear(); - - // 发送准备协议 - sendReadyCommand(client); - } - - /** - * 处理服务器通知协议 - */ - private void handleServerNotification(com.taurus.core.entity.ITObject param) { - huNanChangShaHandler.shanchuchuguopai(param); - } - - /** - * 处理换牌提示协议 - */ - private void handleChangeCardTip(String command, taurus.client.Message message) { - HuNanChangShaHandler.changePlayer(command, message); - } - - /** - * 处理听牌协议 - */ - private void handleTingPai(String command, TaurusClient client) { - com.taurus.core.entity.ITObject params = new com.taurus.core.entity.TObject(); - params.putInt("qi", 0); - params.putInt("id", 1); - params.putString("session", HuNanChangShaHandler.session + "," + HuNanChangShaHandler.token); - - client.send("612", params, response -> { - System.out.println("听牌操作: " + response.returnCode); - }); - } - - /** - * 处理解散房间协议 - */ - private void handleRoomDisband() { - log.info("房间解散,准备下一局"); - } - - /** - * 处理房间检查和踢人逻辑 - */ - private void handleRoomCheckAndKick(ITObject param, TaurusClient client) { - try { - Jedis jedis0 = Redis.use().getJedis(); - Jedis jedis11 = Redis.use("group1_db11").getJedis(); - - try { - for (int i = 0; i < 7; i++) { - Thread.sleep(1000); - if (i == 6) { + // 清空旧数据,用新数据完全覆盖 + playerOutcardsMap.clear(); + playerchisMap.clear(); + playerpengsMap.clear(); + playermingsMap.clear(); + playerzisMap.clear(); + //出过的牌 + if (outcard_map != null) { + for (int i = 0; i < outcard_map.size(); i++) { + ITObject playerData = outcard_map.getTObject(i); + int playerId = playerData.getInt("playerId"); + ITArray outcardsArray = playerData.getTArray("outcards"); - ITObject readyParams = new TObject(); + // 转换为List + List outcardsList = new ArrayList<>(); + for (int j = 0; j < outcardsArray.size(); j++) { + outcardsList.add(outcardsArray.getInt(j)); + } + + // 存储到Map中(覆盖旧数据) + playerOutcardsMap.put(playerId, outcardsList); + + } + } + + //吃的牌 + if (opchicards != null) { + for (int i = 0; i < opchicards.size(); i++) { + ITObject playerData = opchicards.getTObject(i); + int playerId = playerData.getInt("playerId"); + ITArray outchiArray = playerData.getTArray("opchicards"); + + List outchiList = new ArrayList<>(); + for (int j = 0; j < outchiArray.size(); j++) { + outchiList.add(outchiArray.getInt(j)); + } + playerchisMap.put(playerId, outchiList); + } + } + + //碰的牌 + if (oppengcards != null) { + for (int i = 0; i < oppengcards.size(); i++) { + ITObject playerData = oppengcards.getTObject(i); + int playerId = playerData.getInt("playerId"); + ITArray outpengArray = playerData.getTArray("oppengcards"); + + List outpengList = new ArrayList<>(); + for (int j = 0; j < outpengArray.size(); j++) { + outpengList.add(outpengArray.getInt(j)); + } + playerpengsMap.put(playerId, outpengList); + } + } + + //明杠的牌 + if (opmingcards != null) { + for (int i = 0; i < opmingcards.size(); i++) { + ITObject playerData = opmingcards.getTObject(i); + int playerId = playerData.getInt("playerId"); + ITArray outmingArray = playerData.getTArray("opmingcards"); + + List outmingList = new ArrayList<>(); + for (int j = 0; j < outmingArray.size(); j++) { + outmingList.add(outmingArray.getInt(j)); + } + playermingsMap.put(playerId, outmingList); + } + } + + //暗杠的牌 + if (opzicards != null) { + for (int i = 0; i < opzicards.size(); i++) { + ITObject playerData = opzicards.getTObject(i); + int playerId = playerData.getInt("playerId"); + ITArray outziArray = playerData.getTArray("opzicards"); + + List outziList = new ArrayList<>(); + for (int j = 0; j < outziArray.size(); j++) { + outziList.add(outziArray.getInt(j)); + } + playerzisMap.put(playerId, outziList); + } + } + HuNanChangSha.drawCard(command, message); + break; + case "819": //摸牌 + System.out.println("Handling 819 - Draw card"); + huNanChangSha.getCard(command, message); + break; + case "813": //出牌提示 + System.out.println("Handling 813 - Discard card tip received!"); + huNanChangSha.outCard(client,playerOutcardsMap,playerchisMap,playerpengsMap,playermingsMap,playerzisMap); + break; + case "814": //操作提示(吃碰杠胡) + System.out.println("Handling 814 - Action tips (Chi, Peng, Gang, Hu)"); + huNanChangSha.actionCard(param, client); + break; + case "2009": //其他操作 - 房间检查和踢人逻辑 + System.out.println("Handling 2009 - Other operations"); + try { + Jedis jedis22 = Redis.use().getJedis(); + // 使用JiQiRens的sleepTime方法 + try { + sleep(3000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + ITObject params = TObject.newInstance(); String[] playerIds2 = null; - Set roomIds = jedis0.keys("room:*"); + + Set roomIds = jedis22.keys("room:*"); String roomKey = ""; - - //获取所有机器人ID + //拿到所有的机器人 List robotIdsList = new ArrayList<>(); String sql2 = "SELECT id FROM `account` WHERE jiqiren=9998"; ITArray robotId2 = null; try { robotId2 = DataBase.use().executeQueryByTArray(sql2); - } catch (Exception e) { + } catch (SQLException e) { e.printStackTrace(); } - - if (robotId2 != null) { - for (int j = 0; j < robotId2.size(); j++) { - robotIdsList.add(robotId2.getTObject(j).getInt("id")); - } + for (int j = 0; j < Objects.requireNonNull(robotId2).size(); j++) { + robotIdsList.add(robotId2.getTObject(j).getInt("id")); } for (String roomId : roomIds) { String roomId1 = roomId.substring(roomId.indexOf(":") + 1); roomKey = "room:" + roomId1; - log.info("roomKey +++++++++++++++++" + roomKey); - - if (jedis0.hget(roomKey, "players") != null) { - String players = jedis0.hget(roomKey, "players"); + + if (jedis22.hget(roomKey, "players") != null) { + String players = jedis22.hget(roomKey, "players"); if (!players.equals("[]")) { players = players.substring(1, players.length() - 1); playerIds2 = players.split(","); - log.info("playerIds2 ++++++===========" + Arrays.toString(playerIds2)); if (playerIds2.length == 1) { for (String s : playerIds2) { if (robotIdsList.contains(Integer.parseInt(s))) { //房间里的人是机器人 - String gpid = jedis0.hget(roomKey, "gpid"); - String gpId = jedis0.hget(roomKey, "group"); + String gpid = jedis22.hget(roomKey, "gpid"); + String gpId = jedis22.hget(roomKey, "group"); String key = "g{" + gpId + "}:play:" + gpid; - if (!players.equals("[]")) { - System.out.println("roomKey ++++" + roomKey); - System.out.println("gpid ++++++" + gpid); - System.out.println("groupid ++++++" + gpId); - System.out.println("key +++++++" + key); - System.out.println("===================清空机器人准备时间超过六秒的房间 8888888888888888"); + if (!players.equals("[]") && pid == Integer.parseInt(gpid)) { - if (HuNanChangShaHandler.changShaCard != 0) { + if (count != null && count.containsKey(Integer.parseInt(gpid))) { + Integer currentValue = count.get(Integer.parseInt(gpid)); + if (currentValue > 0) { + count.put(Integer.parseInt(gpid), currentValue - 1); + } } + Jedis jedis20 = Redis.use("group1_db11").getJedis(); jedis20.hincrBy(key, "leftover_robot", 1); @@ -368,109 +388,212 @@ public class RobotConnectionManager { try { DataBase.use().executeUpdate(sql); - } catch (Exception e) { + } catch (SQLException e) { e.printStackTrace(); } - readyParams.putString("session", HuNanChangShaHandler.session + "," + HuNanChangShaHandler.token); - - client.send("601", readyParams, response -> { - System.out.println("机器人准备操作: " + response.returnCode); + client.send("1005", params, response -> { + }); + } } } + + } + } + } + } + + jedis22.close(); + } catch (Exception e) { + e.printStackTrace(); + } + break; + case "817": //结算 + huNanChangSha.getChangShaCardInhand().clear(); + huNanChangSha.getChuGuoCardInhand().clear(); + huNanChangSha.getpongGroup().clear(); + huNanChangSha.getchowGroup().clear(); + TinHuChi.lastTingCount = 0; + TinHuChi.isMoreThanLast = false; + ChangShaSuanFaTest.isTin=false; + ChangShaSuanFaTest.isChi=false; + ChangShaSuanFaTest.isPeng=false; + ChangShaSuanFaTest.tinCards.clear(); + Integer type = param.getInt("type"); + if (type == 1 || type == 2) { //为1为大结算 为2为解散 + Jedis jedis11s = Redis.use("group1_db11").getJedis(); + try { + String key = "g{" + groupId + "}:play:" + pid; + jedis11s.hincrBy(key, "leftover_robot", 1); +// + if (count != null && count.containsKey(pid)) { + Integer currentValue = count.get(pid); + if (currentValue > 0) { + count.put(pid, currentValue - 1); + } + } + + + String sql = String.format("UPDATE `account` SET start = %d WHERE id = %d", 0, playerId); + try { + DataBase.use().executeUpdate(sql); + } catch (SQLException e) { + e.printStackTrace(); + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + jedis11s.close(); + } + + if (count != null && count.containsKey(pid)) { + Integer value = count.get(pid); + + // 如果找到了对应的 pid + Jedis jedis12 = Redis.use("group1_db11").getJedis(); + + String shangxianRobot = jedis12.hget(playKey, "shangxian_robot"); + String leftoverRobot = jedis12.hget(playKey, "leftover_robot"); + + if (shangxianRobot != null && leftoverRobot != null) { + if (value == 0) { + jedis12.hset(playKey, "leftover_robot", shangxianRobot); + } + } + jedis12.close(); + } + } +// playerState.pongGroups.clear();; +// playerState.handCards.clear(); +// playerState.chiGroups.clear(); +// playerState.gangGroups.clear();; + + + // 发送准备协议 + ITObject readyParams = new TObject(); + readyParams.putString("session", HuNanChangSha.session + "," + HuNanChangSha.token); + client.send("1005", readyParams, response -> { + System.out.println("准备操作响应: " + response.returnCode); + }); + break; + case "815": //服务器通知客户端有玩家执行了操作 + System.out.println("Handling 815 - Server notifies client of player action"); + //[TCP->815] data:{"playerid":101555,"card":104,"opcard":[105,103],"from_seat":2,"type":1,"opengang":false} + huNanChangSha.shanchuchuguopai(param); + break; + case "820": //换牌提示 + System.out.println("Handling 820 - Change card tips"); + HuNanChangSha.changePlayer(command, message); + break; + case "825": + System.out.println("Handling 825"); + ITObject params25 = TObject.newInstance(); + params25.putInt("qi", 0); + params25.putInt("id", 1); + params25.putString("session", HuNanChangSha.session + "," + HuNanChangSha.token); + client.send("612", params25, response -> { + + }); + break; + case "822": + System.out.println("Handling 822"); + ITObject params22 = TObject.newInstance(); + //params.putInt("qi", 0); + params22.putInt("id", 1); + params22.putString("session", HuNanChangSha.session + "," + HuNanChangSha.token); + //[TCP->822] data:{"tip_list":[{"type":8,"id":1,"opcard":[],"weight":8,"card":0}],"types":[{"type":21,"value":1}]} + //板胡Event [TCP->823] data:{"type":8,"seat":1,"data":[{"opcard":[204,204,204,108,108,108],"type":21,"value":1}]} + client.send("612", params22, response -> { + + }); + break; + case "2008": //解散房间时候恢复机器人账号可以使用 + System.out.println("Handling 2008 - Restore robot accounts when room is dismissed"); +// Jedis jedis11s = Redis.use("group1_db11").getJedis(); +// String key = "g{" + groupId + "}:play:" + pid; +// +// jedis11s.hincrBy(key, "leftover_robot", 1); +// jedis11s.close(); +// try { + + Set roomIds08 = jedis0.keys("room:*"); + String[] playerIds08 = null; + for (String roomId : roomIds08) { + String rid = roomId.substring(roomId.indexOf(":") + 1); + String roomKey08 = "room:" + rid; + + if (jedis0.hget(roomKey08, "players") != null) { + String players = jedis0.hget(roomKey08, "players"); + + if (!players.equals("[]")) { + players = players.substring(1, players.length() - 1); + playerIds08 = players.split(","); + + for (String pyids : playerIds08) { + + if (Integer.parseInt(pyids) == playerId) { + jedis0.del(roomId); } } } } } - } - } finally { - jedis0.close(); - jedis11.close(); + break; + case "838": //补杠事件 + System.out.println("Handling 838 - Bu Gang event"); + int card838 = param.getInt("card"); + ITObject params838 = new TObject(); + params838.putInt("card", card838); + params838.putString("session", HuNanChangSha.session + "," + HuNanChangSha.token); + client.send("839", params838, response -> { + + }); + break; + case "816": //抢杠胡 + System.out.println("Handling 816 - Qiang Gang Hu"); + int card816 = param.getInt("card"); + ITObject params816 = new TObject(); + params816.putInt("type", 21); //胡牌类型 + params816.putInt("card", card816); + params816.putString("session", HuNanChangSha.session + "," + HuNanChangSha.token); + client.send("612", params816, response -> { + log.info("抢杠胡操作: " + response.returnCode); + }); + break; + case "821": //吃牌 + System.out.println("Handling 821 - Chi Pai"); + int card821 = param.getInt("card"); + ITObject params821 = new TObject(); + params821.putInt("type", 21); //吃牌类型 + params821.putInt("card", card821); + params821.putString("session", HuNanChangSha.session + "," + HuNanChangSha.token); + client.send("612", params821, response -> { + log.info("吃牌操作响应: " + response.returnCode); + }); + break; + case "823": //杠后补牌 + System.out.println("Handling 823 - Gang after draw card"); + //处理杠后补牌逻辑 + int card823 = param.getInt("card"); + huNanChangSha.getChangShaCardInhand().add(card823); + log.info("杠后补牌: " + card823 + ", 当前手牌: " + huNanChangSha.getChangShaCardInhand()); + break; + case "835": //听牌相关 + System.out.println("Handling 835 - Ting Pai related"); + ITObject params835 = new TObject(); + params835.putInt("qi", 0); + params835.putInt("id", 1); + params835.putString("session", HuNanChangSha.session + "," + HuNanChangSha.token); + client.send("612", params835, response -> { + + }); + break; + default: + System.out.println("Received unknown protocol command: " + command + ", params: " + param); + break; } - } catch (Exception e) { - e.printStackTrace(); } - - //发送准备协议 - sendReadyCommand(client); - } - - /** - * 处理补杠协议 - */ - private void handleBuGang(ITObject param, TaurusClient client) { - int card = param.getInt("card"); - ITObject params = new TObject(); - params.putInt("type", 21); // 胡牌类型 - params.putInt("card", card); - params.putString("session", HuNanChangShaHandler.session + "," + HuNanChangShaHandler.token); - - client.send("612", params, response -> { - log.info("补杠胡操作: " + response.returnCode); - }); - } - - /** - * 处理抢杠胡协议 - */ - private void handleQiangGangHu(ITObject param, TaurusClient client) { - - int card = param.getInt("card"); - ITObject params = new TObject(); - params.putInt("type", 21); //胡牌类型 - params.putInt("card", card); - params.putString("session", HuNanChangShaHandler.session + "," + HuNanChangShaHandler.token); - - client.send("612", params, response -> { - log.info("抢杠胡操作: " + response.returnCode); - }); - } - - /** - * 处理吃牌协议 - */ - private void handleChiCard(ITObject param, TaurusClient client) { - - int card = param.getInt("card"); - ITObject params = new TObject(); - params.putInt("type", 21); //吃牌类型 - params.putInt("card", card); - params.putString("session", HuNanChangShaHandler.session + "," + HuNanChangShaHandler.token); - - client.send("612", params, response -> { - log.info("吃牌操作响应: " + response.returnCode); - }); - } - - /** - * 处理杠后补牌协议 - */ - private void handleGangBuPai(ITObject param, TaurusClient client) { - //处理杠后补牌逻辑 - int card = param.getInt("card"); - huNanChangShaHandler.getChangShaCardInhand().add(card); - log.info("杠后补牌: " + card + ", 当前手牌: " + huNanChangShaHandler.getChangShaCardInhand()); - } - - /** - * 处理未知协议命令 - */ - private void handleUnknownCommand(String command, ITObject param, TaurusClient client) { - log.info("收到未知协议命令: " + command + ", 参数: " + param); - } - - /** - * 发送准备命令 - */ - private void sendReadyCommand(TaurusClient client) { - com.taurus.core.entity.ITObject params = new com.taurus.core.entity.TObject(); - params.putString("session", HuNanChangShaHandler.session + "," + HuNanChangShaHandler.token); - - client.send("601", params, response -> { - System.out.println("准备操作响应: " + response.returnCode); - }); } } \ No newline at end of file diff --git a/robots/majiang/robot_mj_cs/src/main/java/robot/mj/handler/HuNanChangSha.java b/robots/majiang/robot_mj_cs/src/main/java/robot/mj/handler/HuNanChangSha.java new file mode 100644 index 0000000..cc6de8b --- /dev/null +++ b/robots/majiang/robot_mj_cs/src/main/java/robot/mj/handler/HuNanChangSha.java @@ -0,0 +1,1732 @@ +package robot.mj.handler; + +import com.robot.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.plugin.database.DataBase; +import com.taurus.core.util.Logger; +import com.taurus.core.util.StringUtil; +import taurus.client.Message; +import taurus.client.TaurusClient; +import taurus.util.*; + +import java.sql.SQLException; +import java.util.*; + + +public class HuNanChangSha { + + + public static int changShaCard = 0; + public static boolean isTinChi = false; + public static boolean isTinPeng = false; + + + //湖南长沙麻将手牌 + private List changShaCardInhand = new ArrayList<>(); + + private List changShaCardInhandgang = new ArrayList<>(); + + + //长沙麻将出过的牌 + private List changShachuguopai = new ArrayList<>(); + + private Map chuGuoPainum = new HashMap<>(); + + + //杠的牌 + private List gangdepai = new ArrayList<>(); + + + //碰牌 + private List pongGroup = new ArrayList<>(); + + + //吃牌 + private List chowGroup = new ArrayList<>(); + + + // 玩家座位号 + public static int seat = 0; + + public static int playerId = 0; + + public static int cardToOut1 = 0; + + // 会话标识 + public static String session = ""; + // 访问令牌 + public static String token = ""; + //红中麻将算法 +// private static HongZhongSuanFa hongZhongSuanFa = new HongZhongSuanFa(); + + private static ChangShaSuanFaTest changShaSuanFaTest = new ChangShaSuanFaTest(); + + + // 公共的getter和setter方法 + public List getChangShaCardInhand() { + return changShaCardInhand; + } + + public List getgangdepai() { + return gangdepai; + } + + public List getchangShaCardInhandgang() { + return changShaCardInhandgang; + } + + // 公共的getter和setter方法 + public List getpongGroup() { + return pongGroup; + } + + public List getchowGroup() { + return chowGroup; + } + + public List getChuGuoCardInhand() { + return changShachuguopai; + } + + public Map getChuGuoPainum() { + return chuGuoPainum; + } + + /** + * 出牌广播协议 812 + * + * @param command 协议号 + * @param message 消息对象 + * @return + */ + public static String drawCard(String command, Message message) { + if (command.equalsIgnoreCase("812")) { + ITObject param = message.param; + if (param == null) { + return null; + } + changShaCard = param.getInt("card"); + + + } + return null; + } + + + /** + * 摸牌协议 819 + * + * @param command 协议号 + * @param message 消息对象 + * @return + */ + public String getCard(String command, Message message) { + + if (command.equalsIgnoreCase("819")) { + ITObject param = message.param; + if (param == null) { + return null; + } + + if (param.getInt("player") != null) { + int drawnCard = param.getInt("card"); + changShaSuanFaTest.drawnCards = drawnCard;//存储摸到的牌 + changShaCardInhand.add(drawnCard); + changShaSuanFaTest.analyzeHand(changShaCardInhand); + + + } + } + return null; + } + + /** + * 判断是否应该碰牌 + * + * @param proposedCard 提议碰的牌 + * @return 是否应该碰牌 + */ + public boolean shouldPong(int proposedCard) { + + + // 直接调用hongZhongSuanFaTest中的shouldPong方法,它已经包含了所有需要的规则 + return changShaSuanFaTest.shouldPong(proposedCard, changShaCardInhand); + +// return hongZhongSuanFaTest.shouldPong(proposedCard, Arrays.asList(305,304,303,207,207,204,204,208,208,201,201,412,412)); + } + + + public boolean shouldChow(int proposedCard) { + + return changShaSuanFaTest.shouldChow(proposedCard, changShaCardInhand); + } + + /** + * 初始化手牌协议 811 + * + * @param command 协议号 + * @param message 消息对象 + * @return + */ + + public String cardInHead(String command, Message message, TaurusClient client) { + if (command.equalsIgnoreCase("811")) { + ITObject param = message.param; + if (param == null) { + return null; + } +// {bank_seat=1, laiziCard=0, laiziCard2=0, laiziCard2Before=0, jing=0, laiziCardBefore=0, card_list=[101, 103, 104, 201, 204, 207, 208, 209, 307, 309, 501, 502, 503]} + ITArray cardList = param.getTArray("card_list"); + for (int i = 0; i < cardList.size(); i++) { + changShaCardInhand.add(cardList.getInt(i)); + } +// if (changShaCardInhand.size() > 13) { +// outCard(client); + +// } + + } + return null; + } + + + public static int[][] countTiles(List cardInHand) { + int[][] counts = new int[5][10]; // 类型×值 + + for (Integer card : cardInHand) { + if (card == 0) { + continue; + } + counts[card / 100 - 1][card % 100]++; + } + return counts; + } + + public static 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); + } + int jiangnum = 0; + for (int key : countMap.keySet()) { + if (isJiangPai(key) && countMap.get(key) >= 2) { + jiangnum++; + } + } + return jiangnum; + } + + + /** + * 处理 吃,碰,杠,补,胡 + * @param param + * @param client + * @return + 吃 + [TCP->814] data:{"tip_list":[{"type":1,"id":1,"opcard":[108,109],"weight":1,"card":107}]} + 吃 碰 + {"tip_list":[{"type":1,"id":1,"opcard":[206,205],"weight":1,"card":207},{"type":2,"id":2,"opcard":[207],"weight":2,"card":207}]} + 碰补 + {"tip_list":[{"type":2,"id":1,"opcard":[101],"weight":2,"card":101},{"type":3,"id":2,"opcard":[101],"weight":3,"card":101}]} + 补 + [TCP->814] data:{"tip_list":[{"type":5,"id":1,"opcard":[101],"weight":3,"card":101}]} + 吃,碰,补,杠, + {"tip_list":[{"type":1,"id":1,"opcard":[206,204],"weight":1,"card":205},{"type":1,"id":2,"opcard":[206,207],"weight":1,"card":205},{"type":2,"id":3,"opcard":[205],"weight":2,"card":205},{"type":3,"id":4,"opcard":[205],"weight":3,"card":205},{"type":3,"id":5,"opcard":[205],"weight":4,"card":205}]} + 碰 + {"tip_list":[{"type":2,"id":1,"opcard":[207],"weight":2,"card":207}]} + 补,杠 + {"tip_list":[{"type":5,"id":1,"opcard":[207],"weight":3,"card":207},{"type":5,"id":2,"opcard":[207],"weight":4,"card":207}]} + + //拟定: + 1、对应的提示事件遍历 实行假设做了处理之后的结果 对应结果的对象 + boolean isTing 是否听牌 + boolean canTing 能否听牌 + int tingNum 听多少张 + int isDaHu 是否大胡 + int guzhang 有多少孤章 + int lastHands 差多少手 + int id tiplist id + List opcard + int weight tiplist weight + int card tiplist card + int type tiplist type + 2、通过遍历后的结果进行打分 + 打分系统: + 已经听牌: ---> 40 + 操作后能听 ---> 20 + 操作之后散听 ---> -50 + 听多张,每一张:---> 3 + 孤章,每一张:----> -3 + 差手数:每一手 ----> -5 + 操作之前是否大胡 ----> 10 + 去掉大胡 -----> -10 + */ + public String actionCard(ITObject param, TaurusClient client){ + ITArray tipList = param.getTArray("tip_list"); + ITObject params = TObject.newInstance(); + int card = 0; + + //循环 + List yupanhandcard = new ArrayList<>(); + yupanhandcard.addAll(changShaCardInhand); + //进行操作之前能否下听 + List shifoutingpai = changShaSuanFaTest.handscardshifoutingpai(changShaCardInhand,chowGroup,pongGroup,gangdepai); + boolean beforelisten = false;//记录操作之前的下听状态 + if (shifoutingpai.size() > 0) { + beforelisten = true; + } + + //如果杠了之后还能继续听牌则杠 + //如果下听了,可以杠 + for (int i = 0; i < tipList.size(); i++) { + TObject firstTip = (TObject) tipList.get(i).getObject(); + int type = firstTip.getInt("type"); + int id = firstTip.getInt("id"); + int weight = firstTip.getInt("weight"); + card = firstTip.getInt("card"); + + + if (type == 6){ + params.putString("session", session + "," + token); + params.putInt("qi", 0); + params.putInt("id", id); + client.send("612", params, response -> { + }); + return "胡牌"; + } + + if ((type == 5 || type == 3 || type==4)&&weight==4) { + //判断开杠后是否能下听 + List gangusecars = new ArrayList<>(); + gangusecars.addAll(changShaCardInhand); + + if (type == 3) { + Util.removeCard(gangusecars,card,3); + List shifoutingpai3 = changShaSuanFaTest.handscardshifoutingpai(gangusecars,chowGroup,pongGroup,gangdepai); + System.out.println(shifoutingpai3); + + if (shifoutingpai3.size() > 0) { + //开杠 + //判断是否杠了之后没有牌可以听 + List allList = new ArrayList<>(); + allList.addAll(changShaCardInhand); + allList.addAll(changShachuguopai); + int jutingnum = changShaSuanFaTest.getTingPainum(shifoutingpai3,allList); + if (jutingnum==0){ + params.putString("session", session + "," + token); + params.putInt("qi", 0); + params.putInt("id", 0); + client.send("612", params, response -> { + }); + return "不开杠"; + } + + Util.removeCard(changShaCardInhand,card,3); + params.putString("session", session + "," + token); + params.putInt("qi", 0); + params.putInt("id", id); + client.send("612", params, response -> { + }); + return "开杠"; + }else{ + params.putString("session", session + "," + token); + params.putInt("qi", 0); + params.putInt("id", 0); + client.send("612", params, response -> { + }); + return "不开杠"; + } + } + + if (type == 4) { + + + + // Util.removeCard(changShaCardInhand,card,4); + Util.removeCard(gangusecars,card,4); + List shifoutingpai4 = changShaSuanFaTest.handscardshifoutingpai(gangusecars,chowGroup,pongGroup,gangdepai); + if (shifoutingpai4.size() > 0) { + //开杠 + + //判断是否杠了之后没有牌可以听 + List allList = new ArrayList<>(); + allList.addAll(changShaCardInhand); + allList.addAll(changShachuguopai); + int jutingnum = changShaSuanFaTest.getTingPainum(shifoutingpai4,allList); + if (jutingnum==0){ + params.putString("session", session + "," + token); + params.putInt("qi", 0); + params.putInt("id", 0); + client.send("612", params, response -> { + }); + return "不开杠"; + } + + Util.removeCard(changShaCardInhand,card,4); + params.putString("session", session + "," + token); + params.putInt("qi", 0); + params.putInt("id", id); + client.send("612", params, response -> { + }); + return "开杠"; + }else{ + params.putString("session", session + "," + token); + params.putInt("qi", 0); + params.putInt("id", 0); + client.send("612", params, response -> { + }); + return "不开杠"; + } + } + if (type == 5) { + //Util.removeCard(changShaCardInhand,card,1); + + Util.removeCard(gangusecars,card,1); + List shifoutingpai5 = changShaSuanFaTest.handscardshifoutingpai(gangusecars,chowGroup,pongGroup,gangdepai); + if (shifoutingpai5.size() > 0) { + //开杠 + + //判断是否杠了之后没有牌可以听 + List allList = new ArrayList<>(); + allList.addAll(changShaCardInhand); + allList.addAll(changShachuguopai); + int jutingnum = changShaSuanFaTest.getTingPainum(shifoutingpai5,allList); + if (jutingnum==0){ + params.putString("session", session + "," + token); + params.putInt("qi", 0); + params.putInt("id", 0); + client.send("612", params, response -> { + }); + return "不开杠"; + } + + Util.removeCard(changShaCardInhand,card,1); + params.putString("session", session + "," + token); + params.putInt("qi", 0); + params.putInt("id", id); + client.send("612", params, response -> { + }); + return "开杠"; + }else{ + params.putString("session", session + "," + token); + params.putInt("qi", 0); + params.putInt("id", 0); + client.send("612", params, response -> { + }); + return "不开杠"; + } + } + + + } + } + + //判断是否是大胡 + int beforeIsDahu = 0; + beforeIsDahu = changShaSuanFaTest.checkDahu(yupanhandcard,chowGroup,pongGroup,gangdepai); + //5、门清 + if (yupanhandcard.size()==13&&beforelisten){ + beforeIsDahu = 5;//门清 + } + + //操作之前出现的牌 + List allSeeCard = new ArrayList<>(); + + + Map pingfenResult = new HashMap<>(); + Map idObject = new HashMap<>(); + //int bupaiid = 0;//补牌id + for (int i = 0; i < tipList.size(); i++) { + TObject firstTip = (TObject) tipList.get(i).getObject(); + int type = firstTip.getInt("type"); + int id = firstTip.getInt("id"); + int weight = firstTip.getInt("weight"); + card = firstTip.getInt("card"); + ITArray opcard = TArray.newInstance(); + opcard = firstTip.getTArray("opcard"); + if (type == 6){ + params.putString("session", session + "," + token); + params.putInt("qi", 0); + params.putInt("id", id); + client.send("612", params, response -> { + }); + return "胡牌"; + } + //if((type==4||type==5||type==6)&&weight==3){ + // bupaiid = id; + // } + //对应的数据 + ITObject tmp = new TObject(); + //处理听的结果 + switch (type) { + case 1: + //吃 + tmp = changShaSuanFaTest.pingguChi( beforelisten,card,opcard,yupanhandcard,beforeIsDahu,allSeeCard,chowGroup,pongGroup,gangdepai,changShachuguopai); + break; + case 2: + //碰 + tmp = changShaSuanFaTest.pingguPeng(beforelisten,card,opcard,yupanhandcard,beforeIsDahu,allSeeCard,chowGroup,pongGroup,gangdepai,changShachuguopai); + break; + default: + break; + } + //记录下来事件 + if(tmp.size()>0){ + pingfenResult.put(id,tmp); + } + ITObject sj = new TObject(); + sj.putInt("weight", weight); + sj.putTArray("opcard", opcard); + idObject.put(id,sj); + } + //计算分数 + System.out.println(pingfenResult); + if(pingfenResult.size()>0){ + int changeid=changShaSuanFaTest.suanfen(pingfenResult); + System.out.println("changeid:"+changeid); + //选择最优的分数 + if (changeid==0){ + params.putString("session", session + "," + token); + params.putInt("qi", 0); + params.putInt("id", 0); + client.send("612", params, response -> { + }); + return null; + }else { + //获取 + for(Map.Entry entry : idObject.entrySet()){ + if(entry.getKey()==changeid){ + ITObject tmp = entry.getValue(); + if (tmp.getInt("weight")==2){ + //碰 + ITArray outcards = tmp.getTArray("opcard"); + for (int i = 0; i < outcards.size(); i++) { + Util.removeCard(changShaCardInhand,outcards.getInt(0),2); + } + }else if (tmp.getInt("weight")==1){ + //吃 + ITArray outcards = tmp.getTArray("opcard"); + for (int i = 0; i < outcards.size(); i++) { + Util.removeCard(changShaCardInhand,outcards.getInt(i),1); + } + } + } + } + params.putString("session", session + "," + token); + params.putInt("qi", 0); + params.putInt("id", changeid); + client.send("612", params, response -> { + }); + return null; + } + } + + //计算牌数 + + //如果 不吃,不碰,而且差三手牌情况,则补 + //if (bupaiid>0) { + for (int i = 0; i < tipList.size(); i++) { + TObject firstTip = (TObject) tipList.get(i).getObject(); + int type = firstTip.getInt("type"); + int id = firstTip.getInt("id"); + int weight = firstTip.getInt("weight"); + card = firstTip.getInt("card"); + + Map map = new HashMap<>(); + List tmpChangSch = new ArrayList<>(); + tmpChangSch.addAll(yupanhandcard); + ChangshaWinSplitCard.checkNormalHu(tmpChangSch, map); + System.out.println("map:" + map); + + if (map.size() > 0 && weight==3) { + + if (Integer.parseInt(map.get("remainingMelds").toString()) > 2) { + params.putString("session", session + "," + token); + params.putInt("qi", 0); + params.putInt("id", id); + + if (type == 3) { + Util.removeCard(changShaCardInhand,card,3); + } + if (type == 4) { + Util.removeCard(changShaCardInhand,card,4); + } + if (type == 5) { + Util.removeCard(changShaCardInhand,card,1); + } + client.send("612", params, response -> { + }); + return null; + } else { + params.putString("session", session + "," + token); + params.putInt("qi", 0); + params.putInt("id", 0); + client.send("612", params, response -> { + }); + return null; + } + } + } + params.putString("session", session + "," + token); + params.putInt("qi", 0); + params.putInt("id", 0); + client.send("612", params, response -> { + }); + + return null; + } + + + + + public String actionCardBak2(ITObject param, TaurusClient client) { + ITArray tipList = param.getTArray("tip_list"); + //tipList[{opcard=[207, 208], weight=1, id=1, type=1, card=206}] + boolean chiflag = false;//吃 + boolean pengflag = false; //碰 + boolean bupai = false; //补 + boolean minggang = false; //杠 + boolean angang = false; //暗杠 + boolean penggang = false; //开杠 + + int card = 0;//当前 消息的牌 + ITObject params = TObject.newInstance(); + + for (int i = 0; i < tipList.size(); i++) { + TObject firstTip = (TObject) tipList.get(i).getObject(); + int type = firstTip.getInt("type"); + int id = firstTip.getInt("id"); + int weight = firstTip.getInt("weight"); + card = firstTip.getInt("card"); + if (type == 6){ + params.putString("session", session + "," + token); + params.putInt("qi", 0); + params.putInt("id", id); + client.send("612", params, response -> { + }); + return "胡牌"; + } + if (type == 1){ + chiflag = true; + } + if (type == 2){ + pengflag = true; + } + if (type == 3 && weight ==3){ + //补 + bupai = true; + } + if (type == 3 && weight ==4){ + //杠 + minggang = true; + } + if (type == 5){ + penggang = true; + } + } + + + //如果吃,没有碰 + if (chiflag&&!pengflag){ + chiNoPeng(tipList,card,client); + } + + //如果有吃有碰 + if (chiflag&&pengflag&&!minggang&&!penggang){ + chiOrPeng(tipList,card,client); + } + + //没有吃,有碰 + if (!chiflag&&penggang&&!minggang&&!angang){ + pengNoChi(tipList,card,client); + } + + if (bupai&&!penggang&&!angang){ + //补 + + } + + //杠 + if (minggang||bupai||penggang){ + gangNoChiPeng(tipList,card,client); + } + return null; + } + + + + + /** + * 处理吃 问题 + * @param tipList + * @param client + */ + public int chiNoPeng(ITArray tipList,int card,TaurusClient client){ + ITObject params = TObject.newInstance(); + params.putString("session", session + "," + token); + //判断是否能吃 + //1、吃之后能否下听 + List shifoutingpai = TinHuChi.shifoutingpai(changShaCardInhand); + if (shifoutingpai.size() == 0) { + //没下听 + //如果吃了可以立马下听,则吃 + int chitingpaiid = changShaSuanFaTest.checkChiTingAction(card,changShaCardInhand); + if (chitingpaiid>0){ + //处理吃后的数据 + params.putInt("qi", 0); + params.putInt("id", chitingpaiid-1); + + //记录吃掉的牌 + List> lists = new ArrayList<>(); + lists.addAll(TinHuChi.checkChi(changShaCardInhand, card)); + + List integers = lists.get(chitingpaiid-1); + List result1 = getOtherCards1(integers, card); + + chowGroup.add(card); + chowGroup.add(result1.get(0)); + chowGroup.add(result1.get(1)); + changShaCardInhand.add(card); + Util.removeCard(changShaCardInhand,result1.get(0),1); + Util.removeCard(changShaCardInhand,result1.get(1),1); + Util.removeCard(changShaCardInhand,card,1); + + client.send("612", params, response -> {}); + return chitingpaiid; + } + //2、吃之后是否没了大胡 + //判断是否满足7对 + int pisCardsCount = changShaSuanFaTest.countPairs(changShaCardInhand); + if (pisCardsCount>=4){ + params.putInt("qi", 0); + params.putInt("id", 0); + }else{ + //判断是否可以执行 清一色操作 + boolean isChow = changShaSuanFaTest.checkAllSameSuitAll(card,changShaCardInhand,pongGroup,chowGroup,gangdepai); + if (isChow){ + //判断是否可以吃 + //判断吃之后是否会破坏下听 + //3、吃之后是否破坏牌型 + int canchiId = changShaSuanFaTest.checkCanChiAction(card,changShaCardInhand); + + if (canchiId>0){ + + params.putInt("qi", 0); + params.putInt("id", canchiId-1); + + //记录吃掉的牌 + List> lists = new ArrayList<>(); + lists.addAll(TinHuChi.checkChi(changShaCardInhand, card)); + + List integers2 = lists.get(canchiId-1); + List result2 = getOtherCards1(integers2, card); + + chowGroup.add(card); + chowGroup.add(result2.get(0)); + chowGroup.add(result2.get(1)); + changShaCardInhand.add(card); + Util.removeCard(changShaCardInhand,result2.get(0),1); + Util.removeCard(changShaCardInhand,result2.get(1),1); + Util.removeCard(changShaCardInhand,card,1); + + client.send("612", params, response -> {}); + return chitingpaiid; + + + }else{ + params.putInt("qi", 0); + params.putInt("id", 0); + } + + }else{ + //不可以吃 + params.putInt("qi", 0); + params.putInt("id", 0); + } + } + }else{ + //不吃 + + //吃之后没有下听 + params.putInt("qi", 0); + params.putInt("id", 0); + } + + + + + + + client.send("612", params, response -> {}); + return 0; + } + + /** + * 同时有吃有碰 + * @param tipList + * @param client + */ + public void chiOrPeng(ITArray tipList,int card,TaurusClient client){ + ITObject params = TObject.newInstance(); + //判断碰和吃 + //判断牌型是否下听 + params.putString("session", session + "," + token); + List shifoutingpai = TinHuChi.shifoutingpai(changShaCardInhand); + if (shifoutingpai.size() == 0) { + + //1、先判断是否碰之后破坏听牌 + + + //2、如果能碰,则进入判断吃逻辑,对比吃、碰那个会更优 + + + //3、判断吃或者碰之后都会破坏听牌,不能吃和碰 + + }else{ + //有下听 先不做操作,后续判断换牌 + params.putInt("qi", 0); + params.putInt("id", 0); + } + + + + + + + + + client.send("612", params, response -> {}); + } + + /** + * 没吃,有碰 + * @param tipList + * @param client + */ + public void pengNoChi(ITArray tipList,int card,TaurusClient client){ + ITObject params = TObject.newInstance(); + + client.send("612", params, response -> {}); + } + + /** + * 杠没有吃,没有碰 + * @param tipList + * @param client + */ + public void gangNoChiPeng(ITArray tipList,int card,TaurusClient client){ + ITObject params = TObject.newInstance(); + + + + client.send("612", params, response -> {}); + } + + /** + * 处理杠碰胡操作 + * + * @param param 消息参数 + * @return + */ + public String actionCardbak(ITObject param, TaurusClient client) { + TinHuPeng tinHuPeng = new TinHuPeng(); + + //获取碰杠胡参数 type 和id 后续算法接入,是否能让碰和杠 + ITArray tipList = param.getTArray("tip_list"); + + int id = 0; + int type = 0; + int opcard = 0; + int opcard1 = 0; + int card = 0; + + //胡牌 + ITObject params = TObject.newInstance(); + for (int i = 0; i < tipList.size(); i++) { + TObject firstTip = (TObject) tipList.get(i).getObject(); + id = firstTip.getInt("id"); + type = firstTip.getInt("type"); + + + ITArray opcard2 = firstTip.getTArray("opcard"); + + if (type == 6) { + params.putString("session", session + "," + token); + params.putInt("qi", 0); + params.putInt("id", id); + client.send("612", params, response -> { + + }); + + return "胡牌"; + } + } + + //吃杠 + int type2 = 0; + int opcard3 = 0; + int id3 = 0; + + List gangCardInhand = new ArrayList<>(); + gangCardInhand.addAll(changShaCardInhand); + gangCardInhand.addAll(chowGroup); + gangCardInhand.addAll(pongGroup); + TingPaiChecker.TingResult tingResult = TingPaiChecker.checkTingPai(gangCardInhand); + if (tingResult.isTingPai()) { //听牌状态 + for (int i = 0; i < tipList.size(); i++) { + TObject firstTip = (TObject) tipList.get(i).getObject(); + type2 = firstTip.getInt("type"); + opcard3 = firstTip.getTArray("opcard").getInt(0); + id3 = firstTip.getInt("id"); + + + boolean b = TinHuGang.canGang(gangCardInhand, opcard3, true); //杠牌后是否还能听牌 + + if (type2 == 3) { +// int pisCardsCount = changShaSuanFaTest.countPairs(changShaCardInhand); +// boolean gang = isAllSameSuitgang(changShaCardInhand, opcard3); + if (b) { + System.out.println("听牌 +++进入吃杠===============================------------------------------------------------"); + params.putString("session", session + "," + token); + params.putInt("qi", 0); + params.putInt("id", id3); + Util.removeCard(changShaCardInhand, opcard3, 3); + + client.send("612", params, response -> { + + }); + return "吃杠"; + } else { + params.putString("session", session + "," + token); + params.putInt("qi", 0); + params.putInt("id", 0); + client.send("612", params, response -> { + + }); + + } + } + } + + } else { + for (int i = 0; i < tipList.size(); i++) { + TObject firstTip = (TObject) tipList.get(i).getObject(); + type2 = firstTip.getInt("type"); + opcard3 = firstTip.getTArray("opcard").getInt(0); + id3 = firstTip.getInt("id"); + + if (type2 == 3) { +// int pisCardsCount = changShaSuanFaTest.countPairs(changShaCardInhand); +// boolean gang = isAllSameSuitgang(changShaCardInhand, opcard3); +// if (pisCardsCount < 5 && gang) { //不是七小对和清一色的情况下 才允许杠 +// System.out.println("没有 +++ 听牌 +++进入吃杠===============================------------------------------------------------"); +// +// params.putString("session", session + "," + token); +// params.putInt("qi", 0); +// params.putInt("id", id3); +// Util.removeCard(changShaCardInhand, opcard3, 3); +// +// client.send("612", params, response -> { +// +// }); +// return "吃杠"; +// } else { + params.putString("session", session + "," + token); + params.putInt("qi", 0); + params.putInt("id", 0); + client.send("612", params, response -> { + + }); + + } + } + } + + TingPaiChecker.TingResult tingResult1 = TingPaiChecker.checkTingPai(gangCardInhand); + if (tingResult1.isTingPai()) { + for (int i = 0; i < tipList.size(); i++) { + TObject firstTip = (TObject) tipList.get(i).getObject(); + type2 = firstTip.getInt("type"); + opcard3 = firstTip.getTArray("opcard").getInt(0); + id3 = firstTip.getInt("id"); + + boolean c = TinHuGang.canGang(gangCardInhand, opcard3, false); //杠牌后是否还能听牌 + if (type2 == 4) { + System.out.println("最新自杠 还没进入下面,但是类型为4自杠了==========================="); + if (c) { + System.out.println("听牌 +++进入自杠===============================------------------------------------------------"); + System.out.println("opcard3 ++++++++++++++++++ ++++++++++888 " + opcard3); + System.out.println("id3 ++++++++++++++++++ ++++++++++7777 " + id3); + + params.putString("session", session + "," + token); + params.putInt("qi", 0); + params.putInt("id", id3); + Util.removeCard(changShaCardInhand, opcard3, 4); + client.send("612", params, response -> { + + }); + return "自杠"; + } else { + params.putString("session", session + "," + token); + params.putInt("qi", 0); + params.putInt("id", 0); + client.send("612", params, response -> { + + }); + } + } + } + } else { + for (int i = 0; i < tipList.size(); i++) { + TObject firstTip = (TObject) tipList.get(i).getObject(); + type2 = firstTip.getInt("type"); + + + if (type2 == 4) { + System.out.println("放弃自杠111======================================="); + + params.putString("session", session + "," + token); + params.putInt("qi", 0); + params.putInt("id", 0); + client.send("612", params, response -> { + + }); + + } + } + } + + +// for (int i = 0; i < tipList.size(); i++) { +// TObject firstTip = (TObject) tipList.get(i).getObject(); +// type2 = firstTip.getInt("type"); +// opcard3 = firstTip.getTArray("opcard").getInt(0); +// id3 = firstTip.getInt("id"); +// +// if (type2 == 3) { +// int pisCardsCount = changShaSuanFaTest.countPairs(changShaCardInhand); +// boolean gang = isAllSameSuitgang(changShaCardInhand, opcard3); +// if (pisCardsCount < 5 && gang) { //不是七小对和清一色的情况下 才允许杠 +// params.putString("session", session + "," + token); +// params.putInt("qi", 0); +// params.putInt("id", id3); +// Util.removeCard(changShaCardInhand, opcard3, 3); +// +// client.send("612", params, response -> { +// +// }); +// return "吃杠"; +// } else { +// params.putString("session", session + "," + token); +// params.putInt("qi", 0); +// params.putInt("id", 0); +// client.send("612", params, response -> { +// +// }); +// +// } +// } +// } + + + int id1 = 0; + int type1 = 0; + //吃牌判断 + boolean b = false; + //碰牌判断 + boolean a = false; + if (tipList.size() > 0) { + TObject firstTip = (TObject) tipList.get(0).getObject(); + id1 = firstTip.getInt("id"); + type1 = firstTip.getInt("type"); + + + ITArray opcard2 = firstTip.getTArray("opcard"); + + card = firstTip.getInt("card"); + + b = TinHuChi.canChi(changShaCardInhand, card); + + + List> lists = TinHuChi.checkChi(changShaCardInhand, card); + + + opcard = firstTip.getTArray("opcard").getInt(0); + + + if (type1 == 1) { + opcard1 = firstTip.getTArray("opcard").getInt(1); + } + } + + System.out.println("进入吃type:" + type1); + + if (type1 == 2) { + + boolean isChow = changShaSuanFaTest.isAllSameSuit1(changShaCardInhand); + if (isChow) { + params.putString("session", session + "," + token); + params.putInt("qi", 1); + params.putInt("id", 0); + } else { + List shifoutingpai = TinHuChi.shifoutingpai(changShaCardInhand); + //目前手牌没有听牌 + if (shifoutingpai.size() == 0) { + + //判断是否满足7对 + int pisCardsCount = changShaSuanFaTest.countPairs(changShaCardInhand); + if (pisCardsCount >= 5) { + + params.putString("session", session + "," + token); + params.putInt("qi", 1); + params.putInt("id", 0); + } else { + List temphand = new ArrayList<>(); + temphand.addAll(changShaCardInhand); + temphand.add(opcard); + Util.removeCard(temphand, opcard, 3); + List checktingpai1 = TinHuChi.checktingpai(temphand); + System.out.println("checktingpai1 碰牌" + checktingpai1); + //碰完牌后打牌后可以听牌 + if (checktingpai1.size() > 0) { + System.out.println("===============进入听胡碰牌================== ++++ "); + params.putString("session", session + "," + token); + params.putInt("qi", 0); + params.putInt("id", id1); + + pongGroup.add(opcard); + pongGroup.add(opcard); + pongGroup.add(opcard); + Util.removeCard(changShaCardInhand, opcard, 2); + } else { + //碰之前的map + Map map = new HashMap<>(); + //碰之后的map + Map map2 = new HashMap<>(); + + //先判断碰之前还需要几手牌,以及孤章 +// int jiangnum = checkduijiang(changShaCardInhand); + List tmpChangSch = new ArrayList<>(); + tmpChangSch.addAll(changShaCardInhand); + tmpChangSch.add(opcard); + ChangshaWinSplitCard.checkNormalHu(tmpChangSch, map); + + + //碰之后 + List pengtemphand = new ArrayList<>(); + pengtemphand.addAll(tmpChangSch); + Util.removeCard(pengtemphand, opcard, 3); + + ChangshaWinSplitCard.checkNormalHu(pengtemphand, 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())) { + System.out.println("===============碰之后的手数小于碰之前的手数,可以碰 决定碰牌================== ++++ "); + params.putString("session", session + "," + token); + params.putInt("qi", 0); + params.putInt("id", id1); + + pongGroup.add(opcard); + pongGroup.add(opcard); + pongGroup.add(opcard); + Util.removeCard(changShaCardInhand, opcard, 2); + } else if (Integer.parseInt(map2.get("remainingMelds").toString()) == Integer.parseInt(map.get("remainingMelds").toString())) { //碰完后和碰之前手数相等,需要判断孤章数量 + //碰之后的数量 + int size2 = ((List) map2.get("cardResiue")).size(); + System.out.println("碰之后的孤章数量 size" + size2); + + int size1 = ((List) map.get("cardResiue")).size(); + System.out.println("碰之前的孤章数量 size" + size1); + if (size2 < size1) { + System.out.println("===============碰之后的孤章数量 小于 碰之前的孤章数量可以碰 ================== ++++ "); + params.putString("session", session + "," + token); + params.putInt("qi", 0); + params.putInt("id", id1); + + pongGroup.add(opcard); + pongGroup.add(opcard); + pongGroup.add(opcard); + Util.removeCard(changShaCardInhand, opcard, 2); + } + //碰之后的手数大于碰之前的手数,或者碰之后的孤章数量大于碰之前的孤章数量不能碰 + } else if (Integer.parseInt(map2.get("remainingMelds").toString()) > Integer.parseInt(map.get("remainingMelds").toString()) || ((List) map2.get("cardResiue")).size() > ((List) map.get("cardResiue")).size()) { + params.putString("session", session + "," + token); + params.putInt("qi", 1); + params.putInt("id", 0); + } + } + } + } + + //听牌的时候先不碰 + if (shifoutingpai.size() > 0) { + System.out.println("听牌的时候先不碰++++++++++++++++++++++++++++++++"); + params.putString("session", session + "," + token); + params.putInt("qi", 1); + params.putInt("id", 0); + } + } + + +// a = tinHuPeng.canPeng(changShaCardInhand, opcard); +// ChangShaSuanFaTest changShaSuanFaTest = new ChangShaSuanFaTest(); +// int pisCardsCount = changShaSuanFaTest.countPairs(changShaCardInhand);//分析七小对 +// boolean peng = changShaSuanFaTest.isAllSameSuit1(changShaCardInhand); +// if (a && !ChangShaSuanFaTest.isTin) { +// System.out.println("===============进入听胡碰牌==============================="); +// +// params.putString("session", session + "," + token); +// params.putInt("qi", 0); +// params.putInt("id", id1); +// pongGroup.add(opcard); +// pongGroup.add(opcard); +// pongGroup.add(opcard); +// Util.removeCard(changShaCardInhand, opcard, 2); +// +// } else { +// // 根据规则判断是否应该碰牌 +// if (shouldPong(opcard) && pisCardsCount < 5 && !peng && !isTinPeng) { +// params.putString("session", session + "," + token); +// params.putInt("qi", 0); +// params.putInt("id", id1); +// pongGroup.add(opcard); +// pongGroup.add(opcard); +// pongGroup.add(opcard); +// Util.removeCard(changShaCardInhand, opcard, 2); +// } else { +// params.putString("session", session + "," + token); +// params.putInt("qi", 1); // 放弃碰牌 +// params.putInt("id", 0); +// +// } +// } + + } else if (type1 == 3) { + + int pisCardsCount = changShaSuanFaTest.countPairs(changShaCardInhand);//分析七小对 + boolean gang = changShaSuanFaTest.isAllSameSuit1(changShaCardInhand); + if (pisCardsCount < 5 && !gang) { + params.putString("session", session + "," + token); + params.putInt("qi", 0); + params.putInt("id", id1); + Util.removeCard(changShaCardInhand, opcard, 3); + + } else { + params.putString("session", session + "," + token); + params.putInt("qi", 0); + params.putInt("id", 0); + + } + + } else if (type1 == 1) { + +// int pisCardsCount = changShaSuanFaTest.countPairs(changShaCardInhand);//分析七小对 + boolean isChow = changShaSuanFaTest.isAllSameSuit1(changShaCardInhand); + if (isChow) { + params.putString("session", session + "," + token); + params.putInt("qi", 1); + params.putInt("id", 0); + } else { + + //判断当前手牌是否已经听牌 已经听牌了就不要再吃牌了 + List shifoutingpai = TinHuChi.shifoutingpai(changShaCardInhand); + + //目前手牌没有听牌 + if (shifoutingpai.size() == 0) { + + //判断是否满足7对 + int pisCardsCount = changShaSuanFaTest.countPairs(changShaCardInhand); + if (pisCardsCount >= 5) { + + params.putString("session", session + "," + token); + params.putInt("qi", 1); + params.putInt("id", 0); + + } else { + + 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) { + System.out.println("===============进入听胡吃牌================== ++++ " + flag); + params.putString("session", session + "," + token); + params.putInt("qi", 0); + params.putInt("id", flag); + + int index1 = flag - 1; + List integers = lists.get(index1); + List result1 = getOtherCards1(integers, card); + + chowGroup.add(card); + chowGroup.add(result1.get(0)); + chowGroup.add(result1.get(1)); + changShaCardInhand.add(card); + + Util.removeCard(changShaCardInhand, integers.get(0), 1); + Util.removeCard(changShaCardInhand, integers.get(1), 1); + Util.removeCard(changShaCardInhand, integers.get(2), 1); + } else { + 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) { + System.out.println("flag1:" + flag1); + params.putString("session", session + "," + token); + params.putInt("qi", 0); + params.putInt("id", flag1); + + int index2 = flag1 - 1; + + if (index2 >= lists.size()) { + index2 = 0; + } + System.out.println("index2:" + index2); + System.out.println("lists:" + lists); + List integers = new ArrayList<>(); + integers.addAll(lists.get(index2)); + List result2 = getOtherCards1(integers, card); + + chowGroup.add(card); + chowGroup.add(result2.get(0)); + chowGroup.add(result2.get(1)); + changShaCardInhand.add(card); + + Util.removeCard(changShaCardInhand, integers.get(0), 1); + Util.removeCard(changShaCardInhand, integers.get(1), 1); + Util.removeCard(changShaCardInhand, integers.get(2), 1); + + } else { + params.putString("session", session + "," + token); + params.putInt("qi", 1); + params.putInt("id", 0); + } + } + } + } + + + //听牌的时候先不吃 + if (shifoutingpai.size() > 0) { + System.out.println("听牌的时候先不吃++++++++++++++++++++++++++++++++"); + params.putString("session", session + "," + token); + params.putInt("qi", 1); + params.putInt("id", 0); + } + + +// if (b && TinHuChi.isMoreThanLast() && !ChangShaSuanFaTest.isTin) { +// +// System.out.println("===============进入听胡吃牌=================="); +// params.putString("session", session + "," + token); +// params.putInt("qi", 0); +// params.putInt("id", 1); +// +// chowGroup.add(card); +// chowGroup.add(opcard); +// chowGroup.add(opcard1); +//// changShachuguopai.add(card); +// Util.removeCard(changShaCardInhand, opcard, 1); +// Util.removeCard(changShaCardInhand, opcard1, 1); +//// }else { +//// params.putString("session", session + "," + token); +//// params.putInt("qi", 1); +//// params.putInt("id", 0); +//// } +// } else { +// System.out.println("isTin ++++++++++++++++++++++++++++++++++++" + isTinChi); +// if (shouldChow(card) && pisCardsCount < 5 && !isChow && !isTinChi) { +// params.putString("session", session + "," + token); +// params.putInt("qi", 0); +// params.putInt("id", 1); +// chowGroup.add(card); +// chowGroup.add(opcard); +// chowGroup.add(opcard1); +// +// Util.removeCard(changShaCardInhand, opcard, 1); +// Util.removeCard(changShaCardInhand, opcard1, 1); +// } else { +// params.putString("session", session + "," + token); +// params.putInt("qi", 1); +// params.putInt("id", 0); +// } +// } + + + //自杠 +// } else if (type1 == 4) { +//// TingPaiChecker.TingResult tingResult1 = TingPaiChecker.checkTingPai(gangCardInhand); +// +// if (tingResult1.isTingPai()) { +// boolean c = TinHuGang.canGang(gangCardInhand, opcard, false); //杠牌后是否还能听牌 +// if (c) { +// System.out.println("听牌 +++进入自杠===============================------------------------------------------------"); +// +// params.putString("session", session + "," + token); +// params.putInt("qi", 0); +// params.putInt("id", 1); +// Util.removeCard(changShaCardInhand, opcard, 4); +// +// } else { +// params.putString("session", session + "," + token); +// params.putInt("qi", 0); +// params.putInt("id", 0); +// } +// } else { +//// int pisCardsCount = changShaSuanFaTest.countPairs(changShaCardInhand);//分析七小对 +//// boolean gang = changShaSuanFaTest.isAllSameSuit1(changShaCardInhand); +//// if (pisCardsCount < 5 && !gang) { +// System.out.println("没有 +++ 听牌 +++进入自杠===============================------------------------------------------------"); +//// +//// params.putString("session", session + "," + token); +//// params.putInt("qi", 0); +//// params.putInt("id", 1); +//// Util.removeCard(changShaCardInhand, opcard, 4); +//// } else { +// params.putString("session", session + "," + token); +// params.putInt("qi", 0); +// params.putInt("id", 0); +//// } +// +// } +// +// +// // 碰后补杠 +// } + } + } else if (type1 == 5) { + params.putString("session", session + "," + token); + params.putInt("qi", 0); + params.putInt("id", 0); +// Util.removeCard(changShaCardInhand, opcard, 1); +// } + } + + client.send("612", params, response -> { + + }); + return null; + } + + public static List getOtherCards1(List integers, int card) { + List result = new ArrayList<>(); + for (Integer num : integers) { + if (num != card) { + result.add(num); + } + } + return result; + } + + public static boolean isAllSameSuitgang(List handCards, int opcard3) { + // 统计各花色的牌数量 + Map suitCountMap = new HashMap<>(); + + for (Integer card : handCards) { + 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 >= 9) { + // 检查杠牌的花色是否与当前花色一致 + int gangSuit = opcard3 / 100; + if (suit == gangSuit) { + return true; + } else { + return false; + } + } + } + return true; + } + + public static String changePlayer(String command, Message message) { + if (command.equalsIgnoreCase("820")) { + ITObject param = message.param; + if (param == null) { + return null; + } + + } + return null; + } + + + /** + * 出牌方法 + */ +// public String outCard(TaurusClient client, List list) { + public String outCard(TaurusClient client, Map> playerOutcardsMap, Map> playerchisMap, Map> playerpengsMap, Map> playermingsMap, Map> playerzisMap) { + // playerOutcardsMap + playerchisMap 传到 outCardSuanFa + //对出牌进行整合 + + List resultList = new ArrayList<>(); + for (List cards : playerOutcardsMap.values()) { + resultList.addAll(cards); + } + + for (List chis : playerchisMap.values()) { + resultList.addAll(chis); + } + + for (List pengs : playerpengsMap.values()) { + resultList.addAll(pengs); + } + + for (List minggangs : playermingsMap.values()) { + resultList.addAll(minggangs); + } + + for (List zigang : playerzisMap.values()) { + resultList.addAll(zigang); + } + + + // 长沙麻将出牌 + String changShaOutCard = changShaSuanFaTest.outCardSuanFa(changShaCardInhand, pongGroup, chowGroup,gangdepai, resultList); +// String changShaOutCard = changShaSuanFaTest.outCardSuanFa(list, changShaCard,pongGroup); + ITObject params = TObject.newInstance(); + int cardToOut; + if (StringUtil.isNotEmpty(changShaOutCard)) { + cardToOut = Integer.parseInt(changShaOutCard); + } else { + cardToOut = changShaCardInhand.get(0); + } + + params.putInt("card", cardToOut); + + + int outCountBefore = changShachuguopai.size(); // 当前历史出牌数量 + + // 第n次出牌时,发送前n-1张出牌 + if (outCountBefore >= 1) { + // 发送前n-1张(所有历史出牌) + List cardsToSend = changShachuguopai.subList(0, outCountBefore); + params.putTArray("outcard_list", CardUtil.maJiangToTArray(cardsToSend)); + } + params.putTArray("card_list", CardUtil.maJiangToTArray(changShaCardInhand)); + + // 将当前出的牌添加到历史出牌列表 + changShachuguopai.add(cardToOut); + // 从手牌中移除 + changShaCardInhand.remove(Integer.valueOf(cardToOut)); + + + params.putString("session", session + "," + token); + client.send("611", params, response -> { + + }); + return null; + } + + /** + * 删除出过的牌组 + * + * @param param + * @return + */ + public String shanchuchuguopai(ITObject param) { + if (param == null) { + return null; + } + + Integer card = param.getInt("card"); // 操作牌值 + Integer type = param.getInt("type"); // 操作类型 + Integer from_seat = param.getInt("from_seat"); // 牌来源座位 + + + Integer playerid = param.getInt("playerid"); + + + String sql2 = String.format("SELECT id FROM `account` WHERE jiqiren=9998"); + try { + ITArray robotId2 = DataBase.use().executeQueryByTArray(sql2); + List robotIdsList = new ArrayList<>(); + + for (int j = 0; j < robotId2.size(); j++) { + robotIdsList.add(robotId2.getTObject(j).getInt("id")); + } + + if (!robotIdsList.contains(playerid)) { + if (type == 2 || type == 3 || type == 5 || type == 1) { // 碰,杠 + getChuGuoCardInhand().remove(Integer.valueOf(card)); + } + } + + } catch (SQLException e) { + e.printStackTrace(); + } + + return null; + } + + public static void main(String[] args) { + HuNanChangSha huNanChangSha = new HuNanChangSha(); + ITObject params = TObject.newInstance(); + TaurusClient tc = new TaurusClient("127.0.0.1","10", TaurusClient.ConnectionProtocol.Tcp); + List hands1 = new ArrayList<>(); + hands1.add(209); + hands1.add(209); + hands1.add(207); + hands1.add(206); + + hands1.add(205); + hands1.add(109); + hands1.add(109); + + hands1.add(107); + hands1.add(106); + hands1.add(105); + + hands1.add(203); + hands1.add(202); + hands1.add(201); + + int card= 109; + huNanChangSha.changShaCardInhand.addAll(hands1); + TArray tiplist= new TArray(); + ITArray opcard = TArray.newInstance(); + opcard.addInt(207); + opcard.addInt(206); + + // opcard.addInt(203); + TObject tob = new TObject(); + tob.putInt("weight",1); + tob.putInt("id",1); + tob.putInt("type",1); + tob.putInt("card",card); + tob.putTArray("opcard", opcard); + //tiplist.addTObject(tob); + + ITArray opcard2 = TArray.newInstance(); + opcard2.addInt(109); + // opcard2.addInt(206); + TObject tob2 = new TObject(); + tob2.putInt("weight",1); + tob2.putInt("id",1); + tob2.putInt("type",2); + tob2.putInt("card",card); + tob2.putTArray("opcard", opcard2); + tiplist.addTObject(tob2); + + ITArray opcard3 = TArray.newInstance(); + opcard2.addInt(202); + // opcard2.addInt(206); + TObject tob3 = new TObject(); + tob3.putInt("weight",3); + tob3.putInt("id",2); + tob3.putInt("type",3); + tob3.putInt("card",card); + tob3.putTArray("opcard", opcard2); + // tiplist.addTObject(tob3); + + params.putTArray("tip_list",tiplist); + System.out.println(params); + //已经吃掉数据 + // huNanChangSha.chowGroup.add(205); + //huNanChangSha.chowGroup.add(204); + // huNanChangSha.chowGroup.add(206); + + //huNanChangSha.chowGroup.add(201); + // huNanChangSha.chowGroup.add(202); + // huNanChangSha.chowGroup.add(203); + + String res = huNanChangSha.actionCard(params,tc); + System.out.println(res); + } + + +} diff --git a/robots/majiang/robot_mj_cs/src/main/java/robot/mj/handler/HuNanChangShaHandler.java b/robots/majiang/robot_mj_cs/src/main/java/robot/mj/handler/HuNanChangShaHandler.java deleted file mode 100644 index e8dbb4c..0000000 --- a/robots/majiang/robot_mj_cs/src/main/java/robot/mj/handler/HuNanChangShaHandler.java +++ /dev/null @@ -1,582 +0,0 @@ -package robot.mj.handler; - -import com.robot.Util; -import com.taurus.core.entity.ITArray; -import com.taurus.core.entity.ITObject; -import com.taurus.core.entity.TObject; -import com.taurus.core.plugin.database.DataBase; -import com.taurus.core.util.Logger; -import taurus.client.Message; -import taurus.client.TaurusClient; -import taurus.util.CardUtil; -import taurus.util.ChangShaSuanFaTest; -import taurus.util.ChangshaWinSplitCard; - -import java.sql.SQLException; -import java.util.*; - -public class HuNanChangShaHandler { - - public static int changShaCard = 0; - public static boolean isTinChi = false; - public static boolean isTinPeng = false; - - private static final Logger log = Logger.getLogger(HuNanChangShaHandler.class); - - - //湖南长沙麻将手牌 - private final List changShaCardInhand = new ArrayList<>(); - - private final List changShaCardInhandgang = new ArrayList<>(); - - //长沙麻将出过的牌 - private final List changShachuguopai = new ArrayList<>(); - - private final Map chuGuoPainum = new HashMap<>(); - - //杠的牌 - private final List gangdepai = new ArrayList<>(); - - //碰牌 - private final List pongGroup = new ArrayList<>(); - - //吃牌 - private final List chowGroup = new ArrayList<>(); - - //玩家座位号 - public static int seat = 0; - - public static int playerId = 0; - - public static int cardToOut1 = 0; - - //会话标识 - public static String session = ""; - //访问令牌 - public static String token = ""; - - private static final ChangShaSuanFaTest changShaSuanFaTest = new ChangShaSuanFaTest(); - - - //公共的getter和setter方法 - public List getChangShaCardInhand() { - return changShaCardInhand; - } - - public List getGangdepai() { - return gangdepai; - } - - public List getChangShaCardInhandgang() { - return changShaCardInhandgang; - } - - public List getPongGroup() { - return pongGroup; - } - - public List getChowGroup() { - return chowGroup; - } - - public List getChuGuoCardInhand() { - return changShachuguopai; - } - - public Map getChuGuoPainum() { - return chuGuoPainum; - } - - /** - * 设置会话和令牌 - */ - public void setSessionAndToken(String session, String token) { - HuNanChangShaHandler.session = session; - HuNanChangShaHandler.token = token; - } - - /** - * 出牌广播协议 812 - * - * @param command 协议号 - * @param message 消息对象 - */ - public static void drawCard(String command, Message message) { - if (command.equalsIgnoreCase("812")) { - ITObject param = message.param; - if (param == null) { - return; - } - changShaCard = param.getInt("card"); - - } - } - - /** - * 摸牌协议 819 - * - * @param command 协议号 - * @param message 消息对象 - */ - public void getCard(String command, Message message) { - if (command.equalsIgnoreCase("819")) { - ITObject param = message.param; - if (param == null) { - return; - } - - if (param.getInt("player") != null) { - int drawnCard = param.getInt("card"); - changShaSuanFaTest.drawnCards = drawnCard; - changShaCardInhand.add(drawnCard); - changShaSuanFaTest.analyzeHand(changShaCardInhand); - } - } - } - - /** - * 初始化手牌协议 811 - * - * @param command 协议号 - * @param message 消息对象 - */ - public void cardInHead(String command, Message message, TaurusClient client) { - if (command.equalsIgnoreCase("811")) { - ITObject param = message.param; - if (param == null) { - return; - } - - ITArray cardList = param.getTArray("card_list"); - changShaCardInhand.clear(); - for (int i = 0; i < cardList.size(); i++) { - changShaCardInhand.add(cardList.getInt(i)); - } - } - } - - /** - * 处理 吃,碰,杠,补,胡 - */ - public void actionCard(ITObject param, TaurusClient client) { - ITArray tipList = param.getTArray("tip_list"); - log.info("tipList" +tipList); - ITObject params = TObject.newInstance(); - int card = 0; - - //循环 - List yupanhandcard = new ArrayList<>(changShaCardInhand); - //进行操作之前能否下听 - List shifoutingpai = ChangShaSuanFaTest.handscardshifoutingpai(changShaCardInhand,chowGroup,pongGroup,gangdepai); - //记录操作之前的下听状态 - boolean beforelisten = false; - if (!shifoutingpai.isEmpty()) { - beforelisten = true; - } - - //如果杠了之后还能继续听牌则杠 - //如果下听了,可以杠 - for (int i = 0; i < tipList.size(); i++) { - TObject firstTip = (TObject) tipList.get(i).getObject(); - int type = firstTip.getInt("type"); - int id = firstTip.getInt("id"); - int weight = firstTip.getInt("weight"); - card = firstTip.getInt("card"); - - - if (type == 6){ - params.putString("session", session + "," + token); - params.putInt("qi", 0); - params.putInt("id", id); - client.send("612", params, response -> { - }); - return; - } - - if ((type == 5 || type == 3 || type==4)&&weight==4) { - //判断开杠后是否能下听 - List gangusecars = new ArrayList<>(changShaCardInhand); - - if (type == 3) { - Util.removeCard(gangusecars,card,3); - List shifoutingpai3 = ChangShaSuanFaTest.handscardshifoutingpai(gangusecars,chowGroup,pongGroup,gangdepai); - log.info(shifoutingpai3); - - if (!shifoutingpai3.isEmpty()) { - //开杠 - //判断是否杠了之后没有牌可以听 - List allList = new ArrayList<>(); - allList.addAll(changShaCardInhand); - allList.addAll(changShachuguopai); - int jutingnum = ChangShaSuanFaTest.getTingPainum(shifoutingpai3,allList); - if (jutingnum==0){ - params.putString("session", session + "," + token); - params.putInt("qi", 0); - params.putInt("id", 0); - client.send("612", params, response -> { - }); - return; - } - - Util.removeCard(changShaCardInhand,card,3); - params.putString("session", session + "," + token); - params.putInt("qi", 0); - params.putInt("id", id); - client.send("612", params, response -> { - }); - return; - }else{ - params.putString("session", session + "," + token); - params.putInt("qi", 0); - params.putInt("id", 0); - client.send("612", params, response -> { - }); - return; - } - } - if (type == 4) { - Util.removeCard(gangusecars,card,4); - List shifoutingpai4 = ChangShaSuanFaTest.handscardshifoutingpai(gangusecars,chowGroup,pongGroup,gangdepai); - if (!shifoutingpai4.isEmpty()) { - //开杠 - //判断是否杠了之后没有牌可以听 - List allList = new ArrayList<>(); - allList.addAll(changShaCardInhand); - allList.addAll(changShachuguopai); - int jutingnum = ChangShaSuanFaTest.getTingPainum(shifoutingpai4,allList); - if (jutingnum==0){ - params.putString("session", session + "," + token); - params.putInt("qi", 0); - params.putInt("id", 0); - client.send("612", params, response -> { - }); - return; - } - - Util.removeCard(changShaCardInhand,card,4); - params.putString("session", session + "," + token); - params.putInt("qi", 0); - params.putInt("id", id); - client.send("612", params, response -> { - }); - return; - }else{ - params.putString("session", session + "," + token); - params.putInt("qi", 0); - params.putInt("id", 0); - client.send("612", params, response -> { - }); - return; - } - } - if (type == 5) { - - Util.removeCard(gangusecars,card,1); - List shifoutingpai5 = ChangShaSuanFaTest.handscardshifoutingpai(gangusecars,chowGroup,pongGroup,gangdepai); - if (!shifoutingpai5.isEmpty()) { - //开杠 - //判断是否杠了之后没有牌可以听 - List allList = new ArrayList<>(); - allList.addAll(changShaCardInhand); - allList.addAll(changShachuguopai); - int jutingnum = ChangShaSuanFaTest.getTingPainum(shifoutingpai5,allList); - if (jutingnum==0){ - params.putString("session", session + "," + token); - params.putInt("qi", 0); - params.putInt("id", 0); - client.send("612", params, response -> { - }); - return; - } - - Util.removeCard(changShaCardInhand,card,1); - params.putString("session", session + "," + token); - params.putInt("qi", 0); - params.putInt("id", id); - client.send("612", params, response -> { - }); - return; - }else{ - params.putString("session", session + "," + token); - params.putInt("qi", 0); - params.putInt("id", 0); - client.send("612", params, response -> { - }); - return; - } - } - - - } - } - - //判断是否是大胡 - int beforeIsDahu = 0; - beforeIsDahu = ChangShaSuanFaTest.checkDahu(yupanhandcard,chowGroup,pongGroup,gangdepai); - //5、门清 - if (yupanhandcard.size()==13&&beforelisten){ - beforeIsDahu = 5;//门清 - } - - //操作之前出现的牌 - List allSeeCard = new ArrayList<>(); - - Map pingfenResult = new HashMap<>(); - Map idObject = new HashMap<>(); - for (int i = 0; i < tipList.size(); i++) { - TObject firstTip = (TObject) tipList.get(i).getObject(); - int type = firstTip.getInt("type"); - int id = firstTip.getInt("id"); - int weight = firstTip.getInt("weight"); - card = firstTip.getInt("card"); - ITArray opcard = firstTip.getTArray("opcard"); - if (type == 6){ - params.putString("session", session + "," + token); - params.putInt("qi", 0); - params.putInt("id", id); - client.send("612", params, response -> { - }); - return; - } - //对应的数据 - ITObject tmp = new TObject(); - //处理听的结果 - switch (type) { - case 1: - //吃 - tmp = ChangShaSuanFaTest.pingguChi( beforelisten,card,opcard,yupanhandcard,beforeIsDahu,allSeeCard,chowGroup,pongGroup,gangdepai,changShachuguopai); - break; - case 2: - //碰 - tmp = ChangShaSuanFaTest.pingguPeng(beforelisten,card,opcard,yupanhandcard,beforeIsDahu,allSeeCard,chowGroup,pongGroup,gangdepai,changShachuguopai); - break; - default: - break; - } - //记录下来事件 - if(tmp.size()>0){ - pingfenResult.put(id,tmp); - } - ITObject sj = new TObject(); - sj.putInt("weight", weight); - sj.putTArray("opcard", opcard); - idObject.put(id,sj); - } - //计算分数 - log.info(pingfenResult); - if(!pingfenResult.isEmpty()){ - int changeid= ChangShaSuanFaTest.suanfen(pingfenResult); - log.info("changeid:"+changeid); - //选择最优的分数 - if (changeid==0){ - params.putString("session", session + "," + token); - params.putInt("qi", 0); - params.putInt("id", 0); - client.send("612", params, response -> { - }); - return; - }else { - //获取 - for(Map.Entry entry : idObject.entrySet()){ - if(entry.getKey()==changeid){ - ITObject tmp = entry.getValue(); - if (tmp.getInt("weight")==2){ - //碰 - ITArray outcards = tmp.getTArray("opcard"); - for (int i = 0; i < outcards.size(); i++) { - Util.removeCard(changShaCardInhand,outcards.getInt(0),2); - } - }else if (tmp.getInt("weight")==1){ - //吃 - ITArray outcards = tmp.getTArray("opcard"); - for (int i = 0; i < outcards.size(); i++) { - Util.removeCard(changShaCardInhand,outcards.getInt(i),1); - } - } - } - } - params.putString("session", session + "," + token); - params.putInt("qi", 0); - params.putInt("id", changeid); - client.send("612", params, response -> { - }); - return; - } - } - - //计算牌数 - - //如果 不吃,不碰,而且差三手牌情况,则补 - for (int i = 0; i < tipList.size(); i++) { - TObject firstTip = (TObject) tipList.get(i).getObject(); - int type = firstTip.getInt("type"); - int id = firstTip.getInt("id"); - int weight = firstTip.getInt("weight"); - card = firstTip.getInt("card"); - - Map map = new HashMap<>(); - List tmpChangSch = new ArrayList<>(yupanhandcard); - ChangshaWinSplitCard.checkNormalHu(tmpChangSch, map); - System.out.println("map:" + map); - - if (!map.isEmpty() && weight==3) { - - if (Integer.parseInt(map.get("remainingMelds").toString()) > 2) { - params.putString("session", session + "," + token); - params.putInt("qi", 0); - params.putInt("id", id); - - if (type == 3) { - Util.removeCard(changShaCardInhand,card,3); - } - if (type == 4) { - Util.removeCard(changShaCardInhand,card,4); - } - if (type == 5) { - Util.removeCard(changShaCardInhand,card,1); - } - client.send("612", params, response -> { - }); - return; - } else { - params.putString("session", session + "," + token); - params.putInt("qi", 0); - params.putInt("id", 0); - client.send("612", params, response -> { - }); - return; - } - } - } - params.putString("session", session + "," + token); - params.putInt("qi", 0); - params.putInt("id", 0); - client.send("612", params, response -> { - }); - } - - - /** - * 出牌方法 - */ - public void outCard(TaurusClient client, Map> playerOutcardsMap, - Map> playerchisMap, Map> playerpengsMap, - Map> playermingsMap, Map> playerzisMap) { - //整合所有其他玩家的出牌信息 - List resultList = new ArrayList<>(); - for (List cards : playerOutcardsMap.values()) { - resultList.addAll(cards); - } - - for (List chis : playerchisMap.values()) { - resultList.addAll(chis); - } - - for (List pengs : playerpengsMap.values()) { - resultList.addAll(pengs); - } - - for (List minggangs : playermingsMap.values()) { - resultList.addAll(minggangs); - } - - for (List zigang : playerzisMap.values()) { - resultList.addAll(zigang); - } - - ITObject params = TObject.newInstance(); - int cardToOut; - - if (!changShaCardInhand.isEmpty()) { - //使用长沙麻将算法计算最优出牌 - ChangShaSuanFaTest changShaSuanFaTest = new ChangShaSuanFaTest(); - String cardStr = changShaSuanFaTest.outCardSuanFa(changShaCardInhand, pongGroup, chowGroup, gangdepai, resultList); - try { - cardToOut = Integer.parseInt(cardStr); - } catch (NumberFormatException e) { - //如果算法出错,使用第一张牌 - cardToOut = changShaCardInhand.get(0); - } - } else { - return; //没有牌可出 - } - - params.putInt("card", cardToOut); - - int outCountBefore = changShachuguopai.size(); //当前历史出牌数量 - - //第n次出牌时,发送前n-1张出牌 - if (outCountBefore >= 1) { - //发送前n-1张(所有历史出牌) - List cardsToSend = changShachuguopai.subList(0, outCountBefore); - params.putTArray("outcard_list", CardUtil.maJiangToTArray(cardsToSend)); - } - params.putTArray("card_list", CardUtil.maJiangToTArray(changShaCardInhand)); - - - //将当前出的牌添加到历史出牌列表 - changShachuguopai.add(cardToOut); - //从手牌中移除 - changShaCardInhand.remove(Integer.valueOf(cardToOut)); - - params.putString("session", session + "," + token); - client.send("611", params, response -> { - }); - - } - - - /** - * 删除出过的牌组 - * - */ - public void shanchuchuguopai(ITObject param) { - if (param == null) { - return; - } - - Integer card = param.getInt("card"); //操作牌值 - Integer type = param.getInt("type"); //操作类型 - Integer from_seat = param.getInt("from_seat"); //牌来源座位 - - - Integer playerid = param.getInt("playerid"); - - - String sql2 = "SELECT id FROM `account` WHERE jiqiren=9998"; - try { - ITArray robotId2 = DataBase.use().executeQueryByTArray(sql2); - List robotIdsList = new ArrayList<>(); - - for (int j = 0; j < robotId2.size(); j++) { - robotIdsList.add(robotId2.getTObject(j).getInt("id")); - } - - if (!robotIdsList.contains(playerid)) { - if (type == 2 || type == 3 || type == 5 || type == 1) { //碰,杠 - getChuGuoCardInhand().remove(card); - } - } - - } catch (SQLException e) { - e.printStackTrace(); - } - } - - /** - * 位置转换协议 820 - * - * @param command 协议号 - * @param message 消息对象 - */ - public static void changePlayer(String command, Message message) { - if (command.equalsIgnoreCase("820")) { - ITObject param = message.param; - if (param == null) { - return; - } - - seat = param.getInt("seat"); - } - } -} \ No newline at end of file diff --git a/robots/majiang/robot_mj_cs/src/main/java/taurus/util/TinHuChi.java b/robots/majiang/robot_mj_cs/src/main/java/taurus/util/TinHuChi.java index 745d400..ff64360 100644 --- a/robots/majiang/robot_mj_cs/src/main/java/taurus/util/TinHuChi.java +++ b/robots/majiang/robot_mj_cs/src/main/java/taurus/util/TinHuChi.java @@ -1,7 +1,7 @@ package taurus.util; import com.robot.Util; -import robot.mj.handler.HuNanChangShaHandler; +import robot.mj.handler.HuNanChangSha; import java.util.*; @@ -55,14 +55,14 @@ public class TinHuChi { // // 测试1:你的例子 List hand1 = new ArrayList<>(); - // hand1.add(209); + // hand1.add(209); hand1.add(208); hand1.add(204); - // hand1.add(204); + // hand1.add(204); hand1.add(203); hand1.add(203); - // hand1.add(202); + // hand1.add(202); hand1.add(201); hand1.add(108); @@ -72,7 +72,7 @@ public class TinHuChi { hand1.add(104); hand1.add(104); hand1.add(103); - // hand1.add(102); + // hand1.add(102); int addcard =203; @@ -87,8 +87,8 @@ public class TinHuChi { tmpChangSch.addAll(hand1); tmpChangSch.add(addcard); ChangshaWinSplitCard.checkNormalHu(tmpChangSch, map); - System.out.println("checkNormalHu 孤章数量 " + ((List) map.get("cardResiue")).size()); - System.out.println("checkNormalHu 手数 " + map.get("remainingMelds")); + System.out.println("checkNormalHu 孤章数量 " + ((List) map.get("cardResiue")).size()); + System.out.println("checkNormalHu 手数 " + map.get("remainingMelds")); //碰之后 @@ -134,7 +134,7 @@ public class TinHuChi { // List checktingpai1 = TinHuChi.checktingpai(temphand); // System.out.println("checktingpai1" + checktingpai1); - //hand1.add(101); + //hand1.add(101); // Map map = new HashMap<>(); // Map map2 = new HashMap<>(); @@ -627,7 +627,7 @@ public class TinHuChi { // 5. 检查打牌后是否能听牌 boolean canTing = checkCanTing(afterDiscard, needs258, targetSizeAfterDiscard); if (canTing) { - HuNanChangShaHandler.isTinChi = true; + HuNanChangSha.isTinChi = true; ChangShaSuanFaTest.isChi=true; System.out.println(" ✓ 听牌!"); foundTing = true; diff --git a/robots/majiang/robot_mj_cs/src/main/java/taurus/util/TinHuGang.java b/robots/majiang/robot_mj_cs/src/main/java/taurus/util/TinHuGang.java new file mode 100644 index 0000000..5510452 --- /dev/null +++ b/robots/majiang/robot_mj_cs/src/main/java/taurus/util/TinHuGang.java @@ -0,0 +1,404 @@ +package taurus.util; + +import java.util.*; + +public class TinHuGang { + + + /** + * 判断是否能杠牌(检查杠牌后是否立即听牌) + */ + public static boolean canGang(List handCards, int card, boolean isMingGang) { + int type = card / 100; + int value = card % 100; + + System.out.println("\n检查" + (isMingGang ? "明" : "暗") + "杠" + value + getTypeName(type) + ":"); + + // 检查是否符合杠牌条件 + int count = Collections.frequency(handCards, card); + + if (isMingGang) { + // 明杠:手牌中需要有三张相同的牌 + if (count < 3) { + System.out.println(" 手牌中没有三张相同的" + value + getTypeName(type) + ",不能明杠"); + return false; + } + System.out.println(" 发现三张" + value + getTypeName(type) + ",可以明杠"); + } else { + // 暗杠:手牌中需要有四张相同的牌 + if (count < 4) { + System.out.println(" 手牌中没有四张相同的" + value + getTypeName(type) + ",不能暗杠"); + return false; + } + System.out.println(" 发现四张" + value + getTypeName(type) + ",可以暗杠"); + } + + // 检查杠牌后是否立即听牌(杠牌后手牌本身就是听牌状态) + return canTingImmediatelyAfterGang(handCards, card, isMingGang); + } + + /** + * 检查杠牌后是否能立即听牌 + */ + private static boolean canTingImmediatelyAfterGang(List handCards, int gangCard, boolean isMingGang) { + int type = gangCard / 100; + int value = gangCard % 100; + + System.out.println("\n模拟" + (isMingGang ? "明" : "暗") + "杠" + value + getTypeName(type) + ":"); + + // 1. 模拟杠牌:移除手牌中的牌 + List afterGang = new ArrayList<>(handCards); + + if (isMingGang) { + // 明杠:移除手牌中的三张牌 + for (int i = 0; i < 3; i++) { + afterGang.remove(Integer.valueOf(gangCard)); + } + // 杠牌后手牌数:13张 → 10张 + } else { + // 暗杠:移除手牌中的四张牌 + for (int i = 0; i < 4; i++) { + afterGang.remove(Integer.valueOf(gangCard)); + } + // 暗杠后需要补牌,但这里先不处理 + } + + Collections.sort(afterGang); + + System.out.println(" 杠后手牌(" + afterGang.size() + "张): " + convertToReadable(afterGang)); + + // 2. 检查是否需要258将 + boolean needs258 = !checkSuitCount(afterGang); + if (needs258) { + System.out.println(" 花色牌数不足10张,需要258做将"); + } + + // 3. 检查杠牌后手牌本身是否就是听牌状态 + // 注意:杠牌后手牌数是10张,这10张牌本身应该是听牌状态 + // 也就是说,随便摸一张牌(任何牌)都能胡牌 + + System.out.println("\n 检查杠后手牌是否听牌:"); + + if (afterGang.size() != 10) { + System.out.println(" 手牌数不是10张,不符合听牌条件"); + return false; + } + + // 检查这10张牌是否听牌 + boolean canTing = checkIfHandIsTingPai(afterGang, needs258); + + if (canTing) { + System.out.println(" ✓ 杠后手牌是听牌状态!"); + + // 显示听哪些牌 + Set tingCards = getTingCards(afterGang, needs258); + if (!tingCards.isEmpty()) { + System.out.print(" 听" + tingCards.size() + "张牌: "); + List tingStrs = new ArrayList<>(); + for (int tingCard : tingCards) { + tingStrs.add((tingCard%100) + getTypeName(tingCard/100)); + } + System.out.println(String.join(", ", tingStrs)); + } + return true; + } else { + System.out.println(" ✗ 杠后手牌不是听牌状态"); + return false; + } + } + + /** + * 检查手牌是否处于听牌状态 + */ + private static boolean checkIfHandIsTingPai(List hand, boolean needs258) { + if (hand.size() != 10) { + return false; + } + + // 听牌状态:再摸任何一张牌都能胡牌 + // 我们需要检查是否至少有一张牌能让这手牌胡牌 + Set allCards = getAllCards(); + int tingCount = 0; + + for (int testCard : allCards) { + List tempHand = new ArrayList<>(hand); + tempHand.add(testCard); + + if (canHu(tempHand, needs258)) { + tingCount++; + } + } + + System.out.println(" 可胡" + tingCount + "张牌"); + return tingCount > 0; + } + + /** + * 获取听哪些牌 + */ + private static Set getTingCards(List hand, boolean needs258) { + Set tingCards = new HashSet<>(); + + if (hand.size() != 10) { + return tingCards; + } + + Set allCards = getAllCards(); + + for (int testCard : allCards) { + List tempHand = new ArrayList<>(hand); + tempHand.add(testCard); + + if (canHu(tempHand, needs258)) { + tingCards.add(testCard); + } + } + + return tingCards; + } + + /** + * 检查手牌是否能胡牌 + */ + private static boolean canHu(List handCards, boolean needs258) { + // 手牌排序 + List sorted = new ArrayList<>(handCards); + Collections.sort(sorted); + + // 胡牌时手牌数必须是3n+2 + if (sorted.size() != 11 && sorted.size() != 14) { + return false; + } + + // 检查七对子 + if (checkQiDuiZi(sorted)) { + // 七对子不需要258将 + return true; + } + + // 如果需要258将,检查普通胡牌(必须有258将) + if (needs258) { + return checkNormalHuWith258(sorted); + } else { + // 不需要258将,检查普通胡牌 + return checkNormalHu(sorted); + } + } + + /** + * 检查七对子 + */ + private static boolean checkQiDuiZi(List handCards) { + if (handCards.size() != 14) return false; + + Map countMap = new HashMap<>(); + for (int card : handCards) { + countMap.put(card, countMap.getOrDefault(card, 0) + 1); + } + + // 检查是否都是对子 + for (int count : countMap.values()) { + if (count != 2) { + return false; + } + } + + return true; + } + + /** + * 检查普通胡牌(必须有258将) + */ + private static boolean checkNormalHuWith258(List handCards) { + // 尝试每种258作为将牌 + for (int card : handCards) { + int value = card % 100; + // 检查是否是258 + if (value == 2 || value == 5 || value == 8) { + // 检查是否有至少2张相同的牌做将 + int count = Collections.frequency(handCards, card); + if (count >= 2) { + // 移除将牌 + List remaining = new ArrayList<>(handCards); + for (int i = 0; i < 2; i++) { + remaining.remove(Integer.valueOf(card)); + } + + // 检查剩余牌是否能组成顺子/刻子 + if (canGroup(remaining)) { + return true; + } + } + } + } + + return false; + } + + /** + * 检查普通胡牌(不需要258将) + */ + private static boolean checkNormalHu(List handCards) { + return checkNormalHuRecursive(new ArrayList<>(handCards), false); + } + + private static boolean checkNormalHuRecursive(List handCards, boolean hasJiang) { + if (handCards.isEmpty()) { + return true; // 所有牌都分组成功 + } + + Collections.sort(handCards); + + // 统计第一张牌的数量 + int firstCard = handCards.get(0); + int count = Collections.frequency(handCards, firstCard); + + // 尝试作为刻子(三张相同) + if (count >= 3) { + List remaining = new ArrayList<>(handCards); + for (int i = 0; i < 3; i++) { + remaining.remove(Integer.valueOf(firstCard)); + } + if (checkNormalHuRecursive(remaining, hasJiang)) { + return true; + } + } + + // 尝试作为顺子(三张连续) + int type = firstCard / 100; + int value = firstCard % 100; + + if (type < 4 && value <= 7) { // 字牌和8、9不能组成顺子 + int second = type * 100 + (value + 1); + int third = type * 100 + (value + 2); + + if (handCards.contains(second) && handCards.contains(third)) { + List remaining = new ArrayList<>(handCards); + remaining.remove(Integer.valueOf(firstCard)); + remaining.remove(Integer.valueOf(second)); + remaining.remove(Integer.valueOf(third)); + if (checkNormalHuRecursive(remaining, hasJiang)) { + return true; + } + } + } + + // 尝试作为将(对子)- 只能有一个将 + if (!hasJiang && count >= 2) { + List remaining = new ArrayList<>(handCards); + remaining.remove(Integer.valueOf(firstCard)); + remaining.remove(Integer.valueOf(firstCard)); + if (checkNormalHuRecursive(remaining, true)) { + return true; + } + } + + return false; + } + + /** + * 检查牌是否能组成顺子或刻子 + */ + private static boolean canGroup(List cards) { + if (cards.isEmpty()) { + return true; // 所有牌都分组成功 + } + + List sorted = new ArrayList<>(cards); + Collections.sort(sorted); + + int firstCard = sorted.get(0); + int count = Collections.frequency(sorted, firstCard); + + // 尝试作为刻子(三张相同) + if (count >= 3) { + List remaining = new ArrayList<>(sorted); + for (int i = 0; i < 3; i++) { + remaining.remove(Integer.valueOf(firstCard)); + } + if (canGroup(remaining)) { + return true; + } + } + + // 尝试作为顺子(三张连续) + int type = firstCard / 100; + int value = firstCard % 100; + + if (type < 4 && value <= 7) { // 字牌和8、9不能组成顺子 + int second = type * 100 + (value + 1); + int third = type * 100 + (value + 2); + + if (sorted.contains(second) && sorted.contains(third)) { + List remaining = new ArrayList<>(sorted); + remaining.remove(Integer.valueOf(firstCard)); + remaining.remove(Integer.valueOf(second)); + remaining.remove(Integer.valueOf(third)); + if (canGroup(remaining)) { + return true; + } + } + } + + return false; + } + + /** + * 检查手牌花色牌数是否>=10张 + */ + private static boolean checkSuitCount(List hand) { + Map suitCount = new HashMap<>(); + for (int card : hand) { + int type = card / 100; + if (type < 4) { + suitCount.put(type, suitCount.getOrDefault(type, 0) + 1); + } + } + + for (int count : suitCount.values()) { + if (count >= 10) { + return true; + } + } + + return false; + } + + /** + * 获取所有麻将牌 + */ + private static Set getAllCards() { + Set allCards = new HashSet<>(); + for (int type = 1; type <= 3; type++) { + for (int value = 1; value <= 9; value++) { + allCards.add(type * 100 + value); + } + } + return allCards; + } + + /** + * 转换为可读格式 + */ + private static String convertToReadable(List cards) { + StringBuilder sb = new StringBuilder(); + for (int card : cards) { + int type = card / 100; + int value = card % 100; + sb.append(value).append(getTypeName(type)).append(" "); + } + return sb.toString(); + } + + /** + * 获取牌型名称 + */ + private static String getTypeName(int type) { + switch(type) { + case 1: return "万"; + case 2: return "筒"; + case 3: return "条"; + default: return "字"; + } + } +} \ No newline at end of file diff --git a/robots/majiang/robot_mj_cs/src/main/java/taurus/util/TinHuPeng.java b/robots/majiang/robot_mj_cs/src/main/java/taurus/util/TinHuPeng.java new file mode 100644 index 0000000..e49cef6 --- /dev/null +++ b/robots/majiang/robot_mj_cs/src/main/java/taurus/util/TinHuPeng.java @@ -0,0 +1,425 @@ +package taurus.util; + + +import robot.mj.handler.HuNanChangSha; + +import java.util.*; + +public class TinHuPeng { + + /** + * 判断是否能碰牌(包含碰牌后的听牌检查) + */ + public boolean canPeng(List handCards, int card) { + int type = card / 100; + int value = card % 100; + + if (type >= 4) return false; // 字牌不能碰(长沙麻将没有字牌) + + System.out.println("\n要碰的牌: " + value + getTypeName(type)); + System.out.println("当前手牌数量: " + handCards.size() + "张"); + + // 检查基本碰牌条件:手牌至少有2张相同的牌 + if (!canPengBasic(handCards, card)) { + System.out.println(" 基本碰牌条件不满足:手牌没有2张" + value + getTypeName(type)); + return false; + } + System.out.println(" ✓ 手牌有2张" + value + getTypeName(type) + ",可以碰"); + ChangShaSuanFaTest.isPeng = true; + // 检查碰牌后是否能听牌 + return canTingAfterPengAndDiscard(handCards, card); + } + + /** + * 检查基本碰牌条件 + */ + private boolean canPengBasic(List handCards, int card) { + // 碰牌:手牌有2张相同的牌,别人打出第3张 + int count = 0; + for (int c : handCards) { + if (c == card) { + count++; + } + } + return count >= 2; + } + + /** + * 检查碰牌并打出一张后是否能听牌 + */ + private boolean canTingAfterPengAndDiscard(List handCards, int pengCard) { + // 1. 模拟碰牌:移除两张相同的牌,碰的牌不加入手牌 + List afterPeng = new ArrayList<>(handCards); + + // 移除2张相同的牌 + int removed = 0; + for (int i = 0; i < handCards.size() && removed < 2; i++) { + if (handCards.get(i) == pengCard) { + afterPeng.remove(Integer.valueOf(pengCard)); + removed++; + } + } + Collections.sort(afterPeng); + + System.out.println(" 碰后手牌(" + afterPeng.size() + "张): " + convertToReadable(afterPeng)); + + // 2. 检查是否需要258将(花色是否>=10张) + boolean needs258 = !checkSuitCount(afterPeng); + if (needs258) { + System.out.println(" 花色牌数不足10张,需要258做将"); + } + + // 3. 根据碰后手牌数确定目标手牌数 + // 碰牌前后手牌数变化:碰前N张 -> 碰后(N-2)张 -> 打后(N-3)张 + int targetSizeAfterDiscard = afterPeng.size() - 1; + System.out.println(" 目标手牌数: " + targetSizeAfterDiscard); + + // 4. 尝试打每一张不同的牌 + Set uniqueCards = new HashSet<>(afterPeng); + boolean foundTing = false; + + for (int discardCard : uniqueCards) { + List afterDiscard = new ArrayList<>(afterPeng); + afterDiscard.remove(Integer.valueOf(discardCard)); + Collections.sort(afterDiscard); + + int discardType = discardCard / 100; + int discardValue = discardCard % 100; + + System.out.print("\n 打" + discardValue + getTypeName(discardType) + + " → 剩余" + afterDiscard.size() + "张: " + + convertToReadable(afterDiscard)); + + // 5. 检查打牌后是否能听牌 + boolean canTing = checkCanTing(afterDiscard, needs258, targetSizeAfterDiscard); + + if (canTing) { + HuNanChangSha.isTinPeng = true; + System.out.println(" ✓ 听牌!"); + foundTing = true; + + // 分析听牌详情 + analyzeTingDetails(afterDiscard, needs258); + } else { + System.out.println(" ✗ 不听"); + } + } + + return foundTing; + } + + /** + * 检查手牌是否能听牌 + */ + private boolean checkCanTing(List hand, boolean needs258, int targetSize) { + if (hand.size() != targetSize) { + System.out.print(" [手牌数" + hand.size() + "≠目标" + targetSize + "]"); + return false; + } + + // 获取所有可能的牌 + Set allCards = getAllCards(); + + for (int testCard : allCards) { + List tempHand = new ArrayList<>(hand); + tempHand.add(testCard); + Collections.sort(tempHand); + + // 检查是否能胡牌 + if (canHu(tempHand, needs258)) { + return true; + } + } + + return false; + } + + /** + * 检查手牌是否能胡牌(考虑258将要求) + */ + private boolean canHu(List handCards, boolean needs258) { + // 手牌排序 + List sorted = new ArrayList<>(handCards); + Collections.sort(sorted); + + // 胡牌时手牌数必须是3n+2 + if (sorted.size() % 3 != 2) { + return false; + } + + // 检查七对子(特殊胡牌) + if (checkQiDuiZi(sorted)) { + // 七对子不需要258将 + return true; + } + + // 如果需要258将,检查普通胡牌(必须有258将) + if (needs258) { + return checkNormalHuWith258(sorted); + } else { + // 不需要258将,检查普通胡牌 + return checkNormalHu(sorted); + } + } + + /** + * 分析听牌详情 + */ + private void analyzeTingDetails(List hand, boolean needs258) { + Set tingCards = new HashSet<>(); + Set allCards = getAllCards(); + + for (int testCard : allCards) { + List tempHand = new ArrayList<>(hand); + tempHand.add(testCard); + + if (canHu(tempHand, needs258)) { + tingCards.add(testCard); + } + } + + if (!tingCards.isEmpty()) { + System.out.print(" 听" + tingCards.size() + "张牌: "); + List tingCardStrs = new ArrayList<>(); + for (int card : tingCards) { + int type = card / 100; + int value = card % 100; + tingCardStrs.add(value + getTypeName(type)); + } + System.out.println(String.join(", ", tingCardStrs)); + } + } + + /** + * 检查手牌花色牌数是否>=10张 + */ + private boolean checkSuitCount(List hand) { + // 统计万、筒、条各自的数量 + Map suitCount = new HashMap<>(); + for (int card : hand) { + int type = card / 100; + if (type < 4) { // 只统计万筒条 + suitCount.put(type, suitCount.getOrDefault(type, 0) + 1); + } + } + + // 检查是否有某种花色>=10张 + for (int count : suitCount.values()) { + if (count >= 10) { + return true; // 有花色>=10张,不需要258将 + } + } + + return false; // 没有花色>=10张,需要258将 + } + + /** + * 检查七对子 + */ + private boolean checkQiDuiZi(List handCards) { + if (handCards.size() != 14) return false; + + Map countMap = new HashMap<>(); + for (int card : handCards) { + countMap.put(card, countMap.getOrDefault(card, 0) + 1); + } + + // 检查是否都是对子 + for (int count : countMap.values()) { + if (count != 2) { + return false; + } + } + + return true; + } + + /** + * 检查普通胡牌(必须有258将) + */ + private boolean checkNormalHuWith258(List handCards) { + // 尝试每种258作为将牌 + for (int card : handCards) { + int value = card % 100; + // 检查是否是258 + if (value == 2 || value == 5 || value == 8) { + // 检查是否有至少2张相同的牌做将 + int count = Collections.frequency(handCards, card); + if (count >= 2) { + // 移除将牌 + List remaining = new ArrayList<>(handCards); + for (int i = 0; i < 2; i++) { + remaining.remove(Integer.valueOf(card)); + } + + // 检查剩余牌是否能组成顺子/刻子 + if (canGroup(remaining)) { + return true; + } + } + } + } + + return false; + } + + /** + * 检查普通胡牌(不需要258将) + */ + private boolean checkNormalHu(List handCards) { + return checkNormalHuRecursive(new ArrayList<>(handCards), false); + } + + private boolean checkNormalHuRecursive(List handCards, boolean hasJiang) { + if (handCards.isEmpty()) { + return true; // 所有牌都分组成功 + } + + Collections.sort(handCards); + + // 统计第一张牌的数量 + int firstCard = handCards.get(0); + int count = Collections.frequency(handCards, firstCard); + + // 尝试作为刻子(三张相同) + if (count >= 3) { + List remaining = new ArrayList<>(handCards); + for (int i = 0; i < 3; i++) { + remaining.remove(Integer.valueOf(firstCard)); + } + if (checkNormalHuRecursive(remaining, hasJiang)) { + return true; + } + } + + // 尝试作为顺子(三张连续) + int type = firstCard / 100; + int value = firstCard % 100; + + if (type < 4 && value <= 7) { // 字牌和8、9不能组成顺子 + int second = type * 100 + (value + 1); + int third = type * 100 + (value + 2); + + if (handCards.contains(second) && handCards.contains(third)) { + List remaining = new ArrayList<>(handCards); + remaining.remove(Integer.valueOf(firstCard)); + remaining.remove(Integer.valueOf(second)); + remaining.remove(Integer.valueOf(third)); + if (checkNormalHuRecursive(remaining, hasJiang)) { + return true; + } + } + } + + // 尝试作为将(对子)- 只能有一个将 + if (!hasJiang && count >= 2) { + List remaining = new ArrayList<>(handCards); + remaining.remove(Integer.valueOf(firstCard)); + remaining.remove(Integer.valueOf(firstCard)); + if (checkNormalHuRecursive(remaining, true)) { + return true; + } + } + + return false; + } + + /** + * 检查牌是否能组成顺子或刻子 + */ + private boolean canGroup(List cards) { + if (cards.isEmpty()) { + return true; // 所有牌都分组成功 + } + + List sorted = new ArrayList<>(cards); + Collections.sort(sorted); + + int firstCard = sorted.get(0); + int count = Collections.frequency(sorted, firstCard); + + // 尝试作为刻子(三张相同) + if (count >= 3) { + List remaining = new ArrayList<>(sorted); + for (int i = 0; i < 3; i++) { + remaining.remove(Integer.valueOf(firstCard)); + } + if (canGroup(remaining)) { + return true; + } + } + + // 尝试作为顺子(三张连续) + int type = firstCard / 100; + int value = firstCard % 100; + + if (type < 4 && value <= 7) { // 字牌和8、9不能组成顺子 + int second = type * 100 + (value + 1); + int third = type * 100 + (value + 2); + + if (sorted.contains(second) && sorted.contains(third)) { + List remaining = new ArrayList<>(sorted); + remaining.remove(Integer.valueOf(firstCard)); + remaining.remove(Integer.valueOf(second)); + remaining.remove(Integer.valueOf(third)); + if (canGroup(remaining)) { + return true; + } + } + } + + return false; + } + + /** + * 获取所有麻将牌 + */ + private Set getAllCards() { + Set allCards = new HashSet<>(); + // 万条筒 1-9 + for (int type = 1; type <= 3; type++) { + for (int value = 1; value <= 9; value++) { + allCards.add(type * 100 + value); + } + } + return allCards; + } + + /** + * 转换为可读格式 + */ + private String convertToReadable(List cards) { + StringBuilder sb = new StringBuilder(); + for (int card : cards) { + int type = card / 100; + int value = card % 100; + sb.append(value).append(getTypeName(type)).append(" "); + } + return sb.toString(); + } + + /** + * 获取牌型名称 + */ + private String getTypeName(int type) { + switch(type) { + case 1: return "万"; + case 2: return "筒"; + case 3: return "条"; + default: return "字"; + } + } + + /** + * 手牌分析类 + */ + class HandAnalysis { + int keziCount = 0; // 刻子数量 + int pairCount = 0; // 对子数量 + int shunziCount = 0; // 顺子数量 + int singleCount = 0; // 单张数量 + List singles = new ArrayList<>(); // 单张列表 + boolean has258Jiang = false; // 是否有258将 + List pairs = new ArrayList<>(); // 对子列表(value) + } +} 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 8d262e4..4a440c2 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 @@ -1,271 +1,189 @@ +// +// Source code recreated from a .class file by IntelliJ IDEA +// (powered by Fernflower decompiler) +// + package taurus.util; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; public class ai { - - // 长沙麻将特殊规则:二五八将 private static final Set JIANG_PAIS; - - // 门子类型定义 - private static final int DOOR_NONE = 0; // 无门子 - private static final int DOOR_BIAN = 1; // 边张门子 (12/89) - private static final int DOOR_GE = 2; // 隔张门子 (13/79/24/68等) - private static final int DOOR_XIANGLIN = 3; // 相邻门子 (23/34/45/56/67/78等) - private static final int DOOR_DUIZI = 4; // 对子 - + private static final int DOOR_NONE = 0; + private static final int DOOR_BIAN = 1; + private static final int DOOR_GE = 2; + private static final int DOOR_XIANGLIN = 3; + private static final int DOOR_DUIZI = 4; public boolean isTinAi = false; - public boolean isTingpai = false; - - static { - Set jiangSet = new HashSet<>(); - jiangSet.add(2); - jiangSet.add(5); - jiangSet.add(8); - JIANG_PAIS = Collections.unmodifiableSet(jiangSet); - } - - // 缓存优化 - private Map scoreCache = new HashMap<>(); - private Map> tingCache = new HashMap<>(); + public boolean isTingpai = false; + private Map scoreCache = new HashMap(); + private Map> tingCache = new HashMap(); public static void main(String[] args) { System.out.println("=== 长沙麻将测试(含碰杠吃牌) ==="); ai ai1 = new ai(); - - // 测试1:普通手牌 System.out.println("\n=== 测试1:普通14张牌 ==="); PlayerState state1 = new PlayerState(); -// state1.handCards = Arrays.asList(208, 204, 203, 202, 107, 106, 105, 103, 102, 101, 101, 208, 206, 207); - -// test1.add(209); -// test1.add(208); -// test1.add(208); -// test1.add(207); -// test1.add(207); -// test1.add(206); -// test1.add(205); -// test1.add(205); -// test1.add(202); -// test1.add(201); -// test1.add(201); -// test1.add(107); -// test1.add(107); -// test1.add(104); - state1.handCards = Arrays.asList(209, 209, 208, 207, 206, 205, 203, 202, 202, 202, 108, 105, 104, 103); - -// state1.handCards = Arrays.asList(204, 206, 205, 205, 203, 109, 108, 107, 107, 106, 103, 103, 102, 101); - -// state1.addChiGroup(Arrays.asList(107,108,106)); -// state1.addPongGroup(Arrays.asList(109,109,109)); int discard1 = ai1.findBestDiscard(state1); System.out.println("推荐出牌: " + ai1.formatCard(discard1)); } - - /** - * 主方法:根据完整玩家状态推荐出牌 - */ public int findBestDiscard(PlayerState state) { System.out.println("\n=== 长沙麻将AI出牌分析 ==="); - System.out.println("手牌(" + state.handCards.size() + "张): " + formatCards(state.handCards)); - + System.out.println("手牌(" + state.handCards.size() + "张): " + this.formatCards(state.handCards)); int pongGroups = state.getPongGroupCount(); int gangGroups = state.getGangGroupCount(); int chiGroups = state.getChiGroupCount(); - - System.out.println("碰牌组数: " + pongGroups + - ",杠牌组数: " + gangGroups + - ",吃牌组数: " + chiGroups); - - // 清空缓存 - scoreCache.clear(); - tingCache.clear(); - + System.out.println("碰牌组数: " + pongGroups + ",杠牌组数: " + gangGroups + ",吃牌组数: " + chiGroups); + 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 = state.handCards.get(0); - System.out.println("只剩一张牌,必须出: " + formatCard(onlyCard)); + int onlyCard = (Integer)state.handCards.get(0); + System.out.println("只剩一张牌,必须出: " + this.formatCard(onlyCard)); return onlyCard; - } - - for (int card : uniqueCards) { - // 模拟打出一张牌后的状态 - PlayerState newState = simulateDiscard(state, card); - DiscardResult result = evaluateDiscardWithState(card, state); // 传入原始状态 - results.put(card, result); - - System.out.printf("打出 %s: 评分=%.3f (听牌=%d张)%n", - formatCard(card), result.score, result.tingCards.size()); - } - - // 找出最佳出牌 - 优先考虑听牌数量 - int bestCard = -1; - double bestScore = Double.NEGATIVE_INFINITY; - int maxTingCount = 0; - - // 首先找到最大听牌数量 - for (DiscardResult result : results.values()) { - if (result.tingCards.size() > maxTingCount) { - maxTingCount = result.tingCards.size(); + } else { + for(int card : uniqueCards) { + this.simulateDiscard(state, card); + DiscardResult result = this.evaluateDiscardWithState(card, state); + results.put(card, result); + System.out.printf("打出 %s: 评分=%.3f (听牌=%d张)%n", this.formatCard(card), result.score, result.tingCards.size()); } - } - // 如果有听牌,优先从听牌中选择 - if (maxTingCount > 0) { - // 从所有听牌中选择评分最高的 - for (Map.Entry entry : results.entrySet()) { - if (entry.getValue().tingCards.size() == maxTingCount) { - if (entry.getValue().score > bestScore) { - bestScore = entry.getValue().score; - bestCard = entry.getKey(); + int bestCard = -1; + double bestScore = Double.NEGATIVE_INFINITY; + int maxTingCount = 0; + + for(DiscardResult result : results.values()) { + if (result.tingCards.size() > maxTingCount) { + maxTingCount = result.tingCards.size(); + } + } + + if (maxTingCount > 0) { + for(Map.Entry entry : results.entrySet()) { + if (((DiscardResult)entry.getValue()).tingCards.size() == maxTingCount && ((DiscardResult)entry.getValue()).score > bestScore) { + bestScore = ((DiscardResult)entry.getValue()).score; + bestCard = (Integer)entry.getKey(); + } + } + } else { + for(Map.Entry entry : results.entrySet()) { + if (((DiscardResult)entry.getValue()).score > bestScore) { + bestScore = ((DiscardResult)entry.getValue()).score; + bestCard = (Integer)entry.getKey(); } } } - } else { - // 没有听牌,选择评分最高的 - for (Map.Entry entry : results.entrySet()) { - if (entry.getValue().score > bestScore) { - bestScore = entry.getValue().score; - bestCard = entry.getKey(); + + long endTime = System.currentTimeMillis(); + System.out.printf("分析耗时: %dms%n", endTime - startTime); + if (bestCard != -1) { + DiscardResult bestResult = (DiscardResult)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) { + ChangShaSuanFaTest.tinCards.clear(); + ChangShaSuanFaTest.tinCards.addAll(bestResult.tingCards); + } + + List tingCardsFormatted = (List)bestResult.tingCards.stream().map(this::formatCard).collect(Collectors.toList()); + System.out.println(" " + String.join(" ", tingCardsFormatted)); } + + System.out.println("综合评分: " + String.format("%.3f", bestResult.score)); } + + return bestCard; } - - long endTime = System.currentTimeMillis(); - System.out.printf("分析耗时: %dms%n", (endTime - startTime)); - - if (bestCard != -1) { - DiscardResult bestResult = results.get(bestCard); - System.out.println("\n🎯 AI推荐出牌: " + formatCard(bestCard)); - if (!bestResult.tingCards.isEmpty()) { - isTinAi = true; - isTingpai = true; - System.out.println("✅ 听牌,可听 " + bestResult.tingCards.size() + " 张牌:"); - System.out.println("----牌组----" + bestResult.tingCards); - if (bestResult.tingCards.size() > 0) { - // 如果有ChangShaSuanFaTest类,需要处理 - ChangShaSuanFaTest.tinCards.clear(); - ChangShaSuanFaTest.tinCards.addAll(bestResult.tingCards); - } - List tingCardsFormatted = bestResult.tingCards.stream() - .map(this::formatCard) - .collect(Collectors.toList()); - System.out.println(" " + String.join(" ", tingCardsFormatted)); - } - System.out.println("综合评分: " + String.format("%.3f", bestResult.score)); - } - - return bestCard; } - /** - * 模拟打出一张牌 - */ private PlayerState simulateDiscard(PlayerState originalState, int discardCard) { PlayerState newState = new PlayerState(); + newState.handCards = new ArrayList(originalState.handCards); + newState.handCards.remove(discardCard); - // 复制手牌并移除要打的牌 - newState.handCards = new ArrayList<>(originalState.handCards); - newState.handCards.remove((Integer) discardCard); - - // 复制碰牌组 - for (List group : originalState.pongGroups) { - newState.pongGroups.add(new ArrayList<>(group)); + for(List group : originalState.pongGroups) { + newState.pongGroups.add(new ArrayList(group)); } - // 复制杠牌组 - for (List group : originalState.gangGroups) { - newState.gangGroups.add(new ArrayList<>(group)); + for(List group : originalState.gangGroups) { + newState.gangGroups.add(new ArrayList(group)); } - // 复制吃牌组 - for (List group : originalState.chiGroups) { - newState.chiGroups.add(new ArrayList<>(group)); + for(List group : originalState.chiGroups) { + newState.chiGroups.add(new ArrayList(group)); } newState.isZhuang = originalState.isZhuang; - return newState; } private DiscardResult evaluateDiscardWithState(int discardedCard, PlayerState originalState) { - // 先模拟打出这张牌 - PlayerState simulatedState = simulateDiscard(originalState, discardedCard); + PlayerState simulatedState = this.simulateDiscard(originalState, discardedCard); List hand = simulatedState.handCards; - double totalScore = 0.0; - - // === 1. 基础分 === - totalScore += 10.0; - - // === 新增:花色集中度评估 === - double suitConcentrationScore = evaluateSuitConcentration(discardedCard, hand); + double totalScore = (double)0.0F; + totalScore += (double)10.0F; + double suitConcentrationScore = this.evaluateSuitConcentration(discardedCard, hand); totalScore += suitConcentrationScore; - - // === 2. 门子类型评估 === - System.out.println("\n=== 评估 " + formatCard(discardedCard) + " ==="); - - // 使用原始状态来获取对子信息 + System.out.println("\n=== 评估 " + this.formatCard(discardedCard) + " ==="); int originalCount = 0; - for (int c : originalState.handCards) { - if (c == discardedCard) originalCount++; - } - System.out.println(" [原始] " + formatCard(discardedCard) + " 在原始手牌中有 " + originalCount + " 张"); - int doorType = getDoorType(discardedCard, hand, originalState); // 传入原始状态 - double doorScore = evaluateDoorType(doorType, discardedCard); - totalScore += doorScore; - - System.out.printf(" [门子] 类型=%s, 评分=%.1f%n", - getDoorTypeName(doorType), doorScore); - - // === 3. 检查是否在完整顺子中 === - if (isInCompleteSequence(discardedCard, originalState.handCards)) { // 使用原始状态 - System.out.println(" [顺子] " + formatCard(discardedCard) + " 在完整顺子中"); - - if (originalCount > 1) { - System.out.println(" [冗余] " + formatCard(discardedCard) + - " 在顺子中且有多张,加30分鼓励打出"); - totalScore += 30.0; - } else { - System.out.println(" [保留] " + formatCard(discardedCard) + - " 是顺子中的唯一一张,扣15分"); - totalScore -= 15.0; + for(int c : originalState.handCards) { + if (c == discardedCard) { + ++originalCount; } } - // === 4. 对子和刻子评估 === - double pairScore = evaluatePairsAndPongsFinal(discardedCard, originalState); // 传入原始状态 - totalScore += pairScore; - - // === 5. 绝对孤张评估 === - if (isAbsolutelyLonelyFinal(discardedCard, hand)) { - totalScore += 15.0; - System.out.println(" [孤张] " + formatCard(discardedCard) + " 是绝对孤张,加15分"); + System.out.println(" [原始] " + this.formatCard(discardedCard) + " 在原始手牌中有 " + originalCount + " 张"); + int doorType = this.getDoorType(discardedCard, hand, originalState); + double doorScore = this.evaluateDoorType(doorType, discardedCard); + totalScore += doorScore; + System.out.printf(" [门子] 类型=%s, 评分=%.1f%n", this.getDoorTypeName(doorType), doorScore); + if (this.isInCompleteSequence(discardedCard, originalState.handCards)) { + System.out.println(" [顺子] " + this.formatCard(discardedCard) + " 在完整顺子中"); + if (originalCount > 1) { + System.out.println(" [冗余] " + this.formatCard(discardedCard) + " 在顺子中且有多张,加30分鼓励打出"); + totalScore += (double)30.0F; + } else { + System.out.println(" [保留] " + this.formatCard(discardedCard) + " 是顺子中的唯一一张,扣15分"); + totalScore -= (double)15.0F; + } } - // === 6. 简单结构评估 === - double structureScore = evaluateSimpleStructure(discardedCard, hand); + double pairScore = this.evaluatePairsAndPongsFinal(discardedCard, originalState); + totalScore += pairScore; + if (this.isAbsolutelyLonelyFinal(discardedCard, hand)) { + totalScore += (double)15.0F; + System.out.println(" [孤张] " + this.formatCard(discardedCard) + " 是绝对孤张,加15分"); + } + + double structureScore = this.evaluateSimpleStructure(discardedCard, hand); totalScore += structureScore; - - // === 7. 听牌检查 === - List tingCards = checkTingCardsWithState(simulatedState); + List tingCards = this.checkTingCardsWithState(simulatedState); if (!tingCards.isEmpty()) { - totalScore += 100.0; - totalScore += tingCards.size() * 50.0; + totalScore += (double)100.0F; + totalScore += (double)tingCards.size() * (double)50.0F; - for (int tingCard : tingCards) { - int value = getCardValue(tingCard); + for(int tingCard : tingCards) { + int value = this.getCardValue(tingCard); if (JIANG_PAIS.contains(value)) { - totalScore += 20.0; + totalScore += (double)20.0F; } } } @@ -273,630 +191,529 @@ public class ai { System.out.printf(" [总分] %.1f%n", totalScore); return new DiscardResult(totalScore, tingCards, 0); } - /** - * 花色集中度评估:优先打散张,保留集中花色的牌 - */ - private double evaluateSuitConcentration(int card, List hand) { - int type = getCardType(card); - int value = getCardValue(card); - // 统计该花色的牌数 + private double evaluateSuitConcentration(int card, List hand) { + int type = this.getCardType(card); + this.getCardValue(card); int suitCount = 0; - for (int c : hand) { - if (getCardType(c) == type) { - suitCount++; + + for(int c : hand) { + if (this.getCardType(c) == type) { + ++suitCount; } } - double score = 0.0; - - // 如果该花色牌很少(<=3张),优先打 + double score = (double)0.0F; if (suitCount <= 3) { - score += 20.0; // 鼓励打散张 - } - // 如果该花色牌很多(>=8张),尽量不打 - else if (suitCount >= 8) { - score -= 15.0; // 扣分,尽量不打集中花色的牌 + score += (double)20.0F; + } else if (suitCount >= 8) { + score -= (double)15.0F; } return score; } - /** - * 获取门子类型(改进版) - */ private int getDoorType(int card, List hand, PlayerState state) { - int type = getCardType(card); - int value = getCardValue(card); + int type = this.getCardType(card); + int value = this.getCardValue(card); + if (type > 3) { + return 0; + } else { + int originalCount = 0; - if (type > 3) return DOOR_NONE; - - // 使用传入的状态来获取对子信息 - int originalCount = 0; - for (int c : state.handCards) { - if (c == card) originalCount++; - } - - if (originalCount >= 2) { - return DOOR_DUIZI; - } - - // 检查边张 - if (value == 1 || value == 9) { - int adjacent = (value == 1) ? card + 1 : card - 1; - if (state.handCards.contains(adjacent)) { - return DOOR_BIAN; + for(int c : state.handCards) { + if (c == card) { + ++originalCount; + } } - } - // 检查相邻门子 - for (int other : state.handCards) { - if (getCardType(other) == type) { - int otherValue = getCardValue(other); - int diff = Math.abs(otherValue - value); - - if (diff == 1) { - // 排除12、21、89、98的情况(这些是边张) - if ((value == 1 && otherValue == 2) || (value == 2 && otherValue == 1) || - (value == 8 && otherValue == 9) || (value == 9 && otherValue == 8)) { - return DOOR_BIAN; + if (originalCount >= 2) { + return 4; + } else { + if (value == 1 || value == 9) { + int adjacent = value == 1 ? card + 1 : card - 1; + if (state.handCards.contains(adjacent)) { + return 1; } - return DOOR_XIANGLIN; } + + for(int other : state.handCards) { + if (this.getCardType(other) == type) { + int otherValue = this.getCardValue(other); + int diff = Math.abs(otherValue - value); + if (diff == 1) { + if ((value != 1 || otherValue != 2) && (value != 2 || otherValue != 1) && (value != 8 || otherValue != 9) && (value != 9 || otherValue != 8)) { + return 3; + } + + return 1; + } + } + } + + for(int other : state.handCards) { + if (this.getCardType(other) == type) { + int otherValue = this.getCardValue(other); + int diff = Math.abs(otherValue - value); + if (diff == 2) { + return 2; + } + } + } + + return 0; } } + } - // 检查隔张 - for (int other : state.handCards) { - if (getCardType(other) == type) { - int otherValue = getCardValue(other); - int diff = Math.abs(otherValue - value); - - if (diff == 2) { - return DOOR_GE; - } - } - } - - return DOOR_NONE; - } /** - * 获取门子类型名称 - */ private String getDoorTypeName(int doorType) { switch (doorType) { - case DOOR_BIAN: return "边张门子"; - case DOOR_GE: return "隔张门子"; - case DOOR_XIANGLIN: return "相邻门子"; - case DOOR_DUIZI: return "对子"; - case DOOR_NONE: return "无门子"; - default: return "未知"; + case 0: + return "无门子"; + case 1: + return "边张门子"; + case 2: + return "隔张门子"; + case 3: + return "相邻门子"; + case 4: + return "对子"; + default: + return "未知"; } } private double evaluateDoorType(int doorType, int card) { - double score = 0.0; - int value = getCardValue(card); - int type = getCardType(card); - + double score = (double)0.0F; + int value = this.getCardValue(card); + int type = this.getCardType(card); switch (doorType) { - case DOOR_BIAN: - // 边张门子:12或89,依赖性强 - score += 25.0; // 增加分值,更鼓励打边张 - break; - - case DOOR_GE: - // 隔张门子:差1张才能形成顺子 - if (value == 2 || value == 8) { - score += 5.0; - } else if (value == 3 || value == 7) { - score += 8.0; - } else { - score += 10.0; - } - break; - - case DOOR_XIANGLIN: - // 相邻门子:很容易形成顺子,应该保留 - score -= 15.0; - break; - - case DOOR_DUIZI: + case 0: if (JIANG_PAIS.contains(value)) { - // 258将对子是好牌,不应该扣那么多分 - score -= 20.0; - } else if (value == 1 || value == 9) { - // 边张对子(1万或9万):价值较低,鼓励打出 - score += 10.0; // 改为加分,鼓励打出边张对子 - } else if (value == 9 && type == 1) { // 9万对子 - score -= 15.0; + score += (double)20.0F; + } else if (value != 1 && value != 9) { + score += (double)20.0F; } else { - score -= 10.0; // 中张对子 + score += (double)30.0F; } break; - - case DOOR_NONE: - // 绝对孤张:优先打 + case 1: + score += (double)25.0F; + break; + case 2: + if (value != 2 && value != 8) { + if (value != 3 && value != 7) { + score += (double)10.0F; + } else { + score += (double)8.0F; + } + } else { + score += (double)5.0F; + } + break; + case 3: + score -= (double)15.0F; + break; + case 4: if (JIANG_PAIS.contains(value)) { - score += 20.0; - } else if (value == 1 || value == 9) { - // 边张孤张:19是边张,价值最低 - score += 30.0; // 增加分值,更鼓励打边张孤张 + score -= (double)20.0F; + } else if (value != 1 && value != 9) { + if (value == 9 && type == 1) { + score -= (double)15.0F; + } else { + score -= (double)10.0F; + } } else { - score += 20.0; + score += (double)10.0F; } - break; } return score; } + private boolean isInCompleteSequence(int card, List hand) { - int type = getCardType(card); - int value = getCardValue(card); - - if (type > 3) return false; - - // 统计该牌在手牌中的数量 - int cardCount = Collections.frequency(hand, card); - - // 如果该牌在手牌中,检查是否能形成顺子 - if (cardCount > 0) { - // 检查是否能形成123顺子 - if (value == 1) { - int card2 = card + 1; - int card3 = card + 2; - return hand.contains(card2) && hand.contains(card3); - } - // 检查是否能形成789顺子 - else if (value == 9) { - int card7 = card - 2; - int card8 = card - 1; - return hand.contains(card7) && hand.contains(card8); - } - // 检查其他顺子情况 - else if (value >= 2 && value <= 8) { - // 检查前顺子 - int prev1 = card - 1; - int prev2 = card - 2; - if (value >= 3 && hand.contains(prev1) && hand.contains(prev2)) { - return true; + int type = this.getCardType(card); + int value = this.getCardValue(card); + if (type > 3) { + return false; + } else { + int cardCount = Collections.frequency(hand, card); + if (cardCount > 0) { + if (value == 1) { + int card2 = card + 1; + int card3 = card + 2; + return hand.contains(card2) && hand.contains(card3); } - // 检查后顺子 - int next1 = card + 1; - int next2 = card + 2; - if (value <= 7 && hand.contains(next1) && hand.contains(next2)) { - return true; + + if (value == 9) { + int card7 = card - 2; + int card8 = card - 1; + return hand.contains(card7) && hand.contains(card8); } - // 检查中间顺子 - int prev = card - 1; - int next = card + 1; - if (hand.contains(prev) && hand.contains(next)) { - return true; + + if (value >= 2 && value <= 8) { + int prev1 = card - 1; + int prev2 = card - 2; + if (value >= 3 && hand.contains(prev1) && hand.contains(prev2)) { + return true; + } + + int next1 = card + 1; + int next2 = card + 2; + if (value <= 7 && hand.contains(next1) && hand.contains(next2)) { + return true; + } + + int prev = card - 1; + int next = card + 1; + if (hand.contains(prev) && hand.contains(next)) { + return true; + } } } + + return false; } - - return false; } - /** - * 简单结构评估(简化版) - */ + private double evaluateSimpleStructure(int card, List hand) { - double score = 0.0; - int type = getCardType(card); - int value = getCardValue(card); + double score = (double)0.0F; + int type = this.getCardType(card); + int value = this.getCardValue(card); + if (type > 3) { + return (double)0.0F; + } else { + if (JIANG_PAIS.contains(value)) { + score -= (double)3.0F; + } - if (type > 3) return 0.0; + if (value == 1 || value == 9) { + score -= (double)2.0F; + } - // 258将特殊处理 - if (JIANG_PAIS.contains(value)) { - score -= 3.0; // 258将价值较高 + return score; } - - // 边张扣分 - if (value == 1 || value == 9) { - score -= 2.0; - } - - return score; } - /** - * 检查是否在顺子中间 - */ private boolean isInSequenceMiddle(int card, List hand) { - int type = getCardType(card); - int value = getCardValue(card); - - if (type > 3) return false; - - // 检查前后是否能形成顺子 - int prevCard = card - 1; - int nextCard = card + 1; - - // 需要手牌中有前后两张牌 - return hand.contains(prevCard) && hand.contains(nextCard); + int type = this.getCardType(card); + this.getCardValue(card); + if (type > 3) { + return false; + } else { + int prevCard = card - 1; + int nextCard = card + 1; + return hand.contains(prevCard) && hand.contains(nextCard); + } } private double evaluatePairsAndPongsFinal(int card, PlayerState state) { - // 获取原始手牌中该牌的数量 int originalCount = 0; - for (int c : state.handCards) { - if (c == card) originalCount++; + + for(int c : state.handCards) { + if (c == card) { + ++originalCount; + } } - System.out.println(" [数量] " + formatCard(card) + " 在原始手牌中有 " + originalCount + " 张"); - - // 如果有3张或以上,是刻子 + System.out.println(" [数量] " + this.formatCard(card) + " 在原始手牌中有 " + originalCount + " 张"); if (originalCount >= 3) { - System.out.println(" [刻子] " + formatCard(card) + " 是刻子,扣50分"); - return -50.0; + System.out.println(" [刻子] " + this.formatCard(card) + " 是刻子,扣50分"); + return (double)-50.0F; + } else if (originalCount == 2) { + this.getCardType(card); + int value = this.getCardValue(card); + if (value != 1 && value != 9) { + if (this.isRedundantPairInSequence(card, state.handCards)) { + System.out.println(" [冗余] " + this.formatCard(card) + " 是顺子中的冗余对子,加40分鼓励打出"); + return (double)40.0F; + } else if (JIANG_PAIS.contains(value)) { + System.out.println(" [258将] " + this.formatCard(card) + " 是258将对子,扣30分"); + return (double)-30.0F; + } else { + System.out.println(" [对子] " + this.formatCard(card) + " 是普通对子,扣10分"); + return (double)-10.0F; + } + } else { + System.out.println(" [边张对子] " + this.formatCard(card) + " 是边张对子,加60分鼓励打出"); + return (double)60.0F; + } + } else { + System.out.println(" [单张] " + this.formatCard(card) + " 是单张"); + return (double)0.0F; } - - // 如果有2张,是对子 - if (originalCount == 2) { - int type = getCardType(card); - int value = getCardValue(card); - - // 特别处理边张对子(1万或9万) - if (value == 1 || value == 9) { - // 边张对子价值最低,特别鼓励打出 - System.out.println(" [边张对子] " + formatCard(card) + " 是边张对子,加60分鼓励打出"); - return 60.0; // 增加边张对子的鼓励分 - } - - // 检查这个对子是否在顺子中且是多余的 - if (isRedundantPairInSequence(card, state.handCards)) { - System.out.println(" [冗余] " + formatCard(card) + " 是顺子中的冗余对子,加40分鼓励打出"); - return 40.0; - } - - // 258将的对子要保留 - if (JIANG_PAIS.contains(value)) { - System.out.println(" [258将] " + formatCard(card) + " 是258将对子,扣30分"); - return -30.0; - } - - // 普通对子 - System.out.println(" [对子] " + formatCard(card) + " 是普通对子,扣10分"); - return -10.0; - } - - System.out.println(" [单张] " + formatCard(card) + " 是单张"); - return 0.0; } + private boolean isRedundantPairInSequence(int card, List originalHand) { - int type = getCardType(card); - int value = getCardValue(card); + int type = this.getCardType(card); + int value = this.getCardValue(card); + if (type > 3) { + return false; + } else { + int count = Collections.frequency(originalHand, card); + if (count < 2) { + return false; + } else { + System.out.println(" [检查冗余] " + this.formatCard(card) + " 是" + count + "张的对子"); + if (value == 1) { + int card2 = card + 1; + int card3 = card + 2; + if (originalHand.contains(card2) && originalHand.contains(card3)) { + System.out.println(" [边张冗余] " + this.formatCard(card) + " 是1万且在1-2-3顺子中,应该优先打出"); + return true; + } + } else if (value == 9) { + int card7 = card - 2; + int card8 = card - 1; + if (originalHand.contains(card7) && originalHand.contains(card8)) { + System.out.println(" [边张冗余] " + this.formatCard(card) + " 是9万且在7-8-9顺子中,应该优先打出"); + return true; + } + } - if (type > 3) return false; + if (value == 3) { + int card1 = card - 2; + int card2 = card - 1; + boolean has1 = originalHand.contains(card1); + boolean has2 = originalHand.contains(card2); + System.out.println(" [顺子检查] 1万=" + has1 + ", 2万=" + has2); + if (has1 && has2) { + System.out.println(" [确定冗余] " + this.formatCard(card) + " 在1-2-3顺子中是多余的"); + return true; + } + } - // 统计原始手牌中该牌的数量 - int count = Collections.frequency(originalHand, card); - if (count < 2) return false; // 不是对子 + if (value >= 1 && value <= 7) { + int next1 = card + 1; + int next2 = card + 2; + if (originalHand.contains(next1) && originalHand.contains(next2)) { + System.out.println(" [确定冗余] " + this.formatCard(card) + " 在" + value + "-" + this.getCardValue(next1) + "-" + this.getCardValue(next2) + "顺子中是多余的"); + return true; + } + } - System.out.println(" [检查冗余] " + formatCard(card) + " 是" + count + "张的对子"); + if (value >= 2 && value <= 8) { + int prev = card - 1; + int next = card + 1; + if (originalHand.contains(prev) && originalHand.contains(next)) { + System.out.println(" [确定冗余] " + this.formatCard(card) + " 在" + this.getCardValue(prev) + "-" + value + "-" + this.getCardValue(next) + "顺子中是多余的"); + return true; + } + } - // 特别处理边张(1万或9万) - if (value == 1) { - // 1万对子,检查是否能和2、3形成顺子 - int card2 = card + 1; - int card3 = card + 2; - if (originalHand.contains(card2) && originalHand.contains(card3)) { - System.out.println(" [边张冗余] " + formatCard(card) + " 是1万且在1-2-3顺子中,应该优先打出"); - return true; - } - } else if (value == 9) { - // 9万对子,检查是否能和7、8形成顺子 - int card7 = card - 2; - int card8 = card - 1; - if (originalHand.contains(card7) && originalHand.contains(card8)) { - System.out.println(" [边张冗余] " + formatCard(card) + " 是9万且在7-8-9顺子中,应该优先打出"); - return true; + if (value >= 3 && value <= 9) { + int prev1 = card - 1; + int prev2 = card - 2; + if (originalHand.contains(prev1) && originalHand.contains(prev2)) { + System.out.println(" [确定冗余] " + this.formatCard(card) + " 在" + this.getCardValue(prev2) + "-" + this.getCardValue(prev1) + "-" + value + "顺子中是多余的"); + return true; + } + } + + return false; } } - - // 对于3万,检查是否有1万和2万形成顺子 - if (value == 3) { - int card1 = card - 2; // 1万 - int card2 = card - 1; // 2万 - - boolean has1 = originalHand.contains(card1); - boolean has2 = originalHand.contains(card2); - - System.out.println(" [顺子检查] 1万=" + has1 + ", 2万=" + has2); - - // 如果有1万和2万,且3万有2张,那么多余的1张3万应该打出 - if (has1 && has2) { - System.out.println(" [确定冗余] " + formatCard(card) + " 在1-2-3顺子中是多余的"); - return true; - } - } - - // 检查其他顺子情况 - if (value >= 1 && value <= 7) { - int next1 = card + 1; - int next2 = card + 2; - if (originalHand.contains(next1) && originalHand.contains(next2)) { - System.out.println(" [确定冗余] " + formatCard(card) + " 在" + value + "-" + - getCardValue(next1) + "-" + getCardValue(next2) + "顺子中是多余的"); - return true; - } - } - - if (value >= 2 && value <= 8) { - int prev = card - 1; - int next = card + 1; - if (originalHand.contains(prev) && originalHand.contains(next)) { - System.out.println(" [确定冗余] " + formatCard(card) + " 在" + - getCardValue(prev) + "-" + value + "-" + getCardValue(next) + "顺子中是多余的"); - return true; - } - } - - if (value >= 3 && value <= 9) { - int prev1 = card - 1; - int prev2 = card - 2; - if (originalHand.contains(prev1) && originalHand.contains(prev2)) { - System.out.println(" [确定冗余] " + formatCard(card) + " 在" + - getCardValue(prev2) + "-" + getCardValue(prev1) + "-" + value + "顺子中是多余的"); - return true; - } - } - - return false; } - private boolean isAbsolutelyLonelyFinal(int card, List hand) { - int type = getCardType(card); - int value = getCardValue(card); - - if (type > 3) return false; - - // 检查同花色牌 - for (int other : hand) { - if (getCardType(other) == type) { - int diff = Math.abs(getCardValue(other) - value); - if (diff <= 2) { // 相邻或隔张 - return false; + int type = this.getCardType(card); + int value = this.getCardValue(card); + if (type > 3) { + return false; + } else { + for(int other : hand) { + if (this.getCardType(other) == type) { + int diff = Math.abs(this.getCardValue(other) - value); + if (diff <= 2) { + return false; + } } } - } - return true; + return true; + } } - // 以下是原有方法,保持不变... - // checkTingCardsWithState, canHuWithState, canHuWith258, canFormMelds, - // getReasonableHuCards, getStateKey, getCardValue, getCardType, - // formatCard, formatCards - - /** - * 检查听牌情况 - */ public List checkTingCardsWithState(PlayerState state) { - String stateKey = getStateKey(state); - if (tingCache.containsKey(stateKey)) { - return tingCache.get(stateKey); - } + String stateKey = this.getStateKey(state); + if (this.tingCache.containsKey(stateKey)) { + return (List)this.tingCache.get(stateKey); + } else { + List tingCards = new ArrayList(); + int pongGroups = state.getPongGroupCount(); + int gangGroups = state.getGangGroupCount(); + int chiGroups = state.getChiGroupCount(); + int usedCardsCount = pongGroups * 3 + gangGroups * 4 + chiGroups * 3; + int expectedHandCount = 14 - usedCardsCount - 1; + if (state.handCards.size() != expectedHandCount) { + this.tingCache.put(stateKey, tingCards); + return tingCards; + } else { + for(int huCard : this.getReasonableHuCards(state.handCards)) { + if (this.canHuWithState(state, huCard)) { + tingCards.add(huCard); + } + } - List tingCards = new ArrayList<>(); - - int pongGroups = state.getPongGroupCount(); - int gangGroups = state.getGangGroupCount(); - int chiGroups = state.getChiGroupCount(); - - // 计算已使用的牌数 - int usedCardsCount = pongGroups * 3 + gangGroups * 4 + chiGroups * 3; - - // 听牌时手牌数量应为:14 - 已使用的牌数 - 1(要胡的牌) - int expectedHandCount = 14 - usedCardsCount - 1; - - if (state.handCards.size() != expectedHandCount) { - tingCache.put(stateKey, tingCards); - return tingCards; - } - - // 获取合理的胡牌候选 - Set possibleHuCards = getReasonableHuCards(state.handCards); - - for (int huCard : possibleHuCards) { - if (canHuWithState(state, huCard)) { - tingCards.add(huCard); + this.tingCache.put(stateKey, tingCards); + return tingCards; } } - - tingCache.put(stateKey, tingCards); - return tingCards; } - /** - * 胡牌检查 - */ private boolean canHuWithState(PlayerState state, int huCard) { int totalHandCards = state.handCards.size() + 1; int pongCardsCount = state.getPongGroupCount() * 3; int gangCardsCount = state.getGangGroupCount() * 4; int chiCardsCount = state.getChiGroupCount() * 3; int totalCards = totalHandCards + pongCardsCount + gangCardsCount + chiCardsCount; - if (totalCards != 14) { return false; + } else { + List remainingHand = new ArrayList(state.handCards); + remainingHand.add(huCard); + Collections.sort(remainingHand); + return this.canHuWith258(remainingHand); } - - List remainingHand = new ArrayList<>(state.handCards); - remainingHand.add(huCard); - Collections.sort(remainingHand); - - return canHuWith258(remainingHand); } - /** - * 检查手牌是否能胡牌(考虑258将) - */ private boolean canHuWith258(List hand) { if (hand.size() != 2 && hand.size() != 5 && hand.size() != 8 && hand.size() != 11 && hand.size() != 14) { return false; - } + } else { + List sortedHand = new ArrayList(hand); + Collections.sort(sortedHand); - List sortedHand = new ArrayList<>(hand); - Collections.sort(sortedHand); - - // 尝试所有可能的将牌 - for (int i = 0; i < sortedHand.size() - 1; i++) { - if (sortedHand.get(i).equals(sortedHand.get(i + 1))) { - int cardValue = getCardValue(sortedHand.get(i)); - if (JIANG_PAIS.contains(cardValue)) { - List tempHand = new ArrayList<>(sortedHand); - tempHand.remove(i); - tempHand.remove(i); - - if (canFormMelds(tempHand)) { - return true; + for(int i = 0; i < sortedHand.size() - 1; ++i) { + if (((Integer)sortedHand.get(i)).equals(sortedHand.get(i + 1))) { + int cardValue = this.getCardValue((Integer)sortedHand.get(i)); + if (JIANG_PAIS.contains(cardValue)) { + List tempHand = new ArrayList(sortedHand); + tempHand.remove(i); + tempHand.remove(i); + if (this.canFormMelds(tempHand)) { + return true; + } } } } - } - return false; + return false; + } } - /** - * 检查是否能组成全部顺子/刻子 - */ private boolean canFormMelds(List hand) { if (hand.isEmpty()) { return true; - } - - List sorted = new ArrayList<>(hand); - Collections.sort(sorted); - - // 尝试刻子 - if (sorted.size() >= 3 && - sorted.get(0).equals(sorted.get(1)) && - sorted.get(1).equals(sorted.get(2))) { - List newHand = new ArrayList<>(sorted); - newHand.remove(0); - newHand.remove(0); - newHand.remove(0); - if (canFormMelds(newHand)) { - return true; + } else { + List sorted = new ArrayList(hand); + Collections.sort(sorted); + if (sorted.size() >= 3 && ((Integer)sorted.get(0)).equals(sorted.get(1)) && ((Integer)sorted.get(1)).equals(sorted.get(2))) { + List newHand = new ArrayList(sorted); + newHand.remove(0); + newHand.remove(0); + newHand.remove(0); + if (this.canFormMelds(newHand)) { + return true; + } } - } - // 尝试顺子 - if (sorted.size() >= 3) { - int first = sorted.get(0); - int type = getCardType(first); - - if (type <= 3) { - int second = first + 1; - int third = first + 2; - - if (sorted.contains(second) && sorted.contains(third)) { - List newHand = new ArrayList<>(sorted); - newHand.remove((Integer) first); - newHand.remove((Integer) second); - newHand.remove((Integer) third); - if (canFormMelds(newHand)) { - return true; + if (sorted.size() >= 3) { + int first = (Integer)sorted.get(0); + int type = this.getCardType(first); + if (type <= 3) { + int second = first + 1; + int third = first + 2; + if (sorted.contains(second) && sorted.contains(third)) { + List newHand = new ArrayList(sorted); + newHand.remove(first); + newHand.remove(second); + newHand.remove(third); + if (this.canFormMelds(newHand)) { + return true; + } } } } + + return false; + } + } + + private Set getReasonableHuCards(List hand) { + Set possibleCards = new HashSet(); + + for(int card : hand) { + possibleCards.add(card); + int type = this.getCardType(card); + if (type <= 3) { + int value = this.getCardValue(card); + if (value > 1) { + possibleCards.add(card - 1); + } + + if (value < 9) { + possibleCards.add(card + 1); + } + } } - return false; - } - - /** - * 获取合理的胡牌候选 - */ - private Set getReasonableHuCards(List hand) { - Set possibleCards = new HashSet<>(); - - // 现有牌和相邻牌 - for (int card : hand) { - possibleCards.add(card); - int type = getCardType(card); - if (type <= 3) { - int value = getCardValue(card); - if (value > 1) possibleCards.add(card - 1); - if (value < 9) possibleCards.add(card + 1); - } - } - - // 所有258牌 - for (int type = 1; type <= 3; type++) { - for (int jiang : JIANG_PAIS) { + for(int type = 1; type <= 3; ++type) { + for(int jiang : JIANG_PAIS) { possibleCards.add(type * 100 + jiang); } } - // 限制数量 if (possibleCards.size() > 15) { - List list = new ArrayList<>(possibleCards); + List list = new ArrayList(possibleCards); Collections.shuffle(list); - possibleCards = new HashSet<>(list.subList(0, 15)); + possibleCards = new HashSet(list.subList(0, 15)); } return possibleCards; } - /** - * 生成状态key - */ private String getStateKey(PlayerState state) { - List sortedHand = new ArrayList<>(state.handCards); + List sortedHand = new ArrayList(state.handCards); Collections.sort(sortedHand); + List pongKeys = new ArrayList(); - List pongKeys = new ArrayList<>(); - for (List group : state.pongGroups) { - List sorted = new ArrayList<>(group); + for(List group : state.pongGroups) { + List sorted = new ArrayList(group); Collections.sort(sorted); pongKeys.add(sorted.toString()); } - Collections.sort(pongKeys); - List gangKeys = new ArrayList<>(); - for (List group : state.gangGroups) { - List sorted = new ArrayList<>(group); + Collections.sort(pongKeys); + List gangKeys = new ArrayList(); + + for(List group : state.gangGroups) { + List sorted = new ArrayList(group); Collections.sort(sorted); gangKeys.add(sorted.toString()); } - Collections.sort(gangKeys); - List chiKeys = new ArrayList<>(); - for (List group : state.chiGroups) { - List sorted = new ArrayList<>(group); + Collections.sort(gangKeys); + List chiKeys = new ArrayList(); + + for(List group : state.chiGroups) { + List sorted = new ArrayList(group); Collections.sort(sorted); chiKeys.add(sorted.toString()); } - Collections.sort(chiKeys); - return sortedHand.toString() + "|" + - pongKeys.toString() + "|" + - gangKeys.toString() + "|" + - chiKeys.toString(); + Collections.sort(chiKeys); + return sortedHand.toString() + "|" + pongKeys.toString() + "|" + gangKeys.toString() + "|" + chiKeys.toString(); } - /** - * 获取牌值 - */ private int getCardValue(int card) { return card % 10; } - /** - * 获取牌类型 - */ private int getCardType(int card) { return card / 100; } - /** - * 格式化单张牌 - */ String formatCard(int card) { int type = card / 100; int value = card % 10; @@ -912,18 +729,18 @@ public class ai { } } - /** - * 格式化牌列表 - */ private String formatCards(List cards) { - return cards.stream() - .map(this::formatCard) - .collect(Collectors.joining(" ")); + return (String)cards.stream().map(this::formatCard).collect(Collectors.joining(" ")); + } + + static { + Set jiangSet = new HashSet(); + jiangSet.add(2); + jiangSet.add(5); + jiangSet.add(8); + JIANG_PAIS = Collections.unmodifiableSet(jiangSet); } - /** - * 出牌结果类 - */ private static class DiscardResult { double score; List tingCards; @@ -935,4 +752,4 @@ public class ai { this.searchDepth = searchDepth; } } -} \ No newline at end of file +}