机器人连接0130

master
zhouwei 2026-01-30 06:47:13 +08:00
parent 18a90dd89d
commit 14e81d42ab
22 changed files with 4126 additions and 2032 deletions

View File

@ -116,8 +116,8 @@ public class MainServer extends Extension {
}
Map<Integer, Integer> gameRobotConfig = new HashMap<>();
gameRobotConfig.put(10, 5);
gameRobotConfig.put(22, 3);
gameRobotConfig.put(10, 1);
gameRobotConfig.put(22, 0);
int robotIndex = 0;
//长沙麻将机器人

View File

@ -45,7 +45,7 @@ public class RobotManager {
private final Map<Integer, Integer> wanfaRobotIndex = new ConcurrentHashMap<>();
//玩法房间轮询器
private final Map<Integer, RoomWanfaMatcher> wanfaRoomPollers = new ConcurrentHashMap<>();
private final Map<String, RoomWanfaMatcher> 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<String, String> 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;
}
}
}

View File

@ -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<Integer, RobotAccountInfo> robotAccounts = new ConcurrentHashMap<>();
//机器人ID到连接ID的映射用于标识每个机器人连接的唯一性
private final Map<Integer, String> robotConnectionIds = new ConcurrentHashMap<>();
//机器人管理器引用
private final RobotManager robotManager;
//心跳和重连
private final Map<Integer, ScheduledFuture<?>> heartbeatTasks = new ConcurrentHashMap<>();
private final Map<Integer, String> 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<Boolean> 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<Boolean> 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 {
}
});
}
}

View File

@ -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<String> 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<String> roomIds = jedis0.keys("room:*");
if (roomIds.isEmpty()) {
createRobotWanfaRoomTCP(groupId, this.wanfaId);
return;
}
}
//满人房间
int fullRooms = 0;
//所有房间
int totalRooms = 0;
List<String> 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<String> 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_robotstart
*/
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);

View File

@ -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));

View File

@ -114,7 +114,8 @@ public abstract class MainServer extends Extension implements IEventListener{
* @param recipients session
*/
public void sendEvent(String cmdName, ITObject params, List<Session> recipients) {
TPServer.me().getController().sendEvent(cmdName, params, recipients);
TPServer.me().getController().sendEv
ent(cmdName, params, recipients);
}
/**

View File

@ -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);
}

View File

@ -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<Session> list = new ArrayList<Session>();
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<Integer, Player> 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);
}

View File

@ -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.*;

View File

@ -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);

View File

@ -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);
}

View File

@ -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";
}

View File

@ -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<MessageResponse>() {
@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_csTCP
*/
@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);
}
}
/**
* IDID
*/
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;
}
}

View File

@ -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<String, ScheduledFuture<?>> 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();
}

View File

@ -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<MessageResponse> 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);
}
}
}
}

View File

@ -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<String, TaurusClient> gameClients;
private Map<String, TaurusClient> 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<Integer, List<Integer>> playerOutcardsMap = new HashMap<>();
private Map<Integer, List<Integer>> playerchisMap = new HashMap<>();
private Map<Integer, List<Integer>> playerpengsMap = new HashMap<>();
private Map<Integer, List<Integer>> playermingsMap = new HashMap<>();
private Map<Integer, List<Integer>> playerzisMap = new HashMap<>();
// 全局变量定义
private int groupId = 0;
private int pid = 0;
private int playerId = 0;
private String playKey = "";
private Map<Integer, Integer> count = new HashMap<Integer, Integer>();
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<String> 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<String, TaurusClient> 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<String, String> 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<Integer>
List<Integer> 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<Integer> 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<Integer> 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<Integer> 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<Integer> 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<String> roomIds = jedis0.keys("room:*");
Set<String> roomIds = jedis22.keys("room:*");
String roomKey = "";
//获取所有机器人ID
//拿到所有的机器人
List<Integer> 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<String> 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);
});
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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<Integer> changShaCardInhand = new ArrayList<>();
private final List<Integer> changShaCardInhandgang = new ArrayList<>();
//长沙麻将出过的牌
private final List<Integer> changShachuguopai = new ArrayList<>();
private final Map<Integer, Integer> chuGuoPainum = new HashMap<>();
//杠的牌
private final List<Integer> gangdepai = new ArrayList<>();
//碰牌
private final List<Integer> pongGroup = new ArrayList<>();
//吃牌
private final List<Integer> 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<Integer> getChangShaCardInhand() {
return changShaCardInhand;
}
public List<Integer> getGangdepai() {
return gangdepai;
}
public List<Integer> getChangShaCardInhandgang() {
return changShaCardInhandgang;
}
public List<Integer> getPongGroup() {
return pongGroup;
}
public List<Integer> getChowGroup() {
return chowGroup;
}
public List<Integer> getChuGuoCardInhand() {
return changShachuguopai;
}
public Map<Integer, Integer> 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<Integer> yupanhandcard = new ArrayList<>(changShaCardInhand);
//进行操作之前能否下听
List<Integer> 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<Integer> gangusecars = new ArrayList<>(changShaCardInhand);
if (type == 3) {
Util.removeCard(gangusecars,card,3);
List<Integer> shifoutingpai3 = ChangShaSuanFaTest.handscardshifoutingpai(gangusecars,chowGroup,pongGroup,gangdepai);
log.info(shifoutingpai3);
if (!shifoutingpai3.isEmpty()) {
//开杠
//判断是否杠了之后没有牌可以听
List<Integer> 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<Integer> shifoutingpai4 = ChangShaSuanFaTest.handscardshifoutingpai(gangusecars,chowGroup,pongGroup,gangdepai);
if (!shifoutingpai4.isEmpty()) {
//开杠
//判断是否杠了之后没有牌可以听
List<Integer> 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<Integer> shifoutingpai5 = ChangShaSuanFaTest.handscardshifoutingpai(gangusecars,chowGroup,pongGroup,gangdepai);
if (!shifoutingpai5.isEmpty()) {
//开杠
//判断是否杠了之后没有牌可以听
List<Integer> 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<Integer> allSeeCard = new ArrayList<>();
Map<Integer,ITObject> pingfenResult = new HashMap<>();
Map<Integer,ITObject> 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<Integer,ITObject> 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<String, Object> map = new HashMap<>();
List<Integer> 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<Integer, List<Integer>> playerOutcardsMap,
Map<Integer, List<Integer>> playerchisMap, Map<Integer, List<Integer>> playerpengsMap,
Map<Integer, List<Integer>> playermingsMap, Map<Integer, List<Integer>> playerzisMap) {
//整合所有其他玩家的出牌信息
List<Integer> resultList = new ArrayList<>();
for (List<Integer> cards : playerOutcardsMap.values()) {
resultList.addAll(cards);
}
for (List<Integer> chis : playerchisMap.values()) {
resultList.addAll(chis);
}
for (List<Integer> pengs : playerpengsMap.values()) {
resultList.addAll(pengs);
}
for (List<Integer> minggangs : playermingsMap.values()) {
resultList.addAll(minggangs);
}
for (List<Integer> 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<Integer> 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<Integer> 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");
}
}
}

View File

@ -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<Integer> 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<Integer>) map.get("cardResiue")).size());
System.out.println("checkNormalHu 手数 " + map.get("remainingMelds"));
System.out.println("checkNormalHu 孤章数量 " + ((List<Integer>) map.get("cardResiue")).size());
System.out.println("checkNormalHu 手数 " + map.get("remainingMelds"));
//碰之后
@ -134,7 +134,7 @@ public class TinHuChi {
// List<Integer> checktingpai1 = TinHuChi.checktingpai(temphand);
// System.out.println("checktingpai1" + checktingpai1);
//hand1.add(101);
//hand1.add(101);
// Map<String, Object> map = new HashMap<>();
// Map<String, Object> 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;

View File

@ -0,0 +1,404 @@
package taurus.util;
import java.util.*;
public class TinHuGang {
/**
*
*/
public static boolean canGang(List<Integer> 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<Integer> handCards, int gangCard, boolean isMingGang) {
int type = gangCard / 100;
int value = gangCard % 100;
System.out.println("\n模拟" + (isMingGang ? "明" : "暗") + "杠" + value + getTypeName(type) + ":");
// 1. 模拟杠牌:移除手牌中的牌
List<Integer> 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<Integer> tingCards = getTingCards(afterGang, needs258);
if (!tingCards.isEmpty()) {
System.out.print(" 听" + tingCards.size() + "张牌: ");
List<String> 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<Integer> hand, boolean needs258) {
if (hand.size() != 10) {
return false;
}
// 听牌状态:再摸任何一张牌都能胡牌
// 我们需要检查是否至少有一张牌能让这手牌胡牌
Set<Integer> allCards = getAllCards();
int tingCount = 0;
for (int testCard : allCards) {
List<Integer> tempHand = new ArrayList<>(hand);
tempHand.add(testCard);
if (canHu(tempHand, needs258)) {
tingCount++;
}
}
System.out.println(" 可胡" + tingCount + "张牌");
return tingCount > 0;
}
/**
*
*/
private static Set<Integer> getTingCards(List<Integer> hand, boolean needs258) {
Set<Integer> tingCards = new HashSet<>();
if (hand.size() != 10) {
return tingCards;
}
Set<Integer> allCards = getAllCards();
for (int testCard : allCards) {
List<Integer> tempHand = new ArrayList<>(hand);
tempHand.add(testCard);
if (canHu(tempHand, needs258)) {
tingCards.add(testCard);
}
}
return tingCards;
}
/**
*
*/
private static boolean canHu(List<Integer> handCards, boolean needs258) {
// 手牌排序
List<Integer> 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<Integer> handCards) {
if (handCards.size() != 14) return false;
Map<Integer, Integer> 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<Integer> 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<Integer> 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<Integer> handCards) {
return checkNormalHuRecursive(new ArrayList<>(handCards), false);
}
private static boolean checkNormalHuRecursive(List<Integer> 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<Integer> 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<Integer> 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<Integer> 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<Integer> cards) {
if (cards.isEmpty()) {
return true; // 所有牌都分组成功
}
List<Integer> sorted = new ArrayList<>(cards);
Collections.sort(sorted);
int firstCard = sorted.get(0);
int count = Collections.frequency(sorted, firstCard);
// 尝试作为刻子(三张相同)
if (count >= 3) {
List<Integer> 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<Integer> 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<Integer> hand) {
Map<Integer, Integer> 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<Integer> getAllCards() {
Set<Integer> 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<Integer> 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 "字";
}
}
}

View File

@ -0,0 +1,425 @@
package taurus.util;
import robot.mj.handler.HuNanChangSha;
import java.util.*;
public class TinHuPeng {
/**
*
*/
public boolean canPeng(List<Integer> 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<Integer> handCards, int card) {
// 碰牌手牌有2张相同的牌别人打出第3张
int count = 0;
for (int c : handCards) {
if (c == card) {
count++;
}
}
return count >= 2;
}
/**
*
*/
private boolean canTingAfterPengAndDiscard(List<Integer> handCards, int pengCard) {
// 1. 模拟碰牌:移除两张相同的牌,碰的牌不加入手牌
List<Integer> 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<Integer> uniqueCards = new HashSet<>(afterPeng);
boolean foundTing = false;
for (int discardCard : uniqueCards) {
List<Integer> 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<Integer> hand, boolean needs258, int targetSize) {
if (hand.size() != targetSize) {
System.out.print(" [手牌数" + hand.size() + "≠目标" + targetSize + "]");
return false;
}
// 获取所有可能的牌
Set<Integer> allCards = getAllCards();
for (int testCard : allCards) {
List<Integer> tempHand = new ArrayList<>(hand);
tempHand.add(testCard);
Collections.sort(tempHand);
// 检查是否能胡牌
if (canHu(tempHand, needs258)) {
return true;
}
}
return false;
}
/**
* 258
*/
private boolean canHu(List<Integer> handCards, boolean needs258) {
// 手牌排序
List<Integer> 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<Integer> hand, boolean needs258) {
Set<Integer> tingCards = new HashSet<>();
Set<Integer> allCards = getAllCards();
for (int testCard : allCards) {
List<Integer> tempHand = new ArrayList<>(hand);
tempHand.add(testCard);
if (canHu(tempHand, needs258)) {
tingCards.add(testCard);
}
}
if (!tingCards.isEmpty()) {
System.out.print(" 听" + tingCards.size() + "张牌: ");
List<String> 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<Integer> hand) {
// 统计万、筒、条各自的数量
Map<Integer, Integer> 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<Integer> handCards) {
if (handCards.size() != 14) return false;
Map<Integer, Integer> 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<Integer> 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<Integer> 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<Integer> handCards) {
return checkNormalHuRecursive(new ArrayList<>(handCards), false);
}
private boolean checkNormalHuRecursive(List<Integer> 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<Integer> 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<Integer> 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<Integer> 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<Integer> cards) {
if (cards.isEmpty()) {
return true; // 所有牌都分组成功
}
List<Integer> sorted = new ArrayList<>(cards);
Collections.sort(sorted);
int firstCard = sorted.get(0);
int count = Collections.frequency(sorted, firstCard);
// 尝试作为刻子(三张相同)
if (count >= 3) {
List<Integer> 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<Integer> 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<Integer> getAllCards() {
Set<Integer> 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<Integer> 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<Integer> singles = new ArrayList<>(); // 单张列表
boolean has258Jiang = false; // 是否有258将
List<Integer> pairs = new ArrayList<>(); // 对子列表value
}
}

File diff suppressed because it is too large Load Diff