add
parent
dbcb834b6f
commit
d63c0bac87
|
|
@ -9,6 +9,7 @@
|
||||||
<module name="web_group" />
|
<module name="web_group" />
|
||||||
<module name="game_mj_changsha" />
|
<module name="game_mj_changsha" />
|
||||||
<module name="taurus-core" />
|
<module name="taurus-core" />
|
||||||
|
<module name="robot_changma" />
|
||||||
<module name="taurus-permanent" />
|
<module name="taurus-permanent" />
|
||||||
<module name="robot_common" />
|
<module name="robot_common" />
|
||||||
<module name="taurus-web" />
|
<module name="taurus-web" />
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,14 @@
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="Encoding">
|
<component name="Encoding">
|
||||||
<file url="file://$PROJECT_DIR$/game_web/web_group/src/main/java" charset="UTF-8" />
|
<file url="file://$PROJECT_DIR$/game_web/web_group/src/main/java" charset="UTF-8" />
|
||||||
|
<file url="file://$PROJECT_DIR$/game_web/web_group/src/main/kotlin" charset="UTF-8" />
|
||||||
<file url="file://$PROJECT_DIR$/game_web/web_group/src/main/resources" charset="UTF-8" />
|
<file url="file://$PROJECT_DIR$/game_web/web_group/src/main/resources" charset="UTF-8" />
|
||||||
<file url="file://$PROJECT_DIR$/libs/data_cache/src/main/java" charset="UTF-8" />
|
<file url="file://$PROJECT_DIR$/libs/data_cache/src/main/java" charset="UTF-8" />
|
||||||
<file url="file://$PROJECT_DIR$/libs/data_cache/src/main/resources" charset="UTF-8" />
|
<file url="file://$PROJECT_DIR$/libs/data_cache/src/main/resources" charset="UTF-8" />
|
||||||
<file url="file://$PROJECT_DIR$/libs/game_common/src/main/java" charset="UTF-8" />
|
<file url="file://$PROJECT_DIR$/libs/game_common/src/main/java" charset="UTF-8" />
|
||||||
<file url="file://$PROJECT_DIR$/libs/game_common/src/main/resources" charset="UTF-8" />
|
<file url="file://$PROJECT_DIR$/libs/game_common/src/main/resources" charset="UTF-8" />
|
||||||
<file url="file://$PROJECT_DIR$/libs/robot_common/src/main/java" charset="UTF-8" />
|
<file url="file://$PROJECT_DIR$/libs/robot_common/src/main/java" charset="UTF-8" />
|
||||||
|
<file url="file://$PROJECT_DIR$/libs/robot_common/src/main/kotlin" charset="UTF-8" />
|
||||||
<file url="file://$PROJECT_DIR$/libs/robot_common/src/main/resources" charset="UTF-8" />
|
<file url="file://$PROJECT_DIR$/libs/robot_common/src/main/resources" charset="UTF-8" />
|
||||||
<file url="file://$PROJECT_DIR$/libs/taurus-server/src/main/java" charset="UTF-8" />
|
<file url="file://$PROJECT_DIR$/libs/taurus-server/src/main/java" charset="UTF-8" />
|
||||||
<file url="file://$PROJECT_DIR$/libs/taurus-server/src/main/resources" charset="UTF-8" />
|
<file url="file://$PROJECT_DIR$/libs/taurus-server/src/main/resources" charset="UTF-8" />
|
||||||
|
|
@ -19,5 +21,8 @@
|
||||||
<file url="file://$PROJECT_DIR$/libs/taurus-server/taurus-web/src/main/resources" charset="UTF-8" />
|
<file url="file://$PROJECT_DIR$/libs/taurus-server/taurus-web/src/main/resources" charset="UTF-8" />
|
||||||
<file url="file://$PROJECT_DIR$/majiang/changsha/game_mj_cs/src/main/java" charset="UTF-8" />
|
<file url="file://$PROJECT_DIR$/majiang/changsha/game_mj_cs/src/main/java" charset="UTF-8" />
|
||||||
<file url="file://$PROJECT_DIR$/majiang/changsha/game_mj_cs/src/main/resources" charset="UTF-8" />
|
<file url="file://$PROJECT_DIR$/majiang/changsha/game_mj_cs/src/main/resources" charset="UTF-8" />
|
||||||
|
<file url="file://$PROJECT_DIR$/robots/robotchangma/src/main/java" charset="UTF-8" />
|
||||||
|
<file url="file://$PROJECT_DIR$/robots/robotchangma/src/main/kotlin" charset="UTF-8" />
|
||||||
|
<file url="file://$PROJECT_DIR$/robots/robotchangma/src/main/resources" charset="UTF-8" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
<option value="$PROJECT_DIR$/game_web/web_group/pom.xml" />
|
<option value="$PROJECT_DIR$/game_web/web_group/pom.xml" />
|
||||||
<option value="$PROJECT_DIR$/libs/robot_common/pom.xml" />
|
<option value="$PROJECT_DIR$/libs/robot_common/pom.xml" />
|
||||||
<option value="$PROJECT_DIR$/majiang/changsha/game_mj_cs/pom.xml" />
|
<option value="$PROJECT_DIR$/majiang/changsha/game_mj_cs/pom.xml" />
|
||||||
|
<option value="$PROJECT_DIR$/robots/robotchangma/pom.xml" />
|
||||||
</list>
|
</list>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@
|
||||||
<db>
|
<db>
|
||||||
<name>db1</name>
|
<name>db1</name>
|
||||||
<driverName>com.mysql.cj.jdbc.Driver</driverName>
|
<driverName>com.mysql.cj.jdbc.Driver</driverName>
|
||||||
<jdbcUrl>jdbc:mysql://8.138.242.190:8060/wb_game</jdbcUrl>
|
<jdbcUrl>jdbc:mysql://47.109.55.7:8060/wb_game</jdbcUrl>
|
||||||
<userName>root</userName>
|
<userName>root</userName>
|
||||||
<password>6KYnXJjGhxNceF8e</password>
|
<password>6KYnXJjGhxNceF8e</password>
|
||||||
</db>
|
</db>
|
||||||
|
|
@ -86,15 +86,15 @@
|
||||||
</poolConfig>
|
</poolConfig>
|
||||||
|
|
||||||
<infos>
|
<infos>
|
||||||
<info name="group1_db0" host="8.138.242.190" password="cssq@2020" port="6379" database="0" timeout="5000"/>
|
<info name="group1_db0" host="r-2vc0x3clldkj94synhpd.redis.cn-chengdu.rds.aliyuncs.com" password="cssq@2020" port="6379" database="0" timeout="5000"/>
|
||||||
<info name="group1_db1" host="8.138.242.190" password="cssq@2020" port="6379" database="1" timeout="5000"/>
|
<info name="group1_db1" host="r-2vc0x3clldkj94synhpd.redis.cn-chengdu.rds.aliyuncs.com" password="cssq@2020" port="6379" database="1" timeout="5000"/>
|
||||||
<info name="group1_db2" host="8.138.242.190" password="cssq@2020" port="6379" database="2" timeout="5000"/>
|
<info name="group1_db2" host="r-2vc0x3clldkj94synhpd.redis.cn-chengdu.rds.aliyuncs.com" password="cssq@2020" port="6379" database="2" timeout="5000"/>
|
||||||
<info name="group1_db5" host="8.138.242.190" password="cssq@2020" port="6379" database="5" timeout="5000"/>
|
<info name="group1_db5" host="r-2vc0x3clldkj94synhpd.redis.cn-chengdu.rds.aliyuncs.com" password="cssq@2020" port="6379" database="5" timeout="5000"/>
|
||||||
<info name="group1_db8" host="8.138.242.190" password="cssq@2020" port="6379" database="8" timeout="5000"/>
|
<info name="group1_db8" host="r-2vc0x3clldkj94synhpd.redis.cn-chengdu.rds.aliyuncs.com" password="cssq@2020" port="6379" database="8" timeout="5000"/>
|
||||||
<info name="group1_db9" host="8.138.242.190" password="cssq@2020" port="6379" database="9" timeout="5000"/>
|
<info name="group1_db9" host="r-2vc0x3clldkj94synhpd.redis.cn-chengdu.rds.aliyuncs.com" password="cssq@2020" port="6379" database="9" timeout="5000"/>
|
||||||
<info name="group1_db10" host="8.138.242.190" password="cssq@2020" port="6379" database="10" timeout="5000"/>
|
<info name="group1_db10" host="r-2vc0x3clldkj94synhpd.redis.cn-chengdu.rds.aliyuncs.com" password="cssq@2020" port="6379" database="10" timeout="5000"/>
|
||||||
<info name="group1_db11" host="8.138.242.190" password="cssq@2020" port="6379" database="11" timeout="5000"/>
|
<info name="group1_db11" host="r-2vc0x3clldkj94synhpd.redis.cn-chengdu.rds.aliyuncs.com" password="cssq@2020" port="6379" database="11" timeout="5000"/>
|
||||||
<info name="tmp_group1_db9" host="8.138.242.190" password="654sads" port="6479" database="9" timeout="5000"/>
|
<info name="tmp_group1_db9" host="r-2vc0x3clldkj94synhpd.redis.cn-chengdu.rds.aliyuncs.com" password="654sads" port="6479" database="9" timeout="5000"/>
|
||||||
</infos>
|
</infos>
|
||||||
</plugin>
|
</plugin>
|
||||||
</serivce-core>
|
</serivce-core>
|
||||||
|
|
@ -42,6 +42,7 @@ public class MainServer extends Extension {
|
||||||
public MainServer() {
|
public MainServer() {
|
||||||
super();
|
super();
|
||||||
//3秒
|
//3秒
|
||||||
|
|
||||||
// NetManager.TIMEOUT_TIME =3;
|
// NetManager.TIMEOUT_TIME =3;
|
||||||
timeScheduler = new ScheduledThreadPoolExecutor(1);
|
timeScheduler = new ScheduledThreadPoolExecutor(1);
|
||||||
timeScheduler.scheduleAtFixedRate(new Runnable() {
|
timeScheduler.scheduleAtFixedRate(new Runnable() {
|
||||||
|
|
@ -120,6 +121,7 @@ public class MainServer extends Extension {
|
||||||
// 1. 先启动独立的事件处理线程(只启动一次)
|
// 1. 先启动独立的事件处理线程(只启动一次)
|
||||||
startNetEventThread();
|
startNetEventThread();
|
||||||
|
|
||||||
|
|
||||||
// 2. 定时器只负责创建机器人连接
|
// 2. 定时器只负责创建机器人连接
|
||||||
TPServer.me().getTimerPool().scheduleAtFixedRate(new Runnable() {
|
TPServer.me().getTimerPool().scheduleAtFixedRate(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -130,7 +132,9 @@ public class MainServer extends Extension {
|
||||||
// qingChu.qingchu();
|
// qingChu.qingchu();
|
||||||
|
|
||||||
//DoTest doTe = new DoTest();
|
//DoTest doTe = new DoTest();
|
||||||
//doTe.doTest();
|
//doTe.allLogin();
|
||||||
|
//批量登录
|
||||||
|
|
||||||
AddRoomRobot addRoomRobot = new AddRoomRobot();
|
AddRoomRobot addRoomRobot = new AddRoomRobot();
|
||||||
addRoomRobot.addRoom();
|
addRoomRobot.addRoom();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -126,7 +126,7 @@ public class UpdatePlayRoomJob implements Job{
|
||||||
String exist = Redis.use("group1_db1").get("robot_exist");
|
String exist = Redis.use("group1_db1").get("robot_exist");
|
||||||
if (StringUtil.isNotEmpty(exist))
|
if (StringUtil.isNotEmpty(exist))
|
||||||
{
|
{
|
||||||
createGroupRoom(groupId, pid);
|
//createGroupRoom(groupId, pid);
|
||||||
}
|
}
|
||||||
}finally {
|
}finally {
|
||||||
jedis.close();
|
jedis.close();
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@
|
||||||
<db>
|
<db>
|
||||||
<name>db1</name>
|
<name>db1</name>
|
||||||
<driverName>com.mysql.cj.jdbc.Driver</driverName>
|
<driverName>com.mysql.cj.jdbc.Driver</driverName>
|
||||||
<jdbcUrl>jdbc:mysql://8.138.242.190:8060/wb_game</jdbcUrl>
|
<jdbcUrl>jdbc:mysql://47.109.55.7:8060/wb_game</jdbcUrl>
|
||||||
<userName>root</userName>
|
<userName>root</userName>
|
||||||
<password>6KYnXJjGhxNceF8e</password>
|
<password>6KYnXJjGhxNceF8e</password>
|
||||||
</db>
|
</db>
|
||||||
|
|
@ -86,15 +86,15 @@
|
||||||
</poolConfig>
|
</poolConfig>
|
||||||
|
|
||||||
<infos>
|
<infos>
|
||||||
<info name="group1_db0" host="8.138.242.190" password="cssq@2020" port="6379" database="0" timeout="5000"/>
|
<info name="group1_db0" host="r-2vc0x3clldkj94synhpd.redis.cn-chengdu.rds.aliyuncs.com" password="KuaiLjzhu@2026" port="6379" database="0" timeout="5000"/>
|
||||||
<info name="group1_db1" host="8.138.242.190" password="cssq@2020" port="6379" database="1" timeout="5000"/>
|
<info name="group1_db1" host="r-2vc0x3clldkj94synhpd.redis.cn-chengdu.rds.aliyuncs.com" password="KuaiLjzhu@2026" port="6379" database="1" timeout="5000"/>
|
||||||
<info name="group1_db2" host="8.138.242.190" password="cssq@2020" port="6379" database="2" timeout="5000"/>
|
<info name="group1_db2" host="r-2vc0x3clldkj94synhpd.redis.cn-chengdu.rds.aliyuncs.com" password="KuaiLjzhu@2026" port="6379" database="2" timeout="5000"/>
|
||||||
<info name="group1_db5" host="8.138.242.190" password="cssq@2020" port="6379" database="5" timeout="5000"/>
|
<info name="group1_db5" host="r-2vc0x3clldkj94synhpd.redis.cn-chengdu.rds.aliyuncs.com" password="KuaiLjzhu@2026" port="6379" database="5" timeout="5000"/>
|
||||||
<info name="group1_db8" host="8.138.242.190" password="cssq@2020" port="6379" database="8" timeout="5000"/>
|
<info name="group1_db8" host="r-2vc0x3clldkj94synhpd.redis.cn-chengdu.rds.aliyuncs.com" password="KuaiLjzhu@2026" port="6379" database="8" timeout="5000"/>
|
||||||
<info name="group1_db9" host="8.138.242.190" password="cssq@2020" port="6379" database="9" timeout="5000"/>
|
<info name="group1_db9" host="r-2vc0x3clldkj94synhpd.redis.cn-chengdu.rds.aliyuncs.com" password="KuaiLjzhu@2026" port="6379" database="9" timeout="5000"/>
|
||||||
<info name="group1_db10" host="8.138.242.190" password="cssq@2020" port="6379" database="10" timeout="5000"/>
|
<info name="group1_db10" host="r-2vc0x3clldkj94synhpd.redis.cn-chengdu.rds.aliyuncs.com" password="KuaiLjzhu@2026" port="6379" database="10" timeout="5000"/>
|
||||||
<info name="group1_db11" host="8.138.242.190" password="cssq@2020" port="6379" database="11" timeout="5000"/>
|
<info name="group1_db11" host="r-2vc0x3clldkj94synhpd.redis.cn-chengdu.rds.aliyuncs.com" password="KuaiLjzhu@2026" port="6379" database="11" timeout="5000"/>
|
||||||
<info name="tmp_group1_db9" host="8.138.242.190" password="654sads" port="6479" database="9" timeout="5000"/>
|
<info name="tmp_group1_db9" host="r-2vc0x3clldkj94synhpd.redis.cn-chengdu.rds.aliyuncs.com" password="654sads" port="6479" database="9" timeout="5000"/>
|
||||||
</infos>
|
</infos>
|
||||||
</plugin>
|
</plugin>
|
||||||
</serivce-core>
|
</serivce-core>
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -14,10 +14,7 @@ import taurus.client.business.AccountBusiness;
|
||||||
import taurus.client.business.GroupRoomBusiness;
|
import taurus.client.business.GroupRoomBusiness;
|
||||||
import taurus.util.GroupDatas;
|
import taurus.util.GroupDatas;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public class DoTest {
|
public class DoTest {
|
||||||
|
|
||||||
|
|
@ -42,7 +39,7 @@ public class DoTest {
|
||||||
// 玩家ID
|
// 玩家ID
|
||||||
public static int playerId = 0;
|
public static int playerId = 0;
|
||||||
// 登录密码
|
// 登录密码
|
||||||
public static String password = "123456";
|
public static String password = "blm523";
|
||||||
// 会话标识
|
// 会话标识
|
||||||
public static String session = "";
|
public static String session = "";
|
||||||
// 访问令牌
|
// 访问令牌
|
||||||
|
|
@ -67,7 +64,29 @@ public class DoTest {
|
||||||
static final void get() {
|
static final void get() {
|
||||||
TObject data = GroupDatas.getData();
|
TObject data = GroupDatas.getData();
|
||||||
datas = data;
|
datas = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String allLogin() throws WebException {
|
||||||
|
Jedis jedis2 = Redis.use("group1_db2").getJedis();
|
||||||
|
|
||||||
|
// ITObject object = null;
|
||||||
|
AccountBusiness accountBusiness = null;
|
||||||
|
accountBusiness = new AccountBusiness();
|
||||||
|
//获取
|
||||||
|
Set<String> allRobotKeys = jedis2.keys("{robot}:*");
|
||||||
|
System.out.println(allRobotKeys);
|
||||||
|
for (String robotKey : allRobotKeys) {
|
||||||
|
ITObject object = null;
|
||||||
|
try {
|
||||||
|
//System.out.println("robotKey:"+robotKey.substring(robotKey.indexOf(":") + 1));
|
||||||
|
object = accountBusiness.idPasswordLogin(Integer.parseInt(robotKey.substring(robotKey.indexOf(":") + 1)), "blm523");
|
||||||
|
Thread.sleep(100);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
public String doTest() throws Exception {
|
public String doTest() throws Exception {
|
||||||
|
|
|
||||||
|
|
@ -1844,7 +1844,7 @@ public class HuNanChangSha {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
HuNanChangSha huNanChangSha = new HuNanChangSha();
|
HuNanChangSha huNanChangSha = new HuNanChangSha();
|
||||||
ITObject params = TObject.newInstance();
|
ITObject params = TObject.newInstance();
|
||||||
TaurusClient tc = new TaurusClient("8.138.242.190", "10", TaurusClient.ConnectionProtocol.Tcp);
|
TaurusClient tc = new TaurusClient("47.109.55.7", "10", TaurusClient.ConnectionProtocol.Tcp);
|
||||||
List<Integer> hands1 = new ArrayList<>();
|
List<Integer> hands1 = new ArrayList<>();
|
||||||
hands1.add(209);
|
hands1.add(209);
|
||||||
hands1.add(209);
|
hands1.add(209);
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ public class JiQiRens {
|
||||||
public static int seat = 0;
|
public static int seat = 0;
|
||||||
// 玩家ID
|
// 玩家ID
|
||||||
// 登录密码
|
// 登录密码
|
||||||
public static String password = "123456";
|
public static String password = "blm523";
|
||||||
// 会话标识
|
// 会话标识
|
||||||
private String session = "";
|
private String session = "";
|
||||||
// 访问令牌
|
// 访问令牌
|
||||||
|
|
@ -344,15 +344,15 @@ public class JiQiRens {
|
||||||
|
|
||||||
System.out.println(wanfaId);
|
System.out.println(wanfaId);
|
||||||
if ("22".equalsIgnoreCase(wanfaId)) { //湖南红中麻将
|
if ("22".equalsIgnoreCase(wanfaId)) { //湖南红中麻将
|
||||||
client = new TaurusClient("8.138.242.190:6421", "game", TaurusClient.ConnectionProtocol.Tcp);
|
client = new TaurusClient("47.109.55.7:6421", "game", TaurusClient.ConnectionProtocol.Tcp);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("10".equalsIgnoreCase(wanfaId)) {
|
if ("10".equalsIgnoreCase(wanfaId)) {
|
||||||
client = new TaurusClient("8.138.242.190:6311", "game", TaurusClient.ConnectionProtocol.Tcp);
|
client = new TaurusClient("47.109.55.7:6311", "game", TaurusClient.ConnectionProtocol.Tcp);
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
if ("66".equalsIgnoreCase(wanfaId)) {
|
if ("66".equalsIgnoreCase(wanfaId)) {
|
||||||
client = new TaurusClient("8.138.242.190:6841", "game", TaurusClient.ConnectionProtocol.Tcp);
|
client = new TaurusClient("47.109.55.7:6841", "game", TaurusClient.ConnectionProtocol.Tcp);
|
||||||
}
|
}
|
||||||
client.connect();
|
client.connect();
|
||||||
// }
|
// }
|
||||||
|
|
@ -513,7 +513,7 @@ public class JiQiRens {
|
||||||
count.put(key, count.getOrDefault(key, 0) + 1);
|
count.put(key, count.getOrDefault(key, 0) + 1);
|
||||||
|
|
||||||
|
|
||||||
jedis11.hincrBy(playKey1, "leftover_robot", -1);
|
//jedis11.hincrBy(playKey1, "leftover_robot", -1);
|
||||||
String sql = String.format("UPDATE `account` SET start = %d WHERE id = %d", 1, playerId);
|
String sql = String.format("UPDATE `account` SET start = %d WHERE id = %d", 1, playerId);
|
||||||
DataBase.use().executeUpdate(sql);
|
DataBase.use().executeUpdate(sql);
|
||||||
|
|
||||||
|
|
@ -601,7 +601,7 @@ public class JiQiRens {
|
||||||
Jedis jedis11s = Redis.use("group1_db11").getJedis();
|
Jedis jedis11s = Redis.use("group1_db11").getJedis();
|
||||||
try {
|
try {
|
||||||
String key = "g{" + groupId + "}:play:" + pid;
|
String key = "g{" + groupId + "}:play:" + pid;
|
||||||
jedis11s.hincrBy(key, "leftover_robot", 1);
|
// jedis11s.hincrBy(key, "leftover_robot", 1);
|
||||||
String sql = String.format("UPDATE `account` SET start = %d WHERE id = %d", 0, playerId);
|
String sql = String.format("UPDATE `account` SET start = %d WHERE id = %d", 0, playerId);
|
||||||
try {
|
try {
|
||||||
DataBase.use().executeUpdate(sql);
|
DataBase.use().executeUpdate(sql);
|
||||||
|
|
@ -667,10 +667,10 @@ public class JiQiRens {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Jedis jedis20 = Redis.use("group1_db11").getJedis();
|
//Jedis jedis20 = Redis.use("group1_db11").getJedis();
|
||||||
|
|
||||||
jedis20.hincrBy(key, "leftover_robot", 1);
|
// jedis20.hincrBy(key, "leftover_robot", 1);
|
||||||
jedis20.close();
|
// jedis20.close();
|
||||||
String sql = String.format("UPDATE `account` SET start = %d WHERE id = %d", 0, Integer.parseInt(s));
|
String sql = String.format("UPDATE `account` SET start = %d WHERE id = %d", 0, Integer.parseInt(s));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -739,7 +739,7 @@ public class JiQiRens {
|
||||||
Jedis jedis11s = Redis.use("group1_db11").getJedis();
|
Jedis jedis11s = Redis.use("group1_db11").getJedis();
|
||||||
try {
|
try {
|
||||||
String key = "g{" + groupId + "}:play:" + pid;
|
String key = "g{" + groupId + "}:play:" + pid;
|
||||||
jedis11s.hincrBy(key, "leftover_robot", 1);
|
// jedis11s.hincrBy(key, "leftover_robot", 1);
|
||||||
//
|
//
|
||||||
if (count != null && count.containsKey(pid)) {
|
if (count != null && count.containsKey(pid)) {
|
||||||
Integer currentValue = count.get(pid);
|
Integer currentValue = count.get(pid);
|
||||||
|
|
@ -768,11 +768,11 @@ public class JiQiRens {
|
||||||
String shangxianRobot = jedis11.hget(playKey, "shangxian_robot");
|
String shangxianRobot = jedis11.hget(playKey, "shangxian_robot");
|
||||||
String leftoverRobot = jedis11.hget(playKey, "leftover_robot");
|
String leftoverRobot = jedis11.hget(playKey, "leftover_robot");
|
||||||
|
|
||||||
if (shangxianRobot != null && leftoverRobot != null) {
|
/*if (shangxianRobot != null && leftoverRobot != null) {
|
||||||
if (value == 0) {
|
if (value == 0) {
|
||||||
jedis11.hset(playKey, "leftover_robot", shangxianRobot);
|
// jedis11.hset(playKey, "leftover_robot", shangxianRobot);
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// playerState.pongGroups.clear();;
|
// playerState.pongGroups.clear();;
|
||||||
|
|
@ -1114,10 +1114,10 @@ public class JiQiRens {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Jedis jedis20 = Redis.use("group1_db11").getJedis();
|
//Jedis jedis20 = Redis.use("group1_db11").getJedis();
|
||||||
|
|
||||||
jedis20.hincrBy(key, "leftover_robot", 1);
|
// jedis20.hincrBy(key, "leftover_robot", 1);
|
||||||
jedis20.close();
|
//jedis20.close();
|
||||||
String sql = String.format("UPDATE `account` SET start = %d WHERE id = %d", 0, Integer.parseInt(s));
|
String sql = String.format("UPDATE `account` SET start = %d WHERE id = %d", 0, Integer.parseInt(s));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -1156,7 +1156,7 @@ public class JiQiRens {
|
||||||
Jedis jedis11s = Redis.use("group1_db11").getJedis();
|
Jedis jedis11s = Redis.use("group1_db11").getJedis();
|
||||||
try {
|
try {
|
||||||
String key = "g{" + groupId + "}:play:" + pid;
|
String key = "g{" + groupId + "}:play:" + pid;
|
||||||
jedis11s.hincrBy(key, "leftover_robot", 1);
|
// jedis11s.hincrBy(key, "leftover_robot", 1);
|
||||||
//
|
//
|
||||||
if (count != null && count.containsKey(pid)) {
|
if (count != null && count.containsKey(pid)) {
|
||||||
Integer currentValue = count.get(pid);
|
Integer currentValue = count.get(pid);
|
||||||
|
|
@ -1187,11 +1187,11 @@ public class JiQiRens {
|
||||||
String shangxianRobot = jedis12.hget(playKey, "shangxian_robot");
|
String shangxianRobot = jedis12.hget(playKey, "shangxian_robot");
|
||||||
String leftoverRobot = jedis12.hget(playKey, "leftover_robot");
|
String leftoverRobot = jedis12.hget(playKey, "leftover_robot");
|
||||||
|
|
||||||
if (shangxianRobot != null && leftoverRobot != null) {
|
/*if (shangxianRobot != null && leftoverRobot != null) {
|
||||||
if (value == 0) {
|
if (value == 0) {
|
||||||
jedis12.hset(playKey, "leftover_robot", shangxianRobot);
|
jedis12.hset(playKey, "leftover_robot", shangxianRobot);
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
jedis12.close();
|
jedis12.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1376,7 +1376,7 @@ public class JiQiRens {
|
||||||
|
|
||||||
count.put(key, count.getOrDefault(key, 0) + 1);
|
count.put(key, count.getOrDefault(key, 0) + 1);
|
||||||
|
|
||||||
jedis11.hincrBy(playKey1, "leftover_robot", -1);
|
//jedis11.hincrBy(playKey1, "leftover_robot", -1);
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,7 @@ public class GroupRoomBusiness {
|
||||||
String fake_json = x_room_list.get(0);
|
String fake_json = x_room_list.get(0);
|
||||||
if (fake_json != null) {
|
if (fake_json != null) {
|
||||||
log.error("session:" + session + " public join room:" + room_key + " fail, fake != null");
|
log.error("session:" + session + " public join room:" + room_key + " fail, fake != null");
|
||||||
throw new WebException(ErrorCode.ROOM_CLOSE);
|
//throw new WebException(ErrorCode.ROOM_CLOSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
Jedis jedis0 = Redis.use("group1_db0").getJedis();
|
Jedis jedis0 = Redis.use("group1_db0").getJedis();
|
||||||
|
|
@ -125,7 +125,7 @@ public class GroupRoomBusiness {
|
||||||
if (StringUtil.isNotEmpty(oldRoom)) {
|
if (StringUtil.isNotEmpty(oldRoom)) {
|
||||||
String group = jedis0.hget(oldRoom, "group");
|
String group = jedis0.hget(oldRoom, "group");
|
||||||
if (StringUtil.isEmpty(group) || Integer.parseInt(group) != groupId) {
|
if (StringUtil.isEmpty(group) || Integer.parseInt(group) != groupId) {
|
||||||
throw new WebException(ErrorCode.GROUP_NOT_CURGROUP_ROOM);
|
//throw new WebException(ErrorCode.GROUP_NOT_CURGROUP_ROOM);
|
||||||
}
|
}
|
||||||
enter_old = true;
|
enter_old = true;
|
||||||
// finalRoom = oldRoom;
|
// finalRoom = oldRoom;
|
||||||
|
|
@ -140,13 +140,13 @@ public class GroupRoomBusiness {
|
||||||
log.info("room_list:" + room_list);
|
log.info("room_list:" + room_list);
|
||||||
String status = room_list.get(3);
|
String status = room_list.get(3);
|
||||||
if (StringUtil.isEmpty(status)) {
|
if (StringUtil.isEmpty(status)) {
|
||||||
throw new WebException(ErrorCode.GROUP_ROOM_DEL);
|
//throw new WebException(ErrorCode.GROUP_ROOM_DEL);
|
||||||
}
|
}
|
||||||
int _status = Integer.parseInt(status);
|
int _status = Integer.parseInt(status);
|
||||||
|
|
||||||
if (_status == 2 || _status == 3) {
|
if (_status == 2 || _status == 3) {
|
||||||
Utility.delRoomBySession(jedis0, session, finalRoom);
|
Utility.delRoomBySession(jedis0, session, finalRoom);
|
||||||
throw new WebException(ErrorCode.GROUP_ROOM_DEL);
|
//throw new WebException(ErrorCode.GROUP_ROOM_DEL);
|
||||||
}
|
}
|
||||||
|
|
||||||
String gm_key = GroupMemberCache.genKey(groupId, uid);
|
String gm_key = GroupMemberCache.genKey(groupId, uid);
|
||||||
|
|
@ -165,7 +165,7 @@ public class GroupRoomBusiness {
|
||||||
}
|
}
|
||||||
if (svr == null) {
|
if (svr == null) {
|
||||||
log.error("publicJoinRoom room_key:" + room_key + " gm_key:" + gm_key + " svr:" + svr);
|
log.error("publicJoinRoom room_key:" + room_key + " gm_key:" + gm_key + " svr:" + svr);
|
||||||
throw new WebException(ErrorCode.NO_SERVICE);
|
//throw new WebException(ErrorCode.NO_SERVICE);
|
||||||
}
|
}
|
||||||
jedis0.hset(finalRoom, "svr", svr);
|
jedis0.hset(finalRoom, "svr", svr);
|
||||||
log.info("step1:" + room_list);
|
log.info("step1:" + room_list);
|
||||||
|
|
@ -192,7 +192,7 @@ public class GroupRoomBusiness {
|
||||||
|
|
||||||
if (flag) {
|
if (flag) {
|
||||||
log.info("publicJoinRoom enter group fail, room full:" + gm_key + " room_key:" + room_key);
|
log.info("publicJoinRoom enter group fail, room full:" + gm_key + " room_key:" + room_key);
|
||||||
throw new WebException(ErrorCode.ROOM_CLOSE);
|
//throw new WebException(ErrorCode.ROOM_CLOSE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -200,7 +200,7 @@ public class GroupRoomBusiness {
|
||||||
String open = room_list.get(4);
|
String open = room_list.get(4);
|
||||||
if ((StringUtil.isEmpty(open) || open.equals("0"))) {
|
if ((StringUtil.isEmpty(open) || open.equals("0"))) {
|
||||||
log.info("publicJoinRoom enter group fail, room open close:" + gm_key + " room_key:" + room_key);
|
log.info("publicJoinRoom enter group fail, room open close:" + gm_key + " room_key:" + room_key);
|
||||||
throw new WebException(ErrorCode.ROOM_CLOSE);
|
//throw new WebException(ErrorCode.ROOM_CLOSE);
|
||||||
}
|
}
|
||||||
int AA = Integer.parseInt(room_list.get(10));
|
int AA = Integer.parseInt(room_list.get(10));
|
||||||
if (AA == 1) {
|
if (AA == 1) {
|
||||||
|
|
@ -210,24 +210,24 @@ public class GroupRoomBusiness {
|
||||||
Integer pay = gb.pay.get("pay" + opt + "_" + maxPlayers);
|
Integer pay = gb.pay.get("pay" + opt + "_" + maxPlayers);
|
||||||
if (pay == null) {
|
if (pay == null) {
|
||||||
log.warn("pay no set!");
|
log.warn("pay no set!");
|
||||||
throw new WebException(ErrorCode._FAILED);
|
//throw new WebException(ErrorCode._FAILED);
|
||||||
}
|
}
|
||||||
pay = (int) Math.ceil((double) pay / maxPlayers);
|
pay = (int) Math.ceil((double) pay / maxPlayers);
|
||||||
int result = Utility.checkRoomDiamo(jedis0, session, pay);
|
int result = Utility.checkRoomDiamo(jedis0, session, pay);
|
||||||
if (result != 0) {
|
if (result != 0) {
|
||||||
log.error("publicJoinRoom groupId:" + groupId + " no diamo");
|
log.error("publicJoinRoom groupId:" + groupId + " no diamo");
|
||||||
throw new WebException(result);
|
//throw new WebException(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("step4:" + enter_old);
|
log.info("step4:" + enter_old);
|
||||||
|
|
||||||
if (group_bean.ban == 1) {
|
if (group_bean.ban == 1) {
|
||||||
throw new WebException(ErrorCode.GROUP_BAN);
|
//throw new WebException(ErrorCode.GROUP_BAN);
|
||||||
}
|
}
|
||||||
GroupPlayBean gpb = GroupCache.getPlay(groupId, gpid);
|
GroupPlayBean gpb = GroupCache.getPlay(groupId, gpid);
|
||||||
if (gpb == null || gpb.ban == 1) {
|
if (gpb == null || gpb.ban == 1) {
|
||||||
throw new WebException(ErrorCode.GROUP_BAN_PLAY);
|
//throw new WebException(ErrorCode.GROUP_BAN_PLAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
Jedis jedis10 = Redis.use("group1_db10").getJedis();
|
Jedis jedis10 = Redis.use("group1_db10").getJedis();
|
||||||
|
|
@ -237,7 +237,7 @@ public class GroupRoomBusiness {
|
||||||
List<String> list = jedis10.hmget(gm_key, "ban", "hp");
|
List<String> list = jedis10.hmget(gm_key, "ban", "hp");
|
||||||
String ban = list.get(0);
|
String ban = list.get(0);
|
||||||
if (StringUtil.isNotEmpty(ban) && ban.equals("1")) {
|
if (StringUtil.isNotEmpty(ban) && ban.equals("1")) {
|
||||||
throw new WebException(ErrorCode.GROUP_MEMBER_BAN);
|
//throw new WebException(ErrorCode.GROUP_MEMBER_BAN);
|
||||||
}
|
}
|
||||||
log.info("step5:" + enter_old);
|
log.info("step5:" + enter_old);
|
||||||
|
|
||||||
|
|
@ -250,7 +250,7 @@ public class GroupRoomBusiness {
|
||||||
for (int i = 0; i < arr.size(); ++i) {
|
for (int i = 0; i < arr.size(); ++i) {
|
||||||
String tem = arr.getInt(i) + "";
|
String tem = arr.getInt(i) + "";
|
||||||
if (ban_set.contains(tem)) {
|
if (ban_set.contains(tem)) {
|
||||||
throw new WebException(ErrorCode.GROUP_BAN_DESK);
|
//throw new WebException(ErrorCode.GROUP_BAN_DESK);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -268,10 +268,10 @@ public class GroupRoomBusiness {
|
||||||
log.info("uid_hp:" + uid_hp);
|
log.info("uid_hp:" + uid_hp);
|
||||||
|
|
||||||
if (uid_hp > Integer.MAX_VALUE) {
|
if (uid_hp > Integer.MAX_VALUE) {
|
||||||
throw new WebException(ErrorCode.GROUP_PARTNER_HP_THAN_LIMIET);
|
//throw new WebException(ErrorCode.GROUP_PARTNER_HP_THAN_LIMIET);
|
||||||
}
|
}
|
||||||
// if (uid_hp <= 0) {
|
// if (uid_hp <= 0) {
|
||||||
// throw new WebException(ErrorCode.GROUP_LIMIT_NO_HP);
|
// //throw new WebException(ErrorCode.GROUP_LIMIT_NO_HP);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
if (hpOnOff == 1) {
|
if (hpOnOff == 1) {
|
||||||
|
|
@ -284,13 +284,13 @@ public class GroupRoomBusiness {
|
||||||
hp = "1";
|
hp = "1";
|
||||||
}
|
}
|
||||||
if (Long.parseLong(hp) > Integer.MAX_VALUE) {
|
if (Long.parseLong(hp) > Integer.MAX_VALUE) {
|
||||||
throw new WebException(ErrorCode.GROUP_PARTNER_HP_THAN_LIMIET);
|
//throw new WebException(ErrorCode.GROUP_PARTNER_HP_THAN_LIMIET);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean pass = Redis.use("group1_db1").sismember("pass_games", gameId + "");
|
boolean pass = Redis.use("group1_db1").sismember("pass_games", gameId + "");
|
||||||
if (!pass && StringUtil.isNotEmpty(limitInRoom)) {
|
if (!pass && StringUtil.isNotEmpty(limitInRoom)) {
|
||||||
if (Integer.parseInt(hp) < Integer.parseInt(limitInRoom)) {
|
if (Integer.parseInt(hp) < Integer.parseInt(limitInRoom)) {
|
||||||
throw new WebException(ErrorCode.GROUP_LIMIT_NO_HP);
|
//throw new WebException(ErrorCode.GROUP_LIMIT_NO_HP);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -387,19 +387,21 @@ public class GroupRoomBusiness {
|
||||||
*/
|
*/
|
||||||
public static ITObject joinRoom(int groupId, String roomid, String session, String platform) throws Exception {
|
public static ITObject joinRoom(int groupId, String roomid, String session, String platform) throws Exception {
|
||||||
log.error("玩家进入房间-begin");
|
log.error("玩家进入房间-begin");
|
||||||
|
|
||||||
String grooms_key = GroupCache.genRoomsKey(groupId);
|
String grooms_key = GroupCache.genRoomsKey(groupId);
|
||||||
|
log.error("grooms_key"+grooms_key);
|
||||||
String room_key = roomid;
|
String room_key = roomid;
|
||||||
|
log.error("room_key"+room_key);
|
||||||
Long tem = Redis.use("group1_db11").zrank(grooms_key, room_key);
|
Long tem = Redis.use("group1_db11").zrank(grooms_key, room_key);
|
||||||
if (tem == null) {
|
if (tem == null) {
|
||||||
throw new WebException(ErrorCode.NO_ROOM_NUM);
|
System.out.println(ErrorCode.NO_ROOM_NUM);
|
||||||
|
////throw new WebException(ErrorCode.NO_ROOM_NUM);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> room_list = Redis.use("group1_db0").hmget(room_key, "fake");
|
List<String> room_list = Redis.use("group1_db0").hmget(room_key, "fake");
|
||||||
String fake_json = room_list.get(0);
|
String fake_json = room_list.get(0);
|
||||||
if (fake_json != null) {
|
if (fake_json != null) {
|
||||||
log.error("fake_json:" + fake_json + " is null");
|
log.error("fake_json:" + fake_json + " is null");
|
||||||
throw new WebException(ErrorCode.ROOM_CLOSE);
|
//throw new WebException(ErrorCode.ROOM_CLOSE);
|
||||||
}
|
}
|
||||||
AccountBean acc = AccountCache.getAccount(session);
|
AccountBean acc = AccountCache.getAccount(session);
|
||||||
log.error("玩家进入房间:" + acc.id);
|
log.error("玩家进入房间:" + acc.id);
|
||||||
|
|
@ -408,7 +410,7 @@ public class GroupRoomBusiness {
|
||||||
String robotInfo = s_list.get(0);
|
String robotInfo = s_list.get(0);
|
||||||
if (StringUtil.isNotEmpty(robotInfo) && robotInfo.equals("9999")) {
|
if (StringUtil.isNotEmpty(robotInfo) && robotInfo.equals("9999")) {
|
||||||
log.error("robot:" + acc.id + " match room error");
|
log.error("robot:" + acc.id + " match room error");
|
||||||
throw new WebException(ErrorCode.ROOM_CLOSE);
|
////throw new WebException(ErrorCode.ROOM_CLOSE);
|
||||||
}
|
}
|
||||||
int uid = AccountCache.getAccount(session).id;
|
int uid = AccountCache.getAccount(session).id;
|
||||||
Jedis jedis10 = Redis.use("group1_db10").getJedis();
|
Jedis jedis10 = Redis.use("group1_db10").getJedis();
|
||||||
|
|
@ -430,7 +432,7 @@ public class GroupRoomBusiness {
|
||||||
// ITObject totalHp = GroupService.getHpTotal(groupId, par, 30);
|
// ITObject totalHp = GroupService.getHpTotal(groupId, par, 30);
|
||||||
// Long hp = totalHp.getLong("hp");
|
// Long hp = totalHp.getLong("hp");
|
||||||
// if (hp < autoscore) {
|
// if (hp < autoscore) {
|
||||||
// throw new WebException(ErrorCode.GROUP_PARTNER_HP_NOT_ENOUGH);
|
// //throw new WebException(ErrorCode.GROUP_PARTNER_HP_NOT_ENOUGH);
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
@ -476,7 +478,7 @@ public class GroupRoomBusiness {
|
||||||
} else {
|
} else {
|
||||||
log.error("xingyun:" + acc.id + " can't join rooms:" + room_key
|
log.error("xingyun:" + acc.id + " can't join rooms:" + room_key
|
||||||
+ " xingyun playerId:" + player_id);
|
+ " xingyun playerId:" + player_id);
|
||||||
throw new WebException(ErrorCode.GROUP_BAN_DESK);
|
//////throw new WebException(ErrorCode.GROUP_BAN_DESK);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -545,7 +547,7 @@ public class GroupRoomBusiness {
|
||||||
String robotInfo = s_list.get(0);
|
String robotInfo = s_list.get(0);
|
||||||
if (StringUtil.isNotEmpty(robotInfo) && robotInfo.equals("9999")) {
|
if (StringUtil.isNotEmpty(robotInfo) && robotInfo.equals("9999")) {
|
||||||
log.error("robot:" + acc.id + " match room error");
|
log.error("robot:" + acc.id + " match room error");
|
||||||
throw new WebException(ErrorCode.ROOM_CLOSE);
|
////throw new WebException(ErrorCode.ROOM_CLOSE);
|
||||||
}
|
}
|
||||||
int uid = acc.id;
|
int uid = acc.id;
|
||||||
Jedis jedis10 = Redis.use("group1_db10").getJedis();
|
Jedis jedis10 = Redis.use("group1_db10").getJedis();
|
||||||
|
|
@ -575,7 +577,7 @@ public class GroupRoomBusiness {
|
||||||
// ITObject totalHp = GroupService.getHpTotal(groupId, par, 30);
|
// ITObject totalHp = GroupService.getHpTotal(groupId, par, 30);
|
||||||
// Long hp = totalHp.getLong("hp");
|
// Long hp = totalHp.getLong("hp");
|
||||||
// if (hp < autoscore) {
|
// if (hp < autoscore) {
|
||||||
// throw new WebException(ErrorCode.GROUP_PARTNER_HP_NOT_ENOUGH);
|
// ////throw new WebException(ErrorCode.GROUP_PARTNER_HP_NOT_ENOUGH);
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
@ -929,7 +931,7 @@ public class GroupRoomBusiness {
|
||||||
try {
|
try {
|
||||||
GroupPlayBean gpb = GroupCache.getPlay(groupId, pid);
|
GroupPlayBean gpb = GroupCache.getPlay(groupId, pid);
|
||||||
if (gpb == null) {
|
if (gpb == null) {
|
||||||
throw new WebException(ErrorCode.GROUP_PLAY_EXIST);
|
////throw new WebException(ErrorCode.GROUP_PLAY_EXIST);
|
||||||
}
|
}
|
||||||
|
|
||||||
int gameId = gpb.gameId;
|
int gameId = gpb.gameId;
|
||||||
|
|
@ -937,7 +939,7 @@ public class GroupRoomBusiness {
|
||||||
String svr = _getSvr(jedis0, gb);
|
String svr = _getSvr(jedis0, gb);
|
||||||
if (svr == null) {
|
if (svr == null) {
|
||||||
log.error("createGroupRoom gameId:" + gameId + " svr:" + svr);
|
log.error("createGroupRoom gameId:" + gameId + " svr:" + svr);
|
||||||
throw new WebException(ErrorCode.NO_SERVICE);
|
////throw new WebException(ErrorCode.NO_SERVICE);
|
||||||
}
|
}
|
||||||
|
|
||||||
String grooms_key = GroupCache.genRoomsKey(groupId);
|
String grooms_key = GroupCache.genRoomsKey(groupId);
|
||||||
|
|
@ -951,19 +953,19 @@ public class GroupRoomBusiness {
|
||||||
Integer pay = gb.pay.get("pay" + opt + "_" + maxPlayers);
|
Integer pay = gb.pay.get("pay" + opt + "_" + maxPlayers);
|
||||||
if (pay == null) {
|
if (pay == null) {
|
||||||
log.warn("pay no set!");
|
log.warn("pay no set!");
|
||||||
throw new WebException(ErrorCode._FAILED);
|
//throw new WebException(ErrorCode._FAILED);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pay > 0) {
|
if (pay > 0) {
|
||||||
ArrayList<Long> result_list = Utility.payDiamo(jedis0, owner_session, pay);
|
ArrayList<Long> result_list = Utility.payDiamo(jedis0, owner_session, pay);
|
||||||
if (result_list == null) {
|
if (result_list == null) {
|
||||||
throw new WebException(ErrorCode._FAILED);
|
//throw new WebException(ErrorCode._FAILED);
|
||||||
}
|
}
|
||||||
long result = result_list.get(0);
|
long result = result_list.get(0);
|
||||||
if (result != 0) {
|
if (result != 0) {
|
||||||
log.error("createGroupRoom groupId:" + groupId + " pid:" + pid + " group.owner:" + group.owner
|
log.error("createGroupRoom groupId:" + groupId + " pid:" + pid + " group.owner:" + group.owner
|
||||||
+ " no diamo");
|
+ " no diamo");
|
||||||
throw new WebException(ErrorCode.NO_DIAMO);
|
//throw new WebException(ErrorCode.NO_DIAMO);
|
||||||
}
|
}
|
||||||
long cur_diamo = result_list.get(1);
|
long cur_diamo = result_list.get(1);
|
||||||
Utility.payDiamo(EventType.REDIS_EVENT_GROUP_ROOM, group.owner, gameId, pay, (int) cur_diamo, groupId,
|
Utility.payDiamo(EventType.REDIS_EVENT_GROUP_ROOM, group.owner, gameId, pay, (int) cur_diamo, groupId,
|
||||||
|
|
@ -1055,7 +1057,7 @@ public class GroupRoomBusiness {
|
||||||
GroupPlayBean gpb = GroupCache.getPlay(groupId, pid);
|
GroupPlayBean gpb = GroupCache.getPlay(groupId, pid);
|
||||||
System.out.println("gph+++++++++++++++++++++++++++" + gpb);
|
System.out.println("gph+++++++++++++++++++++++++++" + gpb);
|
||||||
if (gpb == null) {
|
if (gpb == null) {
|
||||||
throw new WebException(ErrorCode.GROUP_PLAY_EXIST);
|
//throw new WebException(ErrorCode.GROUP_PLAY_EXIST);
|
||||||
}
|
}
|
||||||
|
|
||||||
int gameId = gpb.gameId;
|
int gameId = gpb.gameId;
|
||||||
|
|
@ -1065,7 +1067,7 @@ public class GroupRoomBusiness {
|
||||||
System.out.println("svr +++++" + gameId);
|
System.out.println("svr +++++" + gameId);
|
||||||
if (svr == null) {
|
if (svr == null) {
|
||||||
log.error("createGroupRoom gameId:" + gameId + " svr:" + svr);
|
log.error("createGroupRoom gameId:" + gameId + " svr:" + svr);
|
||||||
throw new WebException(ErrorCode.NO_SERVICE);
|
//throw new WebException(ErrorCode.NO_SERVICE);
|
||||||
}
|
}
|
||||||
|
|
||||||
String grooms_key = GroupCache.genRoomsKey(groupId);
|
String grooms_key = GroupCache.genRoomsKey(groupId);
|
||||||
|
|
@ -1081,19 +1083,19 @@ public class GroupRoomBusiness {
|
||||||
|
|
||||||
if (pay == null) {
|
if (pay == null) {
|
||||||
log.warn("pay no set!");
|
log.warn("pay no set!");
|
||||||
throw new WebException(ErrorCode._FAILED);
|
//throw new WebException(ErrorCode._FAILED);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pay > 0) {
|
if (pay > 0) {
|
||||||
ArrayList<Long> result_list = Utility.payDiamo(jedis0, owner_session, pay);
|
ArrayList<Long> result_list = Utility.payDiamo(jedis0, owner_session, pay);
|
||||||
if (result_list == null) {
|
if (result_list == null) {
|
||||||
throw new WebException(ErrorCode._FAILED);
|
//throw new WebException(ErrorCode._FAILED);
|
||||||
}
|
}
|
||||||
long result = result_list.get(0);
|
long result = result_list.get(0);
|
||||||
if (result != 0) {
|
if (result != 0) {
|
||||||
log.error("createGroupRoom groupId:" + groupId + " pid:" + pid + " group.owner:" + group.owner
|
log.error("createGroupRoom groupId:" + groupId + " pid:" + pid + " group.owner:" + group.owner
|
||||||
+ " no diamo");
|
+ " no diamo");
|
||||||
throw new WebException(ErrorCode.NO_DIAMO);
|
//throw new WebException(ErrorCode.NO_DIAMO);
|
||||||
}
|
}
|
||||||
long cur_diamo = result_list.get(1);
|
long cur_diamo = result_list.get(1);
|
||||||
Utility.payDiamo(EventType.REDIS_EVENT_GROUP_ROOM, group.owner, gameId, pay, (int) cur_diamo, groupId,
|
Utility.payDiamo(EventType.REDIS_EVENT_GROUP_ROOM, group.owner, gameId, pay, (int) cur_diamo, groupId,
|
||||||
|
|
@ -1190,11 +1192,11 @@ public class GroupRoomBusiness {
|
||||||
"players");
|
"players");
|
||||||
String status = list.get(5);
|
String status = list.get(5);
|
||||||
if (StringUtil.isEmpty(status)) {
|
if (StringUtil.isEmpty(status)) {
|
||||||
throw new WebException(ErrorCode.NO_ROOM_NUM);
|
//throw new WebException(ErrorCode.NO_ROOM_NUM);
|
||||||
}
|
}
|
||||||
int _status = Integer.parseInt(status);
|
int _status = Integer.parseInt(status);
|
||||||
if (_status == 2 || _status == 3) {
|
if (_status == 2 || _status == 3) {
|
||||||
throw new WebException(ErrorCode.GROUP_ROOM_DEL);
|
//throw new WebException(ErrorCode.GROUP_ROOM_DEL);
|
||||||
}
|
}
|
||||||
String group = list.get(3);
|
String group = list.get(3);
|
||||||
int _gid = 0;
|
int _gid = 0;
|
||||||
|
|
@ -1208,7 +1210,7 @@ public class GroupRoomBusiness {
|
||||||
_gpid = Integer.parseInt(strGpid);
|
_gpid = Integer.parseInt(strGpid);
|
||||||
}
|
}
|
||||||
if (_gid != groupId) {
|
if (_gid != groupId) {
|
||||||
throw new WebException(ErrorCode.GROUP_NOT_CURGROUP_ROOM);
|
//throw new WebException(ErrorCode.GROUP_NOT_CURGROUP_ROOM);
|
||||||
}
|
}
|
||||||
String strFake = list.get(7);
|
String strFake = list.get(7);
|
||||||
if (_status == 0) {
|
if (_status == 0) {
|
||||||
|
|
@ -1243,7 +1245,7 @@ public class GroupRoomBusiness {
|
||||||
// List<String> server_ip = Redis.use("group1_db0").hmget(svr, "ip", "port");
|
// List<String> server_ip = Redis.use("group1_db0").hmget(svr, "ip", "port");
|
||||||
// String ip = server_ip.get(0);
|
// String ip = server_ip.get(0);
|
||||||
// if(StringUtil.isEmpty(ip)) {
|
// if(StringUtil.isEmpty(ip)) {
|
||||||
// throw new WebException(ErrorCode.NO_SERVICE);
|
// //throw new WebException(ErrorCode.NO_SERVICE);
|
||||||
// }
|
// }
|
||||||
// String port =server_ip.get(1);
|
// String port =server_ip.get(1);
|
||||||
ITObject obj = TObject.newInstance();
|
ITObject obj = TObject.newInstance();
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
package taurus.newRobot;
|
package taurus.newRobot;
|
||||||
|
|
||||||
|
import com.taurus.core.entity.ITArray;
|
||||||
|
import com.taurus.core.entity.TArray;
|
||||||
import com.taurus.core.plugin.redis.Redis;
|
import com.taurus.core.plugin.redis.Redis;
|
||||||
import com.taurus.core.util.Logger;
|
import com.taurus.core.util.Logger;
|
||||||
import redis.clients.jedis.*;
|
import redis.clients.jedis.*;
|
||||||
|
|
@ -118,6 +120,25 @@ public class AddRoomRobot {
|
||||||
//获取所有房间
|
//获取所有房间
|
||||||
Set<String> roomIds = jedis0.keys("room:*");
|
Set<String> roomIds = jedis0.keys("room:*");
|
||||||
for (String id : roomIds) {
|
for (String id : roomIds) {
|
||||||
|
List<String> room_list = Redis.use("group1_db0").hmget(id, "fake", "status", "players");
|
||||||
|
//判断是否是真人等待桌
|
||||||
|
String roomKey = "";
|
||||||
|
String roomId = id.substring(id.indexOf(":") + 1);
|
||||||
|
roomKey = "room:" + roomId;
|
||||||
|
//玩法id
|
||||||
|
String fake = room_list.get(0);
|
||||||
|
if (fake != null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
//圈子id
|
||||||
|
if(room_list.get(2)==null){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ITArray players = TArray.newFromJsonData(room_list.get(2));
|
||||||
|
if (players.size()==2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
jiqiren jiqiren = new jiqiren();
|
jiqiren jiqiren = new jiqiren();
|
||||||
jiqiren.tcp_jiqirezhongkouhao(id);
|
jiqiren.tcp_jiqirezhongkouhao(id);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ public class jiqiren {
|
||||||
|
|
||||||
|
|
||||||
String players1 = jedis0.hget(roomKey, "players");
|
String players1 = jedis0.hget(roomKey, "players");
|
||||||
|
System.out.println(roomKey);
|
||||||
if (jedis11.hget(playKey, "leftover_robot") != null) {
|
if (jedis11.hget(playKey, "leftover_robot") != null) {
|
||||||
//玩法配置的机器人数量
|
//玩法配置的机器人数量
|
||||||
String leftover_robot = jedis11.hget(playKey, "leftover_robot");
|
String leftover_robot = jedis11.hget(playKey, "leftover_robot");
|
||||||
|
|
@ -84,6 +84,10 @@ public class jiqiren {
|
||||||
List<String> availableRobots1 = getAvailableRobots1();
|
List<String> availableRobots1 = getAvailableRobots1();
|
||||||
log.info("所有机器人------"+availableRobots1);
|
log.info("所有机器人------"+availableRobots1);
|
||||||
String players = jedis0.hget(roomKey, "players");
|
String players = jedis0.hget(roomKey, "players");
|
||||||
|
System.out.println(players);
|
||||||
|
if (players1.length()>4) {
|
||||||
|
|
||||||
|
|
||||||
players = players.substring(1, players.length() - 1);
|
players = players.substring(1, players.length() - 1);
|
||||||
|
|
||||||
String[] playerIds1 = players.split(",");
|
String[] playerIds1 = players.split(",");
|
||||||
|
|
@ -107,17 +111,23 @@ public class jiqiren {
|
||||||
wanfaId = jedis0.hget(roomKey, "game");
|
wanfaId = jedis0.hget(roomKey, "game");
|
||||||
|
|
||||||
if ("22".equalsIgnoreCase(wanfaId)) { //湖南红中麻将
|
if ("22".equalsIgnoreCase(wanfaId)) { //湖南红中麻将
|
||||||
//client = new TaurusClient("8.138.242.190:6421", "game", TaurusClient.ConnectionProtocol.Tcp);
|
//client = new TaurusClient("47.109.55.7:6421", "game", TaurusClient.ConnectionProtocol.Tcp);
|
||||||
|
|
||||||
|
client = new TaurusClient("47.109.55.7:6421", randomKeyFromList, TaurusClient.ConnectionProtocol.Tcp);
|
||||||
|
//TaurusClient client1=getClient();
|
||||||
|
client.connect();
|
||||||
|
mapclient.put(randomKeyFromList, client);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("10".equalsIgnoreCase(wanfaId)) {
|
if ("10".equalsIgnoreCase(wanfaId)) {
|
||||||
client = new TaurusClient("8.138.242.190:6311", randomKeyFromList, TaurusClient.ConnectionProtocol.Tcp);
|
client = new TaurusClient("47.109.55.7:6311", randomKeyFromList, TaurusClient.ConnectionProtocol.Tcp);
|
||||||
//TaurusClient client1=getClient();
|
//TaurusClient client1=getClient();
|
||||||
client.connect();
|
client.connect();
|
||||||
mapclient.put(randomKeyFromList, client);
|
mapclient.put(randomKeyFromList, client);
|
||||||
}
|
}
|
||||||
if ("66".equalsIgnoreCase(wanfaId)) {
|
if ("66".equalsIgnoreCase(wanfaId)) {
|
||||||
// client = new TaurusClient("8.138.242.190:6841", "game", TaurusClient.ConnectionProtocol.Tcp);
|
// client = new TaurusClient("47.109.55.7:6841", "game", TaurusClient.ConnectionProtocol.Tcp);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ITObject object = null;
|
ITObject object = null;
|
||||||
|
|
@ -130,7 +140,7 @@ public class jiqiren {
|
||||||
Set<String> keys1 = jedis0.keys("{user}:" + randomKeyFromList + "_token");
|
Set<String> keys1 = jedis0.keys("{user}:" + randomKeyFromList + "_token");
|
||||||
if (keys1 == null) {
|
if (keys1 == null) {
|
||||||
try {
|
try {
|
||||||
object = accountBusiness.idPasswordLogin(Integer.parseInt(randomKeyFromList), "123456");
|
object = accountBusiness.idPasswordLogin(Integer.parseInt(randomKeyFromList), "blm523");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
@ -148,7 +158,7 @@ public class jiqiren {
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
object = accountBusiness.idPasswordLogin(Integer.parseInt(randomKeyFromList), "123456");
|
object = accountBusiness.idPasswordLogin(Integer.parseInt(randomKeyFromList), "blm523");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
@ -193,7 +203,7 @@ public class jiqiren {
|
||||||
|
|
||||||
ready(); //准备
|
ready(); //准备
|
||||||
count2.put(Integer.valueOf(gpid), count2.getOrDefault(Integer.parseInt(gpid), 0) + 1);
|
count2.put(Integer.valueOf(gpid), count2.getOrDefault(Integer.parseInt(gpid), 0) + 1);
|
||||||
jedis11.hincrBy(playKey, "leftover_robot", -1);
|
// jedis11.hincrBy(playKey, "leftover_robot", -1);
|
||||||
lianjiejiqiren lianjiejiqiren = new lianjiejiqiren();
|
lianjiejiqiren lianjiejiqiren = new lianjiejiqiren();
|
||||||
lianjiejiqiren.token = token;
|
lianjiejiqiren.token = token;
|
||||||
lianjiejiqiren.session = session;
|
lianjiejiqiren.session = session;
|
||||||
|
|
@ -202,6 +212,7 @@ public class jiqiren {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -247,15 +258,21 @@ public class jiqiren {
|
||||||
wanfaId = jedis0.hget(roomKey, "game");
|
wanfaId = jedis0.hget(roomKey, "game");
|
||||||
|
|
||||||
if ("22".equalsIgnoreCase(wanfaId)) { //湖南红中麻将
|
if ("22".equalsIgnoreCase(wanfaId)) { //湖南红中麻将
|
||||||
// client = new TaurusClient("8.138.242.190:6421", "game", TaurusClient.ConnectionProtocol.Tcp);
|
// client = new TaurusClient("47.109.55.7:6421", "game", TaurusClient.ConnectionProtocol.Tcp);
|
||||||
|
client = new TaurusClient("47.109.55.7:6421", randomKeyFromList, TaurusClient.ConnectionProtocol.Tcp);
|
||||||
|
client.connect();
|
||||||
|
mapclient.put(randomKeyFromList, client);
|
||||||
}
|
}
|
||||||
if ("10".equalsIgnoreCase(wanfaId)) {
|
if ("10".equalsIgnoreCase(wanfaId)) {
|
||||||
client = new TaurusClient("8.138.242.190:6311", randomKeyFromList, TaurusClient.ConnectionProtocol.Tcp);
|
client = new TaurusClient("47.109.55.7:6311", randomKeyFromList, TaurusClient.ConnectionProtocol.Tcp);
|
||||||
client.connect();
|
client.connect();
|
||||||
mapclient.put(randomKeyFromList, client);
|
mapclient.put(randomKeyFromList, client);
|
||||||
}
|
}
|
||||||
if ("66".equalsIgnoreCase(wanfaId)) {
|
if ("66".equalsIgnoreCase(wanfaId)) {
|
||||||
// client = new TaurusClient("8.138.242.190:6841", "game", TaurusClient.ConnectionProtocol.Tcp);
|
// client = new TaurusClient("47.109.55.7:6841", "game", TaurusClient.ConnectionProtocol.Tcp);
|
||||||
|
// client = new TaurusClient("47.109.55.7:6841", randomKeyFromList, TaurusClient.ConnectionProtocol.Tcp);
|
||||||
|
// client.connect();
|
||||||
|
// mapclient.put(randomKeyFromList, client);
|
||||||
}
|
}
|
||||||
|
|
||||||
ITObject object = null;
|
ITObject object = null;
|
||||||
|
|
@ -268,7 +285,7 @@ public class jiqiren {
|
||||||
Set<String> keys1 = jedis0.keys("{user}:" + randomKeyFromList + "_token");
|
Set<String> keys1 = jedis0.keys("{user}:" + randomKeyFromList + "_token");
|
||||||
if (keys1 == null) {
|
if (keys1 == null) {
|
||||||
try {
|
try {
|
||||||
object = accountBusiness.idPasswordLogin(Integer.parseInt(randomKeyFromList), "123456");
|
object = accountBusiness.idPasswordLogin(Integer.parseInt(randomKeyFromList), "blm523");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
@ -286,7 +303,7 @@ public class jiqiren {
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
object = accountBusiness.idPasswordLogin(Integer.parseInt(randomKeyFromList), "123456");
|
object = accountBusiness.idPasswordLogin(Integer.parseInt(randomKeyFromList), "blm523");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
@ -325,7 +342,7 @@ public class jiqiren {
|
||||||
|
|
||||||
ready(); //准备
|
ready(); //准备
|
||||||
count2.put(Integer.valueOf(gpid), count2.getOrDefault(Integer.parseInt(gpid), 0) + 1);
|
count2.put(Integer.valueOf(gpid), count2.getOrDefault(Integer.parseInt(gpid), 0) + 1);
|
||||||
jedis11.hincrBy(playKey, "leftover_robot", -1);
|
//jedis11.hincrBy(playKey, "leftover_robot", -1);
|
||||||
lianjiejiqiren lianjiejiqiren = new lianjiejiqiren();
|
lianjiejiqiren lianjiejiqiren = new lianjiejiqiren();
|
||||||
lianjiejiqiren.token = token;
|
lianjiejiqiren.token = token;
|
||||||
lianjiejiqiren.session = session;
|
lianjiejiqiren.session = session;
|
||||||
|
|
@ -408,7 +425,7 @@ public class jiqiren {
|
||||||
if (jedis0.keys("{user}:" + randomKeyFromList + "_token").size() != 0) {
|
if (jedis0.keys("{user}:" + randomKeyFromList + "_token").size() != 0) {
|
||||||
Set<String> keys1 = jedis0.keys("{user}:" + randomKeyFromList + "_token");
|
Set<String> keys1 = jedis0.keys("{user}:" + randomKeyFromList + "_token");
|
||||||
if (keys1 == null) {
|
if (keys1 == null) {
|
||||||
object = accountBusiness.idPasswordLogin(Integer.parseInt(randomKeyFromList), "123456");
|
object = accountBusiness.idPasswordLogin(Integer.parseInt(randomKeyFromList), "blm523");
|
||||||
ITObject validate = TObject.newInstance();
|
ITObject validate = TObject.newInstance();
|
||||||
validate.putString("token", object.getString("token"));
|
validate.putString("token", object.getString("token"));
|
||||||
token = object.getString("token");
|
token = object.getString("token");
|
||||||
|
|
@ -420,7 +437,7 @@ public class jiqiren {
|
||||||
token = keyList.get(0);
|
token = keyList.get(0);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
object = accountBusiness.idPasswordLogin(Integer.parseInt(randomKeyFromList), "123456");
|
object = accountBusiness.idPasswordLogin(Integer.parseInt(randomKeyFromList), "blm523");
|
||||||
ITObject validate = TObject.newInstance();
|
ITObject validate = TObject.newInstance();
|
||||||
validate.putString("token", object.getString("token"));
|
validate.putString("token", object.getString("token"));
|
||||||
token = object.getString("token");
|
token = object.getString("token");
|
||||||
|
|
@ -441,19 +458,22 @@ public class jiqiren {
|
||||||
|
|
||||||
|
|
||||||
if ("22".equalsIgnoreCase(wanfaId)) { //湖南红中麻将
|
if ("22".equalsIgnoreCase(wanfaId)) { //湖南红中麻将
|
||||||
client = new TaurusClient("8.138.242.190:6421", "game", TaurusClient.ConnectionProtocol.Tcp);
|
client = new TaurusClient("47.109.55.7:6421", "game", TaurusClient.ConnectionProtocol.Tcp);
|
||||||
|
client.connect();
|
||||||
|
mapclient.put(randomKeyFromList, client);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("10".equalsIgnoreCase(wanfaId)) {
|
if ("10".equalsIgnoreCase(wanfaId)) {
|
||||||
client = new TaurusClient("8.138.242.190:6311", randomKeyFromList, TaurusClient.ConnectionProtocol.Tcp);
|
client = new TaurusClient("47.109.55.7:6311", randomKeyFromList, TaurusClient.ConnectionProtocol.Tcp);
|
||||||
// client.setSession(session);
|
// client.setSession(session);
|
||||||
client.connect();
|
client.connect();
|
||||||
mapclient.put(randomKeyFromList, client);
|
mapclient.put(randomKeyFromList, client);
|
||||||
}
|
}
|
||||||
System.out.println("clinet 111 " + client);
|
|
||||||
|
|
||||||
if ("66".equalsIgnoreCase(wanfaId)) {
|
if ("66".equalsIgnoreCase(wanfaId)) {
|
||||||
client = new TaurusClient("8.138.242.190:6841", "game", TaurusClient.ConnectionProtocol.Tcp);
|
// client = new TaurusClient("47.109.55.7:6841", "game", TaurusClient.ConnectionProtocol.Tcp);
|
||||||
}
|
}
|
||||||
|
|
||||||
System.out.println("id ++++++" + mapclient);
|
System.out.println("id ++++++" + mapclient);
|
||||||
|
|
@ -479,7 +499,7 @@ public class jiqiren {
|
||||||
count2.put(Integer.valueOf(playId), count2.getOrDefault(Integer.parseInt(playId), 0) + 1);
|
count2.put(Integer.valueOf(playId), count2.getOrDefault(Integer.parseInt(playId), 0) + 1);
|
||||||
|
|
||||||
// count.put(key, count.getOrDefault(key, 0) + 1);
|
// count.put(key, count.getOrDefault(key, 0) + 1);
|
||||||
jedis11.hincrBy(playKey1, "leftover_robot", -1);
|
// jedis11.hincrBy(playKey1, "leftover_robot", -1);
|
||||||
|
|
||||||
lianjiejiqiren lianjiejiqiren = new lianjiejiqiren();
|
lianjiejiqiren lianjiejiqiren = new lianjiejiqiren();
|
||||||
lianjiejiqiren.token = token;
|
lianjiejiqiren.token = token;
|
||||||
|
|
@ -598,8 +618,9 @@ public class jiqiren {
|
||||||
params.putString("session", session + "," + token);
|
params.putString("session", session + "," + token);
|
||||||
|
|
||||||
client.send("1002", params, response -> {
|
client.send("1002", params, response -> {
|
||||||
ITObject obj = response.messageData.param.getTObject("tableInfo");
|
//ITObject obj = response.messageData.param.getTObject("tableInfo");
|
||||||
});
|
});
|
||||||
|
return "ok";
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -598,8 +598,28 @@ public class lianjiejiqiren {
|
||||||
System.out.println("comds:2002");
|
System.out.println("comds:2002");
|
||||||
|
|
||||||
ITObject params = TObject.newInstance();
|
ITObject params = TObject.newInstance();
|
||||||
|
String aid = client.getGameID();
|
||||||
|
|
||||||
|
String getKey = "{robortInfo}:" + aid;
|
||||||
|
Jedis jedis20 = Redis.use("group1_db2").getJedis();
|
||||||
|
|
||||||
|
String circleId1 = jedis20.hget(getKey, "circleId");
|
||||||
|
String pid = jedis20.hget(getKey, "pid");
|
||||||
|
|
||||||
|
String key = "g{" + circleId1 + "}:play:" + pid;
|
||||||
|
|
||||||
client.send("1005", params, response -> {
|
client.send("1005", params, response -> {
|
||||||
|
log.info("退出状态1---------"+response);
|
||||||
|
log.info("退出状态1getKey---------"+getKey);
|
||||||
|
log.info("退出状态1key---------"+key);
|
||||||
|
log.info("退出状态1aid---------"+aid);
|
||||||
|
jedis20.hset(getKey, "circleId", "0");
|
||||||
|
jedis20.hset(getKey, "pid", "0");
|
||||||
|
jedis20.hset(getKey, "room_id", "0");
|
||||||
|
//退出修改机器人状态
|
||||||
|
jedis20.hset(key,aid, "0");
|
||||||
});
|
});
|
||||||
|
jedis20.close();
|
||||||
|
|
||||||
}else if ("2009".equalsIgnoreCase(command)) {
|
}else if ("2009".equalsIgnoreCase(command)) {
|
||||||
log.info("斤斤计较急急急急急急急急急====="+param);
|
log.info("斤斤计较急急急急急急急急急====="+param);
|
||||||
|
|
@ -657,7 +677,6 @@ public class lianjiejiqiren {
|
||||||
|
|
||||||
if (playerIds2.length == 1) {
|
if (playerIds2.length == 1) {
|
||||||
if (!pid.equals("0")) {
|
if (!pid.equals("0")) {
|
||||||
System.out.println("comds4:2002");
|
|
||||||
Jedis jedis20 = Redis.use("group1_db11").getJedis();
|
Jedis jedis20 = Redis.use("group1_db11").getJedis();
|
||||||
System.out.println("进入 -------------------------");
|
System.out.println("进入 -------------------------");
|
||||||
jedis20.hincrBy(key, "leftover_robot", 1);
|
jedis20.hincrBy(key, "leftover_robot", 1);
|
||||||
|
|
@ -717,7 +736,9 @@ public class lianjiejiqiren {
|
||||||
TaurusClient taurusClient = (TaurusClient) entry.getValue();
|
TaurusClient taurusClient = (TaurusClient) entry.getValue();
|
||||||
taurusClient.send("1005", params, response -> {
|
taurusClient.send("1005", params, response -> {
|
||||||
log.info("退出状态---------"+response);
|
log.info("退出状态---------"+response);
|
||||||
|
log.info("退出状态getKey---------"+getKey);
|
||||||
|
log.info("退出状态key---------"+key);
|
||||||
|
log.info("退出状态aid---------"+aid);
|
||||||
jedis33.hset(getKey, "circleId", "0");
|
jedis33.hset(getKey, "circleId", "0");
|
||||||
jedis33.hset(getKey, "pid", "0");
|
jedis33.hset(getKey, "pid", "0");
|
||||||
jedis33.hset(getKey, "room_id", "0");
|
jedis33.hset(getKey, "room_id", "0");
|
||||||
|
|
@ -782,7 +803,7 @@ public class lianjiejiqiren {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
jedis11s.hincrBy(getStart, "leftover_robot", 1);
|
// jedis11s.hincrBy(getStart, "leftover_robot", 1);
|
||||||
|
|
||||||
jedis21.hset("{robortInfo}:" + key, "circleId", "0");
|
jedis21.hset("{robortInfo}:" + key, "circleId", "0");
|
||||||
jedis21.hset("{robortInfo}:" + key, "pid", "0");
|
jedis21.hset("{robortInfo}:" + key, "pid", "0");
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ public class Test {
|
||||||
// // 玩家ID
|
// // 玩家ID
|
||||||
// public static int playerId = 0;
|
// public static int playerId = 0;
|
||||||
// // 登录密码
|
// // 登录密码
|
||||||
// public static String password = "123456";
|
// public static String password = "blm523";
|
||||||
// // 会话标识
|
// // 会话标识
|
||||||
// public static String session = "";
|
// public static String session = "";
|
||||||
// // 访问令牌
|
// // 访问令牌
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ public class Test2 {
|
||||||
public static List<Integer> cardInhand = new ArrayList<Integer>();
|
public static List<Integer> cardInhand = new ArrayList<Integer>();
|
||||||
public static int seat = 0;
|
public static int seat = 0;
|
||||||
public static int playerId = 219916;
|
public static int playerId = 219916;
|
||||||
public static String password = "123456";
|
public static String password = "blm523";
|
||||||
public static String session = "";
|
public static String session = "";
|
||||||
public static String token = "";
|
public static String token = "";
|
||||||
public static TaurusClient client = null;
|
public static TaurusClient client = null;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="CompilerConfiguration">
|
||||||
|
<annotationProcessing>
|
||||||
|
<profile name="Maven default annotation processors profile" enabled="true">
|
||||||
|
<sourceOutputDir name="target/generated-sources/annotations" />
|
||||||
|
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
|
||||||
|
<outputRelativeToContentRoot value="true" />
|
||||||
|
<module name="robot_changma" />
|
||||||
|
</profile>
|
||||||
|
</annotationProcessing>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Encoding">
|
||||||
|
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
|
||||||
|
<file url="file://$PROJECT_DIR$/src/main/kotlin" charset="UTF-8" />
|
||||||
|
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="RemoteRepositoriesConfiguration">
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="central" />
|
||||||
|
<option name="name" value="Central Repository" />
|
||||||
|
<option name="url" value="https://repo.maven.apache.org/maven2" />
|
||||||
|
</remote-repository>
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="central" />
|
||||||
|
<option name="name" value="Maven Central repository" />
|
||||||
|
<option name="url" value="https://repo1.maven.org/maven2" />
|
||||||
|
</remote-repository>
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="jboss.community" />
|
||||||
|
<option name="name" value="JBoss Community repository" />
|
||||||
|
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
|
||||||
|
</remote-repository>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="KotlinJpsPluginSettings">
|
||||||
|
<option name="version" value="1.6.21" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
|
<component name="MavenProjectsManager">
|
||||||
|
<option name="originalFiles">
|
||||||
|
<list>
|
||||||
|
<option value="$PROJECT_DIR$/pom.xml" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK" />
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$/../.." vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,193 @@
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<groupId>com.robotcm</groupId>
|
||||||
|
<artifactId>robot_changma</artifactId>
|
||||||
|
<packaging>war</packaging>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<maven.compiler.source>1.8</maven.compiler.source>
|
||||||
|
<maven.compiler.target>1.8</maven.compiler.target>
|
||||||
|
<build.type>pro</build.type>
|
||||||
|
<kotlin.version>1.5.10</kotlin.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<version>3.8.1</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!--依赖 data_cache -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.data</groupId>
|
||||||
|
<artifactId>data_cache</artifactId>
|
||||||
|
<version>1.0.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!--依赖 taurus-core -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.taurus</groupId>
|
||||||
|
<artifactId>taurus-core</artifactId>
|
||||||
|
<version>1.0.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!--依赖 taurus-web -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.taurus</groupId>
|
||||||
|
<artifactId>taurus-web</artifactId>
|
||||||
|
<version>1.0.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.taurus</groupId>
|
||||||
|
<artifactId>taurus-permanent</artifactId>
|
||||||
|
<version>1.0.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 需要用redis时导入 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>redis.clients</groupId>
|
||||||
|
<artifactId>jedis</artifactId>
|
||||||
|
<version>2.9.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 需要用HikariCP时导入 ,自己在项目中添加 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.zaxxer</groupId>
|
||||||
|
<artifactId>HikariCP</artifactId>
|
||||||
|
<version>3.3.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 需要用mysql时导入 ,自己在项目中添加 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>mysql</groupId>
|
||||||
|
<artifactId>mysql-connector-java</artifactId>
|
||||||
|
<version>8.0.16</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- https://mvnrepository.com/artifact/jdom/jdom -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>jdom</groupId>
|
||||||
|
<artifactId>jdom</artifactId>
|
||||||
|
<version>1.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>log4j</groupId>
|
||||||
|
<artifactId>log4j</artifactId>
|
||||||
|
<version>1.2.17</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- https://mvnrepository.com/artifact/org.eclipse.jetty/jetty-webapp -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-webapp</artifactId>
|
||||||
|
<version>8.2.0.v20160908</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.quartz-scheduler</groupId>
|
||||||
|
<artifactId>quartz</artifactId>
|
||||||
|
<version>2.2.3</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jetbrains.kotlin</groupId>
|
||||||
|
<artifactId>kotlin-stdlib-jdk8</artifactId>
|
||||||
|
<version>${kotlin.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jetbrains.kotlin</groupId>
|
||||||
|
<artifactId>kotlin-test</artifactId>
|
||||||
|
<version>${kotlin.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.game</groupId>
|
||||||
|
<artifactId>game_common</artifactId>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.code.gson</groupId>
|
||||||
|
<artifactId>gson</artifactId>
|
||||||
|
<version>2.10.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<finalName>ROOT</finalName>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-war-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<source>1.8</source>
|
||||||
|
<target>1.8</target>
|
||||||
|
<encoding>UTF-8</encoding>
|
||||||
|
<warSourceExcludes>logs/**,config/**</warSourceExcludes>
|
||||||
|
<webResources>
|
||||||
|
<resource>
|
||||||
|
<targetPath>config/</targetPath>
|
||||||
|
<directory>${project.basedir}/build/${build.type}/</directory>
|
||||||
|
</resource>
|
||||||
|
</webResources>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.jetbrains.kotlin</groupId>
|
||||||
|
<artifactId>kotlin-maven-plugin</artifactId>
|
||||||
|
<version>${kotlin.version}</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>compile</id>
|
||||||
|
<phase>compile</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>compile</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
<execution>
|
||||||
|
<id>test-compile</id>
|
||||||
|
<phase>test-compile</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>test-compile</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
<configuration>
|
||||||
|
<jvmTarget>1.8</jvmTarget>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>compile</id>
|
||||||
|
<phase>compile</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>compile</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
<execution>
|
||||||
|
<id>testCompile</id>
|
||||||
|
<phase>test-compile</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>testCompile</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,225 @@
|
||||||
|
package com.robotcm;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
|
||||||
|
import com.robotcm.taurus.robot.RobotUser;
|
||||||
|
|
||||||
|
import com.robotcm.taurus.util.ROBOTEventType;
|
||||||
|
import org.jdom.Document;
|
||||||
|
import org.jdom.Element;
|
||||||
|
import org.jdom.input.SAXBuilder;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import com.data.cache.AccountCache;
|
||||||
|
import com.data.cache.GroupCache;
|
||||||
|
|
||||||
|
import com.robotcm.taurus.client.NetManager;
|
||||||
|
import com.taurus.core.plugin.redis.Redis;
|
||||||
|
import com.taurus.core.routes.Extension;
|
||||||
|
import com.taurus.core.routes.Routes;
|
||||||
|
import com.taurus.core.util.FileUtil;
|
||||||
|
|
||||||
|
import com.taurus.permanent.TPServer;
|
||||||
|
import redis.clients.jedis.Jedis;
|
||||||
|
|
||||||
|
|
||||||
|
public class MainServer extends Extension {
|
||||||
|
private ScheduledThreadPoolExecutor timeScheduler;
|
||||||
|
|
||||||
|
private static final String WEB_CONFIG_KEY = "web_config";
|
||||||
|
private static final String FORCE_VER_KEY = "force_ver";
|
||||||
|
|
||||||
|
public static Element gameConfig;
|
||||||
|
|
||||||
|
public static int groupid;
|
||||||
|
public static int pid;
|
||||||
|
private static String password;
|
||||||
|
|
||||||
|
private static String gameHost;
|
||||||
|
private static String gamePort;
|
||||||
|
|
||||||
|
private static Map<Integer,RobotUser> robotUserMap;
|
||||||
|
|
||||||
|
public static Map<String,String> lua_map = new HashMap<>();
|
||||||
|
public MainServer() {
|
||||||
|
super();
|
||||||
|
robotUserMap = new HashMap();
|
||||||
|
String path = System.getProperty("WORKDIR");
|
||||||
|
File file = new File(path+"/config/robot-config.xml");
|
||||||
|
if(file.exists()) {
|
||||||
|
try {
|
||||||
|
InputStream is = new FileInputStream(file);
|
||||||
|
SAXBuilder builder = new SAXBuilder();
|
||||||
|
Document document = builder.build(is);
|
||||||
|
gameConfig = document.getRootElement();
|
||||||
|
|
||||||
|
groupid = Integer.parseInt(gameConfig.getChildTextTrim("groupid"));
|
||||||
|
pid = Integer.parseInt(gameConfig.getChildTextTrim("pid"));
|
||||||
|
password = gameConfig.getChildTextTrim("password");
|
||||||
|
gameHost = gameConfig.getChildTextTrim("gameHost");
|
||||||
|
gamePort = gameConfig.getChildTextTrim("gamePort");
|
||||||
|
}catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//3秒
|
||||||
|
System.out.println("Main Server Started");
|
||||||
|
// NetManager.TIMEOUT_TIME =3;
|
||||||
|
timeScheduler = new ScheduledThreadPoolExecutor(1);
|
||||||
|
timeScheduler.scheduleAtFixedRate(new Runnable() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
AccountCache.clearData();
|
||||||
|
GroupCache.clearData();
|
||||||
|
System.gc();
|
||||||
|
}
|
||||||
|
}, 0,600, TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int readVersion() {
|
||||||
|
int ver = Integer.parseInt(Redis.use("group1_db1").hget(WEB_CONFIG_KEY,FORCE_VER_KEY));
|
||||||
|
return ver;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configRoute(Routes me) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
System.out.println("onStart starting...");
|
||||||
|
// 1. 先启动独立的事件处理线程(只启动一次)
|
||||||
|
startNetEventThread();
|
||||||
|
|
||||||
|
Jedis jedis2 = Redis.use("group1_db2").getJedis();
|
||||||
|
try {
|
||||||
|
String robotskey = "g{"+groupid+"}:play:"+pid;
|
||||||
|
Map<String, String> maprobot = jedis2.hgetAll(robotskey);
|
||||||
|
for(Map.Entry<String, String> entry : maprobot.entrySet()) {
|
||||||
|
System.out.println(entry.getKey()+":"+entry.getValue());
|
||||||
|
//是否创建
|
||||||
|
RobotUser robotUser = new RobotUser();
|
||||||
|
robotUser.setRobotid(Integer.parseInt(entry.getKey()));
|
||||||
|
robotUser.setPassword(password);
|
||||||
|
robotUser.setGameHost(gameHost);
|
||||||
|
robotUser.setGamePort(gamePort);
|
||||||
|
robotUser.setRobotGroupid(groupid);
|
||||||
|
robotUser.setRobotPid(pid);
|
||||||
|
robotUserMap.put(Integer.parseInt(entry.getKey()), robotUser);
|
||||||
|
}
|
||||||
|
// 2. 定时器只负责创建机器人连接
|
||||||
|
TPServer.me().getTimerPool().scheduleAtFixedRate(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
|
||||||
|
//1、获取对应玩法的id
|
||||||
|
System.out.println("groupid"+groupid);
|
||||||
|
System.out.println("pid"+pid);
|
||||||
|
String robotskey = "g{"+groupid+"}:play:"+pid;
|
||||||
|
for(Map.Entry<Integer, RobotUser> entry : robotUserMap.entrySet()) {
|
||||||
|
RobotUser robotUser = entry.getValue();
|
||||||
|
//1、登录
|
||||||
|
//判断是否登录
|
||||||
|
if(!robotUser.isLogin){
|
||||||
|
robotUser.login();
|
||||||
|
}
|
||||||
|
//2、链接
|
||||||
|
//判断是否链接
|
||||||
|
System.out.println("robotUser.isconnect"+robotUser.getisconnect());
|
||||||
|
if(!robotUser.getisconnect()){
|
||||||
|
robotUser.connectGame();
|
||||||
|
}else{
|
||||||
|
robotUser.renconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
//检测是否断开链接
|
||||||
|
robotUser.checkIsConnect();
|
||||||
|
//3、创建房间
|
||||||
|
if(robotUser.getStatus()==0){
|
||||||
|
//没状态时候进入创建房间
|
||||||
|
robotUser.joinRoom();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(robotUser.getStatus()==ROBOTEventType.ROBOT_UNUSE){
|
||||||
|
//没状态时候进入创建房间
|
||||||
|
robotUser.createRoom();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//4、加入房间
|
||||||
|
|
||||||
|
|
||||||
|
//5、退出房间
|
||||||
|
|
||||||
|
|
||||||
|
//6、工作
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//判断机器人是否有创建,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//System.out.println("into");
|
||||||
|
//AddRoomRobot addRoomRobot = new AddRoomRobot();
|
||||||
|
//addRoomRobot.addRoom();
|
||||||
|
|
||||||
|
// System.out.println("机器人返回结果: " + s);
|
||||||
|
|
||||||
|
}
|
||||||
|
}, 0, 3 ,TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
// System.out.println("创建机器人连接失败" + e);
|
||||||
|
}finally {
|
||||||
|
jedis2.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 独立的事件处理线程
|
||||||
|
private void startNetEventThread() {
|
||||||
|
Thread eventThread = new Thread(() -> {
|
||||||
|
while (true) {
|
||||||
|
NetManager.processEvents();
|
||||||
|
try {
|
||||||
|
Thread.sleep(2);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
break;
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, "NetEvent-Thread");
|
||||||
|
|
||||||
|
eventThread.setDaemon(true); // 设置为守护线程
|
||||||
|
eventThread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStop() {
|
||||||
|
if(timeScheduler!=null) {
|
||||||
|
timeScheduler.shutdownNow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,153 @@
|
||||||
|
package com.robotcm.hunan;
|
||||||
|
|
||||||
|
import com.taurus.core.entity.*;
|
||||||
|
import com.taurus.core.plugin.redis.Redis;
|
||||||
|
import com.taurus.core.util.Logger;
|
||||||
|
import com.taurus.web.WebException;
|
||||||
|
import redis.clients.jedis.Jedis;
|
||||||
|
import com.robotcm.taurus.client.TaurusClient;
|
||||||
|
import com.robotcm.taurus.client.business.AccountBusiness;
|
||||||
|
import com.robotcm.taurus.util.GroupDatas;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class DoTest {
|
||||||
|
|
||||||
|
// 扣点点玩家手牌
|
||||||
|
public static List<Integer> cardInhand = new ArrayList<Integer>();
|
||||||
|
|
||||||
|
private static final Logger log = Logger.getLogger(DoTest.class);
|
||||||
|
|
||||||
|
//
|
||||||
|
// //跑得快算法
|
||||||
|
// private static PaoDeKuaiSuanFa paoDeKuaiSuanFa;
|
||||||
|
|
||||||
|
//红中其它玩家打的牌
|
||||||
|
// public static String hongZhongCard = "";
|
||||||
|
|
||||||
|
//扣点点别人打的牌
|
||||||
|
public static String card = "";
|
||||||
|
|
||||||
|
|
||||||
|
// 玩家座位号
|
||||||
|
public static int seat = 0;
|
||||||
|
// 玩家ID
|
||||||
|
public static int playerId = 0;
|
||||||
|
// 登录密码
|
||||||
|
public static String password = "blm523";
|
||||||
|
// 会话标识
|
||||||
|
public static String session = "";
|
||||||
|
// 访问令牌
|
||||||
|
public static String token = "";
|
||||||
|
// 玩法ID
|
||||||
|
public static String wanfaId = "";
|
||||||
|
|
||||||
|
// 客户端连接对象
|
||||||
|
public static TaurusClient client = null;
|
||||||
|
|
||||||
|
public static TObject datas = null;
|
||||||
|
|
||||||
|
|
||||||
|
//圈子id
|
||||||
|
public static int groupId = 0;
|
||||||
|
|
||||||
|
//房间id
|
||||||
|
public static String roomId = "";
|
||||||
|
|
||||||
|
public static int count = 0;
|
||||||
|
|
||||||
|
static final void get() {
|
||||||
|
TObject data = GroupDatas.getData();
|
||||||
|
datas = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String allLogin() throws WebException {
|
||||||
|
Jedis jedis2 = Redis.use("group1_db2").getJedis();
|
||||||
|
|
||||||
|
// ITObject object = null;
|
||||||
|
AccountBusiness accountBusiness = null;
|
||||||
|
accountBusiness = new AccountBusiness();
|
||||||
|
//获取
|
||||||
|
Set<String> allRobotKeys = jedis2.keys("{robot}:*");
|
||||||
|
System.out.println(allRobotKeys);
|
||||||
|
for (String robotKey : allRobotKeys) {
|
||||||
|
ITObject object = null;
|
||||||
|
try {
|
||||||
|
//System.out.println("robotKey:"+robotKey.substring(robotKey.indexOf(":") + 1));
|
||||||
|
object = accountBusiness.idPasswordLogin(Integer.parseInt(robotKey.substring(robotKey.indexOf(":") + 1)), "blm523");
|
||||||
|
Thread.sleep(100);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String doTest() throws Exception {
|
||||||
|
get();
|
||||||
|
ITArray group = datas.getTArray("groupid");
|
||||||
|
Jedis jedis11 = Redis.use("group1_db11").getJedis();
|
||||||
|
Jedis jedis0 = Redis.use("group1_db0").getJedis();
|
||||||
|
try {
|
||||||
|
|
||||||
|
if (group.size() != 0) {
|
||||||
|
log.info("数据--------{}", datas);
|
||||||
|
// System.out.println("数据--------" + datas);
|
||||||
|
ITArray robotIds = datas.getTArray("robotIds");
|
||||||
|
int robotIndex = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < group.size(); i++) {
|
||||||
|
TDataWrapper tDataWrapper = group.get(i);
|
||||||
|
if (tDataWrapper.getTypeId() == TDataType.TOBJECT) {
|
||||||
|
TObject roomObj = (TObject) tDataWrapper.getObject();
|
||||||
|
|
||||||
|
// 获取圈子id和房间列表
|
||||||
|
Set<String> keySet = roomObj.getKeys();
|
||||||
|
for (String groupIdStr : keySet) {
|
||||||
|
|
||||||
|
int currentGroupId = Integer.parseInt(groupIdStr);
|
||||||
|
ITArray roomArray = roomObj.getTArray(groupIdStr);//玩法id
|
||||||
|
|
||||||
|
|
||||||
|
//为每个房间分配一个机器人
|
||||||
|
for (int j = 0; j < roomArray.size() && robotIndex < robotIds.size(); j++) {
|
||||||
|
int currentPlayId = roomArray.getInt(j);
|
||||||
|
int currentPlayerId = robotIds.getInt(robotIndex);
|
||||||
|
|
||||||
|
System.out.println("玩家id: " + currentPlayerId + ", 玩法id: " + currentPlayId + ", 圈子id: " + currentGroupId);
|
||||||
|
JiQiRens jiQiRen = new JiQiRens();
|
||||||
|
String playKey = "g{" + groupIdStr + "}:play:" + currentPlayId;
|
||||||
|
|
||||||
|
String leftover_robot = jedis11.hget(playKey, "leftover_robot");
|
||||||
|
if (jedis11.hget(playKey, "leftover_robot") != null) {
|
||||||
|
// if (Integer.parseInt(leftover_robot) > 0) {
|
||||||
|
jiQiRen.tcp_test(currentPlayerId, currentPlayId, currentGroupId);
|
||||||
|
robotIndex++;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清空数据
|
||||||
|
datas.getTArray("robotIds").clear();
|
||||||
|
datas.getTArray("groupid").clear();
|
||||||
|
playerId = 0;
|
||||||
|
roomId = "";
|
||||||
|
groupId = 0;
|
||||||
|
count = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
jedis11.close();
|
||||||
|
jedis0.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
package com.robotcm.hunan;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class HandAnalysis {
|
||||||
|
public Map<Integer, Integer> cardCounts = new HashMap<>();
|
||||||
|
public List<List<Integer>> completedMelds = new ArrayList<>();
|
||||||
|
public List<Integer> pairs = new ArrayList<>();
|
||||||
|
public List<Integer> isolatedCards = new ArrayList<>();
|
||||||
|
public Map<Integer, Integer> cardUsefulness = new HashMap<>();
|
||||||
|
public Map<Integer, List<Integer>> cardsBySuit = new HashMap<>();
|
||||||
|
|
||||||
|
|
||||||
|
public boolean isTingPai = false;//是否听牌
|
||||||
|
public Set<Integer> tingCards = new HashSet<>(); // 听的牌
|
||||||
|
public int meldCount = 0; // 面子数量
|
||||||
|
public int shantenCount = Integer.MAX_VALUE; // 向听数
|
||||||
|
public int pairCount = 0; // 对子/将数量
|
||||||
|
public boolean hasLongQiDuiPotential = false; // 是否有龙七对潜力
|
||||||
|
public Set<Integer> usedInMelds = new HashSet<>(); // 用于面子的牌
|
||||||
|
public boolean hasPengPengHu = false;//是否有碰碰胡
|
||||||
|
public Set<Integer> usedInPairs = new HashSet<>(); // 用于对子/将的牌
|
||||||
|
public List<Integer> remainingCards = new ArrayList<>(); // 剩余需要分析的牌
|
||||||
|
public int lastDrawnCard = 0; // 最后摸的牌
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,212 @@
|
||||||
|
package com.robotcm.hunan;
|
||||||
|
|
||||||
|
import com.taurus.core.entity.ITArray;
|
||||||
|
import com.taurus.core.entity.ITObject;
|
||||||
|
import com.taurus.core.entity.TObject;
|
||||||
|
import com.robotcm.taurus.client.Message;
|
||||||
|
import com.robotcm.taurus.client.TaurusClient;
|
||||||
|
import com.robotcm.taurus.util.CardUtil;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class HuNanFangPaoFa {
|
||||||
|
|
||||||
|
public static int fangPaoFaCardCard = 0;
|
||||||
|
|
||||||
|
//湖南字牌放炮罚 手牌
|
||||||
|
private List<Integer> fangPaoFaCardInhand = new ArrayList<Integer>();
|
||||||
|
|
||||||
|
// 玩家座位号
|
||||||
|
public static int seat = 0;
|
||||||
|
|
||||||
|
// 会话标识
|
||||||
|
public static String session = "";
|
||||||
|
// 访问令牌
|
||||||
|
public static String token = "";
|
||||||
|
|
||||||
|
public void fangPaoFaCardInhand() {
|
||||||
|
fangPaoFaCardInhand = new ArrayList<Integer>();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public List<Integer> getFangPaoFaCardInhand() {
|
||||||
|
return fangPaoFaCardInhand;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化手牌协议 811
|
||||||
|
*
|
||||||
|
* @param command 协议号
|
||||||
|
* @param message 消息对象
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String cardInHead(String command, Message message, TaurusClient client) {
|
||||||
|
if (command.equalsIgnoreCase("811")) {
|
||||||
|
ITObject param = message.param;
|
||||||
|
if (param == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// {bank_seat=1, laiziCard=0, laiziCard2=0, laiziCard2Before=0, jing=0, laiziCardBefore=0, card_list=[101, 103, 104, 201, 204, 207, 208, 209, 307, 309, 501, 502, 503]}
|
||||||
|
ITArray cardList = param.getTArray("card_list");
|
||||||
|
for (int i = 0; i < cardList.size(); i++) {
|
||||||
|
fangPaoFaCardInhand.add(cardList.getInt(i));
|
||||||
|
}
|
||||||
|
if (fangPaoFaCardInhand.size() > 19) {
|
||||||
|
outCard(client);
|
||||||
|
System.out.println("机器人:" + param.getInt("seat") + "为庄家,需要出牌" + ",牌为:" + fangPaoFaCardInhand.get(0));
|
||||||
|
}
|
||||||
|
System.out.println("机器人:" + param.getInt("seat") + "放炮罚初始化手牌" + ",牌为:" + fangPaoFaCardInhand.toString());
|
||||||
|
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 摸牌协议 819
|
||||||
|
*
|
||||||
|
* @param command 协议号
|
||||||
|
* @param message 消息对象
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String getCard(String command, Message message) {
|
||||||
|
if (command.equalsIgnoreCase("819")) {
|
||||||
|
ITObject param = message.param;
|
||||||
|
if (param == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
System.out.println("轮到座位号:" + param.getInt("seat") + "的用户摸牌" + ",牌为:" + param.getInt("card"));
|
||||||
|
if (param.getInt("seat") != null && seat == param.getInt("seat")) {
|
||||||
|
fangPaoFaCardInhand.add(param.getInt("card"));
|
||||||
|
System.out.println("摸牌后放炮罚 手牌" + fangPaoFaCardInhand);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 出牌方法
|
||||||
|
*/
|
||||||
|
public String outCard(TaurusClient client) {
|
||||||
|
ITObject params = TObject.newInstance();
|
||||||
|
params.putString("session", session + "," + token);
|
||||||
|
params.putInt("card", fangPaoFaCardInhand.get(0));
|
||||||
|
System.out.println("放炮罚出牌 " + fangPaoFaCardInhand.get(0));
|
||||||
|
fangPaoFaCardInhand.remove(0);
|
||||||
|
System.out.println("目前机器人剩余手牌:" + fangPaoFaCardInhand.toString());
|
||||||
|
client.send("611", params, response -> {
|
||||||
|
System.out.println("出牌成功: " + response.returnCode);
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 出牌广播协议 812
|
||||||
|
*
|
||||||
|
* @param command 协议号
|
||||||
|
* @param message 消息对象
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String drawCard(String command, Message message) {
|
||||||
|
if (command.equalsIgnoreCase("812")) {
|
||||||
|
ITObject param = message.param;
|
||||||
|
if (param == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
fangPaoFaCardCard = param.getInt("card");
|
||||||
|
System.out.println("出牌广播" + fangPaoFaCardCard);
|
||||||
|
System.out.println("座位号:" + param.getInt("seat") + "的用户出牌:" + param.getInt("card"));
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理 吃碰 偎提胡
|
||||||
|
*
|
||||||
|
* @param param 消息参数
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String actionCard(ITObject param, TaurusClient client) {
|
||||||
|
//获取碰杠胡参数 type 和id 后续算法接入,是否能让碰和杠
|
||||||
|
ITArray tipList = param.getTArray("tip_list");
|
||||||
|
int id = 0;
|
||||||
|
int type = 0;
|
||||||
|
int opcard = 0;
|
||||||
|
int opcard1 = 0;
|
||||||
|
int card = 0;
|
||||||
|
ITArray bi_list = null;
|
||||||
|
ITObject params = TObject.newInstance();
|
||||||
|
if (tipList != null && tipList.size() > 0) {
|
||||||
|
TObject firstTip = (TObject) tipList.get(0).getObject();
|
||||||
|
id = firstTip.getInt("id");
|
||||||
|
type = firstTip.getInt("type");
|
||||||
|
card = firstTip.getInt("card");
|
||||||
|
// 获取吃牌组合中的第一张牌
|
||||||
|
opcard = firstTip.getTArray("opcard").getInt(0);
|
||||||
|
// 获取吃牌组合中的第二张牌
|
||||||
|
opcard1 = firstTip.getTArray("opcard").getInt(1);
|
||||||
|
bi_list = firstTip.getTArray("bi_list");
|
||||||
|
System.out.println("id ++ " + id);
|
||||||
|
System.out.println("type ++ " + type);
|
||||||
|
System.out.println("opcard ++ " + opcard);
|
||||||
|
System.out.println("card ++ " + card);
|
||||||
|
System.out.println("bi_list ++ " + bi_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
//执行吃
|
||||||
|
if (type == 1) {
|
||||||
|
params.putString("session", session + "," + token);
|
||||||
|
params.putInt("id", id);
|
||||||
|
CardUtil.removeCard1(fangPaoFaCardInhand, opcard, 1);
|
||||||
|
CardUtil.removeCard1(fangPaoFaCardInhand, opcard1, 1);
|
||||||
|
if (bi_list != null ){
|
||||||
|
CardUtil.removeCard1(fangPaoFaCardInhand, card, 1);
|
||||||
|
}
|
||||||
|
System.out.println("删除吃的牌");
|
||||||
|
//执行碰
|
||||||
|
} else if (type == 2) {
|
||||||
|
params.putString("session", session + "," + token);
|
||||||
|
params.putInt("id", id);
|
||||||
|
CardUtil.removeCard1(fangPaoFaCardInhand, card, 1);
|
||||||
|
System.out.println("删除碰的牌");
|
||||||
|
//执行吃杠
|
||||||
|
} else if (type == 3) {
|
||||||
|
params.putString("session", session + "," + token);
|
||||||
|
params.putInt("qi", 0);
|
||||||
|
params.putInt("id", 1);
|
||||||
|
// Util.removeCard(hongZhongCardInhand, opcard, 3);
|
||||||
|
System.out.println("执行吃杠");
|
||||||
|
} else if (type == 4) {
|
||||||
|
params.putString("session", session + "," + token);
|
||||||
|
params.putInt("id", id);
|
||||||
|
CardUtil.removeCard1(fangPaoFaCardInhand, card, 2);
|
||||||
|
System.out.println("执行偎----");
|
||||||
|
// 碰后补杠
|
||||||
|
} else if (type == 5) {
|
||||||
|
// params.putString("session", session + "," + token);
|
||||||
|
// params.putInt("qi", 0);
|
||||||
|
// params.putInt("id", 1);
|
||||||
|
// Util.removeCard(hongZhongCardInhand, opcard, 1);
|
||||||
|
// System.out.println("执行碰后补杠");
|
||||||
|
} else if (type == 8) {
|
||||||
|
params.putInt("id", id);
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("调用612");
|
||||||
|
|
||||||
|
client.send("612", params, response -> {
|
||||||
|
System.out.println("操作成功: " + response.returnCode);
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void fangPaoTipEvent(Message message,TaurusClient client) {
|
||||||
|
ITObject param = message.param;
|
||||||
|
Integer card = param.getInt("card");
|
||||||
|
ITObject params = TObject.newInstance();
|
||||||
|
params.putInt("card", card);
|
||||||
|
client.send("823", params, response -> {
|
||||||
|
System.out.println("操作成功: " + response.returnCode);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,322 @@
|
||||||
|
package com.robotcm.hunan;
|
||||||
|
|
||||||
|
import com.game.Util;
|
||||||
|
import com.taurus.core.entity.ITArray;
|
||||||
|
import com.taurus.core.entity.ITObject;
|
||||||
|
import com.taurus.core.entity.TObject;
|
||||||
|
import com.taurus.core.util.StringUtil;
|
||||||
|
import com.robotcm.taurus.client.Message;
|
||||||
|
import com.robotcm.taurus.client.TaurusClient;
|
||||||
|
import com.robotcm.taurus.util.CardUtil;
|
||||||
|
import com.robotcm.taurus.util.HongZhongSuanFaTest;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class HuNanHongZhong {
|
||||||
|
|
||||||
|
|
||||||
|
public static int hongZhongCard = 0;
|
||||||
|
|
||||||
|
//湖南红中麻将手牌
|
||||||
|
private List<Integer> hongZhongCardInhand = new ArrayList<>();
|
||||||
|
|
||||||
|
//红中麻将出过的牌
|
||||||
|
private List<Integer> hongZhongchuguopai = new ArrayList<>();
|
||||||
|
|
||||||
|
|
||||||
|
// 玩家座位号
|
||||||
|
public static int seat = 0;
|
||||||
|
|
||||||
|
public static int playerId = 0;
|
||||||
|
|
||||||
|
|
||||||
|
// 会话标识
|
||||||
|
public static String session = "";
|
||||||
|
// 访问令牌
|
||||||
|
public static String token = "";
|
||||||
|
//红中麻将算法
|
||||||
|
// private static HongZhongSuanFa hongZhongSuanFa = new HongZhongSuanFa();
|
||||||
|
|
||||||
|
private static HongZhongSuanFaTest hongZhongSuanFaTest = new HongZhongSuanFaTest();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 公共的getter和setter方法
|
||||||
|
public List<Integer> getHongZhongCardInhand() {
|
||||||
|
return hongZhongCardInhand;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Integer> getChuGuoCardInhand() {
|
||||||
|
return hongZhongchuguopai;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 出牌广播协议 812
|
||||||
|
*
|
||||||
|
* @param command 协议号
|
||||||
|
* @param message 消息对象
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String drawCard(String command, Message message) {
|
||||||
|
if (command.equalsIgnoreCase("812")) {
|
||||||
|
ITObject param = message.param;
|
||||||
|
if (param == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
hongZhongCard = param.getInt("card");
|
||||||
|
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 摸牌协议 819
|
||||||
|
*
|
||||||
|
* @param command 协议号
|
||||||
|
* @param message 消息对象
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String getCard(String command, Message message) {
|
||||||
|
if (command.equalsIgnoreCase("819")) {
|
||||||
|
ITObject param = message.param;
|
||||||
|
if (param == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// {seat=2, Ishupai=0, isBaoTing=-1, tingcard=0, isgang=0, card=101, left_count=106}
|
||||||
|
|
||||||
|
if (param.getInt("player") != null) {
|
||||||
|
int drawnCard = param.getInt("card");
|
||||||
|
|
||||||
|
hongZhongSuanFaTest.drawnCards = drawnCard;
|
||||||
|
hongZhongCardInhand.add(drawnCard);
|
||||||
|
|
||||||
|
// 创建包含摸牌后的完整手牌
|
||||||
|
List<Integer> newHand = new ArrayList<>(hongZhongCardInhand);
|
||||||
|
|
||||||
|
// 调用分离分析方法,将刻子、顺子、红中单独拎出后分析剩余牌
|
||||||
|
hongZhongSuanFaTest.separateAndAnalyzeHand(newHand);
|
||||||
|
|
||||||
|
// 直接调用hongZhongSuanFaTest中的analyzeDrawCard方法分析摸牌后是否可听牌
|
||||||
|
hongZhongSuanFaTest.analyzeDrawCard(hongZhongCardInhand, drawnCard);
|
||||||
|
|
||||||
|
|
||||||
|
// 调用新添加的findDiscardToTing方法,分析打出哪张牌可以听牌
|
||||||
|
Map<Integer, Set<Integer>> discardOptions = hongZhongSuanFaTest.findDiscardToTing(newHand);
|
||||||
|
|
||||||
|
// 如果有可打出后听牌的选项,记录信息
|
||||||
|
if (!discardOptions.isEmpty()) {
|
||||||
|
// 这些信息将在出牌决策时被考虑
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否应该碰牌
|
||||||
|
*
|
||||||
|
* @param proposedCard 提议碰的牌
|
||||||
|
* @return 是否应该碰牌
|
||||||
|
*/
|
||||||
|
public boolean shouldPong(int proposedCard) {
|
||||||
|
|
||||||
|
// 直接调用hongZhongSuanFaTest中的shouldPong方法,它已经包含了所有需要的规则
|
||||||
|
return hongZhongSuanFaTest.shouldPong(proposedCard, hongZhongCardInhand);
|
||||||
|
|
||||||
|
// return hongZhongSuanFaTest.shouldPong(proposedCard, Arrays.asList(305,304,303,207,207,204,204,208,208,201,201,412,412));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化手牌协议 811
|
||||||
|
*
|
||||||
|
* @param command 协议号
|
||||||
|
* @param message 消息对象
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String cardInHead(String command, Message message, TaurusClient client) {
|
||||||
|
if (command.equalsIgnoreCase("811")) {
|
||||||
|
ITObject param = message.param;
|
||||||
|
if (param == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// {bank_seat=1, laiziCard=0, laiziCard2=0, laiziCard2Before=0, jing=0, laiziCardBefore=0, card_list=[101, 103, 104, 201, 204, 207, 208, 209, 307, 309, 501, 502, 503]}
|
||||||
|
ITArray cardList = param.getTArray("card_list");
|
||||||
|
for (int i = 0; i < cardList.size(); i++) {
|
||||||
|
hongZhongCardInhand.add(cardList.getInt(i));
|
||||||
|
}
|
||||||
|
if (hongZhongCardInhand.size() > 13) {
|
||||||
|
outCard(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理杠碰胡操作
|
||||||
|
*
|
||||||
|
* @param param 消息参数
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String actionCard(ITObject param, TaurusClient client) {
|
||||||
|
//获取碰杠胡参数 type 和id 后续算法接入,是否能让碰和杠
|
||||||
|
ITArray tipList = param.getTArray("tip_list");
|
||||||
|
int id = 0;
|
||||||
|
int type = 0;
|
||||||
|
int opcard = 0;
|
||||||
|
ITObject params = TObject.newInstance();
|
||||||
|
if (tipList != null && tipList.size() > 0) {
|
||||||
|
TObject firstTip = (TObject) tipList.get(0).getObject();
|
||||||
|
id = firstTip.getInt("id");
|
||||||
|
type = firstTip.getInt("type");
|
||||||
|
opcard = firstTip.getTArray("opcard").getInt(0);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
//弃 是根据算法选择是否要弃掉 不进行碰杠胡
|
||||||
|
//params.putInt("qi", 0);
|
||||||
|
//params.putInt("id", 0);
|
||||||
|
|
||||||
|
//执行碰牌
|
||||||
|
if (type == 2) {
|
||||||
|
// 根据规则判断是否应该碰牌
|
||||||
|
if (shouldPong(opcard)) {
|
||||||
|
params.putString("session", session + "," + token);
|
||||||
|
params.putInt("qi", 0);
|
||||||
|
params.putInt("id", 1);
|
||||||
|
Util.removeCard(hongZhongCardInhand, opcard, 2);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
params.putString("session", session + "," + token);
|
||||||
|
params.putInt("qi", 1); // 放弃碰牌
|
||||||
|
params.putInt("id", 0);
|
||||||
|
|
||||||
|
}
|
||||||
|
// Global.logger.info("删除碰的牌");
|
||||||
|
//执行胡牌
|
||||||
|
} else if (type == 6) {
|
||||||
|
params.putString("session", session + "," + token);
|
||||||
|
params.putInt("qi", 0);
|
||||||
|
params.putInt("id", 1);
|
||||||
|
|
||||||
|
//执行吃杠
|
||||||
|
} else if (type == 3) {
|
||||||
|
params.putString("session", session + "," + token);
|
||||||
|
params.putInt("qi", 0);
|
||||||
|
params.putInt("id", 1);
|
||||||
|
Util.removeCard(hongZhongCardInhand, opcard, 3);
|
||||||
|
|
||||||
|
//执行自杠
|
||||||
|
} else if (type == 4) {
|
||||||
|
params.putString("session", session + "," + token);
|
||||||
|
params.putInt("qi", 0);
|
||||||
|
params.putInt("id", 1);
|
||||||
|
Util.removeCard(hongZhongCardInhand, opcard, 4);
|
||||||
|
|
||||||
|
// 碰后补杠
|
||||||
|
} else if (type == 5) {
|
||||||
|
params.putString("session", session + "," + token);
|
||||||
|
params.putInt("qi", 0);
|
||||||
|
params.putInt("id", 1);
|
||||||
|
Util.removeCard(hongZhongCardInhand, opcard, 1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// cardInhand.remove(0);
|
||||||
|
// cardInhand.remove(1);
|
||||||
|
|
||||||
|
|
||||||
|
client.send("612", params, response -> {
|
||||||
|
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String changePlayer(String command, Message message) {
|
||||||
|
if (command.equalsIgnoreCase("820")) {
|
||||||
|
ITObject param = message.param;
|
||||||
|
if (param == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 出牌方法
|
||||||
|
*/
|
||||||
|
// public String outCard(TaurusClient client, List< Integer> list) {
|
||||||
|
public String outCard(TaurusClient client) {
|
||||||
|
// 调用分离分析方法,将刻子、顺子、红中单独拎出后分析剩余牌
|
||||||
|
|
||||||
|
hongZhongSuanFaTest.separateAndAnalyzeHand(hongZhongCardInhand);
|
||||||
|
|
||||||
|
// 红中麻将出牌
|
||||||
|
String hongzhongOutCard = hongZhongSuanFaTest.outCardSuanFa(hongZhongCardInhand, hongZhongCard);
|
||||||
|
// String hongzhongOutCard = hongZhongSuanFaTest.outCardSuanFa(list, hongZhongCard);
|
||||||
|
ITObject params = TObject.newInstance();
|
||||||
|
int cardToOut;
|
||||||
|
if (StringUtil.isNotEmpty(hongzhongOutCard)) {
|
||||||
|
cardToOut = Integer.parseInt(hongzhongOutCard);
|
||||||
|
} else {
|
||||||
|
cardToOut = hongZhongCardInhand.get(0);
|
||||||
|
}
|
||||||
|
params.putInt("card", cardToOut);
|
||||||
|
|
||||||
|
int outCountBefore = hongZhongchuguopai.size(); // 当前历史出牌数量
|
||||||
|
|
||||||
|
// 第n次出牌时,发送前n-1张出牌
|
||||||
|
if (outCountBefore >= 1) {
|
||||||
|
// 发送前n-1张(所有历史出牌)
|
||||||
|
List<Integer> cardsToSend = hongZhongchuguopai.subList(0, outCountBefore);
|
||||||
|
params.putTArray("outcard_list", CardUtil.maJiangToTArray(cardsToSend));
|
||||||
|
}
|
||||||
|
params.putTArray("card_list", CardUtil.maJiangToTArray(hongZhongCardInhand));
|
||||||
|
|
||||||
|
// 将当前出的牌添加到历史出牌列表
|
||||||
|
hongZhongchuguopai.add(cardToOut);
|
||||||
|
// 从手牌中移除
|
||||||
|
hongZhongCardInhand.remove(Integer.valueOf(cardToOut));
|
||||||
|
|
||||||
|
|
||||||
|
params.putString("session", session + "," + token);
|
||||||
|
client.send("611", params, response -> {
|
||||||
|
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除出过的牌组
|
||||||
|
*
|
||||||
|
* @param param
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String shanchuchuguopai(ITObject param) {
|
||||||
|
if (param == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Integer card = param.getInt("card"); // 操作牌值
|
||||||
|
Integer type = param.getInt("type"); // 操作类型
|
||||||
|
Integer from_seat = param.getInt("from_seat"); // 牌来源座位
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (type == 2 || type == 3 || type == 5) { // 碰,杠
|
||||||
|
getChuGuoCardInhand().remove(Integer.valueOf(card));
|
||||||
|
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,121 @@
|
||||||
|
package com.robotcm.hunan;
|
||||||
|
|
||||||
|
import com.robotcm.taurus.util.CardObj;
|
||||||
|
import com.robotcm.taurus.util.CardUtil;
|
||||||
|
import com.robotcm.taurus.util.test;
|
||||||
|
import com.taurus.core.entity.ITArray;
|
||||||
|
import com.taurus.core.entity.ITObject;
|
||||||
|
import com.taurus.core.entity.TObject;
|
||||||
|
import com.robotcm.taurus.client.TaurusClient;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class HuNanPaoDeKuai {
|
||||||
|
|
||||||
|
public static int seat = 0;
|
||||||
|
|
||||||
|
//座位号 剩余牌数量
|
||||||
|
private Map<Integer, List<Integer>> seatRemainHistory = new HashMap<>();
|
||||||
|
|
||||||
|
// 跑的快玩家手牌
|
||||||
|
private List<CardObj> paoDekuaiCardInhand = new ArrayList<>();
|
||||||
|
|
||||||
|
// 会话标识
|
||||||
|
public static String session = "";
|
||||||
|
// 访问令牌
|
||||||
|
public static String token = "";
|
||||||
|
|
||||||
|
//跑的快别人出的牌
|
||||||
|
private ITObject card_list = TObject.newInstance();
|
||||||
|
|
||||||
|
public static int guangboseat = 0;
|
||||||
|
|
||||||
|
public static int remain = 0;
|
||||||
|
|
||||||
|
public Map<Integer, List<Integer>> getSeatRemainHistory() {
|
||||||
|
return seatRemainHistory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<CardObj> getPaoDekuaiCardInhand() {
|
||||||
|
return paoDekuaiCardInhand;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ITObject getCard_list() {
|
||||||
|
return card_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 出牌方法
|
||||||
|
*/
|
||||||
|
public String outCard(TaurusClient client) {
|
||||||
|
ITArray itArray = null;
|
||||||
|
itArray = test.intelligentPaoDeKuaiOutCard(paoDekuaiCardInhand, card_list, seatRemainHistory);
|
||||||
|
|
||||||
|
if (remain == 1 && itArray == null) { //如果玩家的下家只有一张牌,玩家出单张必须是最大的
|
||||||
|
CardObj maxSingleCard = CardUtil.findMaxSingleCard(paoDekuaiCardInhand);
|
||||||
|
|
||||||
|
itArray = CardUtil.toTArray1(maxSingleCard);
|
||||||
|
}
|
||||||
|
|
||||||
|
//跑得快出牌
|
||||||
|
|
||||||
|
ITObject params = TObject.newInstance();
|
||||||
|
params.putString("session", session + "," + token);
|
||||||
|
params.putTArray("card", itArray);
|
||||||
|
params.putTArray("all_card", CardUtil.toTArray(paoDekuaiCardInhand));
|
||||||
|
CardUtil.removeCard(paoDekuaiCardInhand, CardUtil.toList(itArray)); //删除手牌里打过的牌
|
||||||
|
|
||||||
|
client.send("1013", params, response ->
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 跑得快初始化手牌
|
||||||
|
*
|
||||||
|
* @param param 消息参数
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String paoDeKuaiCardInHead(ITObject param, TaurusClient client) {
|
||||||
|
ITArray cardList = param.getTArray("cards");
|
||||||
|
List<CardObj> cardObjs = new ArrayList<>();
|
||||||
|
if (cardList.size() != 0) {
|
||||||
|
cardObjs = CardUtil.toList(cardList);
|
||||||
|
}
|
||||||
|
paoDekuaiCardInhand.addAll(cardObjs);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 跑的快出牌广播
|
||||||
|
*
|
||||||
|
* @param param 消息参数
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String paoDekuaiChupaiGuangBo(ITObject param) {
|
||||||
|
card_list = param.getTObject("card_obj");
|
||||||
|
guangboseat = param.getInt("player");
|
||||||
|
remain = param.getInt("remain"); //剩余牌数量
|
||||||
|
saveRemainHistory(guangboseat, remain);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveRemainHistory(int guangboseat, int remain) {
|
||||||
|
if (!seatRemainHistory.containsKey(guangboseat)) {
|
||||||
|
seatRemainHistory.put(guangboseat, new ArrayList<>());
|
||||||
|
}
|
||||||
|
seatRemainHistory.get(guangboseat).add(remain);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,341 @@
|
||||||
|
package com.robotcm.hunan;
|
||||||
|
|
||||||
|
import com.game.Util;
|
||||||
|
import com.taurus.core.entity.ITArray;
|
||||||
|
import com.taurus.core.entity.ITObject;
|
||||||
|
import com.taurus.core.entity.TObject;
|
||||||
|
import com.taurus.core.util.StringUtil;
|
||||||
|
import com.robotcm.taurus.client.Message;
|
||||||
|
import com.robotcm.taurus.client.TaurusClient;
|
||||||
|
import com.robotcm.taurus.util.CardUtil;
|
||||||
|
import com.robotcm.taurus.util.ZhuanZhuanSuanFaTest;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class HuNanZhuanZhuan {
|
||||||
|
|
||||||
|
public static int zhuanZhuanCard = 0;
|
||||||
|
|
||||||
|
//湖南转转麻将手牌
|
||||||
|
private List<Integer> zhuanZhuanCardInhand = new ArrayList<Integer>();
|
||||||
|
|
||||||
|
|
||||||
|
//打出的牌
|
||||||
|
private List<Integer> zhuanZhuanchuguopai = new ArrayList<>();
|
||||||
|
|
||||||
|
|
||||||
|
private static ZhuanZhuanSuanFaTest zhuanZhuanFaTest = new ZhuanZhuanSuanFaTest();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 玩家座位号
|
||||||
|
public static int seat = 0;
|
||||||
|
|
||||||
|
// 会话标识
|
||||||
|
public static String session = "";
|
||||||
|
// 访问令牌
|
||||||
|
public static String token = "";
|
||||||
|
|
||||||
|
public List<Integer> getZhuanZhuanCardInhand() {
|
||||||
|
return zhuanZhuanCardInhand;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Integer> getZhuanZhuanchuguopai() {
|
||||||
|
return zhuanZhuanchuguopai;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化手牌协议 811
|
||||||
|
*
|
||||||
|
* @param command 协议号
|
||||||
|
* @param message 消息对象
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String cardInHead(String command, Message message, TaurusClient client ) {
|
||||||
|
if (command.equalsIgnoreCase("811")) {
|
||||||
|
ITObject param = message.param;
|
||||||
|
if (param == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// {bank_seat=1, laiziCard=0, laiziCard2=0, laiziCard2Before=0, jing=0, laiziCardBefore=0, card_list=[101, 103, 104, 201, 204, 207, 208, 209, 307, 309, 501, 502, 503]}
|
||||||
|
ITArray cardList = param.getTArray("card_list");
|
||||||
|
for (int i = 0; i < cardList.size(); i++) {
|
||||||
|
zhuanZhuanCardInhand.add(cardList.getInt(i));
|
||||||
|
}
|
||||||
|
if (zhuanZhuanCardInhand.size() > 13) {
|
||||||
|
outCard(client);
|
||||||
|
if (!zhuanZhuanCardInhand.isEmpty()) {
|
||||||
|
System.out.println("机器人:" + param.getInt("seat") + "为庄家,需要出牌1111" + ",牌为:" + zhuanZhuanCardInhand.get(0));
|
||||||
|
} else {
|
||||||
|
System.out.println("机器人:" + param.getInt("seat") + "为庄家,需要出牌1111,但手牌为空");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
System.out.println("机器人:" + param.getInt("bank_seat") + "初始化手牌" + ",牌为:" + zhuanZhuanCardInhand.toString());
|
||||||
|
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ... existing code ...
|
||||||
|
/**
|
||||||
|
* 出牌方法
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void outCard(TaurusClient client) {
|
||||||
|
//麻将出牌
|
||||||
|
String zhuanzhaunOutCard = zhuanZhuanFaTest.outCardSuanFa(zhuanZhuanCardInhand, zhuanZhuanCard);
|
||||||
|
|
||||||
|
ITObject params = TObject.newInstance();
|
||||||
|
int cardToOut;
|
||||||
|
if (StringUtil.isNotEmpty(zhuanzhaunOutCard)) {
|
||||||
|
cardToOut = Integer.parseInt(zhuanzhaunOutCard);
|
||||||
|
}else {
|
||||||
|
cardToOut = zhuanZhuanCardInhand.get(0);
|
||||||
|
}
|
||||||
|
params.putString("session", session + "," + token);
|
||||||
|
|
||||||
|
params.putInt("card", cardToOut);
|
||||||
|
int outCountBefore = zhuanZhuanchuguopai.size();
|
||||||
|
|
||||||
|
if (outCountBefore >=1){
|
||||||
|
List<Integer> cardsToSend = zhuanZhuanchuguopai.subList(0, outCountBefore);
|
||||||
|
params.putTArray("outcard_list", CardUtil.maJiangToTArray(cardsToSend));
|
||||||
|
}
|
||||||
|
params.putTArray("card_list", CardUtil.maJiangToTArray(zhuanZhuanCardInhand));
|
||||||
|
zhuanZhuanchuguopai.add(cardToOut);
|
||||||
|
System.out.println("机器人出牌: " + cardToOut);
|
||||||
|
zhuanZhuanCardInhand.remove(Integer.valueOf(cardToOut));
|
||||||
|
System.out.println("目前机器人剩余手牌:" + zhuanZhuanCardInhand.toString());
|
||||||
|
|
||||||
|
client.send("611", params, response -> {
|
||||||
|
System.out.println("出牌成功: " + response.returnCode);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
// ... existing code ...
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 出牌广播协议 812
|
||||||
|
*
|
||||||
|
* @param command 协议号
|
||||||
|
* @param message 消息对象
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String drawCard(String command, Message message) {
|
||||||
|
if (command.equalsIgnoreCase("812")) {
|
||||||
|
ITObject param = message.param;
|
||||||
|
if (param == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
zhuanZhuanCard = param.getInt("card");
|
||||||
|
System.out.println("出牌广播" + zhuanZhuanCard);
|
||||||
|
System.out.println("座位号:" + param.getInt("seat") + "的用户出牌:" + param.getInt("card"));
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 摸牌协议 819
|
||||||
|
*
|
||||||
|
* @param command 协议号
|
||||||
|
* @param message 消息对象
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String getCard(String command, Message message) {
|
||||||
|
if (command.equalsIgnoreCase("819")) {
|
||||||
|
ITObject param = message.param;
|
||||||
|
if (param == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// {seat=2, Ishupai=0, isBaoTing=-1, tingcard=0, isgang=0, card=101, left_count=106}
|
||||||
|
int card = param.getInt("card");
|
||||||
|
System.out.println("轮到座位号:" + param.getInt("seat") + "的用户摸牌" + ",牌为:" + card);
|
||||||
|
if (param.getInt("player") != null) {
|
||||||
|
zhuanZhuanCardInhand.add(card);
|
||||||
|
|
||||||
|
// 添加摸牌分析逻辑
|
||||||
|
System.out.println("\n===== 摸牌分析开始 =====");
|
||||||
|
|
||||||
|
// 创建包含摸牌后的手牌副本
|
||||||
|
List<Integer> tempHand = new ArrayList<>(zhuanZhuanCardInhand);
|
||||||
|
tempHand.sort(Integer::compareTo);
|
||||||
|
|
||||||
|
// 调用算法类进行摸牌分析
|
||||||
|
if (zhuanZhuanFaTest != null) {
|
||||||
|
boolean isTing = zhuanZhuanFaTest.analyzeDrawCard(zhuanZhuanCardInhand, card);
|
||||||
|
|
||||||
|
// 如果摸牌后可以听牌,记录信息
|
||||||
|
if (isTing) {
|
||||||
|
System.out.println("\n摸牌后已进入听牌状态!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分析打出哪张牌可以听牌
|
||||||
|
Map<Integer, Set<Integer>> discardToTingMap = zhuanZhuanFaTest.findDiscardToTing(tempHand);
|
||||||
|
if (discardToTingMap != null && !discardToTingMap.isEmpty()) {
|
||||||
|
System.out.println("可打出后听牌的牌数量: " + discardToTingMap.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("===== 摸牌分析结束 =====");
|
||||||
|
// cardInhand.add(104);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String changePlayer(String command, Message message) {
|
||||||
|
if (command.equalsIgnoreCase("820")) {
|
||||||
|
ITObject param = message.param;
|
||||||
|
if (param == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
System.out.println("出牌权转移到座位号:" + param.getInt("seat") + "的用户");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理杠碰胡操作
|
||||||
|
*
|
||||||
|
* @param param 消息参数
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String actionCard(ITObject param,TaurusClient client) {
|
||||||
|
//获取碰杠胡参数 type 和id 后续算法接入,是否能让碰和杠
|
||||||
|
ITArray tipList = param.getTArray("tip_list");
|
||||||
|
int id = 0;
|
||||||
|
int type = 0;
|
||||||
|
int opcard = 0;
|
||||||
|
ITObject params = TObject.newInstance();
|
||||||
|
if (tipList != null && tipList.size() > 0) {
|
||||||
|
TObject firstTip = (TObject) tipList.get(0).getObject();
|
||||||
|
id = firstTip.getInt("id");
|
||||||
|
type = firstTip.getInt("type");
|
||||||
|
opcard = firstTip.getTArray("opcard").getInt(0);
|
||||||
|
System.out.println("id ++ " + id);
|
||||||
|
System.out.println("type ++ " + type);
|
||||||
|
System.out.println("机器人收到牌:" + opcard + ",当前手牌:" + zhuanZhuanCardInhand);
|
||||||
|
}
|
||||||
|
//弃 是根据算法选择是否要弃掉 不进行碰杠胡
|
||||||
|
//params.putInt("qi", 0);
|
||||||
|
//params.putInt("id", 0);
|
||||||
|
|
||||||
|
// 如果是碰操作,使用算法判断是否应该碰
|
||||||
|
if (type == 2 && zhuanZhuanFaTest != null) {
|
||||||
|
// 复制当前手牌进行分析
|
||||||
|
List<Integer> tempHands = new ArrayList<>(zhuanZhuanCardInhand);
|
||||||
|
// 使用算法判断是否应该碰牌
|
||||||
|
boolean shouldPong = zhuanZhuanFaTest.shouldPong(opcard, tempHands);
|
||||||
|
if (!shouldPong) {
|
||||||
|
// 如果算法判断不应该碰,设置放弃操作
|
||||||
|
params.putInt("qi", 1);
|
||||||
|
params.putInt("id", 0);
|
||||||
|
System.out.println("智能决策:不碰该牌,保持手牌灵活性");
|
||||||
|
|
||||||
|
client.send("612", params, response -> {
|
||||||
|
System.out.println("操作成功: " + response.returnCode);
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是杠操作,使用算法判断是否应该杠
|
||||||
|
if (type == 3 || type == 4 || type == 5) {
|
||||||
|
if (zhuanZhuanFaTest != null) {
|
||||||
|
List<Integer> tempHands = new ArrayList<>(zhuanZhuanCardInhand);
|
||||||
|
boolean shouldGang = zhuanZhuanFaTest.shouldGang(opcard, tempHands, type);
|
||||||
|
if (!shouldGang) {
|
||||||
|
params.putInt("qi", 1);
|
||||||
|
params.putInt("id", 0);
|
||||||
|
System.out.println("智能决策:不杠该牌,保持手牌灵活性");
|
||||||
|
|
||||||
|
client.send("612", params, response -> {
|
||||||
|
System.out.println("操作成功: " + response.returnCode);
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//执行碰牌
|
||||||
|
if (type == 2) {
|
||||||
|
params.putString("session", session + "," + token);
|
||||||
|
params.putInt("qi", 0);
|
||||||
|
params.putInt("id", 1);
|
||||||
|
Util.removeCard(zhuanZhuanCardInhand, opcard, 2);
|
||||||
|
System.out.println("执行碰牌:" + opcard);
|
||||||
|
// Global.logger.info("删除碰的牌");
|
||||||
|
//执行胡牌
|
||||||
|
} else if (type == 6) {
|
||||||
|
params.putString("session", session + "," + token);
|
||||||
|
params.putInt("qi", 0);
|
||||||
|
params.putInt("id", 1);
|
||||||
|
System.out.println("执行胡牌:" + opcard);
|
||||||
|
//执行吃杠
|
||||||
|
} else if (type == 3) {
|
||||||
|
params.putString("session", session + "," + token);
|
||||||
|
params.putInt("qi", 0);
|
||||||
|
params.putInt("id", 1);
|
||||||
|
Util.removeCard(zhuanZhuanCardInhand, opcard, 3);
|
||||||
|
System.out.println("执行吃杠:" + opcard);
|
||||||
|
//执行自杠
|
||||||
|
} else if (type == 4) {
|
||||||
|
params.putString("session", session + "," + token);
|
||||||
|
params.putInt("qi", 0);
|
||||||
|
params.putInt("id", 1);
|
||||||
|
Util.removeCard(zhuanZhuanCardInhand, opcard, 4);
|
||||||
|
System.out.println("执行自杠:" + opcard);
|
||||||
|
// 碰后补杠
|
||||||
|
} else if (type == 5) {
|
||||||
|
params.putString("session", session + "," + token);
|
||||||
|
params.putInt("qi", 0);
|
||||||
|
params.putInt("id", 1);
|
||||||
|
Util.removeCard(zhuanZhuanCardInhand, opcard, 1);
|
||||||
|
System.out.println("执行碰后补杠:" + opcard);
|
||||||
|
}
|
||||||
|
|
||||||
|
// cardInhand.remove(0);
|
||||||
|
// cardInhand.remove(1);
|
||||||
|
System.out.println("执行id为:" + 0 + "的操作");
|
||||||
|
|
||||||
|
client.send("612", params, response -> {
|
||||||
|
System.out.println("操作成功: " + response.returnCode);
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除出过的牌组
|
||||||
|
*
|
||||||
|
* @param param
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String shanchuchuguopai(ITObject param) {
|
||||||
|
if (param == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Integer card = param.getInt("card"); // 操作牌值
|
||||||
|
Integer type = param.getInt("type"); // 操作类型
|
||||||
|
Integer from_seat = param.getInt("from_seat"); // 牌来源座位
|
||||||
|
System.out.println("删除出过的牌组 card " + card);
|
||||||
|
System.out.println("删除出过的牌组 type " + type);
|
||||||
|
System.out.println("删除出过的牌组 from_seat " + from_seat);
|
||||||
|
System.out.println("机器人 seat " + seat);
|
||||||
|
|
||||||
|
if (type == 2 || type == 3 || type == 5) { // 碰,杠
|
||||||
|
zhuanZhuanchuguopai.remove(Integer.valueOf(card));
|
||||||
|
System.out.println("删除出过的牌组 成功");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,117 @@
|
||||||
|
package com.robotcm.service;
|
||||||
|
|
||||||
|
import com.taurus.core.entity.ITObject;
|
||||||
|
import com.taurus.core.entity.TObject;
|
||||||
|
import com.taurus.core.plugin.redis.Redis;
|
||||||
|
|
||||||
|
public class GroupPublisherService {
|
||||||
|
public static final String CHANNEL_NAME = "mgr_group";
|
||||||
|
|
||||||
|
private static final String CMD_ADD_ROOM = "add_room";
|
||||||
|
private static final String CMD_DEL_ROOM = "del_room";
|
||||||
|
private static final String CMD_DEL_PLAY = "del_play";
|
||||||
|
private static final String CMD_ADD_PLAY = "add_play";
|
||||||
|
private static final String CMD_UPDATE_PLAY = "update_play";
|
||||||
|
private static final String CMD_DEL_GROUP = "del_group";
|
||||||
|
private static final String CMD_UPDATE_GROUP = "update_group";
|
||||||
|
|
||||||
|
private static final String CMD_UPDATE_JOINS = "update_joins";
|
||||||
|
private static final String CMD_UPDATE_MAIL_TIP = "update_mail_tip";
|
||||||
|
// private static final String CMD_UPDATE_MEMBER = "update_member";
|
||||||
|
|
||||||
|
public static void addRoomEvt(int groupId, String roomid) {
|
||||||
|
ITObject data = TObject.newInstance();
|
||||||
|
data.putInt("gid", groupId);
|
||||||
|
data.putString("roomid", roomid);
|
||||||
|
data.putString("cmd", CMD_ADD_ROOM);
|
||||||
|
Redis.use("group1_db11").publish(CHANNEL_NAME, data.toJson());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void delRoomEvt(int groupId, String roomid) {
|
||||||
|
ITObject data = TObject.newInstance();
|
||||||
|
data.putInt("gid", groupId);
|
||||||
|
data.putString("roomid", roomid);
|
||||||
|
data.putString("cmd", CMD_DEL_ROOM);
|
||||||
|
Redis.use("group1_db11").publish(CHANNEL_NAME, data.toJson());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void delPlayEvt(int groupId, int pid) {
|
||||||
|
ITObject data = TObject.newInstance();
|
||||||
|
data.putInt("gid", groupId);
|
||||||
|
data.putInt("pid", pid);
|
||||||
|
data.putString("cmd", CMD_DEL_PLAY);
|
||||||
|
Redis.use("group1_db11").publish(CHANNEL_NAME, data.toJson());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addPlayEvt(int groupId, int pid) {
|
||||||
|
ITObject data = TObject.newInstance();
|
||||||
|
data.putInt("gid", groupId);
|
||||||
|
data.putInt("pid", pid);
|
||||||
|
data.putString("cmd", CMD_ADD_PLAY);
|
||||||
|
Redis.use("group1_db11").publish(CHANNEL_NAME, data.toJson());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void updatePlayEvt(int groupId, int pid) {
|
||||||
|
ITObject data = TObject.newInstance();
|
||||||
|
data.putInt("gid", groupId);
|
||||||
|
data.putInt("pid", pid);
|
||||||
|
data.putString("cmd", CMD_UPDATE_PLAY);
|
||||||
|
Redis.use("group1_db11").publish(CHANNEL_NAME, data.toJson());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void delGroupEvt(int groupId) {
|
||||||
|
ITObject data = TObject.newInstance();
|
||||||
|
data.putInt("gid", groupId);
|
||||||
|
data.putString("cmd", CMD_DEL_GROUP);
|
||||||
|
Redis.use("group1_db11").publish(CHANNEL_NAME, data.toJson());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void updateGroupEvt(int groupId, String name, boolean ban,String notice,int option,int showNum) {
|
||||||
|
ITObject data = TObject.newInstance();
|
||||||
|
data.putInt("gid", groupId);
|
||||||
|
data.putString("cmd", CMD_UPDATE_GROUP);
|
||||||
|
data.putString("name", name);
|
||||||
|
data.putBoolean("ban", ban);
|
||||||
|
data.putString("notice", notice);
|
||||||
|
data.putInt("option", option);
|
||||||
|
data.putInt("show_num", showNum);
|
||||||
|
Redis.use("group1_db11").publish(CHANNEL_NAME, data.toJson());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void updateJoinsEvt(int groupId, int joins) {
|
||||||
|
ITObject data = TObject.newInstance();
|
||||||
|
data.putInt("gid", groupId);
|
||||||
|
data.putInt("joins", joins);
|
||||||
|
data.putString("cmd", CMD_UPDATE_JOINS);
|
||||||
|
Redis.use("group1_db11").publish(CHANNEL_NAME, data.toJson());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void updateMailTipEvt(int groupId, int uid) {
|
||||||
|
ITObject data = TObject.newInstance();
|
||||||
|
data.putInt("gid", groupId);
|
||||||
|
data.putInt("uid", uid);
|
||||||
|
data.putString("cmd", CMD_UPDATE_MAIL_TIP);
|
||||||
|
Redis.use("group1_db11").publish(CHANNEL_NAME, data.toJson());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1 hp 2 等级 3 合伙人等级
|
||||||
|
*
|
||||||
|
* @param groupId
|
||||||
|
* @param uid
|
||||||
|
* @param type
|
||||||
|
* @param value
|
||||||
|
*/
|
||||||
|
public static void updateMemberEvt(int groupId, int uid, int type, int value) {
|
||||||
|
// if (type == 1) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// ITObject data = TObject.newInstance();
|
||||||
|
// data.putInt("gid", groupId);
|
||||||
|
// data.putInt("uid", uid);
|
||||||
|
// data.putInt("type", type);
|
||||||
|
// data.putInt("value", value);
|
||||||
|
// data.putString("cmd", CMD_UPDATE_MEMBER);
|
||||||
|
// Redis.use("group1_db11").publish(CHANNEL_NAME, data.toJson());
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,221 @@
|
||||||
|
package com.robotcm.taurus.client;
|
||||||
|
|
||||||
|
import com.taurus.core.entity.ITObject;
|
||||||
|
import com.taurus.core.entity.TObject;
|
||||||
|
import com.taurus.core.util.*;
|
||||||
|
import com.robotcm.taurus.client.TaurusClient.ConnectionProtocol;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.ConnectException;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.SocketTimeoutException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.Queue;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Http缃戠粶璇锋眰澶勭悊绫?
|
||||||
|
* @author daixiwei daixiwei15@126.com
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class HttpClient implements ISocketLayer {
|
||||||
|
private static final Logger logger = Logger.getLogger(HttpClient.class);
|
||||||
|
private static final byte[] NULL_BYTES = new byte[0];
|
||||||
|
private static final String _Method = "POST";
|
||||||
|
private static final String _ContentTypeName = "Content-Type";
|
||||||
|
private static final String _ContentType = "application/octet-stream";
|
||||||
|
private static final String _Result = "$r";
|
||||||
|
private static final String _Param = "$p";
|
||||||
|
private static final String _Session = "$s";
|
||||||
|
private static final String _Version = "$v";
|
||||||
|
String _url;
|
||||||
|
String _session_id = "";
|
||||||
|
|
||||||
|
final Queue<Runnable> _actionQueue = new LinkedList<Runnable>();
|
||||||
|
|
||||||
|
|
||||||
|
public HttpClient(String url) {
|
||||||
|
_url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void __sendMsg(Message msg) {
|
||||||
|
byte[] bytes = NULL_BYTES;
|
||||||
|
ITObject _data = TObject.newInstance();
|
||||||
|
_data.putString(_Session, _session_id);
|
||||||
|
_data.putInt(_Version, NetManager.VERSION);
|
||||||
|
if (msg.useText && msg.param == null) {
|
||||||
|
if (StringUtil.isEmpty(msg.textParam)) {
|
||||||
|
msg.param = TObject.newInstance();
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
msg.param = TObject.newFromJsonData(msg.textParam);
|
||||||
|
} catch (Exception e) {
|
||||||
|
msg.param = TObject.newInstance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_data.putTObject(_Param, msg.param);
|
||||||
|
ByteArray buff = new ByteArray(_data.toBinary());
|
||||||
|
try {
|
||||||
|
buff.compress();
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error(e);
|
||||||
|
}
|
||||||
|
bytes = buff.bytes();
|
||||||
|
|
||||||
|
__requestAsync(msg, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void __requestAsync(final Message msg,byte[] bytes) {
|
||||||
|
OutputStream out = null;
|
||||||
|
InputStream in = null;
|
||||||
|
int code = 500;
|
||||||
|
HttpURLConnection conn = null;
|
||||||
|
try {
|
||||||
|
|
||||||
|
URL realUrl = new URL(_url + msg.command);
|
||||||
|
conn = (HttpURLConnection) realUrl.openConnection();
|
||||||
|
conn.setDoOutput(true);
|
||||||
|
conn.setDoInput(true);
|
||||||
|
conn.setRequestProperty(_ContentTypeName, _ContentType);
|
||||||
|
conn.setUseCaches(false);// Post 璇锋眰涓嶈兘浣跨敤缂撳瓨
|
||||||
|
conn.setRequestMethod(_Method);
|
||||||
|
conn.setConnectTimeout(NetManager.TIMEOUT_TIME * 1000);
|
||||||
|
conn.setReadTimeout(NetManager.TIMEOUT_TIME * 1000);
|
||||||
|
conn.connect();
|
||||||
|
|
||||||
|
|
||||||
|
out = conn.getOutputStream();
|
||||||
|
out.write(bytes);
|
||||||
|
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
|
||||||
|
in = conn.getInputStream();
|
||||||
|
bytes = Utils.uncompress(in);
|
||||||
|
if (bytes!=null) {
|
||||||
|
|
||||||
|
ITObject tem = TObject.newFromBinaryData(bytes);
|
||||||
|
int result = tem.getInt(_Result);
|
||||||
|
|
||||||
|
if (result == 0) {
|
||||||
|
ITObject _param = tem.getTObject(_Param);
|
||||||
|
msg.param = _param;
|
||||||
|
if(msg.useText) {
|
||||||
|
msg.textParam = _param.toJson();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
code = result;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (SocketTimeoutException e) {
|
||||||
|
logger.error(e);
|
||||||
|
code = 102;
|
||||||
|
} catch (ConnectException ex1) {
|
||||||
|
logger.error(ex1);
|
||||||
|
code = 101;
|
||||||
|
} catch (Exception ex2) {
|
||||||
|
logger.error(ex2);
|
||||||
|
code = 500;
|
||||||
|
}finally {
|
||||||
|
try {
|
||||||
|
if (out != null) {
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
|
} catch (IOException ex) {
|
||||||
|
logger.error(ex);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (in != null) {
|
||||||
|
in.close();
|
||||||
|
}
|
||||||
|
}catch (IOException ex) {
|
||||||
|
logger.error(ex);
|
||||||
|
}
|
||||||
|
if(conn!=null) {
|
||||||
|
conn.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (msg.callback != null) {
|
||||||
|
final MessageResponse response = new MessageResponse();
|
||||||
|
response.messageData = msg;
|
||||||
|
response.returnCode = code;
|
||||||
|
enqueueActionForDispatch(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
msg.callback.action(response);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void connect(String ip, String game) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void disconnect() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void enqueueActionForDispatch(Runnable action) {
|
||||||
|
synchronized (_actionQueue) {
|
||||||
|
_actionQueue.offer(action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void processEvent() {
|
||||||
|
while (true) {
|
||||||
|
Runnable myAction;
|
||||||
|
synchronized (_actionQueue) {
|
||||||
|
if (_actionQueue.size() <= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
myAction = _actionQueue.poll();
|
||||||
|
}
|
||||||
|
myAction.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void write(final Message msg) {
|
||||||
|
new Thread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
__sendMsg(msg);
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAveragePingTime() {
|
||||||
|
return 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSession(String session) {
|
||||||
|
_session_id = session == null ? StringUtil.Empty : session;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSession() {
|
||||||
|
return _session_id;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setOnConnect(ICallback<SocketCode> onConnect) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setOnEvent(ICallback<Message> onEvent) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConnectionProtocol getProtocol() {
|
||||||
|
return ConnectionProtocol.Web;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
package com.robotcm.taurus.client;
|
||||||
|
|
||||||
|
import com.taurus.core.util.ICallback;
|
||||||
|
import com.robotcm.taurus.client.TaurusClient.ConnectionProtocol;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网络协议处理通用接口
|
||||||
|
* @author daixiwei daixiwei15@126.com
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface ISocketLayer {
|
||||||
|
/**
|
||||||
|
* 连接服务器
|
||||||
|
* @param ip
|
||||||
|
* @param game
|
||||||
|
*/
|
||||||
|
public void connect(String ip, String game);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 断开连接
|
||||||
|
*/
|
||||||
|
public void disconnect();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理事件
|
||||||
|
*/
|
||||||
|
public void processEvent();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送消息
|
||||||
|
* @param msg
|
||||||
|
*/
|
||||||
|
public void write(Message msg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get Protocol type
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public ConnectionProtocol getProtocol();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置连接状态回调
|
||||||
|
*/
|
||||||
|
public void setOnConnect(ICallback<SocketCode> onConnect);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置事件消息回调E
|
||||||
|
*/
|
||||||
|
public void setOnEvent(ICallback<Message> onEvent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网络延时
|
||||||
|
*/
|
||||||
|
public int getAveragePingTime();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取服务器Session字符
|
||||||
|
*/
|
||||||
|
public String getSession();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置服务器Session字符
|
||||||
|
* @param session
|
||||||
|
*/
|
||||||
|
public void setSession(String session);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
package com.robotcm.taurus.client;
|
||||||
|
|
||||||
|
import com.taurus.core.entity.ITObject;
|
||||||
|
import com.taurus.core.util.ICallback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网络协议消息包
|
||||||
|
* @author daixiwei daixiwei15@126.com
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class Message {
|
||||||
|
/**
|
||||||
|
* 使用文本,不转换ITObject
|
||||||
|
*/
|
||||||
|
public boolean useText;
|
||||||
|
/**
|
||||||
|
* 网络协议指令Key
|
||||||
|
*/
|
||||||
|
public String command;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参数
|
||||||
|
*/
|
||||||
|
public ITObject param;
|
||||||
|
/**
|
||||||
|
* 文本数据
|
||||||
|
*/
|
||||||
|
public String textParam;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网络消息回调
|
||||||
|
*/
|
||||||
|
public ICallback<MessageResponse> callback;
|
||||||
|
/**
|
||||||
|
* 自定义参数
|
||||||
|
*/
|
||||||
|
public Object data;
|
||||||
|
/**
|
||||||
|
* 网络包最后写入时间
|
||||||
|
*/
|
||||||
|
public long lastTime;
|
||||||
|
/**
|
||||||
|
* 网络包返回匹配的GID
|
||||||
|
*/
|
||||||
|
public int gid;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
package com.robotcm.taurus.client;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 缃戠粶娑堟伅鍥炶皟鏁版嵁瀵硅薄
|
||||||
|
* @author daixiwei daixiwei15@126.com
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class MessageResponse {
|
||||||
|
/**
|
||||||
|
* 娑堟伅鏁版嵁
|
||||||
|
*/
|
||||||
|
public Message messageData;
|
||||||
|
/**
|
||||||
|
* 杩斿洖缁撴瀯鍊?
|
||||||
|
*/
|
||||||
|
public int returnCode;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,107 @@
|
||||||
|
package com.robotcm.taurus.client;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网络管理对象
|
||||||
|
* @author daixiwei daixiwei15@126.com
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class NetManager {
|
||||||
|
|
||||||
|
private static List<TaurusClient> _clientList = new ArrayList<TaurusClient>();
|
||||||
|
private static Map<Integer, TaurusClient> _clientMap = new HashMap<Integer, TaurusClient>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网络超时时间
|
||||||
|
*/
|
||||||
|
public static int TIMEOUT_TIME = 10;
|
||||||
|
/**
|
||||||
|
* 网络版本号
|
||||||
|
*/
|
||||||
|
public static int VERSION = 10;
|
||||||
|
public static boolean debug_print = false;
|
||||||
|
|
||||||
|
static void addNetClient(TaurusClient net) {
|
||||||
|
if (net == null) {
|
||||||
|
throw new RuntimeException("TaurusClient is null, it can't be added.");
|
||||||
|
}
|
||||||
|
if (_clientMap.containsKey(net.getId())) {
|
||||||
|
throw new RuntimeException("A client with id: " + net.getId() + " already exists! Controller can't be added: " + net);
|
||||||
|
}
|
||||||
|
synchronized (_clientList) {
|
||||||
|
_clientMap.put(net.getId(), net);
|
||||||
|
_clientList.add(net);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The process events
|
||||||
|
*/
|
||||||
|
public static void processEvents() {
|
||||||
|
for (int i = 0; i < _clientList.size(); ++i) {
|
||||||
|
TaurusClient nc = _clientList.get(i);
|
||||||
|
if (nc._kill){
|
||||||
|
synchronized (_clientList) {
|
||||||
|
_clientMap.remove(nc.getId());
|
||||||
|
_clientList.remove(nc);
|
||||||
|
}
|
||||||
|
i--;
|
||||||
|
continue;
|
||||||
|
}else{
|
||||||
|
nc.processEvent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回指定的NetClient
|
||||||
|
* @param id
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static TaurusClient getNetClient(int id) {
|
||||||
|
for (int i = 0; i < _clientList.size(); ++i) {
|
||||||
|
TaurusClient nc = _clientList.get(i);
|
||||||
|
if (nc.getId() == id)
|
||||||
|
return nc;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 断开指定连接
|
||||||
|
* @param id
|
||||||
|
*/
|
||||||
|
public static void killConnection(int id) {
|
||||||
|
for (int i = 0; i < _clientList.size(); ++i) {
|
||||||
|
TaurusClient nc = _clientList.get(i);
|
||||||
|
if (nc.getId() == id) {
|
||||||
|
nc.killConnection();
|
||||||
|
synchronized (_clientList) {
|
||||||
|
_clientMap.remove(id);
|
||||||
|
_clientList.remove(nc);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 断开所有连接
|
||||||
|
*/
|
||||||
|
public static void killAllConnection() {
|
||||||
|
synchronized (_clientList) {
|
||||||
|
for (TaurusClient nc : _clientList) {
|
||||||
|
nc.killConnection();
|
||||||
|
}
|
||||||
|
_clientList.clear();
|
||||||
|
_clientMap.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
package com.robotcm.taurus.client;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Socket code
|
||||||
|
* @author daixiwei daixiwei15@126.com
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public enum SocketCode{
|
||||||
|
/**
|
||||||
|
* 杩炴帴鎴愬姛
|
||||||
|
*/
|
||||||
|
Connect,
|
||||||
|
Disconnect,
|
||||||
|
/**
|
||||||
|
* 鏈煡寮傚父
|
||||||
|
*/
|
||||||
|
Exception,
|
||||||
|
/**
|
||||||
|
* 杩炴帴鏈嶅姟寮傚父
|
||||||
|
*/
|
||||||
|
ExceptionOnConnect,
|
||||||
|
/**
|
||||||
|
* 鍙戦€佹暟鎹祦閿欒
|
||||||
|
*/
|
||||||
|
SendError,
|
||||||
|
/**
|
||||||
|
* 鎺ユ敹鏈嶅姟鍣ㄦ暟鎹祦寮傚父
|
||||||
|
*/
|
||||||
|
ExceptionOnReceive,
|
||||||
|
/**
|
||||||
|
* 鏈嶅姟鍣ㄨ繛鎺ヨ秴鏃?
|
||||||
|
*/
|
||||||
|
TimeoutDisconnect,
|
||||||
|
/**
|
||||||
|
* 鏈嶅姟鍣ㄦ柇寮€杩炴帴
|
||||||
|
*/
|
||||||
|
DisconnectByServer,
|
||||||
|
/**
|
||||||
|
* 瀹㈡埛绔綉缁滃紓甯?
|
||||||
|
*/
|
||||||
|
NetworkException,
|
||||||
|
/**
|
||||||
|
* 杩炴帴瀹夊叏寮傚父锛屼竴鑸负闃茬伀澧欓樆姝?
|
||||||
|
*/
|
||||||
|
SecurityExceptionOnConnect
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,354 @@
|
||||||
|
package com.robotcm.taurus.client;
|
||||||
|
|
||||||
|
import com.taurus.core.entity.ITObject;
|
||||||
|
import com.taurus.core.events.Event;
|
||||||
|
import com.taurus.core.events.EventDispatcher;
|
||||||
|
import com.taurus.core.events.IEventListener;
|
||||||
|
import com.taurus.core.util.ICallback;
|
||||||
|
import com.taurus.core.util.Logger;
|
||||||
|
import com.robotcm.taurus.client.io.SocketEngine;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 缃戠粶鏍稿績鍔熻兘澶勭悊绫?
|
||||||
|
* @author daixiwei daixiwei15@126.com
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class TaurusClient {
|
||||||
|
private static final Logger logger = Logger.getLogger(TaurusClient.class);
|
||||||
|
private int _id;
|
||||||
|
private boolean _connect;
|
||||||
|
private String _gameId;
|
||||||
|
private String _currentIp;
|
||||||
|
|
||||||
|
private ISocketLayer _scoket;
|
||||||
|
private ThreadManager _threadManager;
|
||||||
|
|
||||||
|
private EventDispatcher _dispatcher;
|
||||||
|
private Map<Message, ICallback<MessageResponse>> _responseMap;
|
||||||
|
volatile boolean _kill = false;
|
||||||
|
private static int _tempID;
|
||||||
|
private int _protocolCompression=100;
|
||||||
|
|
||||||
|
private static int _getValidID() {
|
||||||
|
return ++_tempID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param host
|
||||||
|
* @param game
|
||||||
|
* @param protocol
|
||||||
|
*/
|
||||||
|
public TaurusClient(String host, String game, ConnectionProtocol protocol) {
|
||||||
|
ISocketLayer socket = null;
|
||||||
|
switch (protocol) {
|
||||||
|
case Tcp:
|
||||||
|
socket = new SocketEngine(this);
|
||||||
|
break;
|
||||||
|
case Web:
|
||||||
|
socket = new HttpClient(host);
|
||||||
|
_connect = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
socket = new SocketEngine(this);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
__initialize(host, game, socket);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param host
|
||||||
|
* @param game
|
||||||
|
* @param socket
|
||||||
|
* @param protocol
|
||||||
|
*/
|
||||||
|
public TaurusClient(String host, String game, ISocketLayer socket) {
|
||||||
|
__initialize(host, game, socket);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 娣诲姞浜嬩欢
|
||||||
|
* @param eventType
|
||||||
|
* @param listener
|
||||||
|
*/
|
||||||
|
public void addEventListener(String eventType, IEventListener listener) {
|
||||||
|
_dispatcher.addEventListener(eventType, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绉婚櫎鎵€鏈変簨浠?
|
||||||
|
*/
|
||||||
|
public void removeAllListener() {
|
||||||
|
_dispatcher.removeAllListener();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绉婚櫎鎸囧畾浜嬩欢
|
||||||
|
* @param eventType
|
||||||
|
* @param listener
|
||||||
|
*/
|
||||||
|
public void removeEventListener(String eventType, IEventListener listener) {
|
||||||
|
_dispatcher.removeEventListener(eventType, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void __initialize(String host, String game, ISocketLayer socket) {
|
||||||
|
_dispatcher = new EventDispatcher();
|
||||||
|
_responseMap = new HashMap<Message, ICallback<MessageResponse>>();
|
||||||
|
_id = _getValidID();
|
||||||
|
NetManager.addNetClient(this);
|
||||||
|
_currentIp = host;
|
||||||
|
_gameId = game;
|
||||||
|
_threadManager = new ThreadManager();
|
||||||
|
_threadManager.start();
|
||||||
|
_scoket = socket;
|
||||||
|
_scoket.setOnConnect(new ICallback<SocketCode>() {
|
||||||
|
@Override
|
||||||
|
public void action(SocketCode code) {
|
||||||
|
if (code == SocketCode.Connect)
|
||||||
|
_connect = true;
|
||||||
|
else
|
||||||
|
_connect = false;
|
||||||
|
Event evt = new Event(NetClientEvent.Connect);
|
||||||
|
evt.setParameter("code", code);
|
||||||
|
_dispatcher.dispatchEvent(evt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
_scoket.setOnEvent(new ICallback<Message>() {
|
||||||
|
@Override
|
||||||
|
public void action(Message msg) {
|
||||||
|
if (_connect) {
|
||||||
|
Event evt = new Event(NetClientEvent.OnEvent);
|
||||||
|
evt.setParameter("msg", msg);
|
||||||
|
_dispatcher.dispatchEvent(evt);
|
||||||
|
if (NetManager.debug_print){
|
||||||
|
String str = getProtocol() == ConnectionProtocol.Web ? "WEB" : "TCP";
|
||||||
|
String data_str = msg.useText ? msg.textParam : (msg.param != null ? msg.param.toJson() : "null");
|
||||||
|
logger.debug("Event [" + str + "->" + msg.command + "] data:" + data_str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 杩炴帴鏈嶅姟鍣?
|
||||||
|
*/
|
||||||
|
public void connect() {
|
||||||
|
_scoket.connect(_currentIp, _gameId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kill net connection.
|
||||||
|
*/
|
||||||
|
public void killConnection() {
|
||||||
|
removeAllListener();
|
||||||
|
_responseMap.clear();
|
||||||
|
_threadManager.stop();
|
||||||
|
_scoket.disconnect();
|
||||||
|
_connect = false;
|
||||||
|
_kill = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 鍙戦€佺綉缁滄秷鎭寘
|
||||||
|
* @param msg 娑堟伅鏁版嵁瀵硅薄
|
||||||
|
*/
|
||||||
|
protected void send(Message msg) {
|
||||||
|
if (msg == null)
|
||||||
|
return;
|
||||||
|
if (NetManager.debug_print) {
|
||||||
|
String str = getProtocol() == ConnectionProtocol.Web ? "WEB" : "TCP";
|
||||||
|
String data_str = msg.useText ? msg.textParam : (msg.param != null ? msg.param.toJson() : "null");
|
||||||
|
str = "Requst [" + str + "->" + msg.command + "] ->data:" + data_str;
|
||||||
|
logger.debug(str);
|
||||||
|
}
|
||||||
|
if (msg.callback != null) {
|
||||||
|
_responseMap.put(msg, msg.callback);
|
||||||
|
msg.callback = new ICallback<MessageResponse>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void action(MessageResponse response) {
|
||||||
|
ICallback<MessageResponse> callback = _responseMap.get(response.messageData);
|
||||||
|
if (callback == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (NetManager.debug_print){
|
||||||
|
String str = getProtocol() == ConnectionProtocol.Web ? "WEB" : "TCP";
|
||||||
|
String data_str = msg.useText ? msg.textParam : (msg.param != null ? msg.param.toJson() : "null");
|
||||||
|
str = "Response [" + str + "->" + msg.command + "] ->result:"+ response.returnCode + " param:" + data_str;
|
||||||
|
logger.debug(str);
|
||||||
|
}
|
||||||
|
_responseMap.remove(response.messageData);
|
||||||
|
callback.action(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
_threadManager.enqueueSend(new ICallback<Message>() {
|
||||||
|
@Override
|
||||||
|
public void action(Message msg) {
|
||||||
|
sendData(msg);
|
||||||
|
}
|
||||||
|
}, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 鍙戦€佺綉缁滄秷鎭寘
|
||||||
|
* @param cmd 缃戠粶鍗忚鎸囦护Key
|
||||||
|
* @param param 鍙傛暟
|
||||||
|
* @param callback 缃戠粶娑堟伅鍥炶皟
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Message send(String cmd, ITObject param, ICallback<MessageResponse> callback) {
|
||||||
|
Message msg = new Message();
|
||||||
|
msg.useText = false;
|
||||||
|
msg.command = cmd;
|
||||||
|
msg.param = param;
|
||||||
|
msg.callback = callback;
|
||||||
|
this.send(msg);
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 鍙戦€佺綉缁滄秷鎭寘
|
||||||
|
* @param cmd 缃戠粶鍗忚鎸囦护Key
|
||||||
|
* @param text JSON 鏂囨湰
|
||||||
|
* @param callback 缃戠粶娑堟伅鍥炶皟
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Message sendText(String cmd, String text, ICallback<MessageResponse> callback) {
|
||||||
|
Message msg = new Message();
|
||||||
|
msg.command = cmd;
|
||||||
|
msg.textParam = text;
|
||||||
|
msg.callback = callback;
|
||||||
|
msg.useText = true;
|
||||||
|
this.send(msg);
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 鍙戦€佹暟鎹嚎绋嬫柟娉?
|
||||||
|
* @param message
|
||||||
|
*/
|
||||||
|
private void sendData(Message msg) {
|
||||||
|
_scoket.write(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 澶勭悊浜嬩欢
|
||||||
|
*/
|
||||||
|
void processEvent() {
|
||||||
|
if(_scoket!=null) {
|
||||||
|
_scoket.processEvent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 娓呯悊鍝嶅簲鍥炶皟
|
||||||
|
*/
|
||||||
|
public void clearResponse() {
|
||||||
|
_responseMap.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The net client id
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public int getId() {
|
||||||
|
|
||||||
|
return _id;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The curren ip
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String getCurrentIp() {
|
||||||
|
return _currentIp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The curren Game ID
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String getGameID() {
|
||||||
|
return _gameId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 缃戠粶鏄惁宸茶繛鎺?
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public boolean isConnected() {
|
||||||
|
return _connect;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 褰撳墠缃戠粶绫诲瀷
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public ConnectionProtocol getProtocol() {
|
||||||
|
return _scoket.getProtocol();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 缃戠粶寤舵椂
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public int getAveragePingTime() {
|
||||||
|
return _scoket.getAveragePingTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 鑾峰彇鏈嶅姟鍣⊿ession瀛楃
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String getSession() {
|
||||||
|
return _scoket.getSession();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 璁剧疆鏈嶅姟鍣⊿ession瀛楃
|
||||||
|
* @param session
|
||||||
|
*/
|
||||||
|
public void setSession(String session) {
|
||||||
|
_scoket.setSession(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 璁剧疆鍘嬬缉闃€鍊?
|
||||||
|
* @param protocolCompression
|
||||||
|
*/
|
||||||
|
public void setProtocolCompression(int protocolCompression) {
|
||||||
|
_protocolCompression = protocolCompression;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 鑾峰彇鍘嬬缉闃€鍊?
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public int getProtocolCompression() {
|
||||||
|
return _protocolCompression;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static enum ConnectionProtocol {
|
||||||
|
Tcp, Web,
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class NetClientEvent {
|
||||||
|
public static final String Connect = "connect";
|
||||||
|
|
||||||
|
public static final String OnEvent = "onevent";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,123 @@
|
||||||
|
package com.robotcm.taurus.client;
|
||||||
|
|
||||||
|
import com.taurus.core.util.ICallback;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.Queue;
|
||||||
|
import java.util.Stack;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网络(读/写) 线程管理
|
||||||
|
* @author daixiwei daixiwei15@126.com
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class ThreadManager {
|
||||||
|
class MessgaeThread {
|
||||||
|
public ICallback<Message> _dataDelegate;
|
||||||
|
|
||||||
|
public Message _message;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean _running = false;
|
||||||
|
private Thread _outThread;
|
||||||
|
private boolean _outHasQueuedItems = false;
|
||||||
|
private Queue<MessgaeThread> _outThreadQueue = new LinkedList<MessgaeThread>();
|
||||||
|
private Object _outQueueLocker = new Object();
|
||||||
|
private Stack<MessgaeThread> pool = new Stack<MessgaeThread>();
|
||||||
|
|
||||||
|
private MessgaeThread MessgaeThreadGet() {
|
||||||
|
if (pool.size() > 0) {
|
||||||
|
MessgaeThread msg = pool.pop();
|
||||||
|
return msg;
|
||||||
|
} else
|
||||||
|
return new MessgaeThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MessgaeThreadReturn(MessgaeThread value) {
|
||||||
|
pool.push(value);
|
||||||
|
value._dataDelegate = null;
|
||||||
|
value._message = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void sleep(int ms) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(ms);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void outThread() {
|
||||||
|
while (_running) {
|
||||||
|
sleep(5);
|
||||||
|
if (_outHasQueuedItems) {
|
||||||
|
Object obj = _outQueueLocker;
|
||||||
|
synchronized (obj) {
|
||||||
|
while (_outThreadQueue.size() > 0) {
|
||||||
|
MessgaeThread item = _outThreadQueue.poll();
|
||||||
|
processOutItem(item);
|
||||||
|
}
|
||||||
|
_outHasQueuedItems = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processOutItem(MessgaeThread item) {
|
||||||
|
ICallback<Message> _dataDelegate = item._dataDelegate;
|
||||||
|
Message message = item._message;
|
||||||
|
MessgaeThreadReturn(item);
|
||||||
|
if (_dataDelegate != null) {
|
||||||
|
_dataDelegate.action(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start() {
|
||||||
|
if (_running) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_running = true;
|
||||||
|
if (_outThread == null) {
|
||||||
|
_outThread = new Thread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
outThread();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
_outThread.start();
|
||||||
|
_outThread.setName("WriteMessage");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
Thread thread = new Thread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
stopThread();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
thread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stopThread() {
|
||||||
|
_running = false;
|
||||||
|
|
||||||
|
if (_outThread != null) {
|
||||||
|
_outThread.interrupt();
|
||||||
|
}
|
||||||
|
_outThread = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void enqueueSend(ICallback<Message> callback, Message message) {
|
||||||
|
MessgaeThread hashtable = MessgaeThreadGet();
|
||||||
|
hashtable._dataDelegate = callback;
|
||||||
|
hashtable._message = message;
|
||||||
|
Object obj = _outQueueLocker;
|
||||||
|
synchronized (obj) {
|
||||||
|
_outThreadQueue.offer(hashtable);
|
||||||
|
_outHasQueuedItems = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,343 @@
|
||||||
|
package com.robotcm.taurus.client.business;
|
||||||
|
|
||||||
|
import com.data.bean.AccountBean;
|
||||||
|
import com.data.bean.GameBean;
|
||||||
|
import com.data.cache.AccountCache;
|
||||||
|
import com.data.cache.BaseCache;
|
||||||
|
import com.data.cache.GameCache;
|
||||||
|
import com.data.util.ErrorCode;
|
||||||
|
import com.data.util.Utility;
|
||||||
|
import com.taurus.core.entity.ITArray;
|
||||||
|
import com.taurus.core.entity.ITObject;
|
||||||
|
import com.taurus.core.entity.TArray;
|
||||||
|
import com.taurus.core.entity.TObject;
|
||||||
|
import com.taurus.core.plugin.database.DataBase;
|
||||||
|
import com.taurus.core.plugin.redis.Redis;
|
||||||
|
import com.taurus.core.plugin.redis.RedisLock;
|
||||||
|
import com.taurus.core.util.Logger;
|
||||||
|
import com.taurus.core.util.StringUtil;
|
||||||
|
import com.taurus.core.util.Utils;
|
||||||
|
import com.taurus.web.Controller;
|
||||||
|
import com.taurus.web.WebException;
|
||||||
|
import redis.clients.jedis.Jedis;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class AccountBusiness extends Controller {
|
||||||
|
private static Logger logger = Logger.getLogger(AccountBusiness.class);
|
||||||
|
|
||||||
|
public static String request(String httpUrl, String httpArg) {
|
||||||
|
BufferedReader reader = null;
|
||||||
|
String result = null;
|
||||||
|
StringBuffer sbf = new StringBuffer();
|
||||||
|
httpUrl = httpUrl + "?" + httpArg;
|
||||||
|
|
||||||
|
try {
|
||||||
|
URL url = new URL(httpUrl);
|
||||||
|
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||||
|
connection.setRequestMethod("GET");
|
||||||
|
connection.connect();
|
||||||
|
InputStream is = connection.getInputStream();
|
||||||
|
reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
|
||||||
|
String strRead = reader.readLine();
|
||||||
|
if (strRead != null) {
|
||||||
|
sbf.append(strRead);
|
||||||
|
while ((strRead = reader.readLine()) != null) {
|
||||||
|
sbf.append("\n");
|
||||||
|
sbf.append(strRead);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reader.close();
|
||||||
|
result = sbf.toString();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String md5(String plainText) {
|
||||||
|
StringBuffer buf = null;
|
||||||
|
try {
|
||||||
|
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||||
|
md.update(plainText.getBytes());
|
||||||
|
byte b[] = md.digest();
|
||||||
|
int i;
|
||||||
|
buf = new StringBuffer("");
|
||||||
|
for (int offset = 0; offset < b.length; offset++) {
|
||||||
|
i = b[offset];
|
||||||
|
if (i < 0)
|
||||||
|
i += 256;
|
||||||
|
if (i < 16)
|
||||||
|
buf.append("0");
|
||||||
|
buf.append(Integer.toHexString(i));
|
||||||
|
}
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String encodeUrlString(String str, String charset) {
|
||||||
|
String strret = null;
|
||||||
|
if (str == null)
|
||||||
|
return str;
|
||||||
|
try {
|
||||||
|
strret = java.net.URLEncoder.encode(str, charset);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return strret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final ITObject fillLoginData(String session, int accountid) {
|
||||||
|
ITObject resData = TObject.newInstance();
|
||||||
|
ITObject userData = TObject.newInstance();
|
||||||
|
resData.putTObject("account", userData);
|
||||||
|
resData.putUtfString("session_id", session);
|
||||||
|
resData.putTArray("games", getOnlineGames());
|
||||||
|
Jedis jedis0 = Redis.use("group1_db0").getJedis();
|
||||||
|
try {
|
||||||
|
Map<String, String> map = jedis0.hgetAll(session);
|
||||||
|
userData.putInt("id", accountid);
|
||||||
|
userData.putInt("diamo", Integer.parseInt(map.get("diamo")));
|
||||||
|
userData.putUtfString("nick", map.get("nick"));
|
||||||
|
userData.putUtfString("portrait", map.get("portrait"));
|
||||||
|
userData.putInt("sex", Integer.parseInt(map.get("sex")));
|
||||||
|
userData.putInt("type", Integer.parseInt(map.get("type")));
|
||||||
|
int mng = Integer.parseInt(map.get("mng"));
|
||||||
|
userData.putInt("mng", mng);
|
||||||
|
|
||||||
|
String phone = map.get("phone");
|
||||||
|
if (StringUtil.isNotEmpty(phone)) {
|
||||||
|
userData.putUtfString("phone", phone);
|
||||||
|
}
|
||||||
|
|
||||||
|
String address = map.get("address");
|
||||||
|
if (StringUtil.isNotEmpty(address)) {
|
||||||
|
userData.putUtfString("address", address);
|
||||||
|
}
|
||||||
|
|
||||||
|
String real_info = map.get("real_info");
|
||||||
|
if (StringUtil.isNotEmpty(real_info)) {
|
||||||
|
userData.putTObject("real_info", TObject.newFromJsonData(real_info));
|
||||||
|
}
|
||||||
|
String oldRoom = Utility.getOldRoomV2(jedis0, 0, session, accountid);
|
||||||
|
if (StringUtil.isNotEmpty(oldRoom)) {
|
||||||
|
String roomid = oldRoom.replace("room:", "");
|
||||||
|
String group = jedis0.hget(oldRoom, "group");
|
||||||
|
int groupId = 0;
|
||||||
|
if (StringUtil.isNotEmpty(group)) {
|
||||||
|
groupId = Integer.parseInt(group);
|
||||||
|
}
|
||||||
|
userData.putUtfString("roomid", roomid);
|
||||||
|
userData.putInt("groupId", groupId);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
jedis0.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
resData.putUtfString("groupWeb", Redis.use("group1_db1").hget("web_requrl", "groupWeb_jefe"));
|
||||||
|
return resData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final ITObject idPasswordLogin(int id, String password) throws Exception {
|
||||||
|
|
||||||
|
logger.info("id:" + id + " login");
|
||||||
|
|
||||||
|
Jedis jedis0 = Redis.use("group1_db0").getJedis();
|
||||||
|
RedisLock lock = new RedisLock("wx_" + id, jedis0);
|
||||||
|
try {
|
||||||
|
|
||||||
|
logger.info("==========> password111 = " + password);
|
||||||
|
String superPwd = Redis.use("group1_db1").get("superpwd2021");
|
||||||
|
String sql = "";
|
||||||
|
if (!StringUtil.isEmpty(superPwd)) {
|
||||||
|
if (!password.equals(superPwd)) {
|
||||||
|
password = Utils.getMD5Hash(password);
|
||||||
|
sql = String.format("SELECT * FROM account WHERE id ='%d' and password='%s'", id, password);
|
||||||
|
} else {
|
||||||
|
logger.info("==========> password = " + password);
|
||||||
|
|
||||||
|
sql = String.format("SELECT * FROM account WHERE id ='%d' ", id);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
password = Utils.getMD5Hash(password);
|
||||||
|
sql = String.format("SELECT * FROM account WHERE id ='%d' and password='%s'", id, password);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
String idPwdBan = Redis.use("group1_db0").get(id + "_login_ban");
|
||||||
|
if (StringUtil.isNotEmpty(idPwdBan)) {
|
||||||
|
System.out.println("进入了77777777777777777777");
|
||||||
|
logger.error("id:" + id + " ban login");
|
||||||
|
throw new WebException(ErrorCode.BAN_LOGIN);
|
||||||
|
}
|
||||||
|
System.out.println("进入了9999999999999");
|
||||||
|
|
||||||
|
ITArray resultArray = DataBase.use().executeQueryByTArray(sql);
|
||||||
|
if (resultArray.size() == 0) {
|
||||||
|
if (Redis.use("group1_db0").exists(id + "_pwd_token")) {
|
||||||
|
Redis.use("group1_db0").incrBy(id + "_pwd_token", 1);
|
||||||
|
} else {
|
||||||
|
Redis.use("group1_db0").set(id + "_pwd_token", 1 + "");
|
||||||
|
Redis.use("group1_db0").expire(id + "_pwd_token", 300);
|
||||||
|
}
|
||||||
|
|
||||||
|
String idPwdToken = Redis.use("group1_db0").get(id + "_pwd_token");
|
||||||
|
if (StringUtil.isNotEmpty(idPwdToken)) {
|
||||||
|
long count = Long.parseLong(idPwdToken);
|
||||||
|
if (count >= 10) {
|
||||||
|
Redis.use("group1_db0").set(id + "_login_ban", "1");
|
||||||
|
Redis.use("group1_db0").expire(id + "_login_ban", 1800);
|
||||||
|
logger.error("pwd error count:" + count + " not login");
|
||||||
|
System.out.println("进入了00000000000");
|
||||||
|
|
||||||
|
throw new WebException(ErrorCode._NO_SESSION);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
System.out.println("进入了111111111111");
|
||||||
|
|
||||||
|
throw new WebException(ErrorCode._FAILED);
|
||||||
|
}
|
||||||
|
|
||||||
|
ITObject userData = resultArray.getTObject(0);
|
||||||
|
int accountid = userData.getInt("id");
|
||||||
|
UpdateUserData(userData, accountid);
|
||||||
|
|
||||||
|
AccountBean acc_bean = AccountCache.getAccount(accountid);
|
||||||
|
String session = acc_bean.redis_key;
|
||||||
|
this.setSession(session);
|
||||||
|
|
||||||
|
if (resultArray.size() > 0) {
|
||||||
|
this.setSession(session);
|
||||||
|
String old_nick = acc_bean.nick;
|
||||||
|
String old_portrait = acc_bean.portrait;
|
||||||
|
// String new_nick = reqData.getUtfString("nick");
|
||||||
|
// String new_portrait = reqData.getUtfString("portrait");
|
||||||
|
// if (!old_nick.equals(new_nick) || !old_portrait.equals(new_portrait)) {
|
||||||
|
// ITObject userData1 = TObject.newInstance();
|
||||||
|
// userData1.putUtfString("nick", userData.getUtfString("nick"));
|
||||||
|
// userData1.putUtfString("portrait", userData.getUtfString("portrait"));
|
||||||
|
// userData1.putInt("sex", userData.getInt("sex"));
|
||||||
|
// updateSession(userData, accountid);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
ITObject resData = fillLoginData(session, accountid);
|
||||||
|
String token = Utils.getMD5Hash(id + "_" + password + "_" + System.currentTimeMillis() + "e4!Fesu]]{QyUuEA"
|
||||||
|
+ Math.random() * 1000000);
|
||||||
|
Redis.use("group1_db0").sadd(session + "_token", token);
|
||||||
|
|
||||||
|
Redis.use("group1_db0").hset(token, "user", session);
|
||||||
|
Redis.use("group1_db0").hset(token, "create_time", "" + System.currentTimeMillis() / 1000);
|
||||||
|
Redis.use("group1_db0").expire(token, 172800);
|
||||||
|
|
||||||
|
// Set<String> allToken = Redis.use("group1_db0").smembers(session + "_token");
|
||||||
|
// for (String temp : allToken) {
|
||||||
|
// if (!Redis.use("group1_db0").exists(temp)) {
|
||||||
|
// Redis.use("group1_db0").srem(session + "_token", temp);
|
||||||
|
// logger.info("delte timeout token:" + temp);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
System.out.println("进入了2222222222222");
|
||||||
|
|
||||||
|
long tokenNum = Redis.use("group1_db0").scard(session + "_token");
|
||||||
|
if (tokenNum >= 10) {
|
||||||
|
logger.warn("id:" + accountid + " repeat login, token count:" + tokenNum);
|
||||||
|
}
|
||||||
|
System.out.println("进入了33333333333333333332");
|
||||||
|
|
||||||
|
resData.putString("token", token);
|
||||||
|
return resData;
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String updateSession(ITObject userData, int id) {
|
||||||
|
String session = AccountCache.genKey(id);
|
||||||
|
Map<String, String> map = new HashMap<String, String>();
|
||||||
|
Utils.objectToMap(userData, map);
|
||||||
|
|
||||||
|
Jedis jedis0 = Redis.use("group1_db0").getJedis();
|
||||||
|
try {
|
||||||
|
jedis0.hmset(session, map);
|
||||||
|
BaseCache.updateCacheVer(jedis0, session);
|
||||||
|
} finally {
|
||||||
|
jedis0.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取在线游戏
|
||||||
|
*/
|
||||||
|
public static ITArray getOnlineGames() {
|
||||||
|
ITArray games = new TArray();
|
||||||
|
Jedis jedis1 = Redis.use("group1_db1").getJedis();
|
||||||
|
try {
|
||||||
|
Set<String> list = jedis1.zrevrangeByScore("online_games", 1000, 1);
|
||||||
|
for (String game : list) {
|
||||||
|
int gameId = Integer.parseInt(game);
|
||||||
|
GameBean gb = GameCache.getGame(gameId);
|
||||||
|
if (gb == null)
|
||||||
|
continue;
|
||||||
|
ITObject gameObj = gb.getTObject();
|
||||||
|
|
||||||
|
for (Entry<String, Integer> entry : gb.pay.entrySet()) {
|
||||||
|
gameObj.putInt(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
games.addTObject(gameObj);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
jedis1.close();
|
||||||
|
}
|
||||||
|
return games;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
private final int UpdateUserData(ITObject reqData, long id) throws Exception {
|
||||||
|
ITObject userData = TObject.newInstance();
|
||||||
|
userData.putInt("id", (int) id);
|
||||||
|
|
||||||
|
userData.putUtfString("acc", reqData.getUtfString("acc"));
|
||||||
|
userData.putUtfString("portrait", reqData.getUtfString("portrait"));
|
||||||
|
userData.putUtfString("nick", reqData.getUtfString("nick"));
|
||||||
|
int sex = reqData.getInt("sex");
|
||||||
|
if (sex == 0) {
|
||||||
|
sex = 1;
|
||||||
|
reqData.putInt("sex", sex);
|
||||||
|
}
|
||||||
|
userData.putInt("sex", sex);
|
||||||
|
|
||||||
|
userData.putInt("mng", 0);
|
||||||
|
userData.putInt("type", 0);
|
||||||
|
if (reqData.containsKey("diamo")) {
|
||||||
|
userData.putInt("diamo", reqData.getInt("diamo"));
|
||||||
|
}
|
||||||
|
|
||||||
|
userData.putInt("invitation", 1);
|
||||||
|
String session = updateSession(userData, (int) id);
|
||||||
|
this.setSession(session);
|
||||||
|
return (int) id;
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,136 @@
|
||||||
|
package com.robotcm.taurus.client.business;
|
||||||
|
|
||||||
|
import com.data.util.ErrorCode;
|
||||||
|
import com.game.Global;
|
||||||
|
import com.game.MainServer;
|
||||||
|
import com.game.data.JoinRoomData;
|
||||||
|
import com.game.data.Room;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.taurus.core.plugin.redis.Redis;
|
||||||
|
import com.taurus.core.plugin.redis.RedisLock;
|
||||||
|
import com.taurus.core.util.StringUtil;
|
||||||
|
import com.taurus.permanent.data.Session;
|
||||||
|
import redis.clients.jedis.Jedis;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 房间管理类
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class RoomBusiness {
|
||||||
|
|
||||||
|
public ConcurrentHashMap<String, Room> tableMap;
|
||||||
|
|
||||||
|
public RoomBusiness() {
|
||||||
|
this.tableMap = new ConcurrentHashMap<String, Room>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 进入房间
|
||||||
|
*
|
||||||
|
* @param sender
|
||||||
|
* @param session_key
|
||||||
|
* @param gid
|
||||||
|
*/
|
||||||
|
public void joinRoom(Session sender, String session_key, int gid, String gps_pos) {
|
||||||
|
Jedis jedis = Redis.use().getJedis();
|
||||||
|
try {
|
||||||
|
if (!jedis.exists(session_key)) {
|
||||||
|
MainServer.instance.sendResponse(gid, ErrorCode._NO_SESSION, null, sender);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String room_key = jedis.hget(session_key, "room");
|
||||||
|
if (StringUtil.isEmpty(room_key)) {
|
||||||
|
MainServer.instance.sendResponse(gid, ErrorCode.NO_ROOM_NUM, null, sender);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Map<String, String> room_map = null;
|
||||||
|
RedisLock lock = new RedisLock(room_key, jedis);
|
||||||
|
try {
|
||||||
|
lock.lock();
|
||||||
|
room_map = jedis.hgetAll(room_key);
|
||||||
|
String status = room_map.get("status");
|
||||||
|
if (StringUtil.isEmpty(status)) {
|
||||||
|
Global.gameCtr.delRoomSeat(session_key, room_key);
|
||||||
|
MainServer.instance.sendResponse(gid, ErrorCode.NO_ROOM_NUM, null, sender);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (status.equals("3") || status.equals("2")) {
|
||||||
|
Global.gameCtr.delRoomSeat(session_key, room_key);
|
||||||
|
MainServer.instance.sendResponse(gid, ErrorCode.GROUP_ROOM_DEL, null, sender);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (Global.gameId != Integer.parseInt(room_map.get("game"))) {
|
||||||
|
//// Global.logger.error("joinRoom gameId:"+Global.gameId+" != gameId:" + Integer.parseInt(room_map.get("game")));
|
||||||
|
// MainServer.instance.sendResponse(gid, ErrorCode.NO_SERVICE, null, sender);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
String room_id = room_map.get("id");
|
||||||
|
Room room = null;
|
||||||
|
if (this.tableMap.containsKey(room_id)) {
|
||||||
|
room = this.tableMap.get(room_id);
|
||||||
|
if (room.isDestroy) {
|
||||||
|
MainServer.instance.sendResponse(gid, ErrorCode._FAILED, null, sender);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Jedis jedis0 = Redis.use("group1_db0").getJedis();
|
||||||
|
Gson gson = new Gson();
|
||||||
|
String json = jedis0.get("robot_" + room_id);
|
||||||
|
System.out.println("json:"+json);
|
||||||
|
room = gson.fromJson(json, Room.class);
|
||||||
|
System.out.println("room:" + room);
|
||||||
|
// room = MainServer.instance.newRoom(room_id, room_map);
|
||||||
|
room.loadRedisPlayer();
|
||||||
|
this.tableMap.put(room_id, room);
|
||||||
|
room.start();
|
||||||
|
// room.stateMachine.changeState(Global.getState(RoomReloadState.class));
|
||||||
|
}
|
||||||
|
if (room.isActive() == false) {
|
||||||
|
room.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
sender.setHashId(session_key);
|
||||||
|
final JoinRoomData jrd = new JoinRoomData();
|
||||||
|
jrd.room = room;
|
||||||
|
jrd.session_key = session_key;
|
||||||
|
jrd.sender = sender;
|
||||||
|
jrd.gps_pos = gps_pos;
|
||||||
|
Global.gameCtr.joinRoom(jrd, gid);
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
lock.unlock(false);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
// Global.logger.error(e);
|
||||||
|
MainServer.instance.sendResponse(gid, 500, null, sender);
|
||||||
|
} finally {
|
||||||
|
jedis.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解散房间
|
||||||
|
*
|
||||||
|
* @param roomid 房间ID
|
||||||
|
*/
|
||||||
|
public void dismissRoom(String roomid, boolean sendEvt) {
|
||||||
|
Room room = this.tableMap.get(roomid);
|
||||||
|
Jedis jedis0 = Redis.use("group1_db0").getJedis();
|
||||||
|
RedisLock lock = new RedisLock(room.room_key, jedis0);
|
||||||
|
try {
|
||||||
|
lock.lock();
|
||||||
|
room.destroy(sendEvt);
|
||||||
|
this.tableMap.remove(roomid);
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,251 @@
|
||||||
|
package com.robotcm.taurus.client.io;
|
||||||
|
|
||||||
|
import com.taurus.core.entity.ITObject;
|
||||||
|
import com.taurus.core.entity.TObject;
|
||||||
|
import com.taurus.core.util.ByteArray;
|
||||||
|
import com.taurus.core.util.Logger;
|
||||||
|
import com.taurus.core.util.Utils;
|
||||||
|
import com.robotcm.taurus.client.SocketCode;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 鍗忚鍖呭瓧鑺傛祦瑙f瀽
|
||||||
|
*
|
||||||
|
* @author daixiwei daixiwei15@126.com
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class IOHandler {
|
||||||
|
private final Logger logger = Logger.getLogger(IOHandler.class);
|
||||||
|
private static final String ACTION_ID = "a";
|
||||||
|
private static final String PARAM_ID = "p";
|
||||||
|
static final int INT_BYTE_SIZE = 4;
|
||||||
|
|
||||||
|
private static final ByteArray EMPTY_BUFFER = new ByteArray();
|
||||||
|
private int _skipBytes = 0;
|
||||||
|
private ByteArray _buffer;
|
||||||
|
private int _expectedLength = -1;
|
||||||
|
private boolean _compressed = false;
|
||||||
|
|
||||||
|
private SocketEngine _engine;
|
||||||
|
private SystemController _controller;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 璇诲彇鐘舵€?
|
||||||
|
*/
|
||||||
|
private PacketReadState _readState = PacketReadState.WAIT_NEW_PACKET;
|
||||||
|
|
||||||
|
IOHandler(SocketEngine client, SystemController controller) {
|
||||||
|
_engine = client;
|
||||||
|
_controller = controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
* @return
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
private ByteArray handleDataSize(ByteArray data) throws Exception {
|
||||||
|
int num = data.readInt();
|
||||||
|
int pos = INT_BYTE_SIZE;
|
||||||
|
if (num != -1) {
|
||||||
|
_expectedLength = num;
|
||||||
|
data = resizeByteArray(data, pos, data.length() - pos);
|
||||||
|
_readState = PacketReadState.WAIT_DATA;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
_readState = PacketReadState.WAIT_DATA_SIZE_FRAGMENT;
|
||||||
|
_buffer.writeBytes(data.bytes());
|
||||||
|
data = EMPTY_BUFFER;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
* @return
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
private ByteArray handleDataSizeFragment(ByteArray data) throws Exception {
|
||||||
|
int count = INT_BYTE_SIZE - _buffer.length();
|
||||||
|
if (data.length() >= count) {
|
||||||
|
_buffer.writeBytes(data.bytes(), 0, count);
|
||||||
|
int num2 = INT_BYTE_SIZE;
|
||||||
|
ByteArray array = new ByteArray();
|
||||||
|
array.writeBytes(_buffer.bytes(), 0, num2);
|
||||||
|
array.position(0);
|
||||||
|
int num3 = array.readInt();
|
||||||
|
_expectedLength = num3;
|
||||||
|
_buffer = new ByteArray();
|
||||||
|
_readState = PacketReadState.WAIT_DATA;
|
||||||
|
if (data.length() > count) {
|
||||||
|
data = resizeByteArray(data, count, data.length() - count);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
data = EMPTY_BUFFER;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
_buffer.writeBytes(data.bytes());
|
||||||
|
data = EMPTY_BUFFER;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private ByteArray handleInvalidData(ByteArray data) {
|
||||||
|
if (_skipBytes == 0) {
|
||||||
|
_readState = PacketReadState.WAIT_NEW_PACKET;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
int pos = Math.min(data.length(), _skipBytes);
|
||||||
|
data = resizeByteArray(data, pos, data.length() - pos);
|
||||||
|
_skipBytes -= pos;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private ByteArray handleNewPacket(ByteArray data) throws Exception {
|
||||||
|
_buffer = new ByteArray();
|
||||||
|
_expectedLength = -1;
|
||||||
|
_compressed = data.readByte() > 0;
|
||||||
|
data = resizeByteArray(data, 1, data.length() - 1);
|
||||||
|
_readState = PacketReadState.WAIT_DATA_SIZE;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private ByteArray handlePacketData(ByteArray data) throws Exception {
|
||||||
|
int count = _expectedLength - _buffer.length();
|
||||||
|
boolean flag = data.length() > count;
|
||||||
|
ByteArray array = new ByteArray(data.bytes());
|
||||||
|
try {
|
||||||
|
if (data.length() >= count) {
|
||||||
|
_buffer.writeBytes(data.bytes(), 0, count);
|
||||||
|
if (_compressed) {
|
||||||
|
_buffer.uncompress();
|
||||||
|
}
|
||||||
|
dispatchRequest(_buffer);
|
||||||
|
_readState = PacketReadState.WAIT_NEW_PACKET;
|
||||||
|
} else {
|
||||||
|
_buffer.writeBytes(data.bytes());
|
||||||
|
}
|
||||||
|
if (flag) {
|
||||||
|
data = resizeByteArray(data, count, data.length() - count);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
data = EMPTY_BUFFER;
|
||||||
|
} catch (Exception exception) {
|
||||||
|
logger.error("Error handling data ", exception);
|
||||||
|
_skipBytes = count;
|
||||||
|
_readState = PacketReadState.INVALID_DATA;
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dispatchRequest(ByteArray _buffer) throws IOException {
|
||||||
|
ITObject requestObject = TObject.newFromBinaryData(_buffer.bytes());
|
||||||
|
Packet packet = new Packet();
|
||||||
|
if (requestObject.isNull(ACTION_ID)) {
|
||||||
|
throw new IOException("Request rejected: No Action ID in request!");
|
||||||
|
}
|
||||||
|
packet.actionID = requestObject.getByte(ACTION_ID);
|
||||||
|
packet.parameters = requestObject.getTObject(PARAM_ID);
|
||||||
|
_controller.handlePacket(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public void onDataRead(ByteArray data) throws IOException {
|
||||||
|
if (data.length() == 0) {
|
||||||
|
throw new IOException("Unexpected empty packet data: no readable bytes available!");
|
||||||
|
}
|
||||||
|
|
||||||
|
data.position(0);
|
||||||
|
try {
|
||||||
|
while (data.length() > 0) {
|
||||||
|
if (_readState == PacketReadState.WAIT_NEW_PACKET) {
|
||||||
|
data = handleNewPacket(data);
|
||||||
|
} else {
|
||||||
|
if (_readState == PacketReadState.WAIT_DATA_SIZE) {
|
||||||
|
data = handleDataSize(data);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (_readState == PacketReadState.WAIT_DATA_SIZE_FRAGMENT) {
|
||||||
|
data = handleDataSizeFragment(data);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (_readState == PacketReadState.WAIT_DATA) {
|
||||||
|
data = handlePacketData(data);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (_readState == PacketReadState.INVALID_DATA) {
|
||||||
|
data = handleInvalidData(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
this.logger.error(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param packet
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public void onDataWrite(Packet packet) throws IOException {
|
||||||
|
ByteArray binData = new ByteArray();
|
||||||
|
|
||||||
|
ITObject obj2 = new TObject();
|
||||||
|
obj2.putByte(ACTION_ID, (byte) packet.actionID);
|
||||||
|
obj2.putTObject(PARAM_ID, packet.parameters);
|
||||||
|
byte[] array2 = obj2.toBinary();
|
||||||
|
boolean compression = array2.length > _engine.getProtocolCompression();
|
||||||
|
if (compression) {
|
||||||
|
array2 = Utils.compress(array2);
|
||||||
|
}
|
||||||
|
binData.writeByte((byte) (compression ? 1 : 0));
|
||||||
|
binData.writeInt(array2.length);
|
||||||
|
binData.writeBytes(array2);
|
||||||
|
|
||||||
|
if (_engine._socket.isConnected()) {
|
||||||
|
if (!_engine._socket.send(binData.bytes())) {
|
||||||
|
_engine.enqueueStatusCallback(SocketCode.SendError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param array
|
||||||
|
* @param pos
|
||||||
|
* @param len
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private ByteArray resizeByteArray(ByteArray array, int pos, int len) {
|
||||||
|
byte[] dst = new byte[len];
|
||||||
|
System.arraycopy(array.bytes(), pos, dst, 0, len);
|
||||||
|
return new ByteArray(dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum PacketReadState {
|
||||||
|
WAIT_NEW_PACKET, WAIT_DATA_SIZE, WAIT_DATA_SIZE_FRAGMENT, WAIT_DATA, INVALID_DATA
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,113 @@
|
||||||
|
package com.robotcm.taurus.client.io;
|
||||||
|
|
||||||
|
import com.taurus.core.util.Logger;
|
||||||
|
import com.taurus.core.util.StringUtil;
|
||||||
|
import com.robotcm.taurus.client.SocketCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Socket核心基类
|
||||||
|
* @author daixiwei daixiwei15@126.com
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public abstract class ISocket {
|
||||||
|
protected static final Logger logger = Logger.getLogger(ISocket.class);
|
||||||
|
SocketEngine _engine;
|
||||||
|
/**
|
||||||
|
* 当前socket状态
|
||||||
|
*/
|
||||||
|
public volatile SocketState state = SocketState.Disconnected;
|
||||||
|
/**
|
||||||
|
* 服务器地址
|
||||||
|
*/
|
||||||
|
protected String serverAddress;
|
||||||
|
/**
|
||||||
|
* 服务器端口
|
||||||
|
*/
|
||||||
|
protected int serverPort;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网络链接状态
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public boolean isConnected() {
|
||||||
|
return state == SocketState.Connected;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ISocket(SocketEngine peerBase) {
|
||||||
|
if (peerBase == null) {
|
||||||
|
throw new RuntimeException("Can't init without peer");
|
||||||
|
}
|
||||||
|
this._engine = peerBase;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean connect() {
|
||||||
|
boolean result;
|
||||||
|
if (state != SocketState.Disconnected) {
|
||||||
|
logger.error("Calling connect when the socket is not disconnected");
|
||||||
|
result = false;
|
||||||
|
} else if (_engine == null) {
|
||||||
|
result = false;
|
||||||
|
} else if (!tryParseAddress(_engine.getServerAddress())) {
|
||||||
|
logger.error("Failed parsing address: " + _engine.getServerAddress());
|
||||||
|
result = false;
|
||||||
|
} else {
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 断开连接
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public abstract boolean disconnect();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送流数据
|
||||||
|
* @param data
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public abstract boolean send(byte[] data);
|
||||||
|
|
||||||
|
protected void handleException(SocketCode statusCode) {
|
||||||
|
state = SocketState.Disconnecting;
|
||||||
|
_engine.enqueueStatusCallback(statusCode);
|
||||||
|
_engine.enqueueActionForDispatch(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
disconnect();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析网络地址
|
||||||
|
* @param addressAndPort
|
||||||
|
* @param address
|
||||||
|
* @param port
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected boolean tryParseAddress(String addressAndPort) {
|
||||||
|
boolean result = true;
|
||||||
|
if (StringUtil.isEmpty(addressAndPort)) {
|
||||||
|
result = false;
|
||||||
|
} else {
|
||||||
|
int num = addressAndPort.lastIndexOf(':');
|
||||||
|
if (num <= 0) {
|
||||||
|
result = false;
|
||||||
|
} else if (addressAndPort.indexOf(':') != num && (!addressAndPort.contains("[") || !addressAndPort.contains("]"))) {
|
||||||
|
result = false;
|
||||||
|
} else {
|
||||||
|
serverAddress = addressAndPort.substring(0, num);
|
||||||
|
String s = addressAndPort.substring(num + 1);
|
||||||
|
try {
|
||||||
|
serverPort = Integer.parseInt(s);
|
||||||
|
} catch (Exception e) {
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,104 @@
|
||||||
|
package com.robotcm.taurus.client.io;
|
||||||
|
|
||||||
|
import com.taurus.core.util.Logger;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网络监听/心跳
|
||||||
|
* @author daixiwei daixiwei15@126.com
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class LagMonitor extends TimerTask {
|
||||||
|
private static final Logger logger = Logger.getLogger(LagMonitor.class);
|
||||||
|
private int _averagePingTime;
|
||||||
|
private Timer _pollTimer;
|
||||||
|
private int _queueSize;
|
||||||
|
private int _interval;
|
||||||
|
private boolean _run;
|
||||||
|
private SocketEngine _engine;
|
||||||
|
private List<Long> _valueQueue;
|
||||||
|
private Queue<Long> write_queue = new LinkedList<Long>();
|
||||||
|
|
||||||
|
public LagMonitor(SocketEngine engine, int interval, int queueSize) {
|
||||||
|
if (interval < 1) {
|
||||||
|
interval = 1;
|
||||||
|
}
|
||||||
|
_engine = engine;
|
||||||
|
_valueQueue = new ArrayList<Long>();
|
||||||
|
_queueSize = queueSize;
|
||||||
|
_pollTimer = new Timer();
|
||||||
|
_interval = interval * 1000;
|
||||||
|
_averagePingTime = 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void fnPingPong() {
|
||||||
|
long w_time = write_queue.poll();
|
||||||
|
long item = System.currentTimeMillis() - w_time;
|
||||||
|
if (_valueQueue.size() >= _queueSize) {
|
||||||
|
_valueQueue.remove(0);
|
||||||
|
}
|
||||||
|
_valueQueue.add(item);
|
||||||
|
if (_valueQueue.size() == 0) {
|
||||||
|
_averagePingTime = 0;
|
||||||
|
}
|
||||||
|
int num = 0;
|
||||||
|
for (int i = 0; i < _valueQueue.size(); ++i) {
|
||||||
|
num += _valueQueue.get(i);
|
||||||
|
}
|
||||||
|
_averagePingTime = (num / _valueQueue.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启动监听
|
||||||
|
*/
|
||||||
|
public void start() {
|
||||||
|
if (!isRunning()) {
|
||||||
|
this._run = true;
|
||||||
|
_pollTimer.scheduleAtFixedRate(this, 0, _interval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 停止监听
|
||||||
|
*/
|
||||||
|
public void stop() {
|
||||||
|
if (isRunning()) {
|
||||||
|
this._run = false;
|
||||||
|
_pollTimer.purge();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void destroy() {
|
||||||
|
stop();
|
||||||
|
_pollTimer.cancel();
|
||||||
|
_pollTimer = null;
|
||||||
|
_engine = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 平均Ping的时间
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public int getAveragePingTime() {
|
||||||
|
return _averagePingTime;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRunning() {
|
||||||
|
return _run;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
_engine._controller.sendPingPong();
|
||||||
|
write_queue.offer(System.currentTimeMillis());
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
package com.robotcm.taurus.client.io;
|
||||||
|
|
||||||
|
import com.taurus.core.entity.ITObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网络协议数据包
|
||||||
|
* @author daixiwei daixiwei15@126.com
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class Packet {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 协议包ID
|
||||||
|
*/
|
||||||
|
public int actionID;
|
||||||
|
/**
|
||||||
|
* 参数数据
|
||||||
|
*/
|
||||||
|
public ITObject parameters;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,160 @@
|
||||||
|
package com.robotcm.taurus.client.io;
|
||||||
|
|
||||||
|
import com.taurus.core.util.ICallback;
|
||||||
|
import com.taurus.core.util.Logger;
|
||||||
|
import com.robotcm.taurus.client.*;
|
||||||
|
import com.robotcm.taurus.client.TaurusClient.ConnectionProtocol;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.Queue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SocketEngine
|
||||||
|
*
|
||||||
|
* @author daixiwei daixiwei15@126.com
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class SocketEngine implements ISocketLayer {
|
||||||
|
private static final Logger logger = Logger.getLogger(SocketEngine.class);
|
||||||
|
|
||||||
|
ISocket _socket;
|
||||||
|
private String _sessionToken;
|
||||||
|
private String _serverAddress;
|
||||||
|
|
||||||
|
private ICallback<SocketCode> _onConnect;
|
||||||
|
ICallback<Message> _onEvent;
|
||||||
|
private Queue<Runnable> _actionQueue = new LinkedList<Runnable>();
|
||||||
|
|
||||||
|
SystemController _controller;
|
||||||
|
LagMonitor _lagMonitor;
|
||||||
|
IOHandler _packetHandler;
|
||||||
|
TaurusClient _client;
|
||||||
|
|
||||||
|
public SocketEngine(TaurusClient client) {
|
||||||
|
_socket = new SocketTcp(this);
|
||||||
|
_controller = new SystemController(this);
|
||||||
|
_packetHandler = new IOHandler(this, _controller);
|
||||||
|
_lagMonitor = new LagMonitor(this, NetManager.TIMEOUT_TIME, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void connect(String ip, String game) {
|
||||||
|
_serverAddress = ip;
|
||||||
|
_socket.connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void disconnect() {
|
||||||
|
_socket.disconnect();
|
||||||
|
if (_lagMonitor != null)
|
||||||
|
_lagMonitor.destroy();
|
||||||
|
_lagMonitor = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void processEvent() {
|
||||||
|
if (_socket.isConnected() && _controller.isTimeOut()) {
|
||||||
|
if (_onConnect != null)
|
||||||
|
_onConnect.action(SocketCode.TimeoutDisconnect);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
Runnable myAction;
|
||||||
|
synchronized (_actionQueue) {
|
||||||
|
if (_actionQueue.size() <= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
myAction = _actionQueue.poll();
|
||||||
|
}
|
||||||
|
myAction.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void write(Message msg) {
|
||||||
|
try {
|
||||||
|
_controller.sendRequest(msg);
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void onStatusChanged(SocketCode statusCode) {
|
||||||
|
if (statusCode != SocketCode.Connect) {
|
||||||
|
_socket.disconnect();
|
||||||
|
this.disconnect();
|
||||||
|
}
|
||||||
|
System.out.println("status : " + statusCode);
|
||||||
|
logger.info("status : " + statusCode);
|
||||||
|
if (statusCode == SocketCode.Connect) {
|
||||||
|
this.onHandshake();
|
||||||
|
} else {
|
||||||
|
if (_onConnect != null)
|
||||||
|
_onConnect.action(statusCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void onHandshake() {
|
||||||
|
_controller.refreshTime();
|
||||||
|
_lagMonitor.start();
|
||||||
|
if (_onConnect != null)
|
||||||
|
_onConnect.action(SocketCode.Connect);
|
||||||
|
}
|
||||||
|
|
||||||
|
void enqueueActionForDispatch(Runnable action) {
|
||||||
|
synchronized (_actionQueue) {
|
||||||
|
_actionQueue.offer(action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void enqueueStatusCallback(final SocketCode statusValue) {
|
||||||
|
synchronized (_actionQueue) {
|
||||||
|
_actionQueue.offer(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
onStatusChanged(statusValue);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public String getServerAddress() {
|
||||||
|
return _serverAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConnectionProtocol getProtocol() {
|
||||||
|
return ConnectionProtocol.Tcp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAveragePingTime() {
|
||||||
|
return _lagMonitor != null ? _lagMonitor.getAveragePingTime() : 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSession(String session) {
|
||||||
|
_sessionToken = session == null ? "" : session;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSession() {
|
||||||
|
return _sessionToken;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setOnConnect(ICallback<SocketCode> onConnect) {
|
||||||
|
_onConnect = onConnect;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setOnEvent(ICallback<Message> onEvent) {
|
||||||
|
_onEvent = onEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getProtocolCompression() {
|
||||||
|
return 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
package com.robotcm.taurus.client.io;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 缃戠粶鐘舵€佹灇涓?
|
||||||
|
* @author daixiwei daixiwei15@126.com
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public enum SocketState{
|
||||||
|
Disconnected,
|
||||||
|
Connecting,
|
||||||
|
Connected,
|
||||||
|
Disconnecting
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,156 @@
|
||||||
|
package com.robotcm.taurus.client.io;
|
||||||
|
|
||||||
|
import com.taurus.core.util.ByteArray;
|
||||||
|
import com.robotcm.taurus.client.NetManager;
|
||||||
|
import com.robotcm.taurus.client.SocketCode;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.net.SocketAddress;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tcp协议 Socket核心类
|
||||||
|
* @author daixiwei daixiwei15@126.com
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class SocketTcp extends ISocket {
|
||||||
|
private static final int READ_BUFFER_SIZE = 0x1000;
|
||||||
|
private byte[] _byteBuffer = new byte[READ_BUFFER_SIZE];
|
||||||
|
|
||||||
|
private Socket _connection;
|
||||||
|
private final Object _syncer = new Object();
|
||||||
|
|
||||||
|
public SocketTcp(SocketEngine npeer) {
|
||||||
|
super(npeer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean connect() {
|
||||||
|
if (!super.connect()) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
state = SocketState.Connecting;
|
||||||
|
Thread t = new Thread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
connectThread();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
t.setName("mpnet connect thread");
|
||||||
|
t.start();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void connectThread() {
|
||||||
|
try {
|
||||||
|
SocketAddress remoteAddr = new InetSocketAddress(serverAddress, super.serverPort);
|
||||||
|
_connection = new Socket();
|
||||||
|
_connection.setTcpNoDelay(true);
|
||||||
|
_connection.connect(remoteAddr, NetManager.TIMEOUT_TIME * 1000);
|
||||||
|
state = SocketState.Connected;
|
||||||
|
_engine.enqueueStatusCallback(SocketCode.Connect);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
try {
|
||||||
|
if (_connection != null)
|
||||||
|
_connection.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Connection close error", e);
|
||||||
|
}
|
||||||
|
logger.error("Connection error", ex);
|
||||||
|
handleException(SocketCode.ExceptionOnConnect);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Thread _thrSocketReader = new Thread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
__read();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
_thrSocketReader.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean disconnect() {
|
||||||
|
if (state == SocketState.Disconnected)
|
||||||
|
return false;
|
||||||
|
state = SocketState.Disconnecting;
|
||||||
|
synchronized (_syncer) {
|
||||||
|
if (_connection != null) {
|
||||||
|
try {
|
||||||
|
_connection.close();
|
||||||
|
} catch (Exception ex) {
|
||||||
|
logger.error("Disconnect error", ex);
|
||||||
|
}
|
||||||
|
_connection = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state = SocketState.Disconnected;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean send(byte[] data) {
|
||||||
|
return __writeSocket(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean __writeSocket(byte[] buf) {
|
||||||
|
if (_connection == null || state != SocketState.Connected) {
|
||||||
|
logger.error("Trying to write to disconnected socket");
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
OutputStream outStream = _connection.getOutputStream();
|
||||||
|
outStream.write(buf);
|
||||||
|
outStream.flush();
|
||||||
|
} catch (Exception exception2) {
|
||||||
|
logger.error("General error writing to socket", exception2);
|
||||||
|
handleException(SocketCode.SendError);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void __read() {
|
||||||
|
int size = 0;
|
||||||
|
while (state == SocketState.Connected) {
|
||||||
|
try {
|
||||||
|
try {
|
||||||
|
InputStream inputStream = _connection.getInputStream();
|
||||||
|
size = inputStream.read(_byteBuffer);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw new TSocketException(SocketCode.ExceptionOnReceive);
|
||||||
|
}
|
||||||
|
if (size == 0 || size == -1) {
|
||||||
|
// System.out.println("socket----报错--------111111--------");
|
||||||
|
throw new TSocketException(SocketCode.DisconnectByServer);
|
||||||
|
}
|
||||||
|
__handleBinaryData(_byteBuffer, size);
|
||||||
|
|
||||||
|
} catch (TSocketException ex3) {
|
||||||
|
if (state != SocketState.Disconnecting && state.ordinal() > SocketState.Disconnected.ordinal()) {
|
||||||
|
logger.error("Receiving failed.: ", ex3);
|
||||||
|
super.handleException(ex3.socketCode);
|
||||||
|
}
|
||||||
|
} catch (Exception ex4) {
|
||||||
|
if (state != SocketState.Disconnecting && state.ordinal() > SocketState.Disconnected.ordinal()) {
|
||||||
|
logger.error("Receiving Exception: ", ex4);
|
||||||
|
super.handleException(SocketCode.ExceptionOnReceive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void __handleBinaryData(byte[] buf, int size) throws IOException {
|
||||||
|
byte[] dst = new byte[size];
|
||||||
|
System.arraycopy(buf, 0, dst, 0, size);
|
||||||
|
ByteArray array = new ByteArray(dst);
|
||||||
|
_engine._packetHandler.onDataRead(array);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,177 @@
|
||||||
|
package com.robotcm.taurus.client.io;
|
||||||
|
|
||||||
|
import com.taurus.core.entity.ITObject;
|
||||||
|
import com.taurus.core.entity.TObject;
|
||||||
|
import com.robotcm.taurus.client.Message;
|
||||||
|
import com.robotcm.taurus.client.MessageResponse;
|
||||||
|
import com.robotcm.taurus.client.NetManager;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 协议处理控制器
|
||||||
|
*
|
||||||
|
* @author daixiwei daixiwei15@126.com
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class SystemController {
|
||||||
|
public static final String CONNECT_TOKE = "$t";
|
||||||
|
public static final String CONNECT_PROT_COMPRESSION = "$pc";
|
||||||
|
public static final String REQUEST_CMD = "$c";
|
||||||
|
public static final String REQUEST_GID = "$gi";
|
||||||
|
public static final String REQUEST_PARM = "$p";
|
||||||
|
public static final String REQUEST_RESULT = "$r";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pingpong
|
||||||
|
*/
|
||||||
|
public static final int ACTION_PINGPONG = 0;
|
||||||
|
/**
|
||||||
|
* 客户端请求
|
||||||
|
*/
|
||||||
|
public static final int ACTION_REQUST_CMD = 1;
|
||||||
|
/**
|
||||||
|
* 服务器事件消息
|
||||||
|
*/
|
||||||
|
public static final int ACTION_EVENT_CMD = 2;
|
||||||
|
|
||||||
|
private SocketEngine _engine;
|
||||||
|
private long _lastTime;
|
||||||
|
private List<Message> _handleList = new ArrayList<Message>();
|
||||||
|
private int _tempID;
|
||||||
|
|
||||||
|
public SystemController(SocketEngine client) {
|
||||||
|
_engine = client;
|
||||||
|
_lastTime = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
private int _getValidID() {
|
||||||
|
return ++_tempID;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void handlePacket(Packet packet) {
|
||||||
|
_lastTime = System.currentTimeMillis();
|
||||||
|
switch (packet.actionID) {
|
||||||
|
case ACTION_PINGPONG:
|
||||||
|
fnPingPong(packet);
|
||||||
|
break;
|
||||||
|
case ACTION_REQUST_CMD:
|
||||||
|
fnOperationResponse(packet);
|
||||||
|
break;
|
||||||
|
case ACTION_EVENT_CMD:
|
||||||
|
fnOperationEvent(packet);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendRequest(Message msg) throws IOException {
|
||||||
|
Packet packet = new Packet();
|
||||||
|
packet.actionID = SystemController.ACTION_REQUST_CMD;
|
||||||
|
ITObject obj = TObject.newInstance();
|
||||||
|
obj.putString(SystemController.REQUEST_CMD, msg.command);
|
||||||
|
int gid = 0;
|
||||||
|
if (msg.callback != null) {
|
||||||
|
gid = _getValidID();
|
||||||
|
msg.lastTime = System.currentTimeMillis();
|
||||||
|
msg.gid = gid;
|
||||||
|
synchronized (_handleList) {
|
||||||
|
_handleList.add(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
obj.putInt(SystemController.REQUEST_GID, gid);
|
||||||
|
obj.putTObject(SystemController.REQUEST_PARM, msg.param);
|
||||||
|
packet.parameters = obj;
|
||||||
|
_engine._packetHandler.onDataWrite(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendPingPong() throws IOException {
|
||||||
|
Packet packet = new Packet();
|
||||||
|
packet.actionID = SystemController.ACTION_PINGPONG;
|
||||||
|
packet.parameters = TObject.newInstance();
|
||||||
|
_engine._packetHandler.onDataWrite(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final void fnOperationResponse(final Packet packet) {
|
||||||
|
final int gid = packet.parameters.getInt(REQUEST_GID);
|
||||||
|
final int result = packet.parameters.getInt(REQUEST_RESULT);
|
||||||
|
ITObject tem = null;
|
||||||
|
if (packet.parameters.containsKey(REQUEST_PARM)) {
|
||||||
|
tem = packet.parameters.getTObject(REQUEST_PARM);
|
||||||
|
}
|
||||||
|
final ITObject param = tem;
|
||||||
|
_engine.enqueueActionForDispatch(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
handleOperationResponse(result, gid, param);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleOperationResponse(int result, int gid, ITObject param) {
|
||||||
|
Message msg = null;
|
||||||
|
int index = 0;
|
||||||
|
int len = _handleList.size();
|
||||||
|
if (gid > 0) {
|
||||||
|
for (int i = 0; i < len; ++i) {
|
||||||
|
if (gid == _handleList.get(i).gid) {
|
||||||
|
msg = _handleList.get(i);
|
||||||
|
index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg != null) {
|
||||||
|
if (msg.callback != null) {
|
||||||
|
MessageResponse response = new MessageResponse();
|
||||||
|
msg.param = param;
|
||||||
|
response.messageData = msg;
|
||||||
|
response.returnCode = result;
|
||||||
|
msg.callback.action(response);
|
||||||
|
}
|
||||||
|
synchronized (_handleList) {
|
||||||
|
_handleList.remove(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final void fnOperationEvent(Packet packet) {
|
||||||
|
final String cmd = packet.parameters.getString(REQUEST_CMD);
|
||||||
|
ITObject tem = null;
|
||||||
|
if (packet.parameters.containsKey(REQUEST_PARM)) {
|
||||||
|
tem = packet.parameters.getTObject(REQUEST_PARM);
|
||||||
|
}
|
||||||
|
final ITObject param = tem;
|
||||||
|
_engine.enqueueActionForDispatch(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
handleEvent(cmd, param);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleEvent(String cmd, ITObject param) {
|
||||||
|
Message msg = new Message();
|
||||||
|
msg.command = cmd;
|
||||||
|
msg.param = param;
|
||||||
|
if (_engine._onEvent != null)
|
||||||
|
_engine._onEvent.action(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private final void fnPingPong(Packet packet) {
|
||||||
|
_engine._lagMonitor.fnPingPong();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isTimeOut() {
|
||||||
|
return (System.currentTimeMillis() - _lastTime) / 1000 > NetManager.TIMEOUT_TIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void refreshTime() {
|
||||||
|
_lastTime = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
package com.robotcm.taurus.client.io;
|
||||||
|
|
||||||
|
import com.robotcm.taurus.client.SocketCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 缃戠粶閫氫俊寮傚父
|
||||||
|
* @author daixiwei daixiwei15@126.com
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class TSocketException extends Exception{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public SocketCode socketCode;
|
||||||
|
|
||||||
|
public TSocketException(SocketCode code) {
|
||||||
|
socketCode = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
package com.robotcm.taurus.newRobot;
|
||||||
|
|
||||||
|
import com.taurus.core.entity.ITArray;
|
||||||
|
import com.taurus.core.entity.ITObject;
|
||||||
|
import com.taurus.core.plugin.database.DataBase;
|
||||||
|
import com.taurus.core.plugin.redis.Redis;
|
||||||
|
import redis.clients.jedis.Jedis;
|
||||||
|
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class AddRedisRobot {
|
||||||
|
|
||||||
|
|
||||||
|
public void getData() {
|
||||||
|
HashMap<String, String> robotMap = new HashMap<>();
|
||||||
|
Jedis jedis2 = Redis.use("group1_db2").getJedis();
|
||||||
|
//取出可用的机器人
|
||||||
|
String sql = String.format("SELECT id FROM `account` WHERE jiqiren=9998 and start = 0");
|
||||||
|
ITArray robotIds = null;
|
||||||
|
try {
|
||||||
|
robotIds = DataBase.use().executeQueryByTArray(sql);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
if (robotIds != null){
|
||||||
|
for (int i = 0; i < robotIds.size(); i++) {
|
||||||
|
ITObject robot = robotIds.getTObject(i);
|
||||||
|
Integer robotId = robot.getInt("id");
|
||||||
|
|
||||||
|
|
||||||
|
robotMap.put("start", 0+"");
|
||||||
|
jedis2.hmset("{robot}:" + robotId , robotMap);
|
||||||
|
|
||||||
|
//后续改为1
|
||||||
|
// String sqls = String.format("UPDATE `account` SET start = %d WHERE id = %d", 1, robotId);
|
||||||
|
|
||||||
|
//对比 mysql和redis的机器人 把新的加到redis ,redis没有的mysql存在的
|
||||||
|
|
||||||
|
jedis2.close();
|
||||||
|
// try {
|
||||||
|
// DataBase.use().executeUpdate(sqls);
|
||||||
|
// } catch (SQLException e) {
|
||||||
|
// e.printStackTrace();
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,384 @@
|
||||||
|
package com.robotcm.taurus.newRobot;
|
||||||
|
|
||||||
|
import com.taurus.core.entity.ITArray;
|
||||||
|
import com.taurus.core.entity.TArray;
|
||||||
|
import com.taurus.core.plugin.redis.Redis;
|
||||||
|
import com.taurus.core.util.Logger;
|
||||||
|
import redis.clients.jedis.*;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
|
||||||
|
public class AddRoomRobot {
|
||||||
|
|
||||||
|
|
||||||
|
private final static Logger log;
|
||||||
|
|
||||||
|
static {
|
||||||
|
log = Logger.getLogger(AddRoomRobot.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void addRoom() {
|
||||||
|
Jedis jedis2 = Redis.use("group1_db2").getJedis();
|
||||||
|
Jedis jedis11 = Redis.use("group1_db11").getJedis();
|
||||||
|
System.out.println("进入1111111111111111111111111");
|
||||||
|
//给玩法分配机器人
|
||||||
|
if (jedis11.keys("group:*") != null) {
|
||||||
|
Set<String> groupIds = jedis11.keys("group:*");
|
||||||
|
for (String group : groupIds) {
|
||||||
|
String groupId = group.substring(group.indexOf(":") + 1);
|
||||||
|
String groupKey = "g{" + groupId + "}:play:*";
|
||||||
|
if (jedis11.keys(groupKey) != null) {
|
||||||
|
Set<String> playIds = jedis11.keys(groupKey);
|
||||||
|
// 遍历圈子下面的玩法
|
||||||
|
for (String pid : playIds) {
|
||||||
|
String playId = pid.substring(pid.indexOf("play:") + 5);
|
||||||
|
String playKey = "g{" + groupId + "}:play:" + playId;
|
||||||
|
|
||||||
|
String shangxian_robot = "0";
|
||||||
|
if (jedis11.hget(playKey, "shangxian_robot") != null) {
|
||||||
|
shangxian_robot = jedis11.hget(playKey, "shangxian_robot");
|
||||||
|
if (Integer.parseInt(shangxian_robot) > 0) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//分配完机器之后去看有没有初始化“leftover_robot”字段
|
||||||
|
if (jedis11.hget(playKey, "leftover_robot") == null){
|
||||||
|
jedis11.hset(playKey, "leftover_robot", shangxian_robot);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Map<String, String> stringStringMap = jedis2.hgetAll(playKey);
|
||||||
|
log.info("靠靠靠靠靠靠靠靠靠靠靠靠靠靠靠靠靠靠靠" + stringStringMap);
|
||||||
|
//循环stringStringMap
|
||||||
|
boolean b = areAllValuesZero(stringStringMap);
|
||||||
|
log.info("iiiiiiiiiiiiiiiiiiiiiiiiiiiiii" + b);
|
||||||
|
if (b){
|
||||||
|
jedis11.hset(playKey, "leftover_robot", shangxian_robot);
|
||||||
|
}
|
||||||
|
|
||||||
|
//给圈子玩法分配机器人,创建到redis
|
||||||
|
addPlayRobot(groupId, playId, shangxian_robot);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
System.out.println("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
|
||||||
|
//机器人主动找真人
|
||||||
|
joinzhenrenRoom();
|
||||||
|
System.out.println("进入22222222222222222222222");
|
||||||
|
|
||||||
|
//机器人主动进入退出, 这里进入的是中括号机器人退出的房间
|
||||||
|
zhongJoninRoom();
|
||||||
|
// System.out.println("进入3333333333333333333");
|
||||||
|
|
||||||
|
//机器人创建房间 进入房间
|
||||||
|
List<String> gKeys = scanKeysByPattern(jedis2, "g{*}:play:*");
|
||||||
|
try {
|
||||||
|
Map<String, List<String>> stringListMap = analyzePlayStructure(gKeys);
|
||||||
|
//取出 圈子玩法里的 机器人使用所有的状态
|
||||||
|
Map<String, Map<String, Map<String, String>>> playDetails = getPlayDetails(jedis2, stringListMap);
|
||||||
|
System.out.println("result" + playDetails);
|
||||||
|
|
||||||
|
//统计 玩法里 等待中的次数 为1的有几个 ,然后需要建立几个房间
|
||||||
|
Map<String, Map<String, Integer>> stringMapMap = analyzeAndMarkRoomsWithCircle(playDetails);
|
||||||
|
System.out.println("stringIntegerMap" + stringMapMap);
|
||||||
|
// 按圈子ID排序处理
|
||||||
|
List<String> circleIds = new ArrayList<>(stringMapMap.keySet());
|
||||||
|
Collections.sort(circleIds);
|
||||||
|
for (String circleId : circleIds) {
|
||||||
|
Map<String, Integer> playRoomMap = stringMapMap.get(circleId);
|
||||||
|
System.out.println("创建房间,进入房间");
|
||||||
|
jiqiren jiqiren = new jiqiren();
|
||||||
|
System.out.println("circleId +++" + circleId);
|
||||||
|
System.out.println("playRoomMap +++ " + playRoomMap);
|
||||||
|
jiqiren.createRoomsForCircle(circleId, playRoomMap);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
jedis2.close();
|
||||||
|
jedis11.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//机器人进入中括号房间
|
||||||
|
public void zhongJoninRoom() {
|
||||||
|
Jedis jedis0 = Redis.use().getJedis();
|
||||||
|
try {
|
||||||
|
//获取所有房间
|
||||||
|
Set<String> roomIds = jedis0.keys("room:*");
|
||||||
|
for (String id : roomIds) {
|
||||||
|
List<String> room_list = Redis.use("group1_db0").hmget(id, "fake", "status", "players");
|
||||||
|
//判断是否是真人等待桌
|
||||||
|
String roomKey = "";
|
||||||
|
String roomId = id.substring(id.indexOf(":") + 1);
|
||||||
|
roomKey = "room:" + roomId;
|
||||||
|
//玩法id
|
||||||
|
String fake = room_list.get(0);
|
||||||
|
if (fake != null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
//圈子id
|
||||||
|
if(room_list.get(2)==null){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ITArray players = TArray.newFromJsonData(room_list.get(2));
|
||||||
|
if (players.size()==2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
jiqiren jiqiren = new jiqiren();
|
||||||
|
jiqiren.tcp_jiqirezhongkouhao(id);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
jedis0.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断玩家状态是不是都为0
|
||||||
|
public static boolean areAllValuesZero(Map<String, String> map) {
|
||||||
|
if (map == null || map.isEmpty()) {
|
||||||
|
return false; // 或者 true,根据业务需求
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String value : map.values()) {
|
||||||
|
if (!"0".equals(value)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//统计 玩法里 等待中的次数
|
||||||
|
// 方法1:返回 Map<圈子ID, Map<玩法ID, 房间需求>>
|
||||||
|
public Map<String, Map<String, Integer>> analyzeAndMarkRoomsWithCircle(
|
||||||
|
Map<String, Map<String, Map<String, String>>> result) {
|
||||||
|
|
||||||
|
Map<String, Map<String, Integer>> circlePlayRoomMap = new HashMap<>();
|
||||||
|
|
||||||
|
for (Map.Entry<String, Map<String, Map<String, String>>> circleEntry : result.entrySet()) {
|
||||||
|
String circleId = circleEntry.getKey();
|
||||||
|
Map<String, Map<String, String>> playData = circleEntry.getValue();
|
||||||
|
|
||||||
|
Map<String, Integer> playRoomMark = new HashMap<>();
|
||||||
|
|
||||||
|
for (Map.Entry<String, Map<String, String>> playEntry : playData.entrySet()) {
|
||||||
|
String playId = playEntry.getKey();
|
||||||
|
Map<String, String> robotData = playEntry.getValue();
|
||||||
|
|
||||||
|
// 统计值为"1"的机器人数量
|
||||||
|
int countOfOnes = 0;
|
||||||
|
for (String value : robotData.values()) {
|
||||||
|
if ("1".equals(value)) {
|
||||||
|
countOfOnes++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据逻辑进行标记
|
||||||
|
if (countOfOnes == 0) {
|
||||||
|
playRoomMark.put(playId, 2); // 需要建立2个房间
|
||||||
|
} else if (countOfOnes == 1) {
|
||||||
|
playRoomMark.put(playId, 1); // 标记为1
|
||||||
|
} else if (countOfOnes >= 2) {
|
||||||
|
playRoomMark.put(playId, 0); // 标记为0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
circlePlayRoomMap.put(circleId, playRoomMark);
|
||||||
|
}
|
||||||
|
|
||||||
|
return circlePlayRoomMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 统计每个玩法的机器人总数
|
||||||
|
public Map<String, Map<String, Map<String, String>>> getPlayDetails(Jedis jedis,
|
||||||
|
Map<String, List<String>> circleToPlays) {
|
||||||
|
|
||||||
|
Map<String, Map<String, Map<String, String>>> result = new HashMap<>();
|
||||||
|
|
||||||
|
for (Map.Entry<String, List<String>> entry : circleToPlays.entrySet()) {
|
||||||
|
String circleId = entry.getKey();
|
||||||
|
List<String> playIds = entry.getValue();
|
||||||
|
|
||||||
|
Map<String, Map<String, String>> playDetails = new HashMap<>();
|
||||||
|
|
||||||
|
for (String playId : playIds) {
|
||||||
|
// 构建Redis键
|
||||||
|
String redisKey = String.format("g{%s}:play:%s", circleId, playId);
|
||||||
|
|
||||||
|
// 获取Hash中的所有字段和值
|
||||||
|
Map<String, String> robotData = jedis.hgetAll(redisKey);
|
||||||
|
|
||||||
|
playDetails.put(playId, robotData);
|
||||||
|
}
|
||||||
|
|
||||||
|
result.put(circleId, playDetails);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//分割圈子和玩法
|
||||||
|
public static Map<String, List<String>> analyzePlayStructure(List<String> gKeys) {
|
||||||
|
Map<String, List<String>> circleToPlays = new HashMap<>();
|
||||||
|
|
||||||
|
for (String key : gKeys) {
|
||||||
|
// 解析键:g{圈子ID}:play:玩法ID
|
||||||
|
String[] parts = key.split(":");
|
||||||
|
|
||||||
|
if (parts.length >= 3) {
|
||||||
|
// 提取圈子ID,去掉 g{ 和 }
|
||||||
|
String circlePart = parts[0]; // g{330800}
|
||||||
|
String circleId = circlePart.substring(2, circlePart.length() - 1);
|
||||||
|
|
||||||
|
// 提取玩法ID
|
||||||
|
String playId = parts[2]; // 玩法ID
|
||||||
|
|
||||||
|
// 将玩法ID添加到对应圈子的列表中
|
||||||
|
circleToPlays.computeIfAbsent(circleId, k -> new ArrayList<>())
|
||||||
|
.add(playId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return circleToPlays;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用SCAN命令安全地扫描匹配模式的key
|
||||||
|
*/
|
||||||
|
private static List<String> scanKeysByPattern(Jedis jedis, String pattern) {
|
||||||
|
List<String> keys = new ArrayList<>();
|
||||||
|
String cursor = ScanParams.SCAN_POINTER_START;
|
||||||
|
ScanParams scanParams = new ScanParams();
|
||||||
|
scanParams.match(pattern);
|
||||||
|
scanParams.count(1000); // 每次扫描数量
|
||||||
|
|
||||||
|
do {
|
||||||
|
ScanResult<String> scanResult = jedis.scan(cursor, scanParams);
|
||||||
|
cursor = String.valueOf(scanResult.getCursor());
|
||||||
|
keys.addAll(scanResult.getResult());
|
||||||
|
} while (!cursor.equals(ScanParams.SCAN_POINTER_START));
|
||||||
|
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//给圈子玩法分配机器人,创建到redis
|
||||||
|
public void addPlayRobot(String groupId, String playId, String shangxian_robot) {
|
||||||
|
Jedis jedis2 = Redis.use("group1_db2").getJedis();
|
||||||
|
// 为这个玩法创建机器人配置
|
||||||
|
String robotPlayKey = "g{" + groupId + "}:play:" + playId;
|
||||||
|
boolean exists = jedis2.exists(robotPlayKey);
|
||||||
|
if (!exists) {
|
||||||
|
// 计算需要配置的机器人数量
|
||||||
|
int num = Integer.parseInt(shangxian_robot) * 2;
|
||||||
|
|
||||||
|
// 获取所有可用的机器人
|
||||||
|
List<String> availableRobots1 = getAvailableRobots();
|
||||||
|
|
||||||
|
List<String> availableRobots = new ArrayList<>();
|
||||||
|
|
||||||
|
if (availableRobots1.size() > 0) {
|
||||||
|
for (String key : availableRobots1) {
|
||||||
|
String robotId = key.substring(key.indexOf(":") + 1);
|
||||||
|
availableRobots.add(robotId);
|
||||||
|
}
|
||||||
|
// 检查是否有足够的机器人
|
||||||
|
if (availableRobots.size() < num) {
|
||||||
|
System.out.println("警告:玩法 " + playId + " 需要 " + num + " 个机器人,但只有 " + availableRobots.size() + " 个可用");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 存储机器人信息,使用Hash结构
|
||||||
|
Map<String, String> robotConfigs = new HashMap<>();
|
||||||
|
|
||||||
|
for (int i = 0; i < num; i++) {
|
||||||
|
String robotId = availableRobots.get(i);
|
||||||
|
// key格式:robot:机器人ID,value:使用状态(0-未使用)
|
||||||
|
robotConfigs.put(robotId, "0");
|
||||||
|
String robotKey = "{robot}:" + robotId;
|
||||||
|
jedis2.hset(robotKey, "start", "1");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量设置机器人配置
|
||||||
|
if (!robotConfigs.isEmpty()) {
|
||||||
|
jedis2.hmset(robotPlayKey, robotConfigs);
|
||||||
|
System.out.println("玩法 " + playId + " 已配置 " + num + " 个机器人");
|
||||||
|
}
|
||||||
|
jedis2.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有可用的机器人(start=0)
|
||||||
|
*
|
||||||
|
* @return 可用机器人ID列表
|
||||||
|
*/
|
||||||
|
public List<String> getAvailableRobots() {
|
||||||
|
Jedis jedis2 = Redis.use("group1_db2").getJedis();
|
||||||
|
|
||||||
|
Set<String> allRobotKeys = jedis2.keys("{robot}:*");
|
||||||
|
List<String> availableRobots = new ArrayList<>();
|
||||||
|
|
||||||
|
// 使用Pipeline提高效率
|
||||||
|
Pipeline pipeline = jedis2.pipelined();
|
||||||
|
List<Response<String>> responses = new ArrayList<>();
|
||||||
|
|
||||||
|
for (String robotKey : allRobotKeys) {
|
||||||
|
responses.add(pipeline.hget(robotKey, "start"));
|
||||||
|
}
|
||||||
|
|
||||||
|
pipeline.sync();
|
||||||
|
|
||||||
|
// 提取机器人ID并筛选
|
||||||
|
int index = 0;
|
||||||
|
for (String robotKey : allRobotKeys) {
|
||||||
|
String startStatus = responses.get(index).get();
|
||||||
|
if ("0".equals(startStatus)) {
|
||||||
|
String robotId = robotKey.substring(robotKey.indexOf(":") + 1);
|
||||||
|
availableRobots.add(robotId);
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
jedis2.close();
|
||||||
|
return availableRobots;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void joinzhenrenRoom() {
|
||||||
|
Jedis jedis0 = Redis.use().getJedis();
|
||||||
|
Jedis jedis11 = Redis.use("group1_db11").getJedis();
|
||||||
|
Jedis jedis2 = Redis.use("group1_db2").getJedis();
|
||||||
|
try {
|
||||||
|
//获取所有房间
|
||||||
|
Set<String> roomIds = jedis0.keys("room:*");
|
||||||
|
for (String id : roomIds) {
|
||||||
|
jiqiren jiqiren = new jiqiren();
|
||||||
|
jiqiren.tcp_jiqirenzhenren(id);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
jedis0.close();
|
||||||
|
jedis11.close();
|
||||||
|
jedis2.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,661 @@
|
||||||
|
package com.robotcm.taurus.newRobot;
|
||||||
|
|
||||||
|
import com.taurus.core.entity.ITObject;
|
||||||
|
import com.taurus.core.entity.TObject;
|
||||||
|
import com.taurus.core.plugin.redis.Redis;
|
||||||
|
import com.taurus.core.util.ICallback;
|
||||||
|
import com.taurus.core.util.Logger;
|
||||||
|
import redis.clients.jedis.Jedis;
|
||||||
|
import redis.clients.jedis.Pipeline;
|
||||||
|
import redis.clients.jedis.Response;
|
||||||
|
import com.robotcm.taurus.client.MessageResponse;
|
||||||
|
import com.robotcm.taurus.client.TaurusClient;
|
||||||
|
import com.robotcm.taurus.client.business.AccountBusiness;
|
||||||
|
import com.robotcm.taurus.client.business.GroupRoomBusiness;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class jiqiren {
|
||||||
|
|
||||||
|
private final static Logger log;
|
||||||
|
|
||||||
|
static {
|
||||||
|
log = Logger.getLogger(jiqiren.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String wanfaId = "";
|
||||||
|
// 会话标识
|
||||||
|
private String session = "";
|
||||||
|
// 访问令牌
|
||||||
|
public String token = "";
|
||||||
|
|
||||||
|
// 客户端连接对象
|
||||||
|
private TaurusClient client = null;
|
||||||
|
|
||||||
|
|
||||||
|
private Map<String, Object> mapclient = new HashMap<>();
|
||||||
|
|
||||||
|
|
||||||
|
//使用数量
|
||||||
|
public static Map<Integer, Integer> count2 = new HashMap<Integer, Integer>();
|
||||||
|
|
||||||
|
public TaurusClient getClient() {
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setClient(TaurusClient client) {
|
||||||
|
this.client = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
//进入真人房间
|
||||||
|
public void tcp_jiqirenzhenren(String id) {
|
||||||
|
Jedis jedis0 = Redis.use("group1_db0").getJedis();
|
||||||
|
Jedis jedis11 = Redis.use("group1_db11").getJedis();
|
||||||
|
Jedis jedis2 = Redis.use("group1_db2").getJedis();
|
||||||
|
|
||||||
|
String roomKey = "";
|
||||||
|
String roomId = id.substring(id.indexOf(":") + 1);
|
||||||
|
roomKey = "room:" + roomId;
|
||||||
|
//玩法id
|
||||||
|
String gpid = jedis0.hget(roomKey, "gpid");
|
||||||
|
//圈子id
|
||||||
|
String group = jedis0.hget(roomKey, "group");
|
||||||
|
//获取玩法对应的机器人 g{330800}:play:10
|
||||||
|
String playKey = "g{" + group + "}:play:" + gpid;
|
||||||
|
|
||||||
|
|
||||||
|
String players1 = jedis0.hget(roomKey, "players");
|
||||||
|
System.out.println(roomKey);
|
||||||
|
if (jedis11.hget(playKey, "leftover_robot") != null) {
|
||||||
|
//玩法配置的机器人数量
|
||||||
|
String leftover_robot = jedis11.hget(playKey, "leftover_robot");
|
||||||
|
//机器人主动找真人
|
||||||
|
if (players1 != null && !players1.equals("[]")) {
|
||||||
|
List<String> availableRobots1 = getAvailableRobots1();
|
||||||
|
log.info("所有机器人------"+availableRobots1);
|
||||||
|
String players = jedis0.hget(roomKey, "players");
|
||||||
|
System.out.println(players);
|
||||||
|
if (players1.length()>4) {
|
||||||
|
|
||||||
|
|
||||||
|
players = players.substring(1, players.length() - 1);
|
||||||
|
|
||||||
|
String[] playerIds1 = players.split(",");
|
||||||
|
|
||||||
|
String maxPlayers = jedis0.hget(roomKey, "maxPlayers");
|
||||||
|
if (Integer.parseInt(maxPlayers) == 2) {
|
||||||
|
if (playerIds1.length == 1) {
|
||||||
|
//循环playerIds
|
||||||
|
for (int i = 0; i < playerIds1.length; i++) {
|
||||||
|
String playeid = playerIds1[i];
|
||||||
|
if (!availableRobots1.contains(playeid) && Integer.parseInt(leftover_robot) > 0) {
|
||||||
|
Map<String, String> stringStringMap = jedis2.hgetAll(playKey);
|
||||||
|
List<String> zeroValueKeys = getKeysWithValueZero(stringStringMap);
|
||||||
|
log.info("值为0的机器人ID------"+zeroValueKeys);
|
||||||
|
|
||||||
|
String randomKeyFromList = getRandomKeyFromList(zeroValueKeys);
|
||||||
|
log.info("随机给的用户id------"+randomKeyFromList);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
wanfaId = jedis0.hget(roomKey, "game");
|
||||||
|
|
||||||
|
if ("22".equalsIgnoreCase(wanfaId)) { //湖南红中麻将
|
||||||
|
//client = new TaurusClient("47.109.55.7:6421", "game", TaurusClient.ConnectionProtocol.Tcp);
|
||||||
|
|
||||||
|
client = new TaurusClient("47.109.55.7:6421", randomKeyFromList, TaurusClient.ConnectionProtocol.Tcp);
|
||||||
|
//TaurusClient client1=getClient();
|
||||||
|
client.connect();
|
||||||
|
mapclient.put(randomKeyFromList, client);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("10".equalsIgnoreCase(wanfaId)) {
|
||||||
|
client = new TaurusClient("47.109.55.7:6311", randomKeyFromList, TaurusClient.ConnectionProtocol.Tcp);
|
||||||
|
//TaurusClient client1=getClient();
|
||||||
|
client.connect();
|
||||||
|
mapclient.put(randomKeyFromList, client);
|
||||||
|
}
|
||||||
|
if ("66".equalsIgnoreCase(wanfaId)) {
|
||||||
|
// client = new TaurusClient("47.109.55.7:6841", "game", TaurusClient.ConnectionProtocol.Tcp);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ITObject object = null;
|
||||||
|
AccountBusiness accountBusiness = null;
|
||||||
|
accountBusiness = new AccountBusiness();
|
||||||
|
// 登录获取session跟token
|
||||||
|
|
||||||
|
if (jedis0.keys("{user}:" + randomKeyFromList + "_token").size() != 0) {
|
||||||
|
|
||||||
|
Set<String> keys1 = jedis0.keys("{user}:" + randomKeyFromList + "_token");
|
||||||
|
if (keys1 == null) {
|
||||||
|
try {
|
||||||
|
object = accountBusiness.idPasswordLogin(Integer.parseInt(randomKeyFromList), "blm523");
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
ITObject validate = TObject.newInstance();
|
||||||
|
validate.putString("token", object.getString("token"));
|
||||||
|
token = object.getString("token");
|
||||||
|
session = accountBusiness.getSession();
|
||||||
|
} else {
|
||||||
|
Set<String> keys = jedis0.smembers("{user}:" + randomKeyFromList + "_token");
|
||||||
|
List<String> keyList = new ArrayList<>(keys);
|
||||||
|
session = "{user}:" + randomKeyFromList;
|
||||||
|
token = keyList.get(0);
|
||||||
|
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
try {
|
||||||
|
object = accountBusiness.idPasswordLogin(Integer.parseInt(randomKeyFromList), "blm523");
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
ITObject validate = TObject.newInstance();
|
||||||
|
validate.putString("token", object.getString("token"));
|
||||||
|
token = object.getString("token");
|
||||||
|
session = accountBusiness.getSession();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
String roomKey1 = "room:" + roomId;
|
||||||
|
|
||||||
|
try {
|
||||||
|
ITObject roomInfos = GroupRoomBusiness.joinRoom(Integer.parseInt(group), roomKey1, session, null);
|
||||||
|
log.info("房间数据数据时数据1213234453543545------"+roomInfos);
|
||||||
|
|
||||||
|
String roomIds = roomInfos.getString("room_id");
|
||||||
|
|
||||||
|
jedis2.hset(playKey, randomKeyFromList, "1");
|
||||||
|
String robortInfoKey = "{robortInfo}:" + randomKeyFromList;
|
||||||
|
jedis2.hset(robortInfoKey, "circleId", group);
|
||||||
|
jedis2.hset(robortInfoKey, "pid", gpid);
|
||||||
|
jedis2.hset(robortInfoKey, "room_id", roomIds);
|
||||||
|
|
||||||
|
log.info("进入真人-- 用户id ----"+ randomKeyFromList);
|
||||||
|
log.info("进入真人-- group ----"+ jedis2.hget(robortInfoKey, "circleId"));
|
||||||
|
log.info("进入真人-- gpid ----"+ jedis2.hget(robortInfoKey, "pid"));
|
||||||
|
log.info("进入真人-- room_id ----"+ jedis2.hget(robortInfoKey, "room_id"));
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
joinRoom(Integer.parseInt(randomKeyFromList)); //进入房间
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
ready(); //准备
|
||||||
|
count2.put(Integer.valueOf(gpid), count2.getOrDefault(Integer.parseInt(gpid), 0) + 1);
|
||||||
|
// jedis11.hincrBy(playKey, "leftover_robot", -1);
|
||||||
|
lianjiejiqiren lianjiejiqiren = new lianjiejiqiren();
|
||||||
|
lianjiejiqiren.token = token;
|
||||||
|
lianjiejiqiren.session = session;
|
||||||
|
lianjiejiqiren.lianjie(client, wanfaId, mapclient);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
System.out.println(roomKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//进入中括号的房间
|
||||||
|
public void tcp_jiqirezhongkouhao(String id) {
|
||||||
|
Jedis jedis0 = Redis.use("group1_db0").getJedis();
|
||||||
|
Jedis jedis11 = Redis.use("group1_db11").getJedis();
|
||||||
|
Jedis jedis2 = Redis.use("group1_db2").getJedis();
|
||||||
|
String roomKey = "";
|
||||||
|
String roomId = id.substring(id.indexOf(":") + 1);
|
||||||
|
roomKey = "room:" + roomId;
|
||||||
|
//玩法id
|
||||||
|
String gpid = jedis0.hget(roomKey, "gpid");
|
||||||
|
//圈子id
|
||||||
|
String group = jedis0.hget(roomKey, "group");
|
||||||
|
//获取玩法对应的机器人 g{330800}:play:10
|
||||||
|
String playKey = "g{" + group + "}:play:" + gpid;
|
||||||
|
String players1 = jedis0.hget(roomKey, "players");
|
||||||
|
|
||||||
|
if (jedis11.hget(playKey, "leftover_robot") != null && players1 != null) {
|
||||||
|
//玩法配置的机器人数量
|
||||||
|
String leftover_robot = jedis11.hget(playKey, "leftover_robot");
|
||||||
|
//机器人进入退出的房间
|
||||||
|
if (players1.equals("[]")) {
|
||||||
|
String maxPlayers = jedis0.hget(roomKey, "maxPlayers");
|
||||||
|
if (Integer.parseInt(maxPlayers) == 2) {
|
||||||
|
|
||||||
|
if (Integer.parseInt(leftover_robot) > 0) {
|
||||||
|
int count = countValuesEqualToOne(jedis2, playKey);
|
||||||
|
System.out.println("count 数量 +++++++++++++++++++++" + count);
|
||||||
|
if (count < 2) {
|
||||||
|
Map<String, String> stringStringMap = jedis2.hgetAll(playKey);
|
||||||
|
List<String> zeroValueKeys = getKeysWithValueZero(stringStringMap);
|
||||||
|
System.out.println("值为0的机器人ID: " + zeroValueKeys);
|
||||||
|
String randomKeyFromList = getRandomKeyFromList(zeroValueKeys);
|
||||||
|
System.out.println("随机给的用户id----" + randomKeyFromList);
|
||||||
|
|
||||||
|
wanfaId = jedis0.hget(roomKey, "game");
|
||||||
|
|
||||||
|
if ("22".equalsIgnoreCase(wanfaId)) { //湖南红中麻将
|
||||||
|
// client = new TaurusClient("47.109.55.7:6421", "game", TaurusClient.ConnectionProtocol.Tcp);
|
||||||
|
client = new TaurusClient("47.109.55.7:6421", randomKeyFromList, TaurusClient.ConnectionProtocol.Tcp);
|
||||||
|
client.connect();
|
||||||
|
mapclient.put(randomKeyFromList, client);
|
||||||
|
}
|
||||||
|
if ("10".equalsIgnoreCase(wanfaId)) {
|
||||||
|
client = new TaurusClient("47.109.55.7:6311", randomKeyFromList, TaurusClient.ConnectionProtocol.Tcp);
|
||||||
|
client.connect();
|
||||||
|
mapclient.put(randomKeyFromList, client);
|
||||||
|
}
|
||||||
|
if ("66".equalsIgnoreCase(wanfaId)) {
|
||||||
|
// client = new TaurusClient("47.109.55.7:6841", "game", TaurusClient.ConnectionProtocol.Tcp);
|
||||||
|
// client = new TaurusClient("47.109.55.7:6841", randomKeyFromList, TaurusClient.ConnectionProtocol.Tcp);
|
||||||
|
// client.connect();
|
||||||
|
// mapclient.put(randomKeyFromList, client);
|
||||||
|
}
|
||||||
|
|
||||||
|
ITObject object = null;
|
||||||
|
AccountBusiness accountBusiness = null;
|
||||||
|
accountBusiness = new AccountBusiness();
|
||||||
|
// 登录获取session跟token
|
||||||
|
|
||||||
|
if (jedis0.keys("{user}:" + randomKeyFromList + "_token").size() != 0) {
|
||||||
|
|
||||||
|
Set<String> keys1 = jedis0.keys("{user}:" + randomKeyFromList + "_token");
|
||||||
|
if (keys1 == null) {
|
||||||
|
try {
|
||||||
|
object = accountBusiness.idPasswordLogin(Integer.parseInt(randomKeyFromList), "blm523");
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
ITObject validate = TObject.newInstance();
|
||||||
|
validate.putString("token", object.getString("token"));
|
||||||
|
token = object.getString("token");
|
||||||
|
session = accountBusiness.getSession();
|
||||||
|
} else {
|
||||||
|
Set<String> keys = jedis0.smembers("{user}:" + randomKeyFromList + "_token");
|
||||||
|
List<String> keyList = new ArrayList<>(keys);
|
||||||
|
session = "{user}:" + randomKeyFromList;
|
||||||
|
token = keyList.get(0);
|
||||||
|
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
try {
|
||||||
|
object = accountBusiness.idPasswordLogin(Integer.parseInt(randomKeyFromList), "blm523");
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
ITObject validate = TObject.newInstance();
|
||||||
|
validate.putString("token", object.getString("token"));
|
||||||
|
token = object.getString("token");
|
||||||
|
session = accountBusiness.getSession();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
String roomKey1 = "room:" + roomId;
|
||||||
|
|
||||||
|
try {
|
||||||
|
ITObject roomInfos = GroupRoomBusiness.joinRoom(Integer.parseInt(group), roomKey1, session, null);
|
||||||
|
String roomIds = roomInfos.getString("room_id");
|
||||||
|
|
||||||
|
jedis2.hset(playKey, randomKeyFromList, "1");
|
||||||
|
String robortInfoKey = "{robortInfo}:" + randomKeyFromList;
|
||||||
|
jedis2.hset(robortInfoKey, "circleId", group);
|
||||||
|
jedis2.hset(robortInfoKey, "pid", gpid);
|
||||||
|
jedis2.hset(robortInfoKey, "room_id", roomIds);
|
||||||
|
|
||||||
|
|
||||||
|
log.info("中括号-- 用户id ----"+ randomKeyFromList);
|
||||||
|
log.info("中括号-- group ----"+ jedis2.hget(robortInfoKey, "circleId"));
|
||||||
|
log.info("中括号-- gpid ----"+ jedis2.hget(robortInfoKey, "pid"));
|
||||||
|
log.info("中括号-- room_id ----"+ jedis2.hget(robortInfoKey, "room_id"));
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
joinRoom(Integer.parseInt(randomKeyFromList)); //进入房间
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
ready(); //准备
|
||||||
|
count2.put(Integer.valueOf(gpid), count2.getOrDefault(Integer.parseInt(gpid), 0) + 1);
|
||||||
|
//jedis11.hincrBy(playKey, "leftover_robot", -1);
|
||||||
|
lianjiejiqiren lianjiejiqiren = new lianjiejiqiren();
|
||||||
|
lianjiejiqiren.token = token;
|
||||||
|
lianjiejiqiren.session = session;
|
||||||
|
lianjiejiqiren.lianjie(client, wanfaId, mapclient);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
jedis0.close();
|
||||||
|
jedis2.close();
|
||||||
|
jedis11.close();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//创建房间,进入房间
|
||||||
|
public void createRoomsForCircle(String circleId, Map<String, Integer> playRoomMap) {
|
||||||
|
Jedis jedis2 = Redis.use("group1_db2").getJedis();
|
||||||
|
Jedis jedis11 = Redis.use("group1_db11").getJedis();
|
||||||
|
Jedis jedis0 = Redis.use("group1_db0").getJedis();
|
||||||
|
// 按玩法ID排序处理
|
||||||
|
List<Map.Entry<String, Integer>> sortedPlays = playRoomMap.entrySet().stream()
|
||||||
|
.sorted(Map.Entry.comparingByKey())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
for (Map.Entry<String, Integer> entry : sortedPlays) {
|
||||||
|
String playId = entry.getKey();
|
||||||
|
int requiredRooms = entry.getValue();
|
||||||
|
|
||||||
|
if (requiredRooms <= 0) {
|
||||||
|
System.out.println("玩法 " + playId + ": 跳过创建 (需求数=" + requiredRooms + ")");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isPlaying = true;
|
||||||
|
//获取所有房间
|
||||||
|
Set<String> roomIds = jedis0.keys("room:*");
|
||||||
|
for (String id : roomIds) {
|
||||||
|
String roomId = id.substring(id.indexOf(":") + 1);
|
||||||
|
String roomKey = "room:" + roomId;
|
||||||
|
String players = jedis0.hget(roomKey, "players");
|
||||||
|
if (players != null) {
|
||||||
|
if (players.equals("[]")) {
|
||||||
|
isPlaying = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
System.out.println("玩法 " + playId + ": 创建 " + requiredRooms + " 个房间");
|
||||||
|
|
||||||
|
List<Object> roomsForPlay = new ArrayList<>();
|
||||||
|
|
||||||
|
for (int i = 1; i <= requiredRooms; i++) {
|
||||||
|
try {
|
||||||
|
String keys = "g{" + circleId + "}:play:" + playId;
|
||||||
|
if (jedis11.hget(keys, "leftover_robot") != null && isPlaying) {
|
||||||
|
String leftover_robot = jedis11.hget(keys, "leftover_robot");
|
||||||
|
if (Integer.parseInt(leftover_robot) > 0) {
|
||||||
|
Map<String, String> stringStringMap = jedis2.hgetAll(keys);
|
||||||
|
|
||||||
|
|
||||||
|
List<String> zeroValueKeys = getKeysWithValueZero(stringStringMap);
|
||||||
|
System.out.println("值为0的机器人ID: " + zeroValueKeys);
|
||||||
|
String randomKeyFromList = getRandomKeyFromList(zeroValueKeys);
|
||||||
|
System.out.println("随机给的用户id----" + randomKeyFromList);
|
||||||
|
|
||||||
|
jedis2.hset(keys, randomKeyFromList, "1");
|
||||||
|
|
||||||
|
ITObject object = null;
|
||||||
|
AccountBusiness accountBusiness = null;
|
||||||
|
accountBusiness = new AccountBusiness();
|
||||||
|
|
||||||
|
// 登录获取session跟token
|
||||||
|
if (jedis0.keys("{user}:" + randomKeyFromList + "_token").size() != 0) {
|
||||||
|
Set<String> keys1 = jedis0.keys("{user}:" + randomKeyFromList + "_token");
|
||||||
|
if (keys1 == null) {
|
||||||
|
object = accountBusiness.idPasswordLogin(Integer.parseInt(randomKeyFromList), "blm523");
|
||||||
|
ITObject validate = TObject.newInstance();
|
||||||
|
validate.putString("token", object.getString("token"));
|
||||||
|
token = object.getString("token");
|
||||||
|
session = accountBusiness.getSession();
|
||||||
|
} else {
|
||||||
|
Set<String> keys2 = jedis0.smembers("{user}:" + randomKeyFromList + "_token");
|
||||||
|
List<String> keyList = new ArrayList<>(keys2);
|
||||||
|
session = "{user}:" + randomKeyFromList;
|
||||||
|
token = keyList.get(0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
object = accountBusiness.idPasswordLogin(Integer.parseInt(randomKeyFromList), "blm523");
|
||||||
|
ITObject validate = TObject.newInstance();
|
||||||
|
validate.putString("token", object.getString("token"));
|
||||||
|
token = object.getString("token");
|
||||||
|
session = accountBusiness.getSession();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 调用原有的房间匹配逻辑
|
||||||
|
Object room = GroupRoomBusiness.matchRoom(Integer.parseInt(circleId), Integer.parseInt(playId), session, "Android", false);
|
||||||
|
String game = jedis0.hget(String.valueOf(room), "game");
|
||||||
|
wanfaId = String.valueOf(game);
|
||||||
|
String roomKey = String.valueOf(room);
|
||||||
|
System.out.println("roomkey--------" + roomKey);
|
||||||
|
|
||||||
|
// client = null;
|
||||||
|
|
||||||
|
System.out.println("wanfaId" + wanfaId);
|
||||||
|
|
||||||
|
|
||||||
|
if ("22".equalsIgnoreCase(wanfaId)) { //湖南红中麻将
|
||||||
|
client = new TaurusClient("47.109.55.7:6421", "game", TaurusClient.ConnectionProtocol.Tcp);
|
||||||
|
client.connect();
|
||||||
|
mapclient.put(randomKeyFromList, client);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("10".equalsIgnoreCase(wanfaId)) {
|
||||||
|
client = new TaurusClient("47.109.55.7:6311", randomKeyFromList, TaurusClient.ConnectionProtocol.Tcp);
|
||||||
|
// client.setSession(session);
|
||||||
|
client.connect();
|
||||||
|
mapclient.put(randomKeyFromList, client);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if ("66".equalsIgnoreCase(wanfaId)) {
|
||||||
|
// client = new TaurusClient("47.109.55.7:6841", "game", TaurusClient.ConnectionProtocol.Tcp);
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("id ++++++" + mapclient);
|
||||||
|
System.out.println("mapclient----" + mapclient);
|
||||||
|
ITObject roomInfos = GroupRoomBusiness.joinRoom(Integer.parseInt(circleId), roomKey, this.session, null);
|
||||||
|
String roomId = roomInfos.getString("room_id");
|
||||||
|
String robort = "{robortInfo}:" + randomKeyFromList;
|
||||||
|
jedis2.hset(robort, "room_id", roomId);
|
||||||
|
jedis2.hset(robort, "circleId", circleId);
|
||||||
|
jedis2.hset(robort, "pid", playId);
|
||||||
|
|
||||||
|
|
||||||
|
joinRoom(Integer.parseInt(randomKeyFromList)); //进入房间
|
||||||
|
|
||||||
|
|
||||||
|
log.info("创建-- 用户id ----"+ randomKeyFromList);
|
||||||
|
log.info("创建-- group ----"+ jedis2.hget(robort, "circleId"));
|
||||||
|
log.info("创建-- gpid ----"+ jedis2.hget(robort, "pid"));
|
||||||
|
log.info("创建-- room_id ----"+ jedis2.hget(robort, "room_id"));
|
||||||
|
|
||||||
|
String playKey1 = "g{" + circleId + "}:play:" + playId;
|
||||||
|
|
||||||
|
count2.put(Integer.valueOf(playId), count2.getOrDefault(Integer.parseInt(playId), 0) + 1);
|
||||||
|
|
||||||
|
// count.put(key, count.getOrDefault(key, 0) + 1);
|
||||||
|
// jedis11.hincrBy(playKey1, "leftover_robot", -1);
|
||||||
|
|
||||||
|
lianjiejiqiren lianjiejiqiren = new lianjiejiqiren();
|
||||||
|
lianjiejiqiren.token = token;
|
||||||
|
lianjiejiqiren.session = session;
|
||||||
|
lianjiejiqiren.lianjie(client, wanfaId, mapclient);
|
||||||
|
ready(); //准备
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
System.err.println("创建过程被中断");
|
||||||
|
break;
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("创建房间时出错: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
jedis2.close();
|
||||||
|
jedis11.close();
|
||||||
|
jedis0.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统计Hash中值为"1"的字段数量
|
||||||
|
*/
|
||||||
|
public static int countValuesEqualToOne(Jedis jedis, String key) {
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
Map<String, String> hashData = jedis.hgetAll(key);
|
||||||
|
|
||||||
|
for (Map.Entry<String, String> entry : hashData.entrySet()) {
|
||||||
|
if ("1".equals(entry.getValue())) {
|
||||||
|
count++;
|
||||||
|
System.out.println("找到字段: " + entry.getKey() + " = " + entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//获取所有机器人
|
||||||
|
public List<String> getAvailableRobots1() {
|
||||||
|
Jedis jedis2 = Redis.use("group1_db2").getJedis();
|
||||||
|
|
||||||
|
Set<String> allRobotKeys = jedis2.keys("{robot}:*");
|
||||||
|
List<String> availableRobots = new ArrayList<>();
|
||||||
|
|
||||||
|
// 使用Pipeline提高效率
|
||||||
|
Pipeline pipeline = jedis2.pipelined();
|
||||||
|
List<Response<String>> responses = new ArrayList<>();
|
||||||
|
|
||||||
|
for (String robotKey : allRobotKeys) {
|
||||||
|
responses.add(pipeline.hget(robotKey, "start"));
|
||||||
|
}
|
||||||
|
|
||||||
|
pipeline.sync();
|
||||||
|
|
||||||
|
// 提取机器人ID并筛选
|
||||||
|
int index = 0;
|
||||||
|
for (String robotKey : allRobotKeys) {
|
||||||
|
String startStatus = responses.get(index).get();
|
||||||
|
|
||||||
|
String robotId = robotKey.substring(robotKey.indexOf(":") + 1);
|
||||||
|
availableRobots.add(robotId);
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
jedis2.close();
|
||||||
|
return availableRobots;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//获取状态为0的机器人
|
||||||
|
public static List<String> getKeysWithValueZero(Map<String, String> robotData) {
|
||||||
|
List<String> result = new ArrayList<>();
|
||||||
|
|
||||||
|
for (Map.Entry<String, String> entry : robotData.entrySet()) {
|
||||||
|
if ("0".equals(entry.getValue())) {
|
||||||
|
result.add(entry.getKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//随机分配机器人
|
||||||
|
public String getRandomKeyFromList(List<String> zeroValueKeys) {
|
||||||
|
if (zeroValueKeys == null || zeroValueKeys.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Random random = new Random();
|
||||||
|
int randomIndex = random.nextInt(zeroValueKeys.size());
|
||||||
|
return zeroValueKeys.get(randomIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
//监听
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加入房间方法
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String joinRoom(int playerId) {
|
||||||
|
try {
|
||||||
|
// 添加2秒延迟
|
||||||
|
Thread.sleep(5000);
|
||||||
|
|
||||||
|
ITObject params = TObject.newInstance();
|
||||||
|
params.putString("session", session + "," + token);
|
||||||
|
|
||||||
|
client.send("1002", params, response -> {
|
||||||
|
//ITObject obj = response.messageData.param.getTObject("tableInfo");
|
||||||
|
});
|
||||||
|
return "ok";
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 线程休眠方法
|
||||||
|
*
|
||||||
|
* @param time 休眠时间(毫秒)
|
||||||
|
*/
|
||||||
|
public static void sleepTime(int time) {
|
||||||
|
try {
|
||||||
|
// 添加延迟
|
||||||
|
Thread.sleep(time);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 准备方法
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String ready() {
|
||||||
|
try {
|
||||||
|
// 添加1秒延迟
|
||||||
|
Thread.sleep(1000);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ITObject params = TObject.newInstance();
|
||||||
|
params.putString("session", session + "," + token);
|
||||||
|
|
||||||
|
client.send("1003", params, new ICallback<MessageResponse>() {
|
||||||
|
@Override
|
||||||
|
public void action(MessageResponse messageResponse) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,987 @@
|
||||||
|
|
||||||
|
package com.robotcm.taurus.newRobot;
|
||||||
|
|
||||||
|
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.util.ICallback;
|
||||||
|
import com.taurus.core.util.Logger;
|
||||||
|
import com.taurus.core.util.StringUtil;
|
||||||
|
import com.robotcm.hunan.HuNanChangSha;
|
||||||
|
import redis.clients.jedis.Jedis;
|
||||||
|
import com.robotcm.taurus.client.Message;
|
||||||
|
import com.robotcm.taurus.client.MessageResponse;
|
||||||
|
import com.robotcm.taurus.client.SocketCode;
|
||||||
|
import com.robotcm.taurus.client.TaurusClient;
|
||||||
|
import com.robotcm.taurus.util.ChangShaSuanFaTest;
|
||||||
|
import com.robotcm.taurus.util.TinHuChi;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class lianjiejiqiren {
|
||||||
|
|
||||||
|
private final static Logger log;
|
||||||
|
|
||||||
|
static {
|
||||||
|
log = Logger.getLogger(lianjiejiqiren.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 会话标识
|
||||||
|
public String session = "";
|
||||||
|
// 访问令牌
|
||||||
|
public String token = "";
|
||||||
|
|
||||||
|
private HuNanChangSha huNanChangSha = new HuNanChangSha();
|
||||||
|
|
||||||
|
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<>();
|
||||||
|
|
||||||
|
|
||||||
|
public void lianjie(TaurusClient client, String wanfaId, Map<String, Object> mapclient) {
|
||||||
|
// 添加事件监听器处理网络消息
|
||||||
|
client.addEventListener(TaurusClient.NetClientEvent.OnEvent, new IEventListener() {
|
||||||
|
@Override
|
||||||
|
public void handleEvent(Event event) {
|
||||||
|
|
||||||
|
// 获取 msg
|
||||||
|
Message message = (Message) event.getParameter("msg");
|
||||||
|
if (message == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ITObject param = message.param;
|
||||||
|
//回调协议号
|
||||||
|
String command = message.command;
|
||||||
|
System.out.println("command: " + command);
|
||||||
|
//根据玩法id 调用不同的回调
|
||||||
|
if (StringUtil.isNotEmpty(command)) {
|
||||||
|
if ("66".equalsIgnoreCase(wanfaId)) {//跑的快
|
||||||
|
// if ("2011".equalsIgnoreCase(command)) { //跑的快 初始化手牌
|
||||||
|
// huNanPaoDeKuai.paoDeKuaiCardInHead(param, client);
|
||||||
|
// } else if ("2021".equalsIgnoreCase(command)) { //出牌广播
|
||||||
|
// huNanPaoDeKuai.paoDekuaiChupaiGuangBo(param);
|
||||||
|
// } else if ("2004".equalsIgnoreCase(command)) { //出牌提示事件,牌权
|
||||||
|
// Integer player = param.getInt("player");
|
||||||
|
// Integer seat1 = param.getInt("seat");
|
||||||
|
//
|
||||||
|
// if (seat1 != null) {
|
||||||
|
// huNanPaoDeKuai.seat = seat1;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// //出牌
|
||||||
|
// huNanPaoDeKuai.outCard(client);
|
||||||
|
// } else if ("2007".equalsIgnoreCase(command)) { //结算准备
|
||||||
|
// //type为1 为大结算 type为0为小结算
|
||||||
|
// Integer type = param.getInt("type");
|
||||||
|
//
|
||||||
|
// if (type == 0) {
|
||||||
|
// huNanPaoDeKuai.getSeatRemainHistory().clear();
|
||||||
|
// huNanPaoDeKuai.getPaoDekuaiCardInhand().clear();
|
||||||
|
// ITArray card_list = huNanPaoDeKuai.getCard_list().getTArray("card_list");
|
||||||
|
// card_list.clear();
|
||||||
|
// ready();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (type == 1) {
|
||||||
|
// Jedis jedis11s = Redis.use("group1_db11").getJedis();
|
||||||
|
// try {
|
||||||
|
// String key = "g{" + groupId + "}:play:" + pid;
|
||||||
|
// jedis11s.hincrBy(key, "leftover_robot", 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();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// } else if ("2009".equalsIgnoreCase(command)) {
|
||||||
|
// Jedis jedis21 = Redis.use().getJedis();
|
||||||
|
// sleepTime(3000);
|
||||||
|
//
|
||||||
|
// ITObject params = TObject.newInstance();
|
||||||
|
// String[] playerIds2 = null;
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Set<String> roomIds = jedis21.keys("room:*");
|
||||||
|
// String roomKey = "";
|
||||||
|
// //拿到所有的机器人
|
||||||
|
// List<Integer> robotIdsList = new ArrayList<>();
|
||||||
|
// String sql2 = "SELECT id FROM `account` WHERE jiqiren=9998";
|
||||||
|
// ITArray robotId2 = null;
|
||||||
|
// try {
|
||||||
|
// robotId2 = DataBase.use().executeQueryByTArray(sql2);
|
||||||
|
// } catch (SQLException e) {
|
||||||
|
// e.printStackTrace();
|
||||||
|
// }
|
||||||
|
// 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;
|
||||||
|
//
|
||||||
|
// if (jedis21.hget(roomKey, "players") != null) {
|
||||||
|
// String players = jedis21.hget(roomKey, "players");
|
||||||
|
// if (!players.equals("[]")) {
|
||||||
|
// players = players.substring(1, players.length() - 1);
|
||||||
|
// playerIds2 = players.split(",");
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// if (playerIds2.length == 1) {
|
||||||
|
// for (String s : playerIds2) {
|
||||||
|
// if (robotIdsList.contains(Integer.parseInt(s))) { //房间里的人是机器人
|
||||||
|
// String gpid = jedis21.hget(roomKey, "gpid");
|
||||||
|
// String gpId = jedis21.hget(roomKey, "group");
|
||||||
|
// String key = "g{" + gpId + "}:play:" + gpid;
|
||||||
|
// if (!players.equals("[]") && pid == Integer.parseInt(gpid)) {
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// if (JiQiRens.count != null && JiQiRens.count.containsKey(Integer.parseInt(gpid))) {
|
||||||
|
// Integer currentValue = JiQiRens.count.get(Integer.parseInt(gpid));
|
||||||
|
// if (currentValue > 0) {
|
||||||
|
// JiQiRens.count.put(Integer.parseInt(gpid), currentValue - 1);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Jedis jedis20 = Redis.use("group1_db11").getJedis();
|
||||||
|
//
|
||||||
|
// jedis20.hincrBy(key, "leftover_robot", 1);
|
||||||
|
// jedis20.close();
|
||||||
|
// String sql = String.format("UPDATE `account` SET start = %d WHERE id = %d", 0, Integer.parseInt(s));
|
||||||
|
//
|
||||||
|
// try {
|
||||||
|
// DataBase.use().executeUpdate(sql);
|
||||||
|
// } catch (SQLException e) {
|
||||||
|
// e.printStackTrace();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// client.send("1005", params, response -> {
|
||||||
|
//
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// jedis21.close();
|
||||||
|
//
|
||||||
|
// } else if ("2008".equalsIgnoreCase(command)) { //解散房间时候恢复机器人账号可以使用
|
||||||
|
// Set<String> roomIds = scanRooms(jedis0, "room:*");
|
||||||
|
// String[] playerIds = null;
|
||||||
|
// for (String roomId : roomIds) {
|
||||||
|
// String rid = roomId.substring(roomId.indexOf(":") + 1);
|
||||||
|
// String roomKey = "room:" + rid;
|
||||||
|
// if (jedis0.hget(roomKey, "players") != null) {
|
||||||
|
// String players = jedis0.hget(roomKey, "players");
|
||||||
|
// if (!players.equals("[]")) {
|
||||||
|
// players = players.substring(1, players.length() - 1);
|
||||||
|
// playerIds = players.split(",");
|
||||||
|
// for (String pyids : playerIds) {
|
||||||
|
// if (Integer.parseInt(pyids) == playerId) {
|
||||||
|
// jedis0.del(roomId);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// } else if ("2031".equalsIgnoreCase(command)) {
|
||||||
|
// ITObject params = TObject.newInstance();
|
||||||
|
// params.putInt("id", 0);
|
||||||
|
// client.send("1015", params, response -> {
|
||||||
|
//
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
// } else if ("22".equalsIgnoreCase(wanfaId)) { //湖南红中麻将
|
||||||
|
//
|
||||||
|
// if ("812".equalsIgnoreCase(command)) { //出牌广播
|
||||||
|
// HuNanHongZhong.drawCard(command, message);
|
||||||
|
// } else if ("811".equalsIgnoreCase(command)) {//初始化手牌
|
||||||
|
// huNanHongZhong.cardInHead(command, message, client);
|
||||||
|
// } else if ("819".equalsIgnoreCase(command)) { //摸牌
|
||||||
|
// huNanHongZhong.getCard(command, message);
|
||||||
|
// } else if ("813".equalsIgnoreCase(command)) { //出牌,牌权
|
||||||
|
// huNanHongZhong.outCard(client);
|
||||||
|
// } else if ("817".equalsIgnoreCase(command)) {//结算
|
||||||
|
// huNanHongZhong.getHongZhongCardInhand().clear();
|
||||||
|
// huNanHongZhong.getChuGuoCardInhand().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
|
||||||
|
// String shangxianRobot = jedis11.hget(playKey, "shangxian_robot");
|
||||||
|
// String leftoverRobot = jedis11.hget(playKey, "leftover_robot");
|
||||||
|
//
|
||||||
|
// if (shangxianRobot != null && leftoverRobot != null) {
|
||||||
|
// if (value == 0) {
|
||||||
|
// jedis11.hset(playKey, "leftover_robot", shangxianRobot);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//// playerState.pongGroups.clear();;
|
||||||
|
//// playerState.handCards.clear();
|
||||||
|
//// playerState.chiGroups.clear();
|
||||||
|
//// playerState.gangGroups.clear();;
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// ready();
|
||||||
|
// } else if ("2009".equalsIgnoreCase(command)) {
|
||||||
|
// Jedis jedis21 = Redis.use().getJedis();
|
||||||
|
// sleepTime(3000);
|
||||||
|
// ITObject params = TObject.newInstance();
|
||||||
|
// String[] playerIds2 = null;
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Set<String> roomIds = jedis21.keys("room:*");
|
||||||
|
// String roomKey = "";
|
||||||
|
// //拿到所有的机器人
|
||||||
|
// List<Integer> robotIdsList = new ArrayList<>();
|
||||||
|
// String sql2 = "SELECT id FROM `account` WHERE jiqiren=9998";
|
||||||
|
// ITArray robotId2 = null;
|
||||||
|
// try {
|
||||||
|
// robotId2 = DataBase.use().executeQueryByTArray(sql2);
|
||||||
|
// } catch (SQLException e) {
|
||||||
|
// e.printStackTrace();
|
||||||
|
// }
|
||||||
|
// 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;
|
||||||
|
//
|
||||||
|
// if (jedis21.hget(roomKey, "players") != null) {
|
||||||
|
// String players = jedis21.hget(roomKey, "players");
|
||||||
|
// if (!players.equals("[]")) {
|
||||||
|
// players = players.substring(1, players.length() - 1);
|
||||||
|
// playerIds2 = players.split(",");
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// if (playerIds2.length == 1) {
|
||||||
|
// for (String s : playerIds2) {
|
||||||
|
// if (robotIdsList.contains(Integer.parseInt(s))) { //房间里的人是机器人
|
||||||
|
// String gpid = jedis21.hget(roomKey, "gpid");
|
||||||
|
// String gpId = jedis21.hget(roomKey, "group");
|
||||||
|
// String key = "g{" + gpId + "}:play:" + gpid;
|
||||||
|
// if (!players.equals("[]") && pid == Integer.parseInt(gpid)) {
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// if (JiQiRens.count != null && JiQiRens.count.containsKey(Integer.parseInt(gpid))) {
|
||||||
|
// Integer currentValue = JiQiRens.count.get(Integer.parseInt(gpid));
|
||||||
|
// if (currentValue > 0) {
|
||||||
|
// JiQiRens.count.put(Integer.parseInt(gpid), currentValue - 1);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Jedis jedis20 = Redis.use("group1_db11").getJedis();
|
||||||
|
//
|
||||||
|
// jedis20.hincrBy(key, "leftover_robot", 1);
|
||||||
|
// jedis20.close();
|
||||||
|
// String sql = String.format("UPDATE `account` SET start = %d WHERE id = %d", 0, Integer.parseInt(s));
|
||||||
|
//
|
||||||
|
// try {
|
||||||
|
// DataBase.use().executeUpdate(sql);
|
||||||
|
// } catch (SQLException e) {
|
||||||
|
// e.printStackTrace();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// client.send("1005", params, response -> {
|
||||||
|
//
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// jedis21.close();
|
||||||
|
//
|
||||||
|
// } else if ("814".equalsIgnoreCase(command)) {//杠碰胡通知协议
|
||||||
|
//// sleepTime(2000);
|
||||||
|
// huNanHongZhong.actionCard(param, client);
|
||||||
|
// } else if ("820".equalsIgnoreCase(command)) {
|
||||||
|
// HuNanHongZhong.changePlayer(command, message);
|
||||||
|
// } else if ("815".equalsIgnoreCase(command)) { //服务器通知客户端有玩家执行了操作
|
||||||
|
// huNanHongZhong.shanchuchuguopai(param);
|
||||||
|
// } else if ("2008".equalsIgnoreCase(command)) { //解散房间时候恢复机器人账号可以使用
|
||||||
|
//// Jedis jedis11s = Redis.use("group1_db11").getJedis();
|
||||||
|
//// try {
|
||||||
|
//// String key = "g{" + groupId + "}:play:" + pid;
|
||||||
|
//// jedis11s.hincrBy(key, "leftover_robot", 1);
|
||||||
|
//// String sql = String.format("UPDATE `account` SET start = %d WHERE id = %d", 0, playerId);
|
||||||
|
//// DataBase.use().executeUpdate(sql);
|
||||||
|
// Set<String> roomIds = scanRooms(jedis0, "room:*");
|
||||||
|
// String[] playerIds = null;
|
||||||
|
// for (String roomId : roomIds) {
|
||||||
|
// String rid = roomId.substring(roomId.indexOf(":") + 1);
|
||||||
|
// String roomKey = "room:" + rid;
|
||||||
|
// if (jedis0.hget(roomKey, "players") != null) {
|
||||||
|
// String players = jedis0.hget(roomKey, "players");
|
||||||
|
// if (!players.equals("[]")) {
|
||||||
|
// players = players.substring(1, players.length() - 1);
|
||||||
|
// playerIds = players.split(",");
|
||||||
|
// for (String pyids : playerIds) {
|
||||||
|
// if (Integer.parseInt(pyids) == playerId) {
|
||||||
|
// jedis0.del(roomId);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
} else if ("17".equalsIgnoreCase(wanfaId)) { //字牌放炮罚
|
||||||
|
|
||||||
|
// if ("811".equalsIgnoreCase(command)) { //初始化手牌
|
||||||
|
// huNanFangPaoFa.cardInHead(command, message, client);
|
||||||
|
//
|
||||||
|
// } else if ("819".equalsIgnoreCase(command)) { //摸牌
|
||||||
|
// huNanFangPaoFa.getCard(command, message);
|
||||||
|
// } else if ("813".equalsIgnoreCase(command)) { //出牌 牌权
|
||||||
|
// sleepTime(1500);
|
||||||
|
// huNanFangPaoFa.outCard(client);
|
||||||
|
// } else if ("812".equalsIgnoreCase(command)) { //出牌广播
|
||||||
|
// HuNanFangPaoFa.drawCard(command, message);
|
||||||
|
// } else if ("814".equalsIgnoreCase(command)) { //可执行的 操作 吃碰 偎提胡
|
||||||
|
// huNanFangPaoFa.actionCard(param, client);
|
||||||
|
// } else if ("817".equalsIgnoreCase(command)) { //结算
|
||||||
|
// huNanFangPaoFa.getFangPaoFaCardInhand().clear();
|
||||||
|
// ready();
|
||||||
|
// } else if ("822".equalsIgnoreCase(command)) { //放跑提示
|
||||||
|
//
|
||||||
|
// HuNanFangPaoFa.fangPaoTipEvent(message, client);
|
||||||
|
// } else if ("2008".equalsIgnoreCase(command)) { //解散房间时候恢复机器人账号可以使用
|
||||||
|
// try {
|
||||||
|
// String sql = String.format("UPDATE `account` SET start = %d WHERE id = %d", 0, playerId);
|
||||||
|
// DataBase.use().executeUpdate(sql);
|
||||||
|
//
|
||||||
|
// } catch (SQLException e) {
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
} else if ("108".equalsIgnoreCase(wanfaId)) {//转转麻将
|
||||||
|
// if ("811".equalsIgnoreCase(command)) {//初始化收手牌
|
||||||
|
// huNanZhuanZhuan.cardInHead(command, message, client);
|
||||||
|
// } else if ("812".equalsIgnoreCase(command)) {//出牌广播
|
||||||
|
// HuNanZhuanZhuan.drawCard(command, message);
|
||||||
|
// } else if ("819".equalsIgnoreCase(command)) {//摸牌
|
||||||
|
//
|
||||||
|
// huNanZhuanZhuan.getCard(command, message);
|
||||||
|
// } else if ("813".equalsIgnoreCase(command)) {//出牌提示
|
||||||
|
//// sleepTime(1500);
|
||||||
|
// huNanZhuanZhuan.outCard(client);
|
||||||
|
// } else if ("814".equalsIgnoreCase(command)) {//放招提示
|
||||||
|
// huNanZhuanZhuan.actionCard(param, client);
|
||||||
|
// } else if ("817".equalsIgnoreCase(command)) {//结算
|
||||||
|
// huNanZhuanZhuan.getZhuanZhuanCardInhand().clear();
|
||||||
|
// huNanZhuanZhuan.getZhuanZhuanchuguopai().clear();
|
||||||
|
// ready();
|
||||||
|
// } else if ("815".equalsIgnoreCase(command)) { //服务器通知客户端有玩家执行了操作
|
||||||
|
// huNanZhuanZhuan.shanchuchuguopai(param);
|
||||||
|
// } else if ("820".equalsIgnoreCase(command)) {//换牌提示
|
||||||
|
// HuNanZhuanZhuan.changePlayer(command, message);
|
||||||
|
// } else if ("2008".equalsIgnoreCase(command)) { //解散房间时候恢复机器人账号可以使用
|
||||||
|
// try {
|
||||||
|
// String sql = String.format("UPDATE `account` SET start = %d WHERE id = %d", 0, playerId);
|
||||||
|
// DataBase.use().executeUpdate(sql);
|
||||||
|
// } catch (SQLException e) {
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
} else if ("10".equalsIgnoreCase(wanfaId)) {
|
||||||
|
//长沙麻将 机器人处理事件
|
||||||
|
if ("811".equalsIgnoreCase(command)) {//初始化收手牌
|
||||||
|
// System.out.println("初始化手牌" + session);
|
||||||
|
Jedis jedis222 = Redis.use("group1_db2").getJedis();
|
||||||
|
for (Map.Entry<String, Object> entry : mapclient.entrySet()) {
|
||||||
|
log.info("mapclient++++++++++++++++++++++++++++++++"+mapclient);
|
||||||
|
log.info("client++++++++++++++++++++++++++++++++++"+client);
|
||||||
|
TaurusClient taurusClient = (TaurusClient) entry.getValue();
|
||||||
|
if (client.equals(taurusClient)) {
|
||||||
|
String key = entry.getKey();
|
||||||
|
log.info("key+++++++++++++++++++++++++++++++++++" + key);
|
||||||
|
if (jedis222.hget("{robortInfo}:" + key, "circleId") != null && jedis222.hget("{robortInfo}:" + key, "pid") != null) {
|
||||||
|
String circleId = jedis222.hget("{robortInfo}:" + key, "circleId");
|
||||||
|
log.info("circleId +++++++++++++++++++++++++"+circleId);
|
||||||
|
String pid = jedis222.hget("{robortInfo}:" + key, "pid");
|
||||||
|
log.info("pid +++++++++++++++++++++++++"+pid);
|
||||||
|
String getStart = "g{" + circleId + "}:play:" + pid;
|
||||||
|
if (!pid.equals("0")){
|
||||||
|
log.info("getStart +++++++++++++++++++++++++"+getStart);
|
||||||
|
jedis222.hset(getStart, key, "2");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
jedis222.close();
|
||||||
|
huNanChangSha.cardInHead(command, message, client);
|
||||||
|
log.info("client " + client);
|
||||||
|
} else if ("812".equalsIgnoreCase(command)) {//出牌广播
|
||||||
|
//{"opzicards":[{"opzicards":[],"playerId":101555}],"opmingcards":[{"opmingcards":[],"playerId":101555}],"outcard_map":[{"outcards":[209,205],"playerId":101555},{"outcards":[],"playerId":112233}],"card":205,"opchicards":[{"opchicards":[105,103],"playerId":101555}],"oppengcards":[{"oppengcards":[],"playerId":101555}],"seat":1}
|
||||||
|
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");
|
||||||
|
|
||||||
|
// 清空旧数据,用新数据完全覆盖
|
||||||
|
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");
|
||||||
|
|
||||||
|
// 转换为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);
|
||||||
|
} else if ("819".equalsIgnoreCase(command)) {
|
||||||
|
for (Map.Entry<String, Object> entry : mapclient.entrySet()) {
|
||||||
|
TaurusClient taurusClient = (TaurusClient) entry.getValue();
|
||||||
|
if (client.equals(taurusClient)) {
|
||||||
|
//摸牌
|
||||||
|
huNanChangSha.getCard(command, message, taurusClient,mapclient);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if ("813".equalsIgnoreCase(command)) {//出牌提示
|
||||||
|
System.out.println("出牌提示 ++++++++++++++++++++++++++++++++");
|
||||||
|
System.out.println("mapclient ++++++++++++++++" + mapclient);
|
||||||
|
|
||||||
|
System.out.println("进入出牌11111111111111112222222");
|
||||||
|
System.out.println("client4444444444444444" + client);
|
||||||
|
huNanChangSha.outCard(client, playerOutcardsMap, playerchisMap, playerpengsMap, playermingsMap, playerzisMap);
|
||||||
|
|
||||||
|
|
||||||
|
} else if ("814".equalsIgnoreCase(command)) {
|
||||||
|
//吃碰杠
|
||||||
|
huNanChangSha.actionCard(param, client);
|
||||||
|
|
||||||
|
}else if("2002".equalsIgnoreCase(command)) {
|
||||||
|
System.out.println("comds:2002");
|
||||||
|
|
||||||
|
ITObject params = TObject.newInstance();
|
||||||
|
String aid = client.getGameID();
|
||||||
|
|
||||||
|
String getKey = "{robortInfo}:" + aid;
|
||||||
|
Jedis jedis20 = Redis.use("group1_db2").getJedis();
|
||||||
|
|
||||||
|
String circleId1 = jedis20.hget(getKey, "circleId");
|
||||||
|
String pid = jedis20.hget(getKey, "pid");
|
||||||
|
|
||||||
|
String key = "g{" + circleId1 + "}:play:" + pid;
|
||||||
|
|
||||||
|
client.send("1005", params, response -> {
|
||||||
|
log.info("退出状态1---------"+response);
|
||||||
|
log.info("退出状态1getKey---------"+getKey);
|
||||||
|
log.info("退出状态1key---------"+key);
|
||||||
|
log.info("退出状态1aid---------"+aid);
|
||||||
|
jedis20.hset(getKey, "circleId", "0");
|
||||||
|
jedis20.hset(getKey, "pid", "0");
|
||||||
|
jedis20.hset(getKey, "room_id", "0");
|
||||||
|
//退出修改机器人状态
|
||||||
|
jedis20.hset(key,aid, "0");
|
||||||
|
});
|
||||||
|
jedis20.close();
|
||||||
|
|
||||||
|
}else if ("2009".equalsIgnoreCase(command)) {
|
||||||
|
log.info("斤斤计较急急急急急急急急急====="+param);
|
||||||
|
// sleepTime(3000);
|
||||||
|
|
||||||
|
//db0
|
||||||
|
Jedis jedis22 = Redis.use().getJedis();
|
||||||
|
//db2
|
||||||
|
Jedis jedis33 = Redis.use("group1_db2").getJedis();
|
||||||
|
|
||||||
|
Jedis jedis = Redis.use("group1_db0").getJedis();
|
||||||
|
String aid = client.getGameID();
|
||||||
|
|
||||||
|
String getKey = "{robortInfo}:" + aid;
|
||||||
|
|
||||||
|
|
||||||
|
log.info("连接的id---------------:"+aid);
|
||||||
|
|
||||||
|
|
||||||
|
// System.out.println("param ++++++++++++++++ " + param);
|
||||||
|
|
||||||
|
ITObject params = TObject.newInstance();
|
||||||
|
|
||||||
|
String[] playerIds2 = null;
|
||||||
|
|
||||||
|
if (jedis33.hget(getKey, "pid") != null) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
String circleId1 = jedis33.hget(getKey, "circleId");
|
||||||
|
String pid = jedis33.hget(getKey, "pid");
|
||||||
|
|
||||||
|
String key = "g{" + circleId1 + "}:play:" + pid;
|
||||||
|
if (jedis33.hget(getKey, "room_id") != null) {
|
||||||
|
String roomid = jedis33.hget(getKey, "room_id");
|
||||||
|
String roomKey = "room:" + roomid;
|
||||||
|
Integer start = param.getInt("start");
|
||||||
|
log.info("获取数据的key----------------"+ key);
|
||||||
|
if (start == 1){
|
||||||
|
log.info("进入修好状态12345------------"+aid);
|
||||||
|
jedis33.hset(key, aid, "2");
|
||||||
|
log.info("进入修好状态ttttttttttttttt------------"+jedis33.hget(key, aid));
|
||||||
|
}
|
||||||
|
|
||||||
|
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.length);
|
||||||
|
// if (playerIds2.length ==2){
|
||||||
|
// jedis33.hset(key, String.valueOf(aid), "2");
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
if (playerIds2.length == 1) {
|
||||||
|
if (!pid.equals("0")) {
|
||||||
|
Jedis jedis20 = Redis.use("group1_db11").getJedis();
|
||||||
|
System.out.println("进入 -------------------------");
|
||||||
|
jedis20.hincrBy(key, "leftover_robot", 1);
|
||||||
|
jedis20.close();
|
||||||
|
System.out.println("mapclient--------------" + mapclient);
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
for (Map.Entry<String, Object> entry : mapclient.entrySet()) {
|
||||||
|
//
|
||||||
|
log.info("client-----key1----"+ client);
|
||||||
|
log.info("entry.getValue()--------连接"+entry.getValue());
|
||||||
|
if (client.equals(entry.getValue())) {
|
||||||
|
|
||||||
|
log.info("2009状态start---------"+start);
|
||||||
|
|
||||||
|
// 获取指定field的值
|
||||||
|
String value = jedis33.hget(key, aid);
|
||||||
|
log.info("2009-----value----"+ value);
|
||||||
|
|
||||||
|
if (!value.equals("2")) {
|
||||||
|
|
||||||
|
// log.info("jiqiren.count333333333333333333333333-------------"+jiqiren.count2);
|
||||||
|
|
||||||
|
|
||||||
|
if (jiqiren.count2 != null && jiqiren.count2.containsKey(Integer.parseInt(pid))) {
|
||||||
|
Integer currentValue = jiqiren.count2.get(Integer.parseInt(pid));
|
||||||
|
if (currentValue > 0) {
|
||||||
|
jiqiren.count2.put(Integer.parseInt(pid), currentValue - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
System.out.println("jiqiren.count2000000000000-------------"+jiqiren.count2);
|
||||||
|
|
||||||
|
if (jiqiren.count2 != null && jiqiren.count2.containsKey(Integer.parseInt(pid))) {
|
||||||
|
Integer value1 = jiqiren.count2.get(Integer.parseInt(pid));
|
||||||
|
|
||||||
|
// 如果找到了对应的 pid
|
||||||
|
Jedis jedis12 = Redis.use("group1_db11").getJedis();
|
||||||
|
|
||||||
|
String shangxianRobot = jedis12.hget(key, "shangxian_robot");
|
||||||
|
String leftoverRobot = jedis12.hget(key, "leftover_robot");
|
||||||
|
|
||||||
|
if (shangxianRobot != null && leftoverRobot != null) {
|
||||||
|
if (value1 == 0) {
|
||||||
|
System.out.println("key leftover_robot " + key);
|
||||||
|
jedis12.hset(key, "leftover_robot", shangxianRobot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
jedis12.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
sleepTime(5000);
|
||||||
|
String valueq = jedis33.hget(key, aid);
|
||||||
|
log.info("redis----状态状态111223243546----"+ valueq);
|
||||||
|
if (!valueq.equals("2")){
|
||||||
|
|
||||||
|
TaurusClient taurusClient = (TaurusClient) entry.getValue();
|
||||||
|
taurusClient.send("1005", params, response -> {
|
||||||
|
log.info("退出状态---------"+response);
|
||||||
|
log.info("退出状态getKey---------"+getKey);
|
||||||
|
log.info("退出状态key---------"+key);
|
||||||
|
log.info("退出状态aid---------"+aid);
|
||||||
|
jedis33.hset(getKey, "circleId", "0");
|
||||||
|
jedis33.hset(getKey, "pid", "0");
|
||||||
|
jedis33.hset(getKey, "room_id", "0");
|
||||||
|
//退出修改机器人状态
|
||||||
|
jedis33.hset(key,aid, "0");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
jedis33.close();
|
||||||
|
jedis22.close();
|
||||||
|
jedis.close();
|
||||||
|
}
|
||||||
|
} else if ("2001".equalsIgnoreCase(command)){
|
||||||
|
log.info("2001-------状态状态------"+param);
|
||||||
|
}else if ("817".equalsIgnoreCase(command)) {//结算
|
||||||
|
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 {
|
||||||
|
|
||||||
|
//db2
|
||||||
|
Jedis jedis21 = Redis.use("group1_db2").getJedis();
|
||||||
|
try {
|
||||||
|
for (Map.Entry<String, Object> entry : mapclient.entrySet()) {
|
||||||
|
|
||||||
|
TaurusClient taurusClient = (TaurusClient) entry.getValue();
|
||||||
|
if (client.equals(taurusClient)) {
|
||||||
|
String key = entry.getKey();
|
||||||
|
if (jedis21.hget("{robortInfo}:" + key, "circleId") != null && jedis21.hget("{robortInfo}:" + key, "pid") != null) {
|
||||||
|
String circleId = jedis21.hget("{robortInfo}:" + key, "circleId");
|
||||||
|
String pid = jedis21.hget("{robortInfo}:" + key, "pid");
|
||||||
|
String getStart = "g{" + circleId + "}:play:" + pid;
|
||||||
|
jedis21.hset(getStart, key, "0");
|
||||||
|
|
||||||
|
System.out.println("jiqiren.count2886746435243211-------------"+jiqiren.count2);
|
||||||
|
|
||||||
|
if (jiqiren.count2 != null && jiqiren.count2.containsKey(Integer.parseInt(pid))) {
|
||||||
|
Integer currentValue = jiqiren.count2.get(Integer.parseInt(pid));
|
||||||
|
if (currentValue > 0) {
|
||||||
|
jiqiren.count2.put(Integer.parseInt(pid), currentValue - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// jedis11s.hincrBy(getStart, "leftover_robot", 1);
|
||||||
|
|
||||||
|
jedis21.hset("{robortInfo}:" + key, "circleId", "0");
|
||||||
|
jedis21.hset("{robortInfo}:" + key, "pid", "0");
|
||||||
|
jedis21.hset("{robortInfo}:" + key, "room_id", "0");
|
||||||
|
|
||||||
|
System.out.println("jiqiren.count2-------------"+jiqiren.count2);
|
||||||
|
|
||||||
|
if (jiqiren.count2 != null && jiqiren.count2.containsKey(Integer.parseInt(pid))) {
|
||||||
|
Integer value = jiqiren.count2.get(Integer.parseInt(pid));
|
||||||
|
|
||||||
|
// 如果找到了对应的 pid
|
||||||
|
Jedis jedis12 = Redis.use("group1_db11").getJedis();
|
||||||
|
|
||||||
|
String shangxianRobot = jedis12.hget(key, "shangxian_robot");
|
||||||
|
String leftoverRobot = jedis12.hget(key, "leftover_robot");
|
||||||
|
|
||||||
|
if (shangxianRobot != null && leftoverRobot != null) {
|
||||||
|
if (value == 0) {
|
||||||
|
jedis12.hset(key, "leftover_robot", shangxianRobot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
jedis12.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
jedis21.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} 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();
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
ready(client);
|
||||||
|
//写定时器
|
||||||
|
|
||||||
|
} else if ("815".equalsIgnoreCase(command)) { //服务器通知客户端有玩家执行了操作
|
||||||
|
//[TCP->815] data:{"playerid":101555,"card":104,"opcard":[105,103],"from_seat":2,"type":1,"opengang":false}
|
||||||
|
huNanChangSha.shanchuchuguopai(param);
|
||||||
|
|
||||||
|
} else if ("820".equalsIgnoreCase(command)) {//换牌提示
|
||||||
|
huNanChangSha.changePlayer(command, message);
|
||||||
|
} else if ("2008".equalsIgnoreCase(command)) { //解散房间时候恢复机器人账号可以使用
|
||||||
|
|
||||||
|
// Set<String> roomIds = scanRooms(jedis0, "room:*");
|
||||||
|
// String[] playerIds = null;
|
||||||
|
// for (String roomId : roomIds) {
|
||||||
|
// String rid = roomId.substring(roomId.indexOf(":") + 1);
|
||||||
|
// String roomKey = "room:" + rid;
|
||||||
|
//
|
||||||
|
// if (jedis0.hget(roomKey, "players") != null) {
|
||||||
|
// String players = jedis0.hget(roomKey, "players");
|
||||||
|
//
|
||||||
|
// if (!players.equals("[]")) {
|
||||||
|
// players = players.substring(1, players.length() - 1);
|
||||||
|
// playerIds = players.split(",");
|
||||||
|
//
|
||||||
|
// for (String pyids : playerIds) {
|
||||||
|
//
|
||||||
|
// if (Integer.parseInt(pyids) == playerId) {
|
||||||
|
// jedis0.del(roomId);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
} else if ("825".equalsIgnoreCase(command)) {
|
||||||
|
ITObject params = TObject.newInstance();
|
||||||
|
params.putInt("qi", 0);
|
||||||
|
params.putInt("id", 1);
|
||||||
|
client.send("612", params, response -> {
|
||||||
|
|
||||||
|
});
|
||||||
|
} else if ("822".equalsIgnoreCase(command)) {
|
||||||
|
ITObject params = TObject.newInstance();
|
||||||
|
//params.putInt("qi", 0);
|
||||||
|
params.putInt("id", 1);
|
||||||
|
//[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", params, response -> {
|
||||||
|
|
||||||
|
});
|
||||||
|
} else if ("835".equalsIgnoreCase(command)) { //听牌天听
|
||||||
|
ITObject params = TObject.newInstance();
|
||||||
|
params.putInt("qi", 0);
|
||||||
|
params.putInt("id", 1);
|
||||||
|
client.send("612", params, response -> {
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//END 长沙麻将
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 添加连接状态监听器
|
||||||
|
client.addEventListener(TaurusClient.NetClientEvent.Connect, new
|
||||||
|
|
||||||
|
IEventListener() {
|
||||||
|
@Override
|
||||||
|
public void handleEvent(Event event) {
|
||||||
|
SocketCode code = (SocketCode) event.getParameter("code");
|
||||||
|
if (code == SocketCode.Connect) {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 准备方法
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String ready(TaurusClient client) {
|
||||||
|
try {
|
||||||
|
// 添加1秒延迟
|
||||||
|
Thread.sleep(1000);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
// cardInhand = new ArrayList<Integer>();
|
||||||
|
// HuNanHongZhong.hongZhongCardInhand();
|
||||||
|
// HuNanFangPaoFa.fangPaoFaCardInhand();
|
||||||
|
|
||||||
|
ITObject params = TObject.newInstance();
|
||||||
|
params.putString("session", session + "," + token);
|
||||||
|
|
||||||
|
client.send("1003", params, new ICallback<MessageResponse>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void action(MessageResponse messageResponse) {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void sleepTime(int time) {
|
||||||
|
try {
|
||||||
|
// 添加延迟
|
||||||
|
Thread.sleep(time);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,315 @@
|
||||||
|
package com.robotcm.taurus.robot;
|
||||||
|
|
||||||
|
import com.robotcm.taurus.client.MessageResponse;
|
||||||
|
import com.robotcm.taurus.client.SocketCode;
|
||||||
|
import com.robotcm.taurus.client.TaurusClient;
|
||||||
|
import com.robotcm.taurus.client.business.AccountBusiness;
|
||||||
|
import com.robotcm.taurus.client.business.GroupRoomBusiness;
|
||||||
|
import com.robotcm.taurus.newRobot.jiqiren;
|
||||||
|
import com.robotcm.taurus.util.ROBOTEventType;
|
||||||
|
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.util.ICallback;
|
||||||
|
import com.taurus.core.util.Logger;
|
||||||
|
import redis.clients.jedis.Jedis;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class RobotUser {
|
||||||
|
|
||||||
|
public boolean isconnect = false; //是否连接上
|
||||||
|
|
||||||
|
public boolean isLogin = false;
|
||||||
|
|
||||||
|
private final static Logger log;
|
||||||
|
|
||||||
|
static {
|
||||||
|
log = Logger.getLogger(RobotUser.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int robotid;
|
||||||
|
public String password;
|
||||||
|
public int status; //工作状态 0,1:等待,2:干活
|
||||||
|
|
||||||
|
public String gameHost;
|
||||||
|
public String gamePort;
|
||||||
|
|
||||||
|
public int robotGroupid;
|
||||||
|
public int robotPid;
|
||||||
|
|
||||||
|
|
||||||
|
private String token;
|
||||||
|
|
||||||
|
public int intoRoomTime; //进入房间时间戳
|
||||||
|
|
||||||
|
public TaurusClient client = null;
|
||||||
|
|
||||||
|
private String loginsession;
|
||||||
|
|
||||||
|
public void setisconnect(boolean isconnect) {
|
||||||
|
this.isconnect = isconnect;
|
||||||
|
}
|
||||||
|
public boolean getisconnect() {
|
||||||
|
return isconnect;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRobotGroupid(int robotGroupid) {
|
||||||
|
this.robotGroupid = robotGroupid;
|
||||||
|
}
|
||||||
|
public int getRobotGroupid() {
|
||||||
|
return robotGroupid;
|
||||||
|
}
|
||||||
|
public void setRobotPid(int robotPid) {
|
||||||
|
this.robotPid = robotPid;
|
||||||
|
}
|
||||||
|
public int getRobotPid() {
|
||||||
|
return robotPid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRobotid(int robotid) {
|
||||||
|
this.robotid = robotid;
|
||||||
|
}
|
||||||
|
public int getRobotid() {
|
||||||
|
return robotid;
|
||||||
|
}
|
||||||
|
public void setStatus(int status) {
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
public int getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIntoRoomTime(int intoRoomTime) {
|
||||||
|
this.intoRoomTime = intoRoomTime;
|
||||||
|
}
|
||||||
|
public int getIntoRoomTime() {
|
||||||
|
return intoRoomTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLoginsession(String loginsession) {
|
||||||
|
this.loginsession = loginsession;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLoginsession() {
|
||||||
|
return loginsession;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGameHost(String gameHost) {
|
||||||
|
this.gameHost = gameHost;
|
||||||
|
}
|
||||||
|
public String getGameHost() {
|
||||||
|
return gameHost;
|
||||||
|
}
|
||||||
|
public void setGamePort(String gamePort) {
|
||||||
|
this.gamePort = gamePort;
|
||||||
|
}
|
||||||
|
public String getGamePort() {
|
||||||
|
return gamePort;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 机器人工作
|
||||||
|
*/
|
||||||
|
public void working(){
|
||||||
|
//监听client
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 机器人登录
|
||||||
|
*/
|
||||||
|
public void login(){
|
||||||
|
System.out.println("login:"+robotid);
|
||||||
|
ITObject object = null;
|
||||||
|
AccountBusiness accountBusiness = null;
|
||||||
|
accountBusiness = new AccountBusiness();
|
||||||
|
try {
|
||||||
|
object = accountBusiness.idPasswordLogin(robotid, getPassword());
|
||||||
|
if (object != null) {
|
||||||
|
System.out.println("login:"+object);
|
||||||
|
ITObject validate = TObject.newInstance();
|
||||||
|
validate.putString("token", object.getString("token"));
|
||||||
|
token = object.getString("token");
|
||||||
|
loginsession = accountBusiness.getSession();
|
||||||
|
if (loginsession != null) {
|
||||||
|
this.isLogin = true;
|
||||||
|
setLoginsession(loginsession);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void connectGame(){
|
||||||
|
if(isLogin){
|
||||||
|
if(client==null){
|
||||||
|
client = new TaurusClient(gameHost+":"+gamePort, String.valueOf(robotid), TaurusClient.ConnectionProtocol.Tcp);
|
||||||
|
client.setSession(loginsession);
|
||||||
|
client.connect();
|
||||||
|
setisconnect(client.isConnected());
|
||||||
|
}else{
|
||||||
|
if(client.isConnected()){
|
||||||
|
this.isconnect = true;
|
||||||
|
}else{
|
||||||
|
client.connect();
|
||||||
|
setisconnect(client.isConnected());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重连
|
||||||
|
*/
|
||||||
|
public void renconnect(){
|
||||||
|
System.out.println("renconnect:"+robotid);
|
||||||
|
// client.connect();
|
||||||
|
if(client!=null){
|
||||||
|
if(client.isConnected()){
|
||||||
|
client.connect();
|
||||||
|
setisconnect(client.isConnected());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检测是否链接断开
|
||||||
|
*/
|
||||||
|
public void checkIsConnect(){
|
||||||
|
if(isLogin&&client!=null){
|
||||||
|
System.out.println("connectid:"+client.getId());
|
||||||
|
setisconnect(client.isConnected());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 退出 房间
|
||||||
|
*/
|
||||||
|
public void logoutRoom(){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加入房间
|
||||||
|
*/
|
||||||
|
public void joinRoom(){
|
||||||
|
System.out.println("joinRoom:"+robotid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建房间
|
||||||
|
*/
|
||||||
|
public void createRoom(){
|
||||||
|
System.out.println("createRoom:"+robotid);
|
||||||
|
//1、检测有多少个机器人在等待
|
||||||
|
Jedis jedis2 = Redis.use("group1_db2").getJedis();
|
||||||
|
try {
|
||||||
|
String robotskey = "readycreate{"+getRobotGroupid()+"}:play:"+getRobotPid();
|
||||||
|
Set<String> robots = jedis2.keys(robotskey);
|
||||||
|
System.out.println("robotskey:"+robotskey);
|
||||||
|
System.out.println("robots:"+robots);
|
||||||
|
if(robots.size()<2){
|
||||||
|
//创建房间
|
||||||
|
Object room = GroupRoomBusiness.matchRoom(robotGroupid, robotPid, loginsession, "Android", false);
|
||||||
|
|
||||||
|
System.out.println("room:"+room);
|
||||||
|
String roomKey = String.valueOf(room);
|
||||||
|
ITObject roomInfos = GroupRoomBusiness.joinRoom(robotGroupid, roomKey, loginsession, null);
|
||||||
|
System.out.println("roomInfos:"+roomInfos);
|
||||||
|
ready();
|
||||||
|
//doJoinRoom(robotid);
|
||||||
|
this.intoRoomTime = Integer.parseInt((System.currentTimeMillis() + "").substring(0, 10));
|
||||||
|
jianting(this.client);
|
||||||
|
setStatus(ROBOTEventType.ROBOT_INTOROOM_READY);
|
||||||
|
}
|
||||||
|
}catch (Exception e) {
|
||||||
|
log.error("joinRoomOrCreateRoom"+e.getMessage());
|
||||||
|
}finally {
|
||||||
|
jedis2.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void jianting(TaurusClient client){
|
||||||
|
client.addEventListener(TaurusClient.NetClientEvent.OnEvent, new IEventListener() {
|
||||||
|
@Override
|
||||||
|
public void handleEvent(Event event) {
|
||||||
|
System.out.println("addList event:"+event);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
client.addEventListener(TaurusClient.NetClientEvent.Connect, new IEventListener() {
|
||||||
|
@Override
|
||||||
|
public void handleEvent(Event event) {
|
||||||
|
SocketCode code = (SocketCode) event.getParameter("code");
|
||||||
|
System.out.println("code:"+code);
|
||||||
|
if (code == SocketCode.Connect) {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加入房间方法
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String doJoinRoom(int playerId) {
|
||||||
|
try {
|
||||||
|
// 添加2秒延迟
|
||||||
|
Thread.sleep(5000);
|
||||||
|
|
||||||
|
ITObject params = TObject.newInstance();
|
||||||
|
params.putString("session", loginsession + "," + token);
|
||||||
|
|
||||||
|
client.send("1002", params, response -> {
|
||||||
|
ITObject obj = response.messageData.param.getTObject("tableInfo");
|
||||||
|
});
|
||||||
|
return "ok";
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String ready() {
|
||||||
|
try {
|
||||||
|
// 添加1秒延迟
|
||||||
|
Thread.sleep(1000);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ITObject params = TObject.newInstance();
|
||||||
|
params.putString("session", loginsession + "," + token);
|
||||||
|
|
||||||
|
client.send("1003", params, new ICallback<MessageResponse>() {
|
||||||
|
@Override
|
||||||
|
public void action(MessageResponse messageResponse) {
|
||||||
|
System.out.println("ready");
|
||||||
|
System.out.println("params:"+params);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
package com.robotcm.taurus.util;
|
||||||
|
|
||||||
|
public enum CardConfig {
|
||||||
|
DAN( "单牌", Config.TYPE_DANPAI, 1, 1, 1, 1, 0),
|
||||||
|
DUIZI( "单对子", Config.TYPE_DUIZI, 2, 2, 2, 2, 0),
|
||||||
|
SHUNZI( "顺子", Config.TYPE_SHUNZI, 5, 1, 5, 12, 0),
|
||||||
|
LIANDUI( "连对", Config.TYPE_LIANDUI, 3, 2, 6, 20, 0),
|
||||||
|
SAN_2( "三带二", Config.TYPE_3_2, 1, 3, 3, 5, 2),
|
||||||
|
|
||||||
|
|
||||||
|
SAN_BU_DAI( "三不带", Config.TYPE_BU_DAI, 1, 3, 3, 3, 0),
|
||||||
|
|
||||||
|
FEIJI( "飞机", Config.TYPE_FEIJI, 2, 3, 6, 16, 2),
|
||||||
|
|
||||||
|
FEIJI_DAI_DAN( "飞机带单", Config.TYPE_FEIJI_DAI_DAN, 2, 3, 6, 16, 1),
|
||||||
|
|
||||||
|
FEIJI_DAI_SHUANG( "飞机带双", Config.TYPE_FEIJI_DAI_SHUANG, 2, 3, 6, 16, 4),
|
||||||
|
|
||||||
|
|
||||||
|
SI_2( "四带二单", Config.TYPE_4_2, 1, 4, 5, 6, -1),
|
||||||
|
|
||||||
|
SI_1DUI("四带1对", Config.TYPE_4_2_dui, 1, 4, 5, 6, 2),
|
||||||
|
|
||||||
|
ZHA( "炸弹", Config.TYPE_ZHA, 1, 4, 4, 4, 0),
|
||||||
|
|
||||||
|
TYPE_ROCKET("火箭", Config.TYPE_ROCKET, 1, 2, 2, 2, 0),
|
||||||
|
|
||||||
|
SAN_1("三带一", Config.TYPE_3_1,1, 3, 3, 4, 1);
|
||||||
|
|
||||||
|
|
||||||
|
public final int type;
|
||||||
|
public final String name;
|
||||||
|
public final int min_len;
|
||||||
|
public final int repeat_num;
|
||||||
|
public final int min_card_num;
|
||||||
|
public final int max_card_num;
|
||||||
|
public final int with_card_num;
|
||||||
|
|
||||||
|
|
||||||
|
private CardConfig(String name, int type, int min_len, int repeat_num, int min_card_num, int max_card_num, int with_card_num) {
|
||||||
|
this.name = name;
|
||||||
|
this.type = type;
|
||||||
|
|
||||||
|
this.min_len = min_len;
|
||||||
|
this.repeat_num = repeat_num;
|
||||||
|
this.min_card_num = min_card_num;
|
||||||
|
this.max_card_num = max_card_num;
|
||||||
|
this.with_card_num = with_card_num;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
package com.robotcm.taurus.util;
|
||||||
|
|
||||||
|
import com.taurus.core.entity.ITArray;
|
||||||
|
import com.taurus.core.entity.ITObject;
|
||||||
|
import com.taurus.core.entity.TObject;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
public class CardGroup {
|
||||||
|
/**
|
||||||
|
* 当前type
|
||||||
|
*/
|
||||||
|
public CardConfig config;
|
||||||
|
|
||||||
|
public int min_card;
|
||||||
|
/**
|
||||||
|
* 长度
|
||||||
|
*/
|
||||||
|
public int len = 1;
|
||||||
|
|
||||||
|
public List<CardObj> card_list;
|
||||||
|
|
||||||
|
public ITArray card_list_mp;
|
||||||
|
|
||||||
|
public ITObject toObject(boolean card_size) {
|
||||||
|
ITObject obj = TObject.newInstance();
|
||||||
|
// if (card_size) {
|
||||||
|
obj.putInt("card_size", card_list.size());
|
||||||
|
// } else {
|
||||||
|
obj.putTArray("card_list", card_list_mp);
|
||||||
|
// }
|
||||||
|
|
||||||
|
obj.putInt("type", config.type);
|
||||||
|
obj.putInt("min_card", min_card);
|
||||||
|
obj.putInt("len", len);
|
||||||
|
System.out.println("出牌牌型具体" + obj);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return "{type:" + config + " min_card:" + min_card + " len:" + len + " list:" + card_list + "}";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
package com.robotcm.taurus.util;
|
||||||
|
|
||||||
|
public class CardObj implements Comparable<CardObj>{
|
||||||
|
public int card;
|
||||||
|
public int cardMod;
|
||||||
|
|
||||||
|
public CardObj(int card) {
|
||||||
|
this.card = card;
|
||||||
|
this.cardMod = card % 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(CardObj paramT) {
|
||||||
|
return cardMod == paramT.cardMod ? 0 : cardMod < paramT.cardMod ? -1 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return card + "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,378 @@
|
||||||
|
package com.robotcm.taurus.util;
|
||||||
|
|
||||||
|
import com.taurus.core.entity.ITArray;
|
||||||
|
import com.taurus.core.entity.TArray;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class CardUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从手牌中找出最大的单牌
|
||||||
|
*
|
||||||
|
* @param handCards 手牌列表
|
||||||
|
* @return 最大的单牌,如果没有单牌则返回null
|
||||||
|
*/
|
||||||
|
public static CardObj findMaxSingleCard(List<CardObj> handCards) {
|
||||||
|
if (handCards == null || handCards.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
handCards.sort((c1, c2) -> c2.cardMod - c1.cardMod);
|
||||||
|
return handCards.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list to TArray
|
||||||
|
*
|
||||||
|
* @param list
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static final ITArray toTArray(List<CardObj> list) {
|
||||||
|
ITArray result = new TArray();
|
||||||
|
for (CardObj card : list) {
|
||||||
|
result.addInt(card.card);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final ITArray toTArray1(CardObj list) {
|
||||||
|
ITArray result = new TArray();
|
||||||
|
result.addInt(list.card);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final ITArray maJiangToTArray(List<Integer> list) {
|
||||||
|
ITArray result = new TArray();
|
||||||
|
for (Integer integer : list) {
|
||||||
|
result.addInt(integer);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final CardObj getCard(int eventCard, List<CardObj> cardList) {
|
||||||
|
for (CardObj card : cardList) {
|
||||||
|
if (card.cardMod == eventCard) {
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final CardObj getCard1(int eventCard, List<CardObj> cardList) {
|
||||||
|
for (CardObj card : cardList) {
|
||||||
|
if (card.card == eventCard) {
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检测牌数量
|
||||||
|
*
|
||||||
|
* @param eventCard
|
||||||
|
* @param cardList
|
||||||
|
* @param num
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static final boolean checkCard(int eventCard, List<CardObj> cardList, int num) {
|
||||||
|
int result = 0;
|
||||||
|
for (CardObj card : cardList) {
|
||||||
|
if (card.cardMod == eventCard) {
|
||||||
|
result++;
|
||||||
|
if (result == num)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final boolean checkGoodCard(int eventCard, List<CardObj> cardList, int num) {
|
||||||
|
int result = 0;
|
||||||
|
for (CardObj card : cardList) {
|
||||||
|
if (card.cardMod == eventCard) {
|
||||||
|
result++;
|
||||||
|
if (result >= num)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final boolean checkShunZi(int eventCard, List<CardObj> cardList) {
|
||||||
|
int result = 0;
|
||||||
|
if (checkGoodCard(eventCard + 1, cardList, 1)) {
|
||||||
|
result++;
|
||||||
|
if (checkGoodCard(eventCard + 2, cardList, 1)) {
|
||||||
|
result++;
|
||||||
|
if (checkGoodCard(eventCard + 3, cardList, 1)) {
|
||||||
|
result++;
|
||||||
|
if (checkGoodCard(eventCard + 4, cardList, 1)) {
|
||||||
|
result++;
|
||||||
|
if (checkGoodCard(eventCard + 5, cardList, 1)) {
|
||||||
|
result++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (checkGoodCard(eventCard - 1, cardList, 1)) {
|
||||||
|
result++;
|
||||||
|
if (checkGoodCard(eventCard - 2, cardList, 1)) {
|
||||||
|
result++;
|
||||||
|
if (checkGoodCard(eventCard - 3, cardList, 1)) {
|
||||||
|
result++;
|
||||||
|
if (checkGoodCard(eventCard - 4, cardList, 1)) {
|
||||||
|
result++;
|
||||||
|
if (checkGoodCard(eventCard - 5, cardList, 1)) {
|
||||||
|
result++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result >= 4) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final boolean checkSevenShunzi(int eventCard, List<CardObj> cardList) {
|
||||||
|
int result = 0;
|
||||||
|
if (checkGoodCard(eventCard + 1, cardList, 1)) {
|
||||||
|
result++;
|
||||||
|
if (checkGoodCard(eventCard + 2, cardList, 1)) {
|
||||||
|
result++;
|
||||||
|
if (checkGoodCard(eventCard + 3, cardList, 1)) {
|
||||||
|
result++;
|
||||||
|
if (checkGoodCard(eventCard + 4, cardList, 1)) {
|
||||||
|
result++;
|
||||||
|
if (checkGoodCard(eventCard + 5, cardList, 1)) {
|
||||||
|
result++;
|
||||||
|
if (checkGoodCard(eventCard + 6, cardList, 1)) {
|
||||||
|
result++;
|
||||||
|
if (checkGoodCard(eventCard + 7, cardList, 1)) {
|
||||||
|
result++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (checkGoodCard(eventCard - 1, cardList, 1)) {
|
||||||
|
result++;
|
||||||
|
if (checkGoodCard(eventCard - 2, cardList, 1)) {
|
||||||
|
result++;
|
||||||
|
if (checkGoodCard(eventCard - 3, cardList, 1)) {
|
||||||
|
result++;
|
||||||
|
if (checkGoodCard(eventCard - 4, cardList, 1)) {
|
||||||
|
result++;
|
||||||
|
if (checkGoodCard(eventCard - 5, cardList, 1)) {
|
||||||
|
result++;
|
||||||
|
if (checkGoodCard(eventCard - 6, cardList, 1)) {
|
||||||
|
result++;
|
||||||
|
if (checkGoodCard(eventCard - 7, cardList, 1)) {
|
||||||
|
result++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result >= 6) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final boolean checkFenJi(int eventCard, List<CardObj> cardList) {
|
||||||
|
int result = 0;
|
||||||
|
if (checkGoodCard(eventCard, cardList, 2)) {
|
||||||
|
result++;
|
||||||
|
if (checkGoodCard(eventCard + 1, cardList, 3)) {
|
||||||
|
result++;
|
||||||
|
}
|
||||||
|
if (checkGoodCard(eventCard - 1, cardList, 3)) {
|
||||||
|
result++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result >= 2) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final boolean checkFourDui(int eventCard, List<CardObj> cardList) {
|
||||||
|
int result = 0;
|
||||||
|
if (checkGoodCard(eventCard, cardList, 1)) {
|
||||||
|
result++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkGoodCard(eventCard + 1, cardList, 2)) {
|
||||||
|
result++;
|
||||||
|
if (checkGoodCard(eventCard + 2, cardList, 2)) {
|
||||||
|
result++;
|
||||||
|
if (checkGoodCard(eventCard + 3, cardList, 2)) {
|
||||||
|
result++;
|
||||||
|
if (checkGoodCard(eventCard + 4, cardList, 2)) {
|
||||||
|
result++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (checkGoodCard(eventCard - 1, cardList, 2)) {
|
||||||
|
result++;
|
||||||
|
if (checkGoodCard(eventCard - 2, cardList, 2)) {
|
||||||
|
result++;
|
||||||
|
if (checkGoodCard(eventCard - 3, cardList, 2)) {
|
||||||
|
result++;
|
||||||
|
if (checkGoodCard(eventCard - 4, cardList, 2)) {
|
||||||
|
result++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result >= 4) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final boolean checkQPai(int eventCard, List<CardObj> cardList) {
|
||||||
|
int result = 0;
|
||||||
|
if (eventCard >= 12) {
|
||||||
|
result++;
|
||||||
|
for (CardObj card : cardList) {
|
||||||
|
if (card.cardMod >= 12) {
|
||||||
|
result++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result >= 5) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TArray to list
|
||||||
|
*
|
||||||
|
* @param list
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static final List<CardObj> toList(ITArray list) {
|
||||||
|
List<CardObj> tem = new ArrayList<CardObj>();
|
||||||
|
for (int i = 0; i < list.size(); ++i) {
|
||||||
|
tem.add(new CardObj(list.getInt(i)));
|
||||||
|
}
|
||||||
|
return tem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取每张牌的数量MAP
|
||||||
|
*
|
||||||
|
* @param cardList
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static final Map<Integer, Integer> getCardNumMap(List<CardObj> cardList) {
|
||||||
|
Map<Integer, Integer> result = new HashMap<Integer, Integer>();
|
||||||
|
for (CardObj card : cardList) {
|
||||||
|
if (!result.containsKey(card.cardMod)) {
|
||||||
|
result.put(card.cardMod, 1);
|
||||||
|
} else {
|
||||||
|
int num = result.get(card.cardMod);
|
||||||
|
result.put(card.cardMod, (num + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取每张牌的数量MAP
|
||||||
|
*
|
||||||
|
* @param cardList
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static final void getCardNumMap(Map<Integer, Integer> result, List<CardObj> cardList) {
|
||||||
|
result.clear();
|
||||||
|
for (CardObj card : cardList) {
|
||||||
|
if (!result.containsKey(card.cardMod)) {
|
||||||
|
result.put(card.cardMod, 1);
|
||||||
|
} else {
|
||||||
|
int num = result.get(card.cardMod);
|
||||||
|
result.put(card.cardMod, (num + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final void getCardNumMap(Map<Integer, Integer> result, List<CardObj> cardList, CardObj tempCard) {
|
||||||
|
result.clear();
|
||||||
|
result.put(tempCard.cardMod, 1);
|
||||||
|
for (CardObj card : cardList) {
|
||||||
|
if (!result.containsKey(card.cardMod)) {
|
||||||
|
result.put(card.cardMod, 1);
|
||||||
|
} else {
|
||||||
|
int num = result.get(card.cardMod);
|
||||||
|
result.put(card.cardMod, (num + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取每张牌的数量MAP
|
||||||
|
*
|
||||||
|
* @param cardList
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static final Map<Integer, List<CardObj>> getCardListMap(List<CardObj> cardList) {
|
||||||
|
Map<Integer, List<CardObj>> result = new HashMap<Integer, List<CardObj>>();
|
||||||
|
for (CardObj card : cardList) {
|
||||||
|
if (!result.containsKey(card.cardMod)) {
|
||||||
|
List<CardObj> list = new ArrayList<CardObj>();
|
||||||
|
list.add(card);
|
||||||
|
result.put(card.cardMod, list);
|
||||||
|
} else {
|
||||||
|
List<CardObj> list = result.get(card.cardMod);
|
||||||
|
list.add(card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static public void removeCard(List<CardObj> cardList, List<CardObj> cards) {
|
||||||
|
for (int i = 0; i < cards.size(); i++) {
|
||||||
|
for (int j = 0; j < cardList.size(); j++) {
|
||||||
|
if (cardList.get(j).card == cards.get(i).card) {
|
||||||
|
cardList.remove(j);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static public void removeCard1(List<Integer> cardList, int card, int count) {
|
||||||
|
int curCount = 0;
|
||||||
|
for (int i = 0; i < cardList.size(); i++) {
|
||||||
|
if (count == curCount) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (cardList.get(i) == card) {
|
||||||
|
|
||||||
|
cardList.remove(i);
|
||||||
|
i--;
|
||||||
|
curCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,768 @@
|
||||||
|
package com.robotcm.taurus.util;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class ChangshaMahjongAI {
|
||||||
|
|
||||||
|
// 长沙麻将特殊规则:二五八将
|
||||||
|
private static final Set<Integer> JIANG_PAIS;
|
||||||
|
|
||||||
|
static {
|
||||||
|
Set<Integer> jiangSet = new HashSet<>();
|
||||||
|
jiangSet.add(2);
|
||||||
|
jiangSet.add(5);
|
||||||
|
jiangSet.add(8);
|
||||||
|
JIANG_PAIS = Collections.unmodifiableSet(jiangSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 缓存优化
|
||||||
|
private Map<String, Double> scoreCache = new HashMap<>();
|
||||||
|
private Map<String, List<Integer>> tingCache = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 玩家状态类,包含所有相关信息
|
||||||
|
*/
|
||||||
|
public static class PlayerState {
|
||||||
|
public List<Integer> handCards = new ArrayList<>();
|
||||||
|
public List<List<Integer>> pongGroups = new ArrayList<>();
|
||||||
|
public List<List<Integer>> gangGroups = new ArrayList<>();
|
||||||
|
public List<List<Integer>> chiGroups = new ArrayList<>();
|
||||||
|
public boolean isZhuang = false;
|
||||||
|
|
||||||
|
public void addPongGroup(List<Integer> group) {
|
||||||
|
pongGroups.add(new ArrayList<>(group));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addGangGroup(List<Integer> group) {
|
||||||
|
gangGroups.add(new ArrayList<>(group));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addChiGroup(List<Integer> group) {
|
||||||
|
chiGroups.add(new ArrayList<>(group));
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPongGroupCount() {
|
||||||
|
return pongGroups.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getGangGroupCount() {
|
||||||
|
return gangGroups.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getChiGroupCount() {
|
||||||
|
return chiGroups.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTotalGroupCount() {
|
||||||
|
return getPongGroupCount() + getGangGroupCount() + getChiGroupCount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
System.out.println("=== 长沙麻将AI测试(含碰杠吃牌) ===");
|
||||||
|
ChangshaMahjongAI ai = new ChangshaMahjongAI();
|
||||||
|
|
||||||
|
// 测试1:普通14张牌
|
||||||
|
System.out.println("\n=== 测试1:普通14张牌 ===");
|
||||||
|
PlayerState state1 = new PlayerState();
|
||||||
|
state1.handCards = Arrays.asList(204, 206, 205, 205, 203, 109, 108, 107, 107, 106, 103, 103, 102, 101);
|
||||||
|
System.out.println("手牌: " + ai.formatCards(state1.handCards));
|
||||||
|
int discard1 = ai.findBestDiscard(state1);
|
||||||
|
System.out.println("推荐出牌: " + ai.formatCard(discard1));
|
||||||
|
//要打6筒 ======================================================================================================
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主方法:根据完整玩家状态推荐出牌
|
||||||
|
*/
|
||||||
|
public int findBestDiscard(PlayerState state) {
|
||||||
|
System.out.println("\n=== 长沙麻将AI出牌分析 ===");
|
||||||
|
System.out.println("手牌(" + state.handCards.size() + "张): " + formatCards(state.handCards));
|
||||||
|
|
||||||
|
int pongGroups = state.getPongGroupCount();
|
||||||
|
int gangGroups = state.getGangGroupCount();
|
||||||
|
int chiGroups = state.getChiGroupCount();
|
||||||
|
|
||||||
|
if (pongGroups > 0) {
|
||||||
|
System.out.println("碰牌组数: " + pongGroups);
|
||||||
|
}
|
||||||
|
if (gangGroups > 0) {
|
||||||
|
System.out.println("杠牌组数: " + gangGroups);
|
||||||
|
}
|
||||||
|
if (chiGroups > 0) {
|
||||||
|
System.out.println("吃牌组数: " + chiGroups);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清空缓存
|
||||||
|
scoreCache.clear();
|
||||||
|
tingCache.clear();
|
||||||
|
|
||||||
|
long startTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
// 获取可出的牌(手牌中的唯一牌)
|
||||||
|
Map<Integer, DiscardResult> results = new HashMap<>();
|
||||||
|
Set<Integer> uniqueCards = new HashSet<>(state.handCards);
|
||||||
|
|
||||||
|
// 如果只剩一张牌,直接返回
|
||||||
|
if (state.handCards.size() == 1) {
|
||||||
|
int onlyCard = state.handCards.get(0);
|
||||||
|
System.out.println("只剩一张牌,必须出: " + formatCard(onlyCard));
|
||||||
|
return onlyCard;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int card : uniqueCards) {
|
||||||
|
// 模拟打出一张牌后的状态
|
||||||
|
PlayerState newState = simulateDiscard(state, card);
|
||||||
|
DiscardResult result = evaluateDiscardWithState(card, newState);
|
||||||
|
results.put(card, result);
|
||||||
|
|
||||||
|
System.out.printf("打出 %s: 评分=%.3f (听牌=%d张)%n",
|
||||||
|
formatCard(card), result.score, result.tingCards.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 找出最佳出牌
|
||||||
|
int bestCard = -1;
|
||||||
|
double bestScore = Double.NEGATIVE_INFINITY;
|
||||||
|
|
||||||
|
for (Map.Entry<Integer, DiscardResult> entry : results.entrySet()) {
|
||||||
|
if (entry.getValue().score > bestScore) {
|
||||||
|
bestScore = entry.getValue().score;
|
||||||
|
bestCard = entry.getKey();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
long endTime = System.currentTimeMillis();
|
||||||
|
System.out.printf("分析耗时: %dms%n", (endTime - startTime));
|
||||||
|
|
||||||
|
if (bestCard != -1) {
|
||||||
|
DiscardResult bestResult = results.get(bestCard);
|
||||||
|
System.out.println("\n🎯 AI推荐出牌: " + formatCard(bestCard));
|
||||||
|
if (!bestResult.tingCards.isEmpty()) {
|
||||||
|
System.out.println("✅ 听牌,可听 " + bestResult.tingCards.size() + " 张牌:");
|
||||||
|
List<String> tingCardsFormatted = bestResult.tingCards.stream()
|
||||||
|
.map(this::formatCard)
|
||||||
|
.sorted()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
System.out.println(" " + String.join(" ", tingCardsFormatted));
|
||||||
|
}
|
||||||
|
System.out.println("综合评分: " + String.format("%.3f", bestResult.score));
|
||||||
|
}
|
||||||
|
|
||||||
|
return bestCard;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模拟打出一张牌
|
||||||
|
*/
|
||||||
|
private PlayerState simulateDiscard(PlayerState originalState, int discardCard) {
|
||||||
|
PlayerState newState = new PlayerState();
|
||||||
|
|
||||||
|
// 复制手牌并移除要打的牌
|
||||||
|
newState.handCards = new ArrayList<>(originalState.handCards);
|
||||||
|
newState.handCards.remove((Integer) discardCard);
|
||||||
|
|
||||||
|
// 复制碰牌组
|
||||||
|
for (List<Integer> group : originalState.pongGroups) {
|
||||||
|
newState.pongGroups.add(new ArrayList<>(group));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 复制杠牌组
|
||||||
|
for (List<Integer> group : originalState.gangGroups) {
|
||||||
|
newState.gangGroups.add(new ArrayList<>(group));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 复制吃牌组
|
||||||
|
for (List<Integer> group : originalState.chiGroups) {
|
||||||
|
newState.chiGroups.add(new ArrayList<>(group));
|
||||||
|
}
|
||||||
|
|
||||||
|
newState.isZhuang = originalState.isZhuang;
|
||||||
|
|
||||||
|
return newState;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基于完整状态评估出牌 - 重点优化
|
||||||
|
*/
|
||||||
|
private DiscardResult evaluateDiscardWithState(int discardedCard, PlayerState state) {
|
||||||
|
String stateKey = getStateKey(state);
|
||||||
|
if (scoreCache.containsKey(stateKey)) {
|
||||||
|
List<Integer> tingCards = checkTingCardsWithState(state);
|
||||||
|
return new DiscardResult(scoreCache.get(stateKey), tingCards, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
double totalScore = 0.0;
|
||||||
|
|
||||||
|
// 1. 核心:评估保留对子的价值(特别是258将牌)
|
||||||
|
totalScore += evaluatePairRetention(state.handCards);
|
||||||
|
|
||||||
|
// 2. 评估花色分布的合理性
|
||||||
|
totalScore += evaluateSuitDistribution(state.handCards);
|
||||||
|
|
||||||
|
// 3. 评估牌型完整性
|
||||||
|
totalScore += evaluateHandStructure(state.handCards);
|
||||||
|
|
||||||
|
// 4. 检查听牌
|
||||||
|
List<Integer> tingCards = checkTingCardsWithState(state);
|
||||||
|
if (!tingCards.isEmpty()) {
|
||||||
|
totalScore += 50.0; // 听牌大幅加分
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 安全评估 - 打出牌的安全性
|
||||||
|
totalScore += evaluateSafety(discardedCard);
|
||||||
|
|
||||||
|
// 6. 已碰杠吃牌的加分
|
||||||
|
totalScore += evaluateGroupBonus(state);
|
||||||
|
|
||||||
|
scoreCache.put(stateKey, totalScore);
|
||||||
|
return new DiscardResult(totalScore, tingCards, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 评估保留对子的价值 - 新方法,重点关注
|
||||||
|
*/
|
||||||
|
private double evaluatePairRetention(List<Integer> hand) {
|
||||||
|
double score = 0.0;
|
||||||
|
|
||||||
|
// 统计对子
|
||||||
|
Map<Integer, Integer> countMap = new HashMap<>();
|
||||||
|
for (int card : hand) {
|
||||||
|
countMap.put(card, countMap.getOrDefault(card, 0) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查258将牌的对子
|
||||||
|
boolean has258Pair = false;
|
||||||
|
for (Map.Entry<Integer, Integer> entry : countMap.entrySet()) {
|
||||||
|
if (entry.getValue() >= 2) {
|
||||||
|
int cardValue = getCardValue(entry.getKey());
|
||||||
|
if (JIANG_PAIS.contains(cardValue)) {
|
||||||
|
score += 30.0; // 258将牌对子非常重要
|
||||||
|
has258Pair = true;
|
||||||
|
} else {
|
||||||
|
score += 10.0; // 普通对子也有价值
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有258对子,需要更加注意保留258将牌
|
||||||
|
if (!has258Pair) {
|
||||||
|
for (int card : hand) {
|
||||||
|
if (JIANG_PAIS.contains(getCardValue(card))) {
|
||||||
|
score += 5.0; // 单张258将牌也要保留
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 评估花色分布 - 新方法
|
||||||
|
*/
|
||||||
|
private double evaluateSuitDistribution(List<Integer> hand) {
|
||||||
|
double score = 0.0;
|
||||||
|
|
||||||
|
// 按花色分组
|
||||||
|
Map<Integer, List<Integer>> suitGroups = new HashMap<>();
|
||||||
|
for (int card : hand) {
|
||||||
|
int suit = getCardType(card);
|
||||||
|
suitGroups.putIfAbsent(suit, new ArrayList<>());
|
||||||
|
suitGroups.get(suit).add(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 评估每种花色的牌数
|
||||||
|
for (Map.Entry<Integer, List<Integer>> entry : suitGroups.entrySet()) {
|
||||||
|
int suit = entry.getKey();
|
||||||
|
int count = entry.getValue().size();
|
||||||
|
|
||||||
|
// 万筒条花色
|
||||||
|
if (suit == 1 || suit == 2 || suit == 3) {
|
||||||
|
if (count >= 8) {
|
||||||
|
score += 20.0; // 清一色潜力
|
||||||
|
} else if (count >= 5) {
|
||||||
|
score += 10.0; // 一色牌较多
|
||||||
|
} else if (count == 1) {
|
||||||
|
score -= 5.0; // 单张花色,鼓励打掉
|
||||||
|
} else if (count == 2) {
|
||||||
|
score -= 2.0; // 双张花色,酌情考虑
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查听牌情况
|
||||||
|
*/
|
||||||
|
private List<Integer> checkTingCardsWithState(PlayerState state) {
|
||||||
|
String stateKey = getStateKey(state);
|
||||||
|
if (tingCache.containsKey(stateKey)) {
|
||||||
|
return tingCache.get(stateKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Integer> tingCards = new ArrayList<>();
|
||||||
|
|
||||||
|
int pongGroups = state.getPongGroupCount();
|
||||||
|
int gangGroups = state.getGangGroupCount();
|
||||||
|
int chiGroups = state.getChiGroupCount();
|
||||||
|
|
||||||
|
// 计算已使用的牌数
|
||||||
|
int usedCardsCount = pongGroups * 3 + gangGroups * 4 + chiGroups * 3;
|
||||||
|
|
||||||
|
// 听牌时手牌数量应为:14 - 已使用的牌数 - 1(要胡的牌)
|
||||||
|
int expectedHandCount = 14 - usedCardsCount - 1;
|
||||||
|
|
||||||
|
if (state.handCards.size() != expectedHandCount) {
|
||||||
|
tingCache.put(stateKey, tingCards);
|
||||||
|
return tingCards;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取合理的胡牌候选
|
||||||
|
Set<Integer> possibleHuCards = getReasonableHuCards(state.handCards);
|
||||||
|
|
||||||
|
for (int huCard : possibleHuCards) {
|
||||||
|
if (canHuWithState(state, huCard)) {
|
||||||
|
tingCards.add(huCard);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tingCache.put(stateKey, tingCards);
|
||||||
|
return tingCards;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手牌结构评估 - 优化版
|
||||||
|
*/
|
||||||
|
private double evaluateHandStructure(List<Integer> hand) {
|
||||||
|
if (hand.isEmpty()) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double score = 0.0;
|
||||||
|
List<Integer> sortedHand = new ArrayList<>(hand);
|
||||||
|
Collections.sort(sortedHand);
|
||||||
|
|
||||||
|
// 1. 顺子检查
|
||||||
|
score += findSequences(hand) * 15.0;
|
||||||
|
|
||||||
|
// 2. 刻子/对子检查
|
||||||
|
Map<Integer, Integer> countMap = new HashMap<>();
|
||||||
|
for (int card : hand) {
|
||||||
|
countMap.put(card, countMap.getOrDefault(card, 0) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int count : countMap.values()) {
|
||||||
|
if (count >= 3) {
|
||||||
|
score += 20.0; // 刻子
|
||||||
|
} else if (count == 2) {
|
||||||
|
score += 8.0; // 对子
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 搭子检查
|
||||||
|
score += countDazi(hand) * 5.0;
|
||||||
|
|
||||||
|
// 4. 孤立张检查
|
||||||
|
score += evaluateIsolatedCards(hand);
|
||||||
|
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 孤立张评估
|
||||||
|
*/
|
||||||
|
private double evaluateIsolatedCards(List<Integer> hand) {
|
||||||
|
double score = 0.0;
|
||||||
|
|
||||||
|
// 按花色分组
|
||||||
|
Map<Integer, Set<Integer>> suitValues = new HashMap<>();
|
||||||
|
for (int card : hand) {
|
||||||
|
int suit = getCardType(card);
|
||||||
|
int value = getCardValue(card);
|
||||||
|
suitValues.putIfAbsent(suit, new HashSet<>());
|
||||||
|
suitValues.get(suit).add(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查每张牌是否为孤立张
|
||||||
|
for (int card : hand) {
|
||||||
|
int suit = getCardType(card);
|
||||||
|
int value = getCardValue(card);
|
||||||
|
|
||||||
|
if (suit > 3) continue; // 字牌暂不考虑
|
||||||
|
|
||||||
|
Set<Integer> values = suitValues.get(suit);
|
||||||
|
boolean isIsolated = true;
|
||||||
|
|
||||||
|
// 检查相邻牌
|
||||||
|
for (int neighbor : values) {
|
||||||
|
if (neighbor != value && Math.abs(neighbor - value) <= 2) {
|
||||||
|
isIsolated = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isIsolated) {
|
||||||
|
// 孤立张,根据位置扣分
|
||||||
|
if (value == 1 || value == 9) {
|
||||||
|
score -= 3.0; // 边张孤立
|
||||||
|
} else if (JIANG_PAIS.contains(value)) {
|
||||||
|
score -= 8.0; // 258将牌孤立,鼓励打掉
|
||||||
|
} else {
|
||||||
|
score -= 5.0; // 中间张孤立
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全评估
|
||||||
|
*/
|
||||||
|
private double evaluateSafety(int discardedCard) {
|
||||||
|
int value = getCardValue(discardedCard);
|
||||||
|
int type = getCardType(discardedCard);
|
||||||
|
|
||||||
|
if (type > 3) {
|
||||||
|
return -10.0; // 字牌风险高
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value == 1 || value == 9) {
|
||||||
|
return -3.0; // 边张风险
|
||||||
|
}
|
||||||
|
|
||||||
|
if (JIANG_PAIS.contains(value)) {
|
||||||
|
return -15.0; // 258将牌风险很高
|
||||||
|
}
|
||||||
|
|
||||||
|
// 中间张相对安全
|
||||||
|
return 2.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 胡牌检查
|
||||||
|
*/
|
||||||
|
private boolean canHuWithState(PlayerState state, int huCard) {
|
||||||
|
int totalHandCards = state.handCards.size() + 1;
|
||||||
|
int pongCardsCount = state.getPongGroupCount() * 3;
|
||||||
|
int gangCardsCount = state.getGangGroupCount() * 4;
|
||||||
|
int chiCardsCount = state.getChiGroupCount() * 3;
|
||||||
|
int totalCards = totalHandCards + pongCardsCount + gangCardsCount + chiCardsCount;
|
||||||
|
|
||||||
|
if (totalCards != 14) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Integer> remainingHand = new ArrayList<>(state.handCards);
|
||||||
|
remainingHand.add(huCard);
|
||||||
|
Collections.sort(remainingHand);
|
||||||
|
|
||||||
|
return canHuWith258(remainingHand);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查手牌是否能胡牌(考虑258将)
|
||||||
|
*/
|
||||||
|
private boolean canHuWith258(List<Integer> hand) {
|
||||||
|
if (hand.size() != 2 && hand.size() != 5 && hand.size() != 8 && hand.size() != 11 && hand.size() != 14) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Integer> sortedHand = new ArrayList<>(hand);
|
||||||
|
Collections.sort(sortedHand);
|
||||||
|
|
||||||
|
// 尝试所有可能的将牌
|
||||||
|
for (int i = 0; i < sortedHand.size() - 1; i++) {
|
||||||
|
if (sortedHand.get(i).equals(sortedHand.get(i + 1))) {
|
||||||
|
int cardValue = getCardValue(sortedHand.get(i));
|
||||||
|
if (JIANG_PAIS.contains(cardValue)) {
|
||||||
|
List<Integer> tempHand = new ArrayList<>(sortedHand);
|
||||||
|
tempHand.remove(i);
|
||||||
|
tempHand.remove(i);
|
||||||
|
|
||||||
|
if (canFormMelds(tempHand)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否能组成全部顺子/刻子
|
||||||
|
*/
|
||||||
|
private boolean canFormMelds(List<Integer> hand) {
|
||||||
|
if (hand.isEmpty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Integer> sorted = new ArrayList<>(hand);
|
||||||
|
Collections.sort(sorted);
|
||||||
|
|
||||||
|
// 尝试刻子
|
||||||
|
if (sorted.size() >= 3 &&
|
||||||
|
sorted.get(0).equals(sorted.get(1)) &&
|
||||||
|
sorted.get(1).equals(sorted.get(2))) {
|
||||||
|
List<Integer> newHand = new ArrayList<>(sorted);
|
||||||
|
newHand.remove(0);
|
||||||
|
newHand.remove(0);
|
||||||
|
newHand.remove(0);
|
||||||
|
if (canFormMelds(newHand)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试顺子
|
||||||
|
if (sorted.size() >= 3) {
|
||||||
|
int firstCard = sorted.get(0);
|
||||||
|
int firstType = getCardType(firstCard);
|
||||||
|
int firstValue = getCardValue(firstCard);
|
||||||
|
|
||||||
|
if (firstType <= 3 && firstValue <= 7) {
|
||||||
|
int secondCard = firstCard + 1;
|
||||||
|
int thirdCard = firstCard + 2;
|
||||||
|
|
||||||
|
if (sorted.contains(secondCard) && sorted.contains(thirdCard)) {
|
||||||
|
List<Integer> newHand = new ArrayList<>(sorted);
|
||||||
|
newHand.remove((Integer) firstCard);
|
||||||
|
newHand.remove((Integer) secondCard);
|
||||||
|
newHand.remove((Integer) thirdCard);
|
||||||
|
if (canFormMelds(newHand)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 评估碰杠吃牌的加分
|
||||||
|
*/
|
||||||
|
private double evaluateGroupBonus(PlayerState state) {
|
||||||
|
double bonus = 0.0;
|
||||||
|
|
||||||
|
// 碰牌加分
|
||||||
|
for (List<Integer> pongGroup : state.pongGroups) {
|
||||||
|
if (!pongGroup.isEmpty()) {
|
||||||
|
int card = pongGroup.get(0);
|
||||||
|
int value = getCardValue(card);
|
||||||
|
bonus += 5.0;
|
||||||
|
if (JIANG_PAIS.contains(value)) {
|
||||||
|
bonus += 3.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 杠牌加分
|
||||||
|
for (List<Integer> gangGroup : state.gangGroups) {
|
||||||
|
if (!gangGroup.isEmpty()) {
|
||||||
|
int card = gangGroup.get(0);
|
||||||
|
int value = getCardValue(card);
|
||||||
|
bonus += 10.0;
|
||||||
|
if (JIANG_PAIS.contains(value)) {
|
||||||
|
bonus += 3.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 吃牌加分
|
||||||
|
for (List<Integer> chiGroup : state.chiGroups) {
|
||||||
|
if (!chiGroup.isEmpty()) {
|
||||||
|
bonus += 2.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bonus;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取合理的胡牌候选
|
||||||
|
*/
|
||||||
|
private Set<Integer> getReasonableHuCards(List<Integer> hand) {
|
||||||
|
Set<Integer> possibleCards = new HashSet<>();
|
||||||
|
|
||||||
|
// 现有牌和相邻牌
|
||||||
|
for (int card : hand) {
|
||||||
|
possibleCards.add(card);
|
||||||
|
int type = getCardType(card);
|
||||||
|
int value = getCardValue(card);
|
||||||
|
if (type <= 3) {
|
||||||
|
if (value > 1) possibleCards.add(type * 100 + (value - 1));
|
||||||
|
if (value < 9) possibleCards.add(type * 100 + (value + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 所有258将牌
|
||||||
|
for (int type = 1; type <= 3; type++) {
|
||||||
|
for (int jiang : JIANG_PAIS) {
|
||||||
|
possibleCards.add(type * 100 + jiang);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 限制数量
|
||||||
|
if (possibleCards.size() > 20) {
|
||||||
|
List<Integer> list = new ArrayList<>(possibleCards);
|
||||||
|
Collections.shuffle(list);
|
||||||
|
possibleCards = new HashSet<>(list.subList(0, 20));
|
||||||
|
}
|
||||||
|
|
||||||
|
return possibleCards;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成状态key
|
||||||
|
*/
|
||||||
|
private String getStateKey(PlayerState state) {
|
||||||
|
List<Integer> sortedHand = new ArrayList<>(state.handCards);
|
||||||
|
Collections.sort(sortedHand);
|
||||||
|
|
||||||
|
List<String> pongKeys = new ArrayList<>();
|
||||||
|
for (List<Integer> group : state.pongGroups) {
|
||||||
|
List<Integer> sorted = new ArrayList<>(group);
|
||||||
|
Collections.sort(sorted);
|
||||||
|
pongKeys.add(sorted.toString());
|
||||||
|
}
|
||||||
|
Collections.sort(pongKeys);
|
||||||
|
|
||||||
|
List<String> gangKeys = new ArrayList<>();
|
||||||
|
for (List<Integer> group : state.gangGroups) {
|
||||||
|
List<Integer> sorted = new ArrayList<>(group);
|
||||||
|
Collections.sort(sorted);
|
||||||
|
gangKeys.add(sorted.toString());
|
||||||
|
}
|
||||||
|
Collections.sort(gangKeys);
|
||||||
|
|
||||||
|
List<String> chiKeys = new ArrayList<>();
|
||||||
|
for (List<Integer> group : state.chiGroups) {
|
||||||
|
List<Integer> sorted = new ArrayList<>(group);
|
||||||
|
Collections.sort(sorted);
|
||||||
|
chiKeys.add(sorted.toString());
|
||||||
|
}
|
||||||
|
Collections.sort(chiKeys);
|
||||||
|
|
||||||
|
return sortedHand.toString() + "|" +
|
||||||
|
pongKeys.toString() + "|" +
|
||||||
|
gangKeys.toString() + "|" +
|
||||||
|
chiKeys.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统计顺子数量
|
||||||
|
*/
|
||||||
|
private int findSequences(List<Integer> hand) {
|
||||||
|
int count = 0;
|
||||||
|
List<Integer> temp = new ArrayList<>(hand);
|
||||||
|
temp.removeIf(c -> getCardType(c) > 3);
|
||||||
|
Collections.sort(temp);
|
||||||
|
|
||||||
|
boolean[] used = new boolean[temp.size()];
|
||||||
|
|
||||||
|
for (int i = 0; i <= temp.size() - 3; i++) {
|
||||||
|
if (used[i]) continue;
|
||||||
|
|
||||||
|
int card1 = temp.get(i);
|
||||||
|
int card2 = card1 + 1;
|
||||||
|
int card3 = card1 + 2;
|
||||||
|
|
||||||
|
int idx2 = -1, idx3 = -1;
|
||||||
|
for (int j = i + 1; j < temp.size(); j++) {
|
||||||
|
if (!used[j]) {
|
||||||
|
if (temp.get(j) == card2) idx2 = j;
|
||||||
|
if (temp.get(j) == card3) idx3 = j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (idx2 != -1 && idx3 != -1) {
|
||||||
|
count++;
|
||||||
|
used[i] = true;
|
||||||
|
used[idx2] = true;
|
||||||
|
used[idx3] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统计搭子数量
|
||||||
|
*/
|
||||||
|
private int countDazi(List<Integer> hand) {
|
||||||
|
int count = 0;
|
||||||
|
Set<Integer> unique = new HashSet<>(hand);
|
||||||
|
|
||||||
|
for (int card : unique) {
|
||||||
|
if (getCardType(card) > 3) continue;
|
||||||
|
|
||||||
|
// 对子
|
||||||
|
if (Collections.frequency(hand, card) >= 2) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 连张
|
||||||
|
if (unique.contains(card + 1)) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取牌值
|
||||||
|
*/
|
||||||
|
private int getCardValue(int card) {
|
||||||
|
return card % 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取牌类型
|
||||||
|
*/
|
||||||
|
private int getCardType(int card) {
|
||||||
|
return card / 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化单张牌
|
||||||
|
*/
|
||||||
|
String formatCard(int card) {
|
||||||
|
int type = card / 100;
|
||||||
|
int value = card % 10;
|
||||||
|
switch (type) {
|
||||||
|
case 1: return value + "万";
|
||||||
|
case 2: return value + "筒";
|
||||||
|
case 3: return value + "条";
|
||||||
|
default: return "字牌";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化牌列表
|
||||||
|
*/
|
||||||
|
private String formatCards(List<Integer> cards) {
|
||||||
|
return cards.stream()
|
||||||
|
.sorted()
|
||||||
|
.map(this::formatCard)
|
||||||
|
.collect(Collectors.joining(" "));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 出牌结果类
|
||||||
|
*/
|
||||||
|
private static class DiscardResult {
|
||||||
|
double score;
|
||||||
|
List<Integer> tingCards;
|
||||||
|
int searchDepth;
|
||||||
|
|
||||||
|
DiscardResult(double score, List<Integer> tingCards, int searchDepth) {
|
||||||
|
this.score = score;
|
||||||
|
this.tingCards = tingCards;
|
||||||
|
this.searchDepth = searchDepth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,901 @@
|
||||||
|
package com.robotcm.taurus.util;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class ChangshaWinSplitCard {
|
||||||
|
// 获取数值
|
||||||
|
// 获取数值
|
||||||
|
public static int GetCardValue(int cbCardData) {
|
||||||
|
return cbCardData % 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取花色
|
||||||
|
public static int GetCardColor(int cbCardData) {
|
||||||
|
return cbCardData / 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 扑克转换
|
||||||
|
public static int SwitchToCardData(int cbCardIndex) {
|
||||||
|
int cbCardColor = cbCardIndex / 10;
|
||||||
|
int cbCardValue = cbCardIndex % 10;
|
||||||
|
if (cbCardIndex < 27) { // 9 -> 200 + 1 //10 -> 202 //11 -> 203 // 17 -> 209 // 18 -> 301 19->302 20 ->
|
||||||
|
// 303 26->309
|
||||||
|
if (cbCardValue + (cbCardColor + 1) >= 10) {
|
||||||
|
return (cbCardColor + 2) * 100 + (cbCardValue + (cbCardColor + 1)) % 10 + 1;
|
||||||
|
} else {
|
||||||
|
return (cbCardColor + 1) * 100 + cbCardValue + (cbCardColor + 1);
|
||||||
|
}
|
||||||
|
} else { // 27 -> 400 //28 -> 403 // 29 -> 406 // 30 -> 409 //31 -> 412 // 32 -> 415 //
|
||||||
|
// 33 -> 418
|
||||||
|
if (cbCardValue + (cbCardColor + 1) >= 10) {
|
||||||
|
return (cbCardColor + 2) * 100 + (cbCardValue + (cbCardColor + 1)) % 10 * 3;
|
||||||
|
} else {
|
||||||
|
return (cbCardColor + 1) * 100 + (cbCardValue + cbCardColor) * 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 扑克转换
|
||||||
|
public static int SwitchToCardIndex(int cbCardData) {
|
||||||
|
// 扑克属性
|
||||||
|
int cbCardColor = GetCardColor(cbCardData);
|
||||||
|
int cbCardValue = GetCardValue(cbCardData);
|
||||||
|
if (cbCardColor <= 3) {
|
||||||
|
return (cbCardColor - 1) * 10 + cbCardValue - cbCardColor;
|
||||||
|
} else {
|
||||||
|
return (cbCardColor - 1) * 10 + cbCardValue / 3 - cbCardColor + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 扑克转换
|
||||||
|
public static int[] SwitchToCardData(List<Integer> cardInhand) {
|
||||||
|
int[] cardIndex = new int[34];
|
||||||
|
for (int i = 0; i < cardIndex.length; i++) {
|
||||||
|
cardIndex[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < cardInhand.size(); i++) {
|
||||||
|
|
||||||
|
int pos = SwitchToCardIndex(cardInhand.get(i));
|
||||||
|
cardIndex[pos]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cardIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 扑克转换
|
||||||
|
public static List<Integer> SwitchToCardIndex(int[] cardIndex) {
|
||||||
|
List<Integer> cardIndexList = new ArrayList<Integer>();
|
||||||
|
for (int i = 0; i < cardIndex.length; i++) {
|
||||||
|
for (int j = 0; j < cardIndex[i]; j++) {
|
||||||
|
int card = SwitchToCardData(i);
|
||||||
|
cardIndexList.add(card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cardIndexList;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 统计牌数
|
||||||
|
public static int[][] countTiles(List<Integer> cardInHand) {
|
||||||
|
int[][] counts = new int[5][10]; // 类型×值
|
||||||
|
|
||||||
|
for (Integer card : cardInHand) {
|
||||||
|
if (card == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
counts[card / 100 - 1][card % 100]++;
|
||||||
|
}
|
||||||
|
return counts;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检测不带精湖
|
||||||
|
public static int checkNormalHu(List<Integer> cardInHand, Map<String, Object> map) {
|
||||||
|
String info = "";
|
||||||
|
int[][] counts = countTiles(cardInHand);
|
||||||
|
int group = 0;
|
||||||
|
// 尝试找将牌
|
||||||
|
for (int type = 0; type < 5; type++) {
|
||||||
|
int max = 9;
|
||||||
|
for (int value = 1; value <= max; value++) {
|
||||||
|
if (counts[type][value] >= 2 && (value == 2 || value == 5 || value == 8)) {
|
||||||
|
// 找到可能的将牌
|
||||||
|
counts[type][value] -= 2;
|
||||||
|
group = checkMelds(counts, cardInHand.size() / 3, map);
|
||||||
|
if (group == 0) { // 剩余12张牌需要组成4个顺子或刻子
|
||||||
|
counts[type][value] += 2;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
counts[type][value] += 2;
|
||||||
|
} else {
|
||||||
|
group = checkMelds(counts, (cardInHand.size() / 3) + 1, map);
|
||||||
|
|
||||||
|
if (group == 0) { // 剩余12张牌需要组成4个顺子或刻子
|
||||||
|
counts[type][value] += 2;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
List<Integer> cardResiue = (List<Integer>) map.get("cardResiue");
|
||||||
|
|
||||||
|
for (int i = 0; i < cardResiue.size() - 1; i++) {
|
||||||
|
if (cardResiue.get(i).equals(cardResiue.get(i + 1))
|
||||||
|
&& (cardResiue.get(i) % 100 == 2 || cardResiue.get(i) % 100 == 5 || cardResiue.get(i) % 100 == 8)) {
|
||||||
|
cardResiue.remove(i);
|
||||||
|
cardResiue.remove(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
haveBetween(cardResiue, cardInHand);
|
||||||
|
|
||||||
|
System.out.println("可以打出的牌:" + cardResiue);
|
||||||
|
System.out.println("总共差几手牌:" + map.get("remainingMelds"));
|
||||||
|
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 递归检查是否可以组成顺子或刻子
|
||||||
|
public static int checkMelds(int[][] counts, int remainingMelds, Map<String, Object> map) {
|
||||||
|
if (remainingMelds == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 优先检查刻子
|
||||||
|
for (int type = 0; type < 5; type++) {
|
||||||
|
int max = 9;
|
||||||
|
for (int value = 1; value <= max; value++) {
|
||||||
|
if (counts[type][value] >= 3) {
|
||||||
|
counts[type][value] -= 3;
|
||||||
|
if (checkMelds(counts, remainingMelds - 1, map) == 0) {
|
||||||
|
counts[type][value] += 3;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
counts[type][value] += 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查顺子
|
||||||
|
for (int type = 0; type < 5; type++) {
|
||||||
|
int max = 7;
|
||||||
|
|
||||||
|
for (int value = 1; value <= max; value++) {
|
||||||
|
if (counts[type][value] >= 1 && counts[type][value + 1] >= 1 && counts[type][value + 2] >= 1) {
|
||||||
|
|
||||||
|
counts[type][value]--;
|
||||||
|
counts[type][value + 1]--;
|
||||||
|
counts[type][value + 2]--;
|
||||||
|
|
||||||
|
if (checkMelds(counts, remainingMelds - 1, map) == 0) {
|
||||||
|
counts[type][value]++;
|
||||||
|
counts[type][value + 1]++;
|
||||||
|
counts[type][value + 2]++;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
counts[type][value]++;
|
||||||
|
counts[type][value + 1]++;
|
||||||
|
counts[type][value + 2]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 判断孤章
|
||||||
|
List<Integer> cardResiue = convertCountToCards(counts);
|
||||||
|
|
||||||
|
// 优先处理顺子
|
||||||
|
|
||||||
|
|
||||||
|
removeConsecutivePairs3(cardResiue);
|
||||||
|
|
||||||
|
|
||||||
|
// System.out.println("只差几手牌" + remainingMelds + "," + cardResiue);
|
||||||
|
if (cardResiue.size() != 0) {
|
||||||
|
// 优先保留连续张
|
||||||
|
removeConsecutivePairs1(cardResiue);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cardResiue.size() != 0) {
|
||||||
|
// 第二优先保留对子
|
||||||
|
removeConsecutivePairsTwo(cardResiue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 最后保留坎张
|
||||||
|
// 如果坎张,为空则取边张
|
||||||
|
if (cardResiue.size() != 0) {
|
||||||
|
List<Integer> tmp3 = new ArrayList<>();
|
||||||
|
tmp3.addAll(cardResiue);
|
||||||
|
removeConsecutivePairs2(cardResiue);
|
||||||
|
if (cardResiue.size() == 0) {
|
||||||
|
// 取边站
|
||||||
|
cardResiue = qubianzhang(tmp3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remainingMelds == 0) {
|
||||||
|
map.put("remainingMelds", 0);
|
||||||
|
map.put("cardResiue", "已经胡牌");
|
||||||
|
|
||||||
|
}
|
||||||
|
if (map.get("remainingMelds") == null) {
|
||||||
|
map.put("remainingMelds", remainingMelds);
|
||||||
|
|
||||||
|
}
|
||||||
|
if (map.get("cardResiue") == null) {
|
||||||
|
map.put("cardResiue", cardResiue);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < cardResiue.size() - 1; i++) {
|
||||||
|
if (cardResiue.get(i).equals(cardResiue.get(i + 1))
|
||||||
|
&& (cardResiue.get(i) % 100 == 2 || cardResiue.get(i) % 100 == 5 || cardResiue.get(i) % 100 == 8)) {
|
||||||
|
cardResiue.remove(i);
|
||||||
|
cardResiue.remove(i);
|
||||||
|
if (remainingMelds >= 2) {
|
||||||
|
remainingMelds--;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Integer.parseInt(map.get("remainingMelds").toString()) >= remainingMelds && cardResiue.size() > 0) {
|
||||||
|
// 如果没有258做将则需要进入这里判断
|
||||||
|
|
||||||
|
if (remainingMelds == 1 && cardResiue.size() == 2) {
|
||||||
|
for (int i = 0; i < cardResiue.size(); i++) {
|
||||||
|
int diff = Math.abs(cardResiue.get(0) - cardResiue.get(1));
|
||||||
|
if (diff > 1) {
|
||||||
|
map.put("remainingMelds", remainingMelds + 1);// 删除符合258做将的牌
|
||||||
|
if (cardResiue.size() > 1) {
|
||||||
|
cardResiue.remove(i);
|
||||||
|
}
|
||||||
|
map.put("cardResiue", cardResiue);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (cardResiue.get(i) % 100 == 2 || cardResiue.get(i) % 100 == 5
|
||||||
|
|| cardResiue.get(i) % 100 == 8) {
|
||||||
|
// 删除符合258做将的牌
|
||||||
|
if (cardResiue.size() > 1) {
|
||||||
|
cardResiue.remove(i);
|
||||||
|
}
|
||||||
|
map.put("remainingMelds", remainingMelds);
|
||||||
|
map.put("cardResiue", cardResiue);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
map.put("remainingMelds", remainingMelds);
|
||||||
|
map.put("cardResiue", cardResiue);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
map.put("remainingMelds", remainingMelds);
|
||||||
|
map.put("cardResiue", cardResiue);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return remainingMelds;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void removeConsecutivePairs3(List<Integer> cards) {
|
||||||
|
if (cards == null || cards.size() < 3) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 先排序
|
||||||
|
List<Integer> sorted = new ArrayList<>(cards);
|
||||||
|
Collections.sort(sorted);
|
||||||
|
|
||||||
|
List<Integer> result = new ArrayList<>();
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
while (i < sorted.size()) {
|
||||||
|
boolean isleft = sorted.get(i) % 100 == 1 || sorted.get(i) % 100 == 8;
|
||||||
|
// 检查是否是连续牌(需要确保有足够的下一个元素)
|
||||||
|
if (!isleft && i < sorted.size() - 2 && sorted.get(i + 1) - sorted.get(i) == 1
|
||||||
|
&& sorted.get(i + 2) - sorted.get(i + 1) == 1) { // 修正:应该检查i+1和i+2的差
|
||||||
|
// 跳过这三张连续牌
|
||||||
|
i += 3;
|
||||||
|
} else {
|
||||||
|
// 保留这张牌
|
||||||
|
result.add(sorted.get(i));
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cards.clear();
|
||||||
|
cards.addAll(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void removeConsecutivePairsTwo(List<Integer> cards) {
|
||||||
|
if (cards == null || cards.size() < 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 先排序
|
||||||
|
List<Integer> sorted = new ArrayList<>(cards);
|
||||||
|
Collections.sort(sorted);
|
||||||
|
|
||||||
|
List<Integer> result = new ArrayList<>();
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
while (i < sorted.size()) {
|
||||||
|
// boolean isleft = sorted.get(i) % 100 == 1 || sorted.get(i) % 100 == 8;
|
||||||
|
|
||||||
|
if (i < sorted.size() - 2 && sorted.get(i + 2) - sorted.get(i) == 0) {
|
||||||
|
// 跳过对子
|
||||||
|
i += 3;
|
||||||
|
}else if (i < sorted.size() - 1 && sorted.get(i + 1) - sorted.get(i) == 0) {
|
||||||
|
// 跳过对子
|
||||||
|
i += 2;
|
||||||
|
} else {
|
||||||
|
// 保留这张牌
|
||||||
|
result.add(sorted.get(i));
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cards.clear();
|
||||||
|
cards.addAll(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 辅助方法
|
||||||
|
public static void removeConsecutivePairs1(List<Integer> cards) {
|
||||||
|
if (cards == null || cards.size() < 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 先排序
|
||||||
|
List<Integer> sorted = new ArrayList<>(cards);
|
||||||
|
Collections.sort(sorted);
|
||||||
|
|
||||||
|
List<Integer> result = new ArrayList<>();
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
while (i < sorted.size()) {
|
||||||
|
boolean isleft = sorted.get(i) % 100 == 1 || sorted.get(i) % 100 == 8;
|
||||||
|
if (!isleft && i < sorted.size() - 1 && sorted.get(i + 1) - sorted.get(i) == 1) {
|
||||||
|
// 跳过这对连续牌
|
||||||
|
i += 2;
|
||||||
|
} else {
|
||||||
|
// 保留这张牌
|
||||||
|
result.add(sorted.get(i));
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
cards.clear();
|
||||||
|
cards.addAll(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Integer> qubianzhang(List<Integer> cards) {
|
||||||
|
List<Integer> result = new ArrayList<>();
|
||||||
|
for (int i = 0; i < cards.size(); i++) {
|
||||||
|
if (cards.get(i) % 100 == 1 || cards.get(i) % 100 == 9) {
|
||||||
|
result.add(cards.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void removeConsecutivePairs2(List<Integer> cards) {
|
||||||
|
if (cards == null || cards.size() < 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 先排序
|
||||||
|
List<Integer> sorted = new ArrayList<>(cards);
|
||||||
|
Collections.sort(sorted);
|
||||||
|
|
||||||
|
// 使用布尔数组标记要删除的牌
|
||||||
|
boolean[] toRemove = new boolean[sorted.size()];
|
||||||
|
|
||||||
|
// 找出所有隔张(相差2的牌对,如1和3、2和4等)
|
||||||
|
for (int i = 0; i < sorted.size(); i++) {
|
||||||
|
if (!toRemove[i]) { // 如果这张牌还没被标记删除
|
||||||
|
for (int j = i + 1; j < sorted.size(); j++) {
|
||||||
|
if (!toRemove[j]) { // 如果这张牌还没被标记删除
|
||||||
|
int diff = sorted.get(j) - sorted.get(i);
|
||||||
|
|
||||||
|
if (diff == 2 && sorted.size() > 2) {
|
||||||
|
// 找到隔张,标记删除
|
||||||
|
toRemove[i] = true;
|
||||||
|
toRemove[j] = true;
|
||||||
|
|
||||||
|
if (j + 1 < sorted.size()) {
|
||||||
|
if (sorted.get(j + 1) % 100 == 2 || sorted.get(j + 1) % 100 == 5
|
||||||
|
|| sorted.get(j + 1) % 100 == 6) {
|
||||||
|
toRemove[i] = false;
|
||||||
|
toRemove[j] = false;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break; // 找到一对就跳出内层循环
|
||||||
|
|
||||||
|
} else if (diff > 2) {
|
||||||
|
// 由于已排序,差值只会越来越大
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建结果列表(保留未标记删除的牌)
|
||||||
|
List<Integer> result = new ArrayList<>();
|
||||||
|
for (int i = 0; i < sorted.size(); i++) {
|
||||||
|
if (!toRemove[i]) {
|
||||||
|
result.add(sorted.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cards.clear();
|
||||||
|
cards.addAll(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void haveBetween(List<Integer> cards, List<Integer> cardInHand) {
|
||||||
|
if (cards.size() <= 1 || cardInHand.size() <= 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建要删除的牌列表
|
||||||
|
Set<Integer> toRemove = new HashSet<>();
|
||||||
|
|
||||||
|
// 遍历手牌中的每一张牌
|
||||||
|
for (int handCard : cardInHand) {
|
||||||
|
// 检查cards中是否有与手牌形成靠章的牌
|
||||||
|
for (int card : cards) {
|
||||||
|
// 定义靠章规则:相同、相邻或隔一张
|
||||||
|
int diff = Math.abs(card - handCard);
|
||||||
|
if (diff == 1 || diff == 2) {
|
||||||
|
toRemove.add(card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 只有当cards中还有多于1张牌时才执行删除
|
||||||
|
if (cards.size() - toRemove.size() > 0) {
|
||||||
|
cards.removeAll(toRemove);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 可选:打印信息
|
||||||
|
if (!toRemove.isEmpty()) {
|
||||||
|
System.out.println("删除的牌: " + toRemove);
|
||||||
|
System.out.println("cards剩余: " + cards);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从二维数组counts还原到手牌列表
|
||||||
|
private static List<Integer> convertCountToCards(int[][] counts) {
|
||||||
|
List<Integer> cardInHand = new ArrayList<>();
|
||||||
|
|
||||||
|
// counts数组结构:[类型索引][牌值]
|
||||||
|
// 类型索引: 0=万, 1=筒, 2=条, 3=风, 4=箭 (根据您的编码规则)
|
||||||
|
// 牌值: 1-9表示牌面值
|
||||||
|
|
||||||
|
for (int suitIdx = 0; suitIdx < counts.length; suitIdx++) {
|
||||||
|
int suit = suitIdx + 1; // 还原百位数 (1,2,3,4,5)
|
||||||
|
|
||||||
|
for (int value = 1; value < counts[suitIdx].length; value++) {
|
||||||
|
int count = counts[suitIdx][value];
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
int card = suit * 100 + value;
|
||||||
|
cardInHand.add(card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cardInHand;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int ckeckOutCard(List<Integer> cardInhand) {
|
||||||
|
int card = 0;
|
||||||
|
Collections.sort(cardInhand);
|
||||||
|
System.out.println("手牌:" + cardInhand);
|
||||||
|
Map<String, Object> map = new HashMap<String, Object>();
|
||||||
|
checkNormalHu(cardInhand, map);
|
||||||
|
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Integer> checktingpai(List<Integer> cardhand) {
|
||||||
|
List<Integer> tpcards = new ArrayList<>();
|
||||||
|
List<Integer> tmphc = cardhand;
|
||||||
|
for (int i = 0; i < cardhand.size(); i++) {
|
||||||
|
|
||||||
|
int tmpcard = tmphc.get(0);
|
||||||
|
tmphc.remove(0);
|
||||||
|
for (int j = 101; j <= 109; j++) {
|
||||||
|
WinCard win = new WinCard(tmphc, j);
|
||||||
|
if (win.tryWin()) {
|
||||||
|
if (!tpcards.contains(tmpcard)) {
|
||||||
|
tpcards.add(tmpcard);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int j = 201; j <= 209; j++) {
|
||||||
|
WinCard win = new WinCard(tmphc, j);
|
||||||
|
if (win.tryWin()) {
|
||||||
|
if (!tpcards.contains(tmpcard)) {
|
||||||
|
tpcards.add(tmpcard);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tmphc.add(tmpcard);
|
||||||
|
}
|
||||||
|
return tpcards;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isJiangPai(int card) {
|
||||||
|
if (card % 100 == 2 || card % 100 == 5 || card % 100 == 8) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int checkduijiang(List<Integer> cardInHand, Map<String, List<Integer>> map) {
|
||||||
|
Map<Integer, Integer> countMap = new HashMap<>();
|
||||||
|
for (Integer item : cardInHand) {
|
||||||
|
countMap.put(item, countMap.getOrDefault(item, 0) + 1);
|
||||||
|
}
|
||||||
|
int jiangnum = 0;
|
||||||
|
for (int key : countMap.keySet()) {
|
||||||
|
if (isJiangPai(key) && countMap.get(key) >= 2) {
|
||||||
|
jiangnum++;
|
||||||
|
List<Integer> tmpI = new ArrayList<>();
|
||||||
|
tmpI.add(key);
|
||||||
|
map.put("jiangcard", tmpI);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return jiangnum;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分析最优出牌
|
||||||
|
public static List<Integer> analyzeBestDiscard(List<Integer> cardInHand) {
|
||||||
|
|
||||||
|
// 听牌
|
||||||
|
// 返回要打的牌,打后可以听牌
|
||||||
|
List<Integer> checktingpai = checktingpai(cardInHand);
|
||||||
|
System.out.println("打出这种牌后可以听牌 " + checktingpai);
|
||||||
|
Map<String, Object> map = new HashMap<>();
|
||||||
|
Map<String, List<Integer>> jmap = new HashMap<>();// 对将map
|
||||||
|
// 先定将,再做分析
|
||||||
|
/*
|
||||||
|
* int jiangnum = checkduijiang(cardInHand,jmap);
|
||||||
|
* System.out.println(jmap.get("jiangcard").get(0)); if (jiangnum==1){ //如果只有一对将
|
||||||
|
* //去将 后判断方案 List<Integer> tmphc = new ArrayList<>(); tmphc.addAll(cardInHand);
|
||||||
|
* Util.removeCard(tmphc,jmap.get("jiangcard").get(0),2); Map<String, Object>
|
||||||
|
* tmap = new HashMap<>(); checkNormalHu(tmphc, tmap);
|
||||||
|
*
|
||||||
|
* //不去将 判断
|
||||||
|
*
|
||||||
|
* Map<String, Object> tmap2 = new HashMap<>(); System.out.println(cardInHand);
|
||||||
|
* checkNormalHu(cardInHand, tmap2); System.out.println(tmap2);
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* }else if(jiangnum>1){
|
||||||
|
*
|
||||||
|
* }else{
|
||||||
|
*
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
|
||||||
|
checkNormalHu(cardInHand, map);
|
||||||
|
|
||||||
|
List<Integer> suggested = (List<Integer>) map.get("cardResiue");
|
||||||
|
|
||||||
|
// 返回要打的牌,打后可以听牌
|
||||||
|
List<Integer> zuizhongchupai = new ArrayList<>();
|
||||||
|
// 如果打出然后可以听牌,跟递归判断出来的牌一样,优先打出这张
|
||||||
|
if (checktingpai.size() > 0) {
|
||||||
|
for (Integer integer : checktingpai) {
|
||||||
|
for (Integer integer1 : suggested) {
|
||||||
|
if (integer.equals(integer1)) {
|
||||||
|
zuizhongchupai.add(integer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 推荐打几个牌都能听牌,需要判断打哪个听的牌更多
|
||||||
|
if (checktingpai.size() > 1 && zuizhongchupai.size() == 0) {
|
||||||
|
zuizhongchupai.addAll(checktingpai);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果判断听牌和递归出来的牌不一样 优先以听牌为准
|
||||||
|
if (zuizhongchupai.size() == 0) {
|
||||||
|
zuizhongchupai.addAll(checktingpai);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果判断最后打出牌还未听牌,就用递归判断的牌
|
||||||
|
if (zuizhongchupai.size() == 0) {
|
||||||
|
zuizhongchupai.addAll(suggested);
|
||||||
|
}
|
||||||
|
System.out.println("zuizhongchupai" + zuizhongchupai);
|
||||||
|
if (zuizhongchupai.isEmpty()) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
return zuizhongchupai;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算听牌数量(简化版,只计算数量不具体分析是哪些牌)
|
||||||
|
private static int countTingCards(List<Integer> hand) {
|
||||||
|
int tingCount = 0;
|
||||||
|
|
||||||
|
// 只尝试添加万、筒、条牌(1-9)
|
||||||
|
for (int type = 1; type <= 3; type++) {
|
||||||
|
for (int value = 1; value <= 9; value++) {
|
||||||
|
int testCard = type * 100 + value;
|
||||||
|
|
||||||
|
// 模拟添加这张牌
|
||||||
|
List<Integer> testHand = new ArrayList<>(hand);
|
||||||
|
testHand.add(testCard);
|
||||||
|
Collections.sort(testHand);
|
||||||
|
|
||||||
|
// 检查是否能胡牌
|
||||||
|
Map<String, Object> testMap = new HashMap<>();
|
||||||
|
if (checkNormalHu(testHand, testMap) == 0) {
|
||||||
|
tingCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 风牌和箭牌(可能性较小,但也要考虑)
|
||||||
|
for (int type = 4; type <= 5; type++) {
|
||||||
|
int max = (type == 4) ? 4 : 3;
|
||||||
|
for (int value = 1; value <= max; value++) {
|
||||||
|
int testCard = type * 100 + value;
|
||||||
|
|
||||||
|
List<Integer> testHand = new ArrayList<>(hand);
|
||||||
|
testHand.add(testCard);
|
||||||
|
Collections.sort(testHand);
|
||||||
|
|
||||||
|
Map<String, Object> testMap = new HashMap<>();
|
||||||
|
if (checkNormalHu(testHand, testMap) == 0) {
|
||||||
|
tingCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tingCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 或者使用更简洁的lambda写法(确保能进入)
|
||||||
|
// 或者更灵活的版本,可以指定优先级
|
||||||
|
public static Integer selectBestCardByPriority(List<Integer> cards) {
|
||||||
|
if (cards == null || cards.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Integer bestCard = null;
|
||||||
|
int bestPriority = Integer.MAX_VALUE;
|
||||||
|
|
||||||
|
for (Integer card : cards) {
|
||||||
|
int value = card % 100;
|
||||||
|
int priority = getCardPriority(value);
|
||||||
|
|
||||||
|
if (priority < bestPriority) {
|
||||||
|
bestPriority = priority;
|
||||||
|
bestCard = card;
|
||||||
|
} else if (priority == bestPriority) {
|
||||||
|
// 优先级相同,比较牌值(小的优先)
|
||||||
|
if (bestCard == null || value < (bestCard % 100)) {
|
||||||
|
bestCard = card;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bestCard;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 牌的优先级(数字越小优先级越高)
|
||||||
|
private static int getCardPriority(int value) {
|
||||||
|
if (value == 9)
|
||||||
|
return 1;
|
||||||
|
if (value == 1)
|
||||||
|
return 2;
|
||||||
|
if (value == 2 || value == 8)
|
||||||
|
return 3; // 靠近边张
|
||||||
|
if (value >= 3 && value <= 7)
|
||||||
|
return 4; // 中间牌
|
||||||
|
return 5; // 其他
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Integer selectBestCardRemove258(List<Integer> cards) {
|
||||||
|
if (cards == null || cards.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果只有一张牌,直接返回
|
||||||
|
if (cards.size() == 1) {
|
||||||
|
return cards.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建非258牌的列表
|
||||||
|
List<Integer> non258Cards = new ArrayList<>();
|
||||||
|
List<Integer> is258Cards = new ArrayList<>();
|
||||||
|
|
||||||
|
// 分类:258牌和非258牌
|
||||||
|
for (Integer card : cards) {
|
||||||
|
if (is258Jiang(card)) {
|
||||||
|
is258Cards.add(card);
|
||||||
|
} else {
|
||||||
|
non258Cards.add(card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("258将牌: " + is258Cards);
|
||||||
|
System.out.println("非258牌: " + non258Cards);
|
||||||
|
//判断是否手牌有其他对将
|
||||||
|
|
||||||
|
|
||||||
|
// 优先从非258牌中选
|
||||||
|
if (!non258Cards.isEmpty()) {
|
||||||
|
return selectBestSingleCard(non258Cards);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有非258牌,从258牌中选
|
||||||
|
|
||||||
|
if (!is258Cards.isEmpty()) {
|
||||||
|
return selectBestSingleCard(is258Cards);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检测是否满足258做将的条件
|
||||||
|
private static boolean is258Jiang(int card) {
|
||||||
|
// 提取牌的类型和值
|
||||||
|
int type = card / 100; // 百位数:1-万,2-筒,3-条,4-风,5-箭
|
||||||
|
int value = card % 100; // 个位数:牌面值
|
||||||
|
|
||||||
|
// 只有万、筒、条有258做将的限制
|
||||||
|
if (type >= 1 && type <= 3) {
|
||||||
|
// 258做将:二万(102)、五万(105)、八万(108)
|
||||||
|
// 二筒(202)、五筒(205)、八筒(208)
|
||||||
|
// 二条(302)、五条(305)、八条(308)
|
||||||
|
return value == 2 || value == 5 || value == 8;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从候选牌中选出最优的一张牌(优先边张1或9)
|
||||||
|
public static Integer selectBestSingleCard(List<Integer> cards) {
|
||||||
|
if (cards == null || cards.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 优先找边张(1或9)
|
||||||
|
for (Integer card : cards) {
|
||||||
|
int value = card % 100;
|
||||||
|
if (value == 1 || value == 9) {
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 没有边张,找靠近边张的牌(2或8)- 注意这里2是258将牌,但我们已经去除了258
|
||||||
|
for (Integer card : cards) {
|
||||||
|
int value = card % 100;
|
||||||
|
if (value == 2 || value == 8) {
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 都没有,返回第一张
|
||||||
|
return cards.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 主方法
|
||||||
|
public static void main(String[] args) {
|
||||||
|
// 测试用例:没有258将牌的情况
|
||||||
|
List<Integer> test1 = new ArrayList<Integer>();
|
||||||
|
test1.add(109);
|
||||||
|
test1.add(109);
|
||||||
|
test1.add(109);
|
||||||
|
|
||||||
|
test1.add(101);
|
||||||
|
test1.add(101);
|
||||||
|
test1.add(204);
|
||||||
|
test1.add(206);
|
||||||
|
removeConsecutivePairsTwo(test1);
|
||||||
|
// test1 = Arrays.asList(208, 204, 203, 202, 107, 106, 105, 103, 102, 101, 103);
|
||||||
|
// test1.add(208);
|
||||||
|
// test1.add(204);
|
||||||
|
// test1.add(203);
|
||||||
|
// test1.add(202);
|
||||||
|
// test1.add(107);
|
||||||
|
// test1.add(106);
|
||||||
|
// test1.add(105);
|
||||||
|
// test1.add(103);
|
||||||
|
// test1.add(102);
|
||||||
|
// test1.add(101);
|
||||||
|
// test1.add(101);
|
||||||
|
|
||||||
|
// test1.add(208);
|
||||||
|
// test1.add(207);
|
||||||
|
// test1.add(205);
|
||||||
|
// test1.add(204);
|
||||||
|
// test1.add(203);
|
||||||
|
// test1.add(202);
|
||||||
|
// test1.add(201);
|
||||||
|
// test1.add(109);
|
||||||
|
// test1.add(108);
|
||||||
|
// test1.add(106);
|
||||||
|
// test1.add(205);
|
||||||
|
|
||||||
|
// test1.add(205);
|
||||||
|
// test1.add(203);
|
||||||
|
// test1.add(202);
|
||||||
|
// test1.add(201);
|
||||||
|
// test1.add(204);
|
||||||
|
// test1.add(107);
|
||||||
|
// test1.add(106);
|
||||||
|
// test1.add(105);
|
||||||
|
|
||||||
|
// test1.add(201);
|
||||||
|
// test1.add(107);
|
||||||
|
// test1.add(107);
|
||||||
|
// test1.add(104);
|
||||||
|
|
||||||
|
// test1.add(101);
|
||||||
|
// test1.add(102);
|
||||||
|
// test1.add(103);
|
||||||
|
// test1.add(104);
|
||||||
|
// test1.add(109);
|
||||||
|
// test1.add(201);
|
||||||
|
// test1.add(204);
|
||||||
|
// test1.add(204);
|
||||||
|
// test1.add(206);
|
||||||
|
// test1.add(207);
|
||||||
|
// test1.add(207);
|
||||||
|
// test1.add(209);
|
||||||
|
// test1.add(209);
|
||||||
|
// test1.add(209);
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
/*test1.add(209);
|
||||||
|
test1.add(208);
|
||||||
|
test1.add(208);
|
||||||
|
test1.add(207);
|
||||||
|
test1.add(205);
|
||||||
|
test1.add(107);
|
||||||
|
test1.add(107);
|
||||||
|
test1.add(105);
|
||||||
|
test1.add(104);
|
||||||
|
test1.add(103);
|
||||||
|
|
||||||
|
test1.add(103);
|
||||||
|
|
||||||
|
test1.add(103);
|
||||||
|
test1.add(102);
|
||||||
|
test1.add(104);
|
||||||
|
// test1.add(103);
|
||||||
|
// test1.add(102);
|
||||||
|
|
||||||
|
System.out.println("手牌:" + test1);
|
||||||
|
List<Integer> bestDiscard = analyzeBestDiscard(test1);
|
||||||
|
// Integer integer = selectBestCardByPriority(bestDiscard);
|
||||||
|
Integer integer1 = selectBestCardRemove258(bestDiscard);
|
||||||
|
|
||||||
|
Map<String, Object> mapSS = new HashMap<>();
|
||||||
|
checkNormalHu(test1, mapSS);
|
||||||
|
System.out.println("map:" + mapSS);
|
||||||
|
|
||||||
|
System.out.println("ww" + integer1);*/
|
||||||
|
// if (!bestDiscard.isEmpty()) {
|
||||||
|
// System.out.println("建议出牌:" + bestDiscard.get(0));
|
||||||
|
// } else {
|
||||||
|
// System.out.println("没有出牌建议");
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,270 @@
|
||||||
|
package com.robotcm.taurus.util;
|
||||||
|
|
||||||
|
public class Config {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 玩法 1 经典玩法 2临汾玩法 3运城玩法
|
||||||
|
*/
|
||||||
|
public static final String ROOM_CONFIG_WANFA = "wanfa";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模式 1普通模式 2积分模式
|
||||||
|
*/
|
||||||
|
public static final String ROOM_CONFIG_MOSHI = "moshi";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 积分 底分
|
||||||
|
*/
|
||||||
|
// public static final String ROOM_CONFIG_JI_FEN= "tex_jrzxjf";
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 房费 1 房主支付 2 AA支付 3大赢家支付
|
||||||
|
*/
|
||||||
|
public static final String ROOM_CONFIG_FANGFEI = "fangfei";
|
||||||
|
/**
|
||||||
|
* 封顶 1 --3炸 2-- 4炸 3-- 5炸 4-- 不封顶
|
||||||
|
*/
|
||||||
|
public static final String ROOM_CONFIG_FENGDING = "fengding";
|
||||||
|
/**
|
||||||
|
* 叫地主 1-- 经典123 2-- 固定 叫抢地主
|
||||||
|
*/
|
||||||
|
public static final String ROOM_CONFIG_JIAODIZHU = "jiaodizhu";
|
||||||
|
/**
|
||||||
|
* 不显示牌数 0 未勾选 1 已勾选
|
||||||
|
*/
|
||||||
|
public static final String ROOM_CONFIG_BUZHANSHIPAISHU = "buxianshipaishu";
|
||||||
|
/**
|
||||||
|
* 暗牌 0 未勾选 1 已勾选
|
||||||
|
*/
|
||||||
|
public static final String ROOM_CONFIG_ANPAI = "anpai";
|
||||||
|
/**
|
||||||
|
* 双王或四个2必叫地主 0 未勾选 1 已勾选
|
||||||
|
*/
|
||||||
|
public static final String ROOM_CONFIG_SHUANGWANGSIER = "shuangwangsier";
|
||||||
|
/**
|
||||||
|
* 可三带二 0 未勾选 1 已勾选
|
||||||
|
*/
|
||||||
|
public static final String ROOM_CONFIG_KESANDAIER = "kesandaier";
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任俩三为炸弹 0为未勾选 1 已勾选
|
||||||
|
*/
|
||||||
|
public static final String renliasanweizhadan = "renliasanweizhadan";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 同色三为炸弹 0为未勾选 1 已勾选
|
||||||
|
*/
|
||||||
|
public static final String tongsesanweizhadan = "tongsesanweizhadan";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 人数
|
||||||
|
*/
|
||||||
|
public static final String maxPlayers = "maxPlayers";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 赖子
|
||||||
|
*/
|
||||||
|
public static final String laizi = "laizi"; //0 是无赖子,1是有赖子
|
||||||
|
|
||||||
|
|
||||||
|
public static final String tuoguanshijian = "tuoguan_active_time";
|
||||||
|
|
||||||
|
|
||||||
|
//-------------------------------牌类型---------------------------------
|
||||||
|
/**
|
||||||
|
* 单牌
|
||||||
|
*/
|
||||||
|
public final static int TYPE_DANPAI = 1;
|
||||||
|
/**
|
||||||
|
* 对子
|
||||||
|
*/
|
||||||
|
public final static int TYPE_DUIZI = 2;
|
||||||
|
/**
|
||||||
|
* 顺子
|
||||||
|
*/
|
||||||
|
public final static int TYPE_SHUNZI = 3;
|
||||||
|
/**
|
||||||
|
* 连对
|
||||||
|
*/
|
||||||
|
public final static int TYPE_LIANDUI = 4;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 三带一
|
||||||
|
*/
|
||||||
|
public final static int TYPE_3_1 = 5;
|
||||||
|
/**
|
||||||
|
* 3带2
|
||||||
|
*/
|
||||||
|
public final static int TYPE_3_2 = 7;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 3不带
|
||||||
|
*/
|
||||||
|
public final static int TYPE_BU_DAI = 11;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 飞机
|
||||||
|
*/
|
||||||
|
public final static int TYPE_FEIJI = 6;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 飞机带单
|
||||||
|
*/
|
||||||
|
public final static int TYPE_FEIJI_DAI_DAN = 14;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 飞机带双
|
||||||
|
*/
|
||||||
|
public final static int TYPE_FEIJI_DAI_SHUANG = 15;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 4带2
|
||||||
|
*/
|
||||||
|
public final static int TYPE_4_2 = 8;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 四带一对 4444 55
|
||||||
|
*/
|
||||||
|
public final static int TYPE_4_2_dui = 12;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 炸
|
||||||
|
*/
|
||||||
|
public final static int TYPE_ZHA = 8;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 火箭-双王
|
||||||
|
*/
|
||||||
|
public final static int TYPE_ROCKET = 10;
|
||||||
|
|
||||||
|
|
||||||
|
//-------------------------------协议---------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 明牌
|
||||||
|
*/
|
||||||
|
public static final String GAME_EVT_MING_PAI = "2117";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发牌协议g
|
||||||
|
*/
|
||||||
|
public static final String GAME_EVT_PLAYER_DEAL = "2011";
|
||||||
|
/**
|
||||||
|
* 出牌
|
||||||
|
*/
|
||||||
|
public static final String GAME_DIS_CARD = "1013";
|
||||||
|
/**
|
||||||
|
* 不出
|
||||||
|
*/
|
||||||
|
public static final String GAME_DIS_PASS = "1014";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 出牌事件
|
||||||
|
*/
|
||||||
|
public static final String GAME_EVT_DISCARD = "2021";
|
||||||
|
/**
|
||||||
|
* 出牌提示事件
|
||||||
|
*/
|
||||||
|
public static final String GAME_EVT_DISCARD_TIP = "2004";
|
||||||
|
/**
|
||||||
|
* 转盘指向事件
|
||||||
|
*/
|
||||||
|
public static final String GAME_EVT_CHANGE_ACTIVE_PLAYER = "2016";
|
||||||
|
/**
|
||||||
|
* pass事件
|
||||||
|
*/
|
||||||
|
public static final String GAME_EVT_PASS = "2030";
|
||||||
|
/**
|
||||||
|
* 更新炸弹分数
|
||||||
|
*/
|
||||||
|
public static final String GAME_EVT_UPDATE_BOM_SCORE = "2118";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 小结算
|
||||||
|
*/
|
||||||
|
public static final String GAME_EVT_RESULT1 = "2007";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通知玩家叫地主 1
|
||||||
|
*/
|
||||||
|
public static final String JIAO_DIZHU = "2041";
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 玩家叫地主 2
|
||||||
|
*/
|
||||||
|
public static final String JAIO_DIZHU_REO = "1042";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通知玩家踢人 抢地主 3
|
||||||
|
*/
|
||||||
|
public static final String TONG_ZHI_TONG_ZHI_WANJIA = "2043";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 玩家叫地主广播
|
||||||
|
*/
|
||||||
|
public static final String TONG_ZHI_JIAO_DI_ZHU_GUANG_BO = "2070";
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流局通知
|
||||||
|
*/
|
||||||
|
public static final String TONG_ZHI_LIU_JU = "2015";
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 玩家抢地主广播
|
||||||
|
*/
|
||||||
|
public static final String TONG_ZHI_QIANG_DI_ZHU_GUANG_BO = "2046";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通知所有人谁是地主广播
|
||||||
|
*/
|
||||||
|
public static final String TONG_ZHI_DI_ZHU_GUANG_BO = "2047";
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 玩家抢地主 踢人/不踢
|
||||||
|
*/
|
||||||
|
public static final String TI_OR_BUTI_RSQ = "1044";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 轮流叫地主 1是 轮流 没有的话就默认第一把庄家先出,第二把谁赢了谁是庄家就先出
|
||||||
|
*/
|
||||||
|
public static final String lunliujiaodizhu = "lunliujiaodizhu";
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 出牌错误
|
||||||
|
*/
|
||||||
|
public static final String GAME_EVT_PUT_ERROR = "2111";
|
||||||
|
|
||||||
|
public static final int PUT_ERROR_MUST_OUT_MIN = 0;
|
||||||
|
public static final int PUT_ERROR_INVALID_TYPE = 1;
|
||||||
|
public static final int PUT_ERROR_MUST_OUT_MAX = 2;
|
||||||
|
public static final int PUT_ERROR_BOMB_CHAI = 3;
|
||||||
|
|
||||||
|
public static final String SETTLE_WIN_COUNT = "winnum";
|
||||||
|
public static final String SETTLE_CHUNTIAN_COUNT = "springnum";
|
||||||
|
public static final String SETTLE_BOMB_COUNT = "boomnum";
|
||||||
|
public static final String SETTLE_MAX_SCORE = "maxscore";
|
||||||
|
public static final String ENABLE_DOUBLE_AFTER_LANDLORD = "keti";
|
||||||
|
/**
|
||||||
|
* 广播加倍
|
||||||
|
*/
|
||||||
|
public static final String TONG_ZHI_DOUBLE_CHOICE = "2053";
|
||||||
|
/**
|
||||||
|
* 通知农民加倍
|
||||||
|
*/
|
||||||
|
public static final String GAME_EVT_FARMER_DOUBLE_TIP = "2060";
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,115 @@
|
||||||
|
package com.robotcm.taurus.util;
|
||||||
|
|
||||||
|
import com.taurus.core.entity.ITArray;
|
||||||
|
import com.taurus.core.entity.ITObject;
|
||||||
|
import com.taurus.core.entity.TArray;
|
||||||
|
import com.taurus.core.entity.TObject;
|
||||||
|
import com.taurus.core.plugin.database.DataBase;
|
||||||
|
import com.taurus.core.plugin.redis.Redis;
|
||||||
|
import redis.clients.jedis.Jedis;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class GroupDatas {
|
||||||
|
|
||||||
|
public static TObject getData() {
|
||||||
|
Jedis jedis0 = Redis.use("group1_db0").getJedis();
|
||||||
|
Jedis jedis11 = Redis.use("group1_db11").getJedis();
|
||||||
|
|
||||||
|
TObject param = TObject.newInstance();
|
||||||
|
|
||||||
|
//存储所有有配置机器人的玩法id
|
||||||
|
Set<Integer> pids = new HashSet<>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//存储可配机器人圈子
|
||||||
|
Set<Integer> gids = new HashSet<>();
|
||||||
|
|
||||||
|
|
||||||
|
TArray groupid = TArray.newInstance();
|
||||||
|
TArray robotid = TArray.newInstance();
|
||||||
|
TArray playid = TArray.newInstance();
|
||||||
|
|
||||||
|
//圈子对应的房间
|
||||||
|
|
||||||
|
try {
|
||||||
|
//取出可用的机器人
|
||||||
|
String sql = String.format("SELECT id FROM `account` WHERE jiqiren=9998 and start = 0");
|
||||||
|
ITArray robotIds = DataBase.use().executeQueryByTArray(sql);
|
||||||
|
|
||||||
|
for (int i = 0; i < robotIds.size(); i++) {
|
||||||
|
ITObject robot = robotIds.getTObject(i);
|
||||||
|
Integer robotId = robot.getInt("id");
|
||||||
|
robotid.addInt(robotId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//获取所有圈子
|
||||||
|
Set<String> groupIds = jedis11.keys("group:*");
|
||||||
|
for (String group : groupIds) {
|
||||||
|
String groupId = group.substring(group.indexOf(":") + 1);
|
||||||
|
String groupKey = "g{" + groupId + "}:play:*";
|
||||||
|
Set<String> playIds = jedis11.keys(groupKey);
|
||||||
|
|
||||||
|
//圈子下面的玩法
|
||||||
|
for (String pid : playIds) {
|
||||||
|
String playId = pid.substring(pid.indexOf("play:") + 5);
|
||||||
|
String playKey = "g{" + groupId + "}:play:" + playId;
|
||||||
|
|
||||||
|
if (jedis11.hget(playKey, "leftover_robot") == null){
|
||||||
|
if (jedis11.hget(playKey, "shangxian_robot") != null){
|
||||||
|
String shangxian_robot = jedis11.hget(playKey, "shangxian_robot");
|
||||||
|
jedis11.hset(playKey, "leftover_robot", shangxian_robot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
String leftover_robot = "0";
|
||||||
|
if (jedis11.hget(playKey, "leftover_robot")!=null){
|
||||||
|
leftover_robot = jedis11.hget(playKey, "leftover_robot");
|
||||||
|
}
|
||||||
|
//有配置机器人的玩法
|
||||||
|
if ( Integer.parseInt(leftover_robot)!=0) {
|
||||||
|
pids.add(Integer.parseInt(playId));
|
||||||
|
gids.add(Integer.parseInt(groupId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, List<Integer>> groupRoomMap = new HashMap<>();
|
||||||
|
for (Integer gid : gids){
|
||||||
|
for (Integer pid : pids){
|
||||||
|
String key = "g{" + gid + "}:play:" + pid;
|
||||||
|
|
||||||
|
if (jedis11.hget(key, "groupId")!=null){
|
||||||
|
String groupId = jedis11.hget(key, "groupId");
|
||||||
|
if (gid==Integer.parseInt(groupId)){
|
||||||
|
groupRoomMap.computeIfAbsent(String.valueOf(gid), k -> new ArrayList<>()).add(pid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
for (Map.Entry<String, List<Integer>> entry : groupRoomMap.entrySet()) {
|
||||||
|
TObject groupPlay = TObject.newInstance();
|
||||||
|
groupPlay.putTArray(entry.getKey(), TArray.newFromJsonData(entry.getValue().toString()));
|
||||||
|
groupid.addTObject(groupPlay);
|
||||||
|
}
|
||||||
|
param.putTArray("groupid", groupid);
|
||||||
|
param.putTArray("robotIds", robotid);
|
||||||
|
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.out.println(e);
|
||||||
|
} finally {
|
||||||
|
jedis0.close();
|
||||||
|
jedis11.close();
|
||||||
|
}
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,224 @@
|
||||||
|
package com.robotcm.taurus.util;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class NewChangshaSuanFaTest {
|
||||||
|
|
||||||
|
public int drawnCards; //摸牌
|
||||||
|
public static List<Integer> tinCards = new ArrayList<>();
|
||||||
|
public static boolean isTin = false;
|
||||||
|
|
||||||
|
public static boolean isChi =false;
|
||||||
|
|
||||||
|
public static boolean isPeng=false;
|
||||||
|
|
||||||
|
//清一色
|
||||||
|
//碰碰胡
|
||||||
|
//七小对
|
||||||
|
//将将胡
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public String newOutCardSuanFa(List<Integer> cardInhand, List<Integer> pengCard, List<Integer> chowGroup, List<Integer> resultList) {
|
||||||
|
List<Integer> handCards = new ArrayList<>(cardInhand);
|
||||||
|
handCards.sort(Integer::compareTo);
|
||||||
|
int i = countPengGroups(handCards, pengCard); //刻子的数量
|
||||||
|
boolean jiangHu = isJiangHu(handCards);//将将胡
|
||||||
|
boolean isPengPengHu = hasThreeKeziAndTwoPairs(handCards, pengCard);//碰碰胡
|
||||||
|
boolean hasBigSuit = isAllSameSuit(handCards, pengCard); // 清一色花色
|
||||||
|
int pisCardsCount = countPairs(handCards);//分析七小对
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private int countPengGroups(List<Integer> handCards, List<Integer> pengCard) {
|
||||||
|
List<Integer> handCards2 = new ArrayList<>();
|
||||||
|
handCards2.addAll(handCards);
|
||||||
|
handCards2.addAll(pengCard);
|
||||||
|
|
||||||
|
|
||||||
|
// 按花色分组
|
||||||
|
Map<Integer, List<Integer>> suitGroupMap = new HashMap<>();
|
||||||
|
for (Integer card : handCards2) {
|
||||||
|
int suit = card / 100;
|
||||||
|
suitGroupMap.computeIfAbsent(suit, k -> new ArrayList<>()).add(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int keziCount = 0;
|
||||||
|
|
||||||
|
// 对每个花色分别统计刻子
|
||||||
|
for (Map.Entry<Integer, List<Integer>> entry : suitGroupMap.entrySet()) {
|
||||||
|
int suit = entry.getKey();
|
||||||
|
List<Integer> suitCards = entry.getValue();
|
||||||
|
|
||||||
|
// 统计该花色下每张牌的数量
|
||||||
|
Map<Integer, Integer> valueCountMap = new HashMap<>();
|
||||||
|
for (Integer card : suitCards) {
|
||||||
|
int value = card % 100;
|
||||||
|
valueCountMap.put(value, valueCountMap.getOrDefault(value, 0) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 找出该花色的刻子
|
||||||
|
for (Map.Entry<Integer, Integer> valueEntry : valueCountMap.entrySet()) {
|
||||||
|
if (valueEntry.getValue() >= 3) {
|
||||||
|
keziCount++;
|
||||||
|
int cardValue = suit * 100 + valueEntry.getKey();
|
||||||
|
// logInfo("找到刻子: " + getCardName(cardValue) + " (花色:" + getSuitName(suit) +
|
||||||
|
// ", 点数:" + valueEntry.getKey() + ", 数量:" + valueEntry.getValue() + ")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// logInfo("手牌中共找到 " + keziCount + " 组刻子");
|
||||||
|
return keziCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isJiangHu(List<Integer> handCards) {
|
||||||
|
// 统计手牌中258牌的数量
|
||||||
|
int count258 = 0;
|
||||||
|
|
||||||
|
for (Integer card : handCards) {
|
||||||
|
if (is258Card(card)) {
|
||||||
|
count258++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断258牌是否大于7张
|
||||||
|
return count258 >= 10;
|
||||||
|
}
|
||||||
|
private boolean is258Card(int card) {
|
||||||
|
// 假设牌型编码规则:
|
||||||
|
// 101-109: 一万到九万
|
||||||
|
// 201-209: 一筒到九筒
|
||||||
|
// 301-309: 一条到九条
|
||||||
|
|
||||||
|
// 获取牌的数字(个位数)
|
||||||
|
int cardValue = card % 10;
|
||||||
|
|
||||||
|
// 258牌的数字必须是2、5、8
|
||||||
|
if (cardValue == 2 || cardValue == 5 || cardValue == 8) {
|
||||||
|
// 确保是万、筒、条(排除风牌等)
|
||||||
|
int cardType = card / 100;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasThreeKeziAndTwoPairs(List<Integer> handCards, List<Integer> pengCard) {
|
||||||
|
List<Integer> handCards2 = new ArrayList<>();
|
||||||
|
handCards2.addAll(handCards);
|
||||||
|
handCards2.addAll(pengCard);
|
||||||
|
|
||||||
|
// 统计每张牌出现的次数
|
||||||
|
Map<Integer, Integer> cardCountMap = new HashMap<>();
|
||||||
|
for (Integer card : handCards2) {
|
||||||
|
cardCountMap.put(card, cardCountMap.getOrDefault(card, 0) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int keziCount = 0; // 刻子数(三张相同)
|
||||||
|
int pairCount = 0; // 对子数(两张相同)
|
||||||
|
|
||||||
|
// 统计刻子和对子
|
||||||
|
for (int count : cardCountMap.values()) {
|
||||||
|
if (count >= 3) {
|
||||||
|
// 如果有3张或以上,可以算作一个刻子
|
||||||
|
keziCount++;
|
||||||
|
// 如果有4张,剩余的1张不能算对子,但可能算单张
|
||||||
|
if (count >= 4) {
|
||||||
|
// 4张可以看作1个刻子+1张单牌,或者如果有需要可以调整
|
||||||
|
// 这里暂时不额外处理
|
||||||
|
}
|
||||||
|
} else if (count == 2) {
|
||||||
|
pairCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否有至少3个刻子和至少2个对子
|
||||||
|
return keziCount >= 3 && pairCount >= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAllSameSuit(List<Integer> handCards, List<Integer> pengCard) {
|
||||||
|
// 统计各花色的牌数量
|
||||||
|
Map<Integer, Integer> suitCountMap = new HashMap<>();
|
||||||
|
List<Integer> handCards2 = new ArrayList<>();
|
||||||
|
handCards2.addAll(handCards);
|
||||||
|
handCards2.addAll(pengCard);
|
||||||
|
|
||||||
|
for (Integer card : handCards2) {
|
||||||
|
int suit = card / 100; // 获取花色,100=万,200=筒,300=条
|
||||||
|
suitCountMap.put(suit, suitCountMap.getOrDefault(suit, 0) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否有花色的牌数量超过8张
|
||||||
|
for (Map.Entry<Integer, Integer> entry : suitCountMap.entrySet()) {
|
||||||
|
int suit = entry.getKey();
|
||||||
|
|
||||||
|
int count = entry.getValue();
|
||||||
|
|
||||||
|
if (count >= 10) {
|
||||||
|
// String suitName = getSuitName(suit);
|
||||||
|
// logInfo("检测到可能的清一色花色: " + suitName + ", 数量: " + count);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int countPairs(List<Integer> handCards) {
|
||||||
|
// 1. 先根据花色和点数排序
|
||||||
|
List<Integer> sortedCards = new ArrayList<>(handCards);
|
||||||
|
sortedCards.sort((a, b) -> {
|
||||||
|
int suitA = a / 100;
|
||||||
|
int suitB = b / 100;
|
||||||
|
int rankA = a % 100;
|
||||||
|
int rankB = b % 100;
|
||||||
|
|
||||||
|
// 先按花色排序,再按点数排序
|
||||||
|
if (suitA != suitB) {
|
||||||
|
return suitA - suitB;
|
||||||
|
}
|
||||||
|
return rankA - rankB;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2. 统计对子
|
||||||
|
int pairCount = 0;
|
||||||
|
for (int i = 0; i < sortedCards.size() - 1; i++) {
|
||||||
|
if (sortedCards.get(i).equals(sortedCards.get(i + 1))) {
|
||||||
|
pairCount++;
|
||||||
|
i++; // 跳过下一张
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pairCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private Integer findIsolatedCardInFei6(List<Integer> feiCandidates) {
|
||||||
|
// 统计每张牌的出现次数
|
||||||
|
Map<Integer, Integer> countMap = new HashMap<>();
|
||||||
|
for (int card : feiCandidates) {
|
||||||
|
countMap.put(card, countMap.getOrDefault(card, 0) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 寻找出现次数为 1 的牌(非对子)
|
||||||
|
for (int card : feiCandidates) {
|
||||||
|
if (countMap.get(card) == 1) {
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有非对子的牌,返回 null 或选择第一张牌等策略
|
||||||
|
return feiCandidates.get(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,293 @@
|
||||||
|
package com.robotcm.taurus.util;
|
||||||
|
|
||||||
|
import com.game.Util;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
|
||||||
|
public class Paixing {
|
||||||
|
|
||||||
|
private final static boolean qs_yijihua_check(Map<Integer, Integer> cardMap,int se,int index) {
|
||||||
|
Integer num = cardMap.get(se+index);
|
||||||
|
if(num!=null&&num==1) {
|
||||||
|
for(int i=1;i<=9;++i) {
|
||||||
|
if(i !=index) {
|
||||||
|
if(cardMap.containsKey(se+i)){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
List<Integer> cardInhand = new ArrayList<>();
|
||||||
|
List<Integer> opCards = new ArrayList<>();
|
||||||
|
//opCards.add(new OpCard(RuleWeight.TYPE_PONG, 109));
|
||||||
|
//opCards.add(new OpCard(RuleWeight.TYPE_PONG, 209));
|
||||||
|
cardInhand.add(104);
|
||||||
|
cardInhand.add(204);
|
||||||
|
cardInhand.add(204);
|
||||||
|
cardInhand.add(203);
|
||||||
|
cardInhand.add(203);
|
||||||
|
//cardInhand.add(204);
|
||||||
|
|
||||||
|
cardInhand.add(202);
|
||||||
|
cardInhand.add(202);
|
||||||
|
cardInhand.add(202);
|
||||||
|
cardInhand.add(202);
|
||||||
|
cardInhand.add(201);
|
||||||
|
cardInhand.add(201);
|
||||||
|
|
||||||
|
cardInhand.add(105);
|
||||||
|
cardInhand.add(105);
|
||||||
|
//cardInhand.add(104);
|
||||||
|
|
||||||
|
List<Integer> chi = new ArrayList<>();
|
||||||
|
List<Integer> peng = new ArrayList<>();
|
||||||
|
List<Integer> gang = new ArrayList<>();
|
||||||
|
|
||||||
|
|
||||||
|
System.out.println(tingCheck( cardInhand,chi,peng,gang));
|
||||||
|
}
|
||||||
|
|
||||||
|
private final static boolean quanqiuren(List<Integer> opCards,List<Integer> cardInhand, int drawCard) {
|
||||||
|
if (cardInhand.size() > 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(drawCard == 0)return true;
|
||||||
|
if (cardInhand.get(0) != drawCard)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final static boolean qingyise(List<Integer> opCards, List<Integer> cardInhand, int drawCard) {
|
||||||
|
int se = cardInhand.get(0) / 100;
|
||||||
|
if (drawCard!=0&&drawCard / 100 != se)
|
||||||
|
return false;
|
||||||
|
for (Integer card : opCards) {
|
||||||
|
if (se != card / 100) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int card : cardInhand) {
|
||||||
|
if (se != card / 100) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final static boolean pongpong(List<Integer> opCards, List<Integer> cardInhand, int drawCard,boolean quanqiuren) {
|
||||||
|
int card1count=0,card2count=0;
|
||||||
|
Map<Integer, Integer> cardMap =Util.getCardNumMap(cardInhand);
|
||||||
|
for (Entry<Integer, Integer> entry : cardMap.entrySet()) {
|
||||||
|
int card = entry.getKey();
|
||||||
|
int num = entry.getValue();
|
||||||
|
if(num == 4) return false;
|
||||||
|
switch(num) {
|
||||||
|
case 1:
|
||||||
|
if(card1count>=1)return false;
|
||||||
|
if(drawCard!=0&&card != drawCard)return false;
|
||||||
|
card1count++;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
if(card2count >=2)return false;
|
||||||
|
card2count ++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(card1count>0&&card2count>0)return false;
|
||||||
|
if(card1count>0&&drawCard!=0&&!cardMap.containsKey(drawCard))return false;
|
||||||
|
if(card2count == 2&&drawCard!=0) {
|
||||||
|
if(!cardMap.containsKey(drawCard))return false;
|
||||||
|
if(cardMap.get(drawCard)!=2)return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final static boolean jiangjiang(List<Integer> opCards, List<Integer> cardInhand, int drawCard) {
|
||||||
|
if(drawCard!=0){
|
||||||
|
if (drawCard % 100 != 2 && drawCard % 100 != 5 && drawCard % 100 != 8) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (Integer card : opCards) {
|
||||||
|
if (card % 100 != 2 && card % 100 != 5 && card % 100 != 8) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int card : cardInhand) {
|
||||||
|
if (card % 100 != 2 && card % 100 != 5 && card % 100 != 8) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final static int qixiaodui(List<Integer> opCards, List<Integer> cardInhand, int drawCard) {
|
||||||
|
if (cardInhand.size() != 13) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
List<Integer> cardlist = cardInhand;
|
||||||
|
int pair = 0;
|
||||||
|
int card4count=0;
|
||||||
|
Map<Integer, Integer> cardMap =Util.getCardNumMap(cardlist);
|
||||||
|
for (Entry<Integer, Integer> entry : cardMap.entrySet()) {
|
||||||
|
int num = entry.getValue();
|
||||||
|
int card = entry.getKey();
|
||||||
|
if (num == 4) {
|
||||||
|
pair +=2;
|
||||||
|
card4count ++;
|
||||||
|
}else if(num >=2) {
|
||||||
|
if(num==3&&drawCard!=0&&drawCard != card) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
pair +=1;
|
||||||
|
}else if(num ==1) {
|
||||||
|
if(drawCard!=0&&drawCard != card) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(pair<6) return -1;
|
||||||
|
if(drawCard ==0)return card4count+1;
|
||||||
|
if(!cardMap.containsKey(drawCard)) return -1;
|
||||||
|
if(cardMap.get(drawCard) == 3) {
|
||||||
|
card4count++;
|
||||||
|
}
|
||||||
|
return card4count+1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final static WinCardType putWinCardType(Map<Integer, WinCardType> map, int type, int value,int score) {
|
||||||
|
if (map == null)
|
||||||
|
return null;
|
||||||
|
if (map.containsKey(type)) {
|
||||||
|
WinCardType wct = map.get(type);
|
||||||
|
wct.value += value;
|
||||||
|
return wct;
|
||||||
|
} else {
|
||||||
|
WinCardType wct = new WinCardType(type, value,score);
|
||||||
|
map.put(type, wct);
|
||||||
|
return wct;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean checkWin(Map<Integer, WinCardType> map, List<Integer> opCards, List<Integer> cardInhand,int drawCard, int difen) {
|
||||||
|
return checkWin(map,opCards,cardInhand,drawCard,true, difen);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean checkWin(Map<Integer, WinCardType> map, List<Integer> opCards, List<Integer> cardInhand,int drawCard,boolean jiang, int difen) {
|
||||||
|
int qixiaodui = qixiaodui(opCards,cardInhand, drawCard);
|
||||||
|
if (qixiaodui != -1) {
|
||||||
|
putWinCardType(map, WinCardType.QIXIAODUI, qixiaodui,WinCardType.WIN_TYPE_SCORE);
|
||||||
|
if (qingyise(opCards,cardInhand, drawCard)) {
|
||||||
|
putWinCardType(map, WinCardType.QINGYISE, 1,WinCardType.WIN_TYPE_SCORE);
|
||||||
|
}
|
||||||
|
if (jiangjiang(opCards,cardInhand, drawCard)) {
|
||||||
|
putWinCardType(map, WinCardType.JIANGJIANGHU, 1,WinCardType.WIN_TYPE_SCORE);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (jiangjiang(opCards,cardInhand, drawCard)) {
|
||||||
|
putWinCardType(map, WinCardType.JIANGJIANGHU, 1,WinCardType.WIN_TYPE_SCORE);
|
||||||
|
boolean _qqr =quanqiuren(opCards,cardInhand, drawCard);
|
||||||
|
if (_qqr) {
|
||||||
|
putWinCardType(map, WinCardType.QUANQIUREN, 1,WinCardType.WIN_TYPE_SCORE);
|
||||||
|
}
|
||||||
|
if (pongpong(opCards,cardInhand, drawCard,_qqr)) {
|
||||||
|
putWinCardType(map, WinCardType.PENGPENGHU, 1,WinCardType.WIN_TYPE_SCORE);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (quanqiuren(opCards,cardInhand, drawCard)) {
|
||||||
|
putWinCardType(map, WinCardType.QUANQIUREN, 1,WinCardType.WIN_TYPE_SCORE);
|
||||||
|
if (qingyise(opCards,cardInhand, drawCard)) {
|
||||||
|
putWinCardType(map, WinCardType.QINGYISE, 1,WinCardType.WIN_TYPE_SCORE);
|
||||||
|
}
|
||||||
|
if (jiangjiang(opCards,cardInhand, drawCard)) {
|
||||||
|
putWinCardType(map, WinCardType.JIANGJIANGHU, 1,WinCardType.WIN_TYPE_SCORE);
|
||||||
|
}
|
||||||
|
if (pongpong(opCards,cardInhand, drawCard,true)) {
|
||||||
|
putWinCardType(map, WinCardType.PENGPENGHU, 1,WinCardType.WIN_TYPE_SCORE);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (pongpong(opCards,cardInhand, drawCard,false)) {
|
||||||
|
putWinCardType(map, WinCardType.PENGPENGHU, 1,WinCardType.WIN_TYPE_SCORE);
|
||||||
|
if (qingyise(opCards,cardInhand, drawCard)) {
|
||||||
|
putWinCardType(map, WinCardType.QINGYISE, 1,WinCardType.WIN_TYPE_SCORE);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (qingyise(opCards,cardInhand, drawCard)) {
|
||||||
|
WinCard win = new WinCard(cardInhand, drawCard);
|
||||||
|
win.jiang = false;
|
||||||
|
if (win.tryWin()) {
|
||||||
|
putWinCardType(map, WinCardType.QINGYISE, 1,WinCardType.WIN_TYPE_SCORE);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
WinCard win = new WinCard(cardInhand, drawCard);
|
||||||
|
win.jiang = jiang;
|
||||||
|
if (win.tryWin()) {
|
||||||
|
putWinCardType(map, WinCardType.XIAOHU, 1, difen);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static public boolean tingCheck(List<Integer> cardInhand,List<Integer> chowGrop,List<Integer> pengGroup,List<Integer> gangdepai) {
|
||||||
|
List<Integer> listadd = new ArrayList<>();
|
||||||
|
listadd.addAll(chowGrop);
|
||||||
|
listadd.addAll(pengGroup);
|
||||||
|
listadd.addAll(gangdepai);
|
||||||
|
|
||||||
|
if(qixiaodui(listadd,cardInhand, 0)!=-1)return true;
|
||||||
|
|
||||||
|
if (quanqiuren(chowGrop,cardInhand, 0))return true;
|
||||||
|
|
||||||
|
if (chowGrop.size()==0){
|
||||||
|
if (jiangjiang(pengGroup,cardInhand, 0))return true;
|
||||||
|
}
|
||||||
|
if (chowGrop.size()==0){
|
||||||
|
if (pongpong(pengGroup,cardInhand, 0,false))return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (qingyise(listadd,cardInhand, 0)) {
|
||||||
|
WinCard win = new WinCard(cardInhand, 0);
|
||||||
|
win.jiang = false;
|
||||||
|
return win.tryWin();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
//WinCard win = new WinCard(cardInhand, 0);
|
||||||
|
//return win.tryWin();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static public boolean tingKongCheck(List<Integer> opCards,List<Integer> cardInhand,int kongCard,boolean jiang) {
|
||||||
|
if (quanqiuren(opCards,cardInhand, 0))return true;
|
||||||
|
if (jiangjiang(opCards,cardInhand, kongCard))return true;
|
||||||
|
if (pongpong(opCards,cardInhand, 0,false))return true;
|
||||||
|
if (qingyise(opCards,cardInhand, kongCard)) {
|
||||||
|
WinCard win = new WinCard(cardInhand, 0);
|
||||||
|
win.jiang = false;
|
||||||
|
return win.tryWin();
|
||||||
|
}
|
||||||
|
WinCard win = new WinCard(cardInhand, 0);
|
||||||
|
win.jiang = jiang;
|
||||||
|
return win.tryWin();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,674 @@
|
||||||
|
package com.robotcm.taurus.util;
|
||||||
|
|
||||||
|
import com.taurus.core.entity.ITArray;
|
||||||
|
import com.taurus.core.entity.ITObject;
|
||||||
|
import com.robotcm.hunan.HuNanPaoDeKuai;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
|
||||||
|
public class PaoDeKuaiSuanFaTest {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param cardInhand 手牌
|
||||||
|
* @param card_list 别人打的牌
|
||||||
|
* @return 返回可出的牌
|
||||||
|
*/
|
||||||
|
public static ITArray paoDeKuaiOutCardSuanFa(List<CardObj> cardInhand, ITObject card_list, Map<Integer, List<Integer>> seatRemainHistory) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 排序手牌
|
||||||
|
List<CardObj> sortedHand = new ArrayList<>(cardInhand);
|
||||||
|
Collections.sort(sortedHand);
|
||||||
|
List<CardObj> playCards;
|
||||||
|
// 判断是否是首轮出牌或者上一轮是自己出的牌(对面要不起)
|
||||||
|
boolean isFirstTurn = card_list == null || !card_list.containsKey("card_list") ||
|
||||||
|
card_list.getTArray("card_list").size() == 0;
|
||||||
|
|
||||||
|
if (isFirstTurn) {
|
||||||
|
|
||||||
|
// 自己先出牌
|
||||||
|
playCards = playAsFirst(sortedHand);
|
||||||
|
} else {
|
||||||
|
// 解析对手出的牌
|
||||||
|
ITArray opponentCardsArray = card_list.getTArray("card_list");
|
||||||
|
List<CardObj> opponentCards = CardUtil.toList(opponentCardsArray);
|
||||||
|
|
||||||
|
// 获取出牌信息
|
||||||
|
int minCard = card_list.getInt("min_card");
|
||||||
|
int len = card_list.getInt("len");
|
||||||
|
int type = card_list.getInt("type");
|
||||||
|
int seat = HuNanPaoDeKuai.seat;
|
||||||
|
//如果座位号第二次还是自己,代表对面要不起,自己再次出牌
|
||||||
|
if (HuNanPaoDeKuai.seat == HuNanPaoDeKuai.guangboseat) {
|
||||||
|
Map<Integer, Integer> otherSeatsLastRemain = getOtherSeatsLastRemain(seat, seatRemainHistory);
|
||||||
|
boolean hasAnyEqualOne = otherSeatsLastRemain.values().stream()
|
||||||
|
.anyMatch(value -> value == 1);
|
||||||
|
if (hasAnyEqualOne) {
|
||||||
|
|
||||||
|
ITArray itArray = null;
|
||||||
|
CardObj maxSingleCard = CardUtil.findMaxSingleCard(cardInhand);
|
||||||
|
itArray = CardUtil.toTArray1(maxSingleCard);
|
||||||
|
return itArray;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
playCards = playAsFirst(sortedHand);
|
||||||
|
return CardUtil.toTArray(playCards);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//座位号不是第二次自己的情况
|
||||||
|
//对面剩一张牌时,需要出最大牌
|
||||||
|
Map<Integer, Integer> otherSeatsLastRemain = getOtherSeatsLastRemain(seat, seatRemainHistory);
|
||||||
|
boolean hasAnyEqualOne = otherSeatsLastRemain.values().stream()
|
||||||
|
.anyMatch(value -> value == 1);
|
||||||
|
|
||||||
|
if (hasAnyEqualOne && type == 1) {
|
||||||
|
|
||||||
|
ITArray itArray = null;
|
||||||
|
CardObj maxSingleCard = CardUtil.findMaxSingleCard(cardInhand);
|
||||||
|
itArray = CardUtil.toTArray1(maxSingleCard);
|
||||||
|
return itArray;
|
||||||
|
} else {
|
||||||
|
playCards = playInResponse(sortedHand, minCard, len, opponentCards, type);
|
||||||
|
return CardUtil.toTArray(playCards);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return CardUtil.toTArray(playCards);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取除当前座位外所有其他座位的最后记录
|
||||||
|
public static Map<Integer, Integer> getOtherSeatsLastRemain(int currentSeat, Map<Integer, List<Integer>> seatRemainHistory) {
|
||||||
|
Map<Integer, Integer> result = new HashMap<>();
|
||||||
|
|
||||||
|
for (Map.Entry<Integer, List<Integer>> entry : seatRemainHistory.entrySet()) {
|
||||||
|
int seat = entry.getKey();
|
||||||
|
List<Integer> remainList = entry.getValue();
|
||||||
|
|
||||||
|
// 排除当前座位
|
||||||
|
if (seat != currentSeat && !remainList.isEmpty()) {
|
||||||
|
// 取list最后一个记录
|
||||||
|
int lastRemain = remainList.get(remainList.size() - 1);
|
||||||
|
result.put(seat, lastRemain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 响应出牌
|
||||||
|
*/
|
||||||
|
private static List<CardObj> playInResponse(List<CardObj> handCards, int minCard, int len,
|
||||||
|
List<CardObj> opponentCards, int type) {
|
||||||
|
if (handCards.isEmpty()) return new ArrayList<>();
|
||||||
|
|
||||||
|
List<List<CardObj>> possiblePlays = findPossiblePlays(handCards, minCard, len, type);
|
||||||
|
|
||||||
|
if (possiblePlays.isEmpty()) {
|
||||||
|
return new ArrayList<>(); // 要不起
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选择最优出牌
|
||||||
|
return selectBestPlay(possiblePlays, type, handCards.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找所有可能的出牌
|
||||||
|
*/
|
||||||
|
private static List<List<CardObj>> findPossiblePlays(List<CardObj> handCards, int minCard, int len, int type) {
|
||||||
|
List<List<CardObj>> result = new ArrayList<>();
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case Config.TYPE_DANPAI:
|
||||||
|
result.addAll(findSinglesAbove(handCards, minCard));
|
||||||
|
break;
|
||||||
|
case Config.TYPE_DUIZI:
|
||||||
|
result.addAll(findPairsAbove(handCards, minCard));
|
||||||
|
break;
|
||||||
|
case Config.TYPE_SHUNZI:
|
||||||
|
result.addAll(findStraightsAbove(handCards, minCard, len));
|
||||||
|
break;
|
||||||
|
case Config.TYPE_LIANDUI:
|
||||||
|
result.addAll(findConsecutivePairsAbove(handCards, minCard, len));
|
||||||
|
break;
|
||||||
|
case Config.TYPE_3_1:
|
||||||
|
result.addAll(findTrioWithOneAbove(handCards, minCard));
|
||||||
|
break;
|
||||||
|
case Config.TYPE_FEIJI:
|
||||||
|
result.addAll(findTrioWithPairAbove(handCards, minCard));
|
||||||
|
break;
|
||||||
|
case Config.TYPE_ZHA:
|
||||||
|
result.addAll(findBombsAbove(handCards, minCard));
|
||||||
|
break;
|
||||||
|
case Config.TYPE_4_2_dui:
|
||||||
|
result.addAll(findFourWithTwoPairsAbove(handCards, minCard));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// 如果正常牌型找不到,且当前不是炸弹类型,就查找炸弹
|
||||||
|
if (result.isEmpty() && type != Config.TYPE_ZHA) {
|
||||||
|
result.addAll(findBombsAbove(handCards, -1)); // 找所有炸弹
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找大于指定值的单牌
|
||||||
|
*/
|
||||||
|
private static List<List<CardObj>> findSinglesAbove(List<CardObj> handCards, int minValue) {
|
||||||
|
List<List<CardObj>> result = new ArrayList<>();
|
||||||
|
for (CardObj card : handCards) {
|
||||||
|
if (card.cardMod > minValue) {
|
||||||
|
result.add(Arrays.asList(card));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找大于指定值的对子
|
||||||
|
*/
|
||||||
|
private static List<List<CardObj>> findPairsAbove(List<CardObj> handCards, int minValue) {
|
||||||
|
List<List<CardObj>> result = new ArrayList<>();
|
||||||
|
Map<Integer, List<CardObj>> valueGroups = groupByValue(handCards);
|
||||||
|
|
||||||
|
for (List<CardObj> cards : valueGroups.values()) {
|
||||||
|
if (cards.size() >= 2 && cards.get(0).cardMod > minValue) {
|
||||||
|
result.add(Arrays.asList(cards.get(0), cards.get(1)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按牌值分组
|
||||||
|
*/
|
||||||
|
private static Map<Integer, List<CardObj>> groupByValue(List<CardObj> handCards) {
|
||||||
|
Map<Integer, List<CardObj>> groups = new HashMap<>();
|
||||||
|
for (CardObj card : handCards) {
|
||||||
|
groups.computeIfAbsent(card.cardMod, k -> new ArrayList<>()).add(card);
|
||||||
|
}
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找大于指定值的顺子
|
||||||
|
*/
|
||||||
|
private static List<List<CardObj>> findStraightsAbove(List<CardObj> handCards, int minValue, int length) {
|
||||||
|
List<List<CardObj>> result = new ArrayList<>();
|
||||||
|
List<Integer> distinctValues = handCards.stream()
|
||||||
|
.map(c -> c.cardMod)
|
||||||
|
.distinct()
|
||||||
|
.sorted()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
for (int i = 0; i <= distinctValues.size() - length; i++) {
|
||||||
|
if (isConsecutive(distinctValues, i, length) && distinctValues.get(i) > minValue) {
|
||||||
|
result.add(buildStraight(handCards, distinctValues.subList(i, i + length)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 庄家先出牌策略
|
||||||
|
*/
|
||||||
|
private static List<CardObj> playAsFirst(List<CardObj> handCards) {
|
||||||
|
if (handCards.isEmpty()) return new ArrayList<>();
|
||||||
|
|
||||||
|
Map<Integer, Integer> valueCount = getValueCount(handCards);
|
||||||
|
|
||||||
|
// 策略优先级:顺子 → 三带2 → 单牌 → 对子 → 最小牌
|
||||||
|
List<CardObj> play = tryPlayStraight(handCards);
|
||||||
|
if (!play.isEmpty()) return play;
|
||||||
|
|
||||||
|
play = tryPlayTrioWithOne(handCards, valueCount);
|
||||||
|
if (!play.isEmpty()) return play;
|
||||||
|
|
||||||
|
play = tryPlaySingle(handCards, valueCount);
|
||||||
|
if (!play.isEmpty()) return play;
|
||||||
|
|
||||||
|
play = tryPlayPair(handCards, valueCount);
|
||||||
|
if (!play.isEmpty()) return play;
|
||||||
|
|
||||||
|
|
||||||
|
//出最小的牌
|
||||||
|
return Arrays.asList(handCards.get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统计牌值数量
|
||||||
|
*/
|
||||||
|
private static Map<Integer, Integer> getValueCount(List<CardObj> cards) {
|
||||||
|
Map<Integer, Integer> countMap = new HashMap<>();
|
||||||
|
for (CardObj card : cards) {
|
||||||
|
countMap.put(card.cardMod, countMap.getOrDefault(card.cardMod, 0) + 1);
|
||||||
|
}
|
||||||
|
return countMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 尝试出单牌
|
||||||
|
*/
|
||||||
|
private static List<CardObj> tryPlaySingle(List<CardObj> handCards, Map<Integer, Integer> valueCount) {
|
||||||
|
|
||||||
|
handCards.sort(Comparator.comparingInt(c -> c.cardMod));
|
||||||
|
|
||||||
|
// 找出所有可能的出牌选择(真单牌 + 对子拆出的单牌)
|
||||||
|
List<CardObj> allOptions = new ArrayList<>();
|
||||||
|
|
||||||
|
// 添加所有真单牌
|
||||||
|
for (CardObj card : handCards) {
|
||||||
|
if (valueCount.get(card.cardMod) == 1) {
|
||||||
|
allOptions.add(card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加对子拆出的单牌(每个对子取一张)
|
||||||
|
for (CardObj card : handCards) {
|
||||||
|
if (valueCount.get(card.cardMod) >= 2) {
|
||||||
|
// 确保不重复添加同一个对子
|
||||||
|
if (allOptions.stream().noneMatch(c -> c.cardMod == card.cardMod)) {
|
||||||
|
allOptions.add(card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 排序选择
|
||||||
|
allOptions.sort(Comparator.comparingInt(c -> c.cardMod));
|
||||||
|
|
||||||
|
if (!allOptions.isEmpty()) {
|
||||||
|
CardObj bestChoice = allOptions.get(0);
|
||||||
|
boolean isFromPair = valueCount.get(bestChoice.cardMod) > 1;
|
||||||
|
|
||||||
|
if (isFromPair) {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
return Arrays.asList(bestChoice);
|
||||||
|
}
|
||||||
|
return Arrays.asList(handCards.get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 尝试出对子
|
||||||
|
*/
|
||||||
|
private static List<CardObj> tryPlayPair(List<CardObj> handCards, Map<Integer, Integer> valueCount) {
|
||||||
|
for (CardObj card : handCards) {
|
||||||
|
if (valueCount.get(card.cardMod) >= 2) {
|
||||||
|
// 返回前两张同值的牌
|
||||||
|
return handCards.stream()
|
||||||
|
.filter(c -> c.cardMod == card.cardMod)
|
||||||
|
.limit(2)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 尝试出顺子
|
||||||
|
*/
|
||||||
|
private static List<CardObj> tryPlayStraight(List<CardObj> handCards) {
|
||||||
|
final int MIN_STRAIGHT_VALUE = 3;
|
||||||
|
final int MAX_STRAIGHT_VALUE = 14;
|
||||||
|
|
||||||
|
List<Integer> distinctValues = handCards.stream()
|
||||||
|
.map(c -> c.cardMod)
|
||||||
|
.filter(value -> value >= MIN_STRAIGHT_VALUE && value <= MAX_STRAIGHT_VALUE)
|
||||||
|
.distinct()
|
||||||
|
.sorted()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 如果可用牌值少于5张,直接返回
|
||||||
|
if (distinctValues.size() < 5) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
int maxPossibleLength = Math.min(distinctValues.size(), 12); // 最多12张
|
||||||
|
|
||||||
|
for (int length = maxPossibleLength; length >= 5; length--) {
|
||||||
|
for (int i = 0; i <= distinctValues.size() - length; i++) {
|
||||||
|
if (isConsecutive(distinctValues, i, length)) {
|
||||||
|
List<Integer> straightValues = distinctValues.subList(i, i + length);
|
||||||
|
return buildStraight(handCards, straightValues);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 尝试出三带一
|
||||||
|
*/
|
||||||
|
private static List<CardObj> tryPlayTrioWithOne(List<CardObj> handCards, Map<Integer, Integer> valueCount) {
|
||||||
|
// 先按牌值排序(从小到大)
|
||||||
|
handCards.sort(Comparator.comparingInt(c -> c.cardMod));
|
||||||
|
|
||||||
|
// 查找最小的三张
|
||||||
|
for (CardObj card : handCards) {
|
||||||
|
if (valueCount.getOrDefault(card.cardMod, 0) >= 3) {
|
||||||
|
List<CardObj> trio = handCards.stream()
|
||||||
|
.filter(c -> c.cardMod == card.cardMod)
|
||||||
|
.limit(3)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
// 寻找两张带牌,优先不破坏对子和顺子牌型
|
||||||
|
List<CardObj> sideCards = findBestSideCardsForTrio(handCards, card.cardMod, valueCount);
|
||||||
|
|
||||||
|
if (sideCards.size() >= 2) {
|
||||||
|
List<CardObj> result = new ArrayList<>(trio);
|
||||||
|
result.addAll(sideCards.subList(0, 2));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<CardObj> findBestSideCardsForTrio(List<CardObj> handCards, int trioValue,
|
||||||
|
Map<Integer, Integer> valueCount) {
|
||||||
|
List<CardObj> candidateCards = new ArrayList<>();
|
||||||
|
|
||||||
|
// 收集所有不是三张牌本身的牌
|
||||||
|
for (CardObj card : handCards) {
|
||||||
|
if (card.cardMod != trioValue) {
|
||||||
|
candidateCards.add(card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按优先级选择带牌策略
|
||||||
|
List<CardObj> bestChoice = new ArrayList<>();
|
||||||
|
|
||||||
|
// 策略1:优先选择单张牌(不破坏对子)
|
||||||
|
List<CardObj> singleCards = findSingleCards(candidateCards, valueCount);
|
||||||
|
if (singleCards.size() >= 2) {
|
||||||
|
return singleCards.subList(0, Math.min(2, singleCards.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 策略2:选择最小的对子(如果只剩一个对子,拆开带)
|
||||||
|
List<CardObj> pairCards = findSmallestPair(candidateCards, valueCount);
|
||||||
|
if (!pairCards.isEmpty()) {
|
||||||
|
if (pairCards.size() >= 2) {
|
||||||
|
return pairCards.subList(0, 2); // 带整个对子
|
||||||
|
} else {
|
||||||
|
bestChoice.add(pairCards.get(0)); // 先带一张
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 策略3:如果还不够,选择其他单张
|
||||||
|
for (CardObj card : candidateCards) {
|
||||||
|
if (bestChoice.size() < 2 && !bestChoice.contains(card)) {
|
||||||
|
bestChoice.add(card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bestChoice;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<CardObj> findSingleCards(List<CardObj> cards, Map<Integer, Integer> valueCount) {
|
||||||
|
List<CardObj> singleCards = new ArrayList<>();
|
||||||
|
|
||||||
|
// 找出真正的单张牌(没有其他同值的牌)
|
||||||
|
for (CardObj card : cards) {
|
||||||
|
if (valueCount.getOrDefault(card.cardMod, 0) == 1) {
|
||||||
|
singleCards.add(card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按牌值排序
|
||||||
|
singleCards.sort(Comparator.comparingInt(c -> c.cardMod));
|
||||||
|
return singleCards;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<CardObj> findSmallestPair(List<CardObj> cards, Map<Integer, Integer> valueCount) {
|
||||||
|
List<CardObj> pairCards = new ArrayList<>();
|
||||||
|
|
||||||
|
// 找出最小的对子
|
||||||
|
for (CardObj card : cards) {
|
||||||
|
if (valueCount.getOrDefault(card.cardMod, 0) >= 2) {
|
||||||
|
List<CardObj> pair = cards.stream()
|
||||||
|
.filter(c -> c.cardMod == card.cardMod)
|
||||||
|
.limit(2)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
// 如果这个对子还没被加入,且比当前选择更小
|
||||||
|
if (pairCards.isEmpty() ||
|
||||||
|
(pair.size() >= 2 && pair.get(0).cardMod < pairCards.get(0).cardMod)) {
|
||||||
|
pairCards = pair;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pairCards;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否连续
|
||||||
|
*/
|
||||||
|
private static boolean isConsecutive(List<Integer> values, int start, int length) {
|
||||||
|
for (int i = start; i < start + length - 1; i++) {
|
||||||
|
if (values.get(i + 1) - values.get(i) != 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建顺子
|
||||||
|
*/
|
||||||
|
private static List<CardObj> buildStraight(List<CardObj> handCards, List<Integer> values) {
|
||||||
|
List<CardObj> straight = new ArrayList<>();
|
||||||
|
for (int value : values) {
|
||||||
|
// 取第一张该值的牌
|
||||||
|
for (CardObj card : handCards) {
|
||||||
|
if (card.cardMod == value) {
|
||||||
|
straight.add(card);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return straight;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 选择最优出牌
|
||||||
|
*/
|
||||||
|
private static List<CardObj> selectBestPlay(List<List<CardObj>> possiblePlays, int type, int handSize) {
|
||||||
|
if (possiblePlays.isEmpty()) return new ArrayList<>();
|
||||||
|
//优先出小牌,保留大牌
|
||||||
|
possiblePlays.sort((a, b) -> {
|
||||||
|
int maxA = a.stream().mapToInt(c -> c.cardMod).max().orElse(0);
|
||||||
|
int maxB = b.stream().mapToInt(c -> c.cardMod).max().orElse(0);
|
||||||
|
return Integer.compare(maxA, maxB);
|
||||||
|
});
|
||||||
|
// 手牌少时优先出牌数多的
|
||||||
|
if (handSize <= 5) {
|
||||||
|
possiblePlays.sort((a, b) -> Integer.compare(b.size(), a.size()));
|
||||||
|
return possiblePlays.get(0);
|
||||||
|
}
|
||||||
|
return possiblePlays.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 各种牌型的查找方法实现
|
||||||
|
private static List<List<CardObj>> findConsecutivePairsAbove(List<CardObj> handCards, int minValue, int pairCount) {
|
||||||
|
List<List<CardObj>> result = new ArrayList<>();
|
||||||
|
if (handCards.isEmpty() || pairCount < 2) return result;
|
||||||
|
|
||||||
|
// 按牌值排序
|
||||||
|
handCards.sort(Comparator.comparingInt(c -> c.cardMod));
|
||||||
|
|
||||||
|
Map<Integer, List<CardObj>> valueGroups = new HashMap<>();
|
||||||
|
for (CardObj card : handCards) {
|
||||||
|
valueGroups.computeIfAbsent(card.cardMod, k -> new ArrayList<>()).add(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 找出所有至少有2张的牌值(对子)
|
||||||
|
List<Integer> pairValues = new ArrayList<>();
|
||||||
|
for (Map.Entry<Integer, List<CardObj>> entry : valueGroups.entrySet()) {
|
||||||
|
if (entry.getValue().size() >= 2) {
|
||||||
|
pairValues.add(entry.getKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pairValues.size() < pairCount) return result;
|
||||||
|
|
||||||
|
Collections.sort(pairValues);
|
||||||
|
|
||||||
|
for (int i = 0; i <= pairValues.size() - pairCount; i++) {
|
||||||
|
// 检查是否连续
|
||||||
|
boolean isConsecutive = true;
|
||||||
|
for (int j = i; j < i + pairCount - 1; j++) {
|
||||||
|
if (pairValues.get(j + 1) - pairValues.get(j) != 1) {
|
||||||
|
isConsecutive = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调试信息
|
||||||
|
|
||||||
|
|
||||||
|
// 如果连续且最小值大于指定值
|
||||||
|
if (isConsecutive && pairValues.get(i) > minValue) {
|
||||||
|
List<CardObj> consecutivePairs = new ArrayList<>();
|
||||||
|
|
||||||
|
// 添加每个对子的前两张牌
|
||||||
|
for (int k = i; k < i + pairCount; k++) {
|
||||||
|
int value = pairValues.get(k);
|
||||||
|
List<CardObj> pair = valueGroups.get(value).subList(0, 2);
|
||||||
|
consecutivePairs.addAll(pair);
|
||||||
|
}
|
||||||
|
|
||||||
|
result.add(consecutivePairs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<List<CardObj>> findTrioWithOneAbove(List<CardObj> handCards, int minValue) {
|
||||||
|
List<List<CardObj>> result = new ArrayList<>();
|
||||||
|
Map<Integer, List<CardObj>> valueGroups = groupByValue(handCards);
|
||||||
|
|
||||||
|
// 找出所有三张的牌
|
||||||
|
List<Integer> trioValues = new ArrayList<>();
|
||||||
|
for (Map.Entry<Integer, List<CardObj>> entry : valueGroups.entrySet()) {
|
||||||
|
if (entry.getValue().size() >= 3 && entry.getKey() > minValue) {
|
||||||
|
trioValues.add(entry.getKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Collections.sort(trioValues);
|
||||||
|
|
||||||
|
for (int trioValue : trioValues) {
|
||||||
|
List<CardObj> trio = valueGroups.get(trioValue).subList(0, 3);
|
||||||
|
|
||||||
|
// 找出所有可带的牌(排除三张的牌值)
|
||||||
|
List<CardObj> availableCards = new ArrayList<>();
|
||||||
|
for (Map.Entry<Integer, List<CardObj>> entry : valueGroups.entrySet()) {
|
||||||
|
if (entry.getKey() != trioValue) {
|
||||||
|
availableCards.addAll(entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (availableCards.size() < 2) continue;
|
||||||
|
|
||||||
|
// 找出所有可能的两个牌组合(可以是一对或两个单牌)
|
||||||
|
findTrioCombinations(result, trio, availableCards);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 找出所有可能的三带2组合
|
||||||
|
*/
|
||||||
|
private static void findTrioCombinations(List<List<CardObj>> result,
|
||||||
|
List<CardObj> trio,
|
||||||
|
List<CardObj> availableCards) {
|
||||||
|
availableCards.sort(CardObj::compareTo);
|
||||||
|
|
||||||
|
Set<String> combinationSet = new HashSet<>();
|
||||||
|
|
||||||
|
for (int i = 0; i < availableCards.size() - 1; i++) {
|
||||||
|
CardObj card1 = availableCards.get(i);
|
||||||
|
|
||||||
|
for (int j = i + 1; j < availableCards.size(); j++) {
|
||||||
|
CardObj card2 = availableCards.get(j);
|
||||||
|
|
||||||
|
if (card1.card != card2.card) {
|
||||||
|
String comboKey;
|
||||||
|
if (card1.cardMod <= card2.cardMod) {
|
||||||
|
comboKey = card1.cardMod + "-" + card2.cardMod;
|
||||||
|
} else {
|
||||||
|
comboKey = card2.cardMod + "-" + card1.cardMod;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!combinationSet.contains(comboKey)) {
|
||||||
|
combinationSet.add(comboKey);
|
||||||
|
|
||||||
|
List<CardObj> play = new ArrayList<>(trio);
|
||||||
|
play.add(card1);
|
||||||
|
play.add(card2);
|
||||||
|
result.add(play);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<List<CardObj>> findTrioWithPairAbove(List<CardObj> handCards, int minValue) {
|
||||||
|
List<List<CardObj>> result = new ArrayList<>();
|
||||||
|
// 飞机逻辑
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static List<List<CardObj>> findBombsAbove(List<CardObj> handCards, int minValue) {
|
||||||
|
List<List<CardObj>> result = new ArrayList<>();
|
||||||
|
|
||||||
|
// 先按牌值排序
|
||||||
|
handCards.sort(Comparator.comparingInt(c -> c.cardMod));
|
||||||
|
|
||||||
|
// 统计每种牌值的数量
|
||||||
|
Map<Integer, List<CardObj>> valueGroups = new HashMap<>();
|
||||||
|
for (CardObj card : handCards) {
|
||||||
|
valueGroups.computeIfAbsent(card.cardMod, k -> new ArrayList<>()).add(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 找出所有炸弹(4张或以上相同牌值)
|
||||||
|
for (Map.Entry<Integer, List<CardObj>> entry : valueGroups.entrySet()) {
|
||||||
|
int cardValue = entry.getKey();
|
||||||
|
List<CardObj> cards = entry.getValue();
|
||||||
|
|
||||||
|
// 如果是炸弹(4张或以上)且大于minValue
|
||||||
|
if (cards.size() >= 4 && cardValue > minValue) {
|
||||||
|
result.add(cards);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<List<CardObj>> findFourWithTwoSinglesAbove(List<CardObj> handCards, int minValue) {
|
||||||
|
List<List<CardObj>> result = new ArrayList<>();
|
||||||
|
// 实现四带二单牌逻辑
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<List<CardObj>> findFourWithTwoPairsAbove(List<CardObj> handCards, int minValue) {
|
||||||
|
List<List<CardObj>> result = new ArrayList<>();
|
||||||
|
// 实现四带二对子逻辑
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,94 @@
|
||||||
|
package com.robotcm.taurus.util;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class PlayerState {
|
||||||
|
public List<Integer> handCards; // 当前手牌
|
||||||
|
public List<List<Integer>> pongGroups; // 碰的牌组(每组3张)
|
||||||
|
public List<List<Integer>> gangGroups; // 杠的牌组(每组4张)
|
||||||
|
public List<List<Integer>> chiGroups; // 吃的牌组(每组3张)
|
||||||
|
public boolean isZhuang;
|
||||||
|
|
||||||
|
|
||||||
|
public PlayerState() {
|
||||||
|
this.handCards = new ArrayList<>();
|
||||||
|
this.pongGroups = new ArrayList<>();
|
||||||
|
this.gangGroups = new ArrayList<>();
|
||||||
|
this.chiGroups = new ArrayList<>();
|
||||||
|
this.isZhuang = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PlayerState(List<Integer> handCards) {
|
||||||
|
this();
|
||||||
|
this.handCards = new ArrayList<>(handCards);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加碰牌组
|
||||||
|
public void addPongGroup(List<Integer> pongGroup) {
|
||||||
|
if (pongGroup.size() == 3) {
|
||||||
|
this.pongGroups.add(new ArrayList<>(pongGroup));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加杠牌组
|
||||||
|
public void addGangGroup(List<Integer> gangGroup) {
|
||||||
|
if (gangGroup.size() == 4) {
|
||||||
|
this.gangGroups.add(new ArrayList<>(gangGroup));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加吃牌组
|
||||||
|
public void addChiGroup(List<Integer> chiGroup) {
|
||||||
|
if (chiGroup.size() == 3) {
|
||||||
|
this.chiGroups.add(new ArrayList<>(chiGroup));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取所有碰牌(扁平化)
|
||||||
|
public List<Integer> getAllPongCards() {
|
||||||
|
List<Integer> all = new ArrayList<>();
|
||||||
|
for (List<Integer> group : pongGroups) {
|
||||||
|
all.addAll(group);
|
||||||
|
}
|
||||||
|
return all;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取所有杠牌(扁平化)
|
||||||
|
public List<Integer> getAllGangCards() {
|
||||||
|
List<Integer> all = new ArrayList<>();
|
||||||
|
for (List<Integer> group : gangGroups) {
|
||||||
|
all.addAll(group);
|
||||||
|
}
|
||||||
|
return all;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取所有吃牌(扁平化)
|
||||||
|
public List<Integer> getAllChiCards() {
|
||||||
|
List<Integer> all = new ArrayList<>();
|
||||||
|
for (List<Integer> group : chiGroups) {
|
||||||
|
all.addAll(group);
|
||||||
|
}
|
||||||
|
return all;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取碰牌组数
|
||||||
|
public int getPongGroupCount() {
|
||||||
|
return pongGroups.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取杠牌组数
|
||||||
|
public int getGangGroupCount() {
|
||||||
|
return gangGroups.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取吃牌组数
|
||||||
|
public int getChiGroupCount() {
|
||||||
|
return chiGroups.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取总组数
|
||||||
|
public int getTotalGroupCount() {
|
||||||
|
return pongGroups.size() + gangGroups.size() + chiGroups.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
package com.robotcm.taurus.util;
|
||||||
|
|
||||||
|
public class ROBOTEventType {
|
||||||
|
/**
|
||||||
|
* 机器人状态
|
||||||
|
*/
|
||||||
|
public static final int ROBOT_INTOROOM_READY = 1;//等待状态
|
||||||
|
public static final int ROBOT_INTOROOM_WORKING = 2;//工作状态
|
||||||
|
public static final int ROBOT_UNUSE = 0;//未使用状态
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,994 @@
|
||||||
|
package com.robotcm.taurus.util;
|
||||||
|
|
||||||
|
import com.game.Util;
|
||||||
|
import com.robotcm.hunan.HuNanChangSha;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public class TinHuChi {
|
||||||
|
public static boolean isMoreThanLast = false;
|
||||||
|
public static int lastTingCount = 0;
|
||||||
|
|
||||||
|
|
||||||
|
public static int[][] countTiles(List<Integer> cardInHand) {
|
||||||
|
int[][] counts = new int[5][10]; // 类型×值
|
||||||
|
|
||||||
|
for (Integer card : cardInHand) {
|
||||||
|
if (card == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
counts[card / 100 - 1][card % 100]++;
|
||||||
|
}
|
||||||
|
return counts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isJiangPai(int card){
|
||||||
|
if (card%100==2||card%100==5||card%100==8){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int checkduijiang(List<Integer> cardInHand) {
|
||||||
|
Map<Integer, Integer> countMap = new HashMap<>();
|
||||||
|
for (Integer item : cardInHand) {
|
||||||
|
countMap.put(item, countMap.getOrDefault(item, 0) + 1);
|
||||||
|
}
|
||||||
|
int jiangnum = 0;
|
||||||
|
for (int key : countMap.keySet()) {
|
||||||
|
if (isJiangPai(key)&&countMap.get(key)>=2){
|
||||||
|
jiangnum++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return jiangnum;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试方法
|
||||||
|
*/
|
||||||
|
public static void main(String[] args) {
|
||||||
|
// System.out.println("=== 测试开始 ===\n");
|
||||||
|
//
|
||||||
|
// // 测试1:你的例子
|
||||||
|
List<Integer> hand1 = new ArrayList<>();
|
||||||
|
|
||||||
|
// hand1.add(209);
|
||||||
|
hand1.add(208);
|
||||||
|
hand1.add(204);
|
||||||
|
// hand1.add(204);
|
||||||
|
|
||||||
|
hand1.add(203);
|
||||||
|
hand1.add(203);
|
||||||
|
// hand1.add(202);
|
||||||
|
|
||||||
|
hand1.add(201);
|
||||||
|
hand1.add(108);
|
||||||
|
hand1.add(105);
|
||||||
|
|
||||||
|
|
||||||
|
hand1.add(104);
|
||||||
|
hand1.add(104);
|
||||||
|
hand1.add(103);
|
||||||
|
// hand1.add(102);
|
||||||
|
|
||||||
|
|
||||||
|
int addcard =203;
|
||||||
|
|
||||||
|
Map<String, Object> map = new HashMap<>();
|
||||||
|
//碰之后的map
|
||||||
|
Map<String, Object> map2 = new HashMap<>();
|
||||||
|
|
||||||
|
//先判断碰之前还需要几手牌,以及孤章
|
||||||
|
// int jiangnum = checkduijiang(changShaCardInhand);
|
||||||
|
List<Integer> tmpChangSch = new ArrayList<>();
|
||||||
|
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"));
|
||||||
|
|
||||||
|
|
||||||
|
//碰之后
|
||||||
|
List<Integer> pengtemphand = new ArrayList<>();
|
||||||
|
pengtemphand.addAll(tmpChangSch);
|
||||||
|
Util.removeCard(pengtemphand, addcard, 3);
|
||||||
|
|
||||||
|
ChangshaWinSplitCard.checkNormalHu(pengtemphand, map2);
|
||||||
|
//判断两个map是否找到更优
|
||||||
|
System.out.println("碰之前 map1: 手数" + Integer.parseInt(map.get("remainingMelds").toString()));
|
||||||
|
System.out.println("碰之后 map2 手数:" + Integer.parseInt(map2.get("remainingMelds").toString()));
|
||||||
|
//碰之后的手数小于碰之前的手数,可以碰
|
||||||
|
if (Integer.parseInt(map2.get("remainingMelds").toString()) < Integer.parseInt(map.get("remainingMelds").toString())) {
|
||||||
|
System.out.println("===============碰之后的手数小于碰之前的手数,可以碰 决定碰牌================== ++++ ");
|
||||||
|
} else if (Integer.parseInt(map2.get("remainingMelds").toString()) == Integer.parseInt(map.get("remainingMelds").toString())) { //碰完后和碰之前手数相等,需要判断孤章数量
|
||||||
|
//碰之后的数量
|
||||||
|
int size2 = ((List<Integer>) map2.get("cardResiue")).size();
|
||||||
|
System.out.println("碰之后的孤章数量 " + size2);
|
||||||
|
|
||||||
|
int size1 = ((List<Integer>) map.get("cardResiue")).size();
|
||||||
|
System.out.println("碰之前的孤章数量 " + size1);
|
||||||
|
if (size2 < size1) {
|
||||||
|
System.out.println("===============碰之后的孤章数量 小于 碰之前的孤章数量可以碰 ================== ++++ ");
|
||||||
|
}
|
||||||
|
//碰之后的手数大于碰之前的手数,或者碰之后的孤章数量大于碰之前的孤章数量不能碰
|
||||||
|
} else if (Integer.parseInt(map2.get("remainingMelds").toString()) > Integer.parseInt(map.get("remainingMelds").toString()) || ((List<Integer>) map2.get("cardResiue")).size() > ((List<Integer>) map.get("cardResiue")).size()) {
|
||||||
|
System.out.println("碰之后的手数大于碰之前的手数,或者碰之后的孤章数量大于碰之前的孤章数量不能碰");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// List<Integer> temphand = new ArrayList<>();
|
||||||
|
// temphand.addAll(hand1);
|
||||||
|
// temphand.add(104);
|
||||||
|
// Util.removeCard(temphand, 104, 3);
|
||||||
|
// List<Integer> checktingpai1 = TinHuChi.checktingpai(temphand);
|
||||||
|
// System.out.println("checktingpai1" + checktingpai1);
|
||||||
|
|
||||||
|
//hand1.add(101);
|
||||||
|
|
||||||
|
// Map<String, Object> map = new HashMap<>();
|
||||||
|
// Map<String, Object> map2 = new HashMap<>();
|
||||||
|
//
|
||||||
|
// List<List<Integer>> lists = checkChi(hand1, addcard);
|
||||||
|
// //吃之前的逻辑
|
||||||
|
// int jiangnum = checkduijiang(hand1);
|
||||||
|
// hand1.add(addcard);
|
||||||
|
// System.out.println(hand1);
|
||||||
|
// ChangshaWinSplitCard.checkNormalHu(hand1, map);
|
||||||
|
//
|
||||||
|
// System.out.println("checkNormalHu" + map.get("cardResiue"));
|
||||||
|
//
|
||||||
|
// System.out.println("checktingpai1" + lists);
|
||||||
|
// int index = 0;
|
||||||
|
// int flag = 0;
|
||||||
|
// for (List<Integer> list : lists) {
|
||||||
|
// List<Integer> temphand = hand1;
|
||||||
|
// //temphand.add(addcard);
|
||||||
|
// Util.removeCard(temphand,list.get(0),1);
|
||||||
|
// Util.removeCard(temphand,list.get(1),1);
|
||||||
|
// Util.removeCard(temphand,list.get(2),1);
|
||||||
|
// System.out.println("temphand" + temphand);
|
||||||
|
// ChangshaWinSplitCard.checkNormalHu(temphand, map2);
|
||||||
|
// //判断两个map是否找到更优
|
||||||
|
// System.out.println("map1:"+Integer.parseInt(map.get("remainingMelds").toString()));
|
||||||
|
// System.out.println("map2:"+Integer.parseInt(map2.get("remainingMelds").toString()));
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// if (Integer.parseInt(map2.get("remainingMelds").toString()) < Integer.parseInt(map.get("remainingMelds").toString()) ){
|
||||||
|
// flag=index+1;
|
||||||
|
// }else if (Integer.parseInt(map2.get("remainingMelds").toString()) == Integer.parseInt(map.get("remainingMelds").toString())){
|
||||||
|
// int size2 = ((List<Integer>) map2.get("cardResiue")).size();
|
||||||
|
// System.out.println("size2" + size2);
|
||||||
|
// int size1 = ((List<Integer>) map.get("cardResiue")).size();
|
||||||
|
// System.out.println("size1" + size1);
|
||||||
|
// if (size2 < size1){
|
||||||
|
// flag=index+1;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// //如果手里没有将,则可以吃
|
||||||
|
// if (jiangnum>0) {
|
||||||
|
// int chihoujiangnum = checkduijiang(temphand);
|
||||||
|
// if (chihoujiangnum>0){
|
||||||
|
// //吃之后还有将
|
||||||
|
// flag=index+1;
|
||||||
|
// }
|
||||||
|
// }else{
|
||||||
|
// System.out.println("没队将");
|
||||||
|
// //孤章1张 差一手 不是将 而且card不是将
|
||||||
|
// if (Integer.parseInt(map.get("remainingMelds").toString())==1&&size1==1&&!isJiangPai(((List<Integer>) map.get("cardResiue")).get(0))&&!isJiangPai(addcard)){
|
||||||
|
// //吃
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// flag=index+1;
|
||||||
|
// // break;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// index ++ ;
|
||||||
|
// System.out.println("checkNormalHu2" + map2);
|
||||||
|
// }
|
||||||
|
// System.out.println("flag" + flag);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*List<Integer> shifoutingpai = shifoutingpai(hand1);
|
||||||
|
System.out.println("shifoutingpai" + shifoutingpai);
|
||||||
|
//
|
||||||
|
// List<Integer> checktingpai = checktingpai(hand1);
|
||||||
|
// System.out.println("checktingpai" + checktingpai);
|
||||||
|
//
|
||||||
|
//
|
||||||
|
List<List<Integer>> lists = checkChi(hand1, addcard);
|
||||||
|
int index = 0;
|
||||||
|
int flag = 0;
|
||||||
|
for (List<Integer> list : lists) {
|
||||||
|
List<Integer> temphand = hand1;
|
||||||
|
temphand.add(addcard);
|
||||||
|
Util.removeCard(temphand,list.get(0),1);
|
||||||
|
Util.removeCard(temphand,list.get(1),1);
|
||||||
|
Util.removeCard(temphand,list.get(2),1);
|
||||||
|
List<Integer> checktingpai1 = checktingpai(temphand);
|
||||||
|
if (checktingpai1.size() > 0) {
|
||||||
|
flag =index +1;
|
||||||
|
}
|
||||||
|
index ++ ;
|
||||||
|
System.out.println("checktingpai1" + checktingpai1);
|
||||||
|
}
|
||||||
|
System.out.println("flag" + flag);
|
||||||
|
System.out.println(lists);
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
public static List<Integer> shifoutingpai(List<Integer> cardhand) {
|
||||||
|
List<Integer> tpcards = new ArrayList<>();
|
||||||
|
List<Integer> tmphc = cardhand;
|
||||||
|
for (int i = 0; i < cardhand.size(); i++) {
|
||||||
|
|
||||||
|
int tmpcard = tmphc.get(i);
|
||||||
|
// tmphc.remove(i);
|
||||||
|
for (int j = 101; j <= 109; j++) {
|
||||||
|
WinCard win = new WinCard(tmphc, j);
|
||||||
|
if (win.tryWin()) {
|
||||||
|
if (!tpcards.contains(j)) {
|
||||||
|
tpcards.add(j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int j = 201; j <= 209; j++) {
|
||||||
|
WinCard win = new WinCard(tmphc, j);
|
||||||
|
if (win.tryWin()) {
|
||||||
|
if (!tpcards.contains(j)) {
|
||||||
|
tpcards.add(j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// tmphc.add(tmpcard);
|
||||||
|
}
|
||||||
|
return tpcards;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Integer> checktingpai(List<Integer> cardhand) {
|
||||||
|
List<Integer> tpcards = new ArrayList<>();
|
||||||
|
List<Integer> tmphc = cardhand;
|
||||||
|
for (int i = 0; i < cardhand.size(); i++) {
|
||||||
|
|
||||||
|
int tmpcard = tmphc.get(0);
|
||||||
|
tmphc.remove(0);
|
||||||
|
for (int j = 101; j <= 109; j++) {
|
||||||
|
WinCard win = new WinCard(tmphc, j);
|
||||||
|
if (win.tryWin()) {
|
||||||
|
if (!tpcards.contains(tmpcard)) {
|
||||||
|
tpcards.add(tmpcard);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int j = 201; j <= 209; j++) {
|
||||||
|
WinCard win = new WinCard(tmphc, j);
|
||||||
|
if (win.tryWin()) {
|
||||||
|
if (!tpcards.contains(tmpcard)) {
|
||||||
|
tpcards.add(tmpcard);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tmphc.add(tmpcard);
|
||||||
|
}
|
||||||
|
return tpcards;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<List<Integer>> checkChi(List<Integer> hand, int card) {
|
||||||
|
List<List<Integer>> result = new ArrayList<>();
|
||||||
|
if (Util.checkCard(card-1, hand ) &&Util.checkCard(card - 2, hand)) {
|
||||||
|
List<Integer> opcard = new ArrayList<>();
|
||||||
|
opcard.add(card-1);
|
||||||
|
opcard.add(card - 2);
|
||||||
|
opcard.add(card);
|
||||||
|
result.add(opcard);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Util.checkCard(card + 1, hand) && Util.checkCard(card - 1, hand)) {
|
||||||
|
List<Integer> opcard = new ArrayList<>();
|
||||||
|
opcard.add(card + 1);
|
||||||
|
opcard.add(card - 1);
|
||||||
|
opcard.add(card);
|
||||||
|
result.add(opcard);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Util.checkCard(card + 1, hand) && Util.checkCard(card + 2, hand)) {
|
||||||
|
List<Integer> opcard = new ArrayList<>();
|
||||||
|
opcard.add(card + 1);
|
||||||
|
opcard.add(card + 2);
|
||||||
|
opcard.add(card);
|
||||||
|
result.add(opcard);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean canChi(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() + "张");
|
||||||
|
|
||||||
|
List<int[]> chiOptions = new ArrayList<>(); // 存储所有能吃的方式
|
||||||
|
|
||||||
|
// 检查三种吃法并记录
|
||||||
|
if (value >= 3) {
|
||||||
|
int prev2 = type * 100 + (value - 2);
|
||||||
|
int prev1 = type * 100 + (value - 1);
|
||||||
|
if (handCards.contains(prev2) && handCards.contains(prev1)) {
|
||||||
|
chiOptions.add(new int[]{prev2, prev1});
|
||||||
|
System.out.println(" 可以组成" + (value - 2) + (value - 1) + value + "顺子");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value >= 2 && value <= 8) {
|
||||||
|
int prev = type * 100 + (value - 1);
|
||||||
|
int next = type * 100 + (value + 1);
|
||||||
|
if (handCards.contains(prev) && handCards.contains(next)) {
|
||||||
|
chiOptions.add(new int[]{prev, next});
|
||||||
|
System.out.println(" 可以组成" + (value - 1) + value + (value + 1) + "顺子");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value <= 7) {
|
||||||
|
int next1 = type * 100 + (value + 1);
|
||||||
|
int next2 = type * 100 + (value + 2);
|
||||||
|
if (handCards.contains(next1) && handCards.contains(next2)) {
|
||||||
|
chiOptions.add(new int[]{next1, next2});
|
||||||
|
System.out.println(" 可以组成" + value + (value + 1) + (value + 2) + "顺子");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chiOptions.isEmpty()) {
|
||||||
|
System.out.println(" 没有能吃的方式");
|
||||||
|
return false; // 没有能吃的方式
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("发现" + chiOptions.size() + "种吃法");
|
||||||
|
|
||||||
|
// 记录所有能听牌的吃法及其听牌详情
|
||||||
|
List<TingChiOption> tingOptions = new ArrayList<>();
|
||||||
|
|
||||||
|
for (int[] chiPair : chiOptions) {
|
||||||
|
System.out.println("\n尝试吃法: 用" + (chiPair[0] % 100) + getTypeName(type) +
|
||||||
|
"和" + (chiPair[1] % 100) + getTypeName(type) +
|
||||||
|
"吃" + value + getTypeName(type));
|
||||||
|
|
||||||
|
// 检查这种吃法能听多少张牌以及听什么牌
|
||||||
|
TingResult tingResult = getTingResultAfterChi(handCards, card, chiPair[0], chiPair[1]);
|
||||||
|
if (tingResult != null && tingResult.tingCount > 0) {
|
||||||
|
tingOptions.add(new TingChiOption(chiPair, tingResult.tingCount, tingResult.tingCards, tingResult.discardCard));
|
||||||
|
System.out.println(" → 打" + (tingResult.discardCard % 100) + getTypeName(tingResult.discardCard / 100) +
|
||||||
|
"可听" + tingResult.tingCount + "张牌: " + formatCards(tingResult.tingCards));
|
||||||
|
} else {
|
||||||
|
System.out.println(" → 不能听牌");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果有能听牌的吃法
|
||||||
|
if (!tingOptions.isEmpty()) {
|
||||||
|
// 选择听牌数量最多的吃法
|
||||||
|
tingOptions.sort((a, b) -> {
|
||||||
|
// 优先按听牌数量排序
|
||||||
|
if (b.tingCount != a.tingCount) {
|
||||||
|
return b.tingCount - a.tingCount;
|
||||||
|
}
|
||||||
|
// 如果听牌数量相同,可以考虑其他因素,比如听牌质量
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
TingChiOption bestOption = tingOptions.get(0);
|
||||||
|
|
||||||
|
System.out.println("\n=== 选择最佳吃法 ===");
|
||||||
|
System.out.println("用" + (bestOption.chiPair[0] % 100) + getTypeName(type) +
|
||||||
|
"和" + (bestOption.chiPair[1] % 100) + getTypeName(type) +
|
||||||
|
"吃" + value + getTypeName(type));
|
||||||
|
System.out.println("打" + (bestOption.bestDiscard % 100) + getTypeName(bestOption.bestDiscard / 100));
|
||||||
|
System.out.println("听" + bestOption.tingCount + "张牌: " + formatCards(bestOption.tingCards));
|
||||||
|
|
||||||
|
// 分析听牌类型
|
||||||
|
analyzeTingType(bestOption.tingCards);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false; // 所有吃法都不能形成好牌型
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 新增:获取吃牌后的听牌数量
|
||||||
|
public static int getTingCountAfterChi(List<Integer> handCards, int chiCard,
|
||||||
|
int remove1, int remove2) {
|
||||||
|
// 模拟吃牌
|
||||||
|
List<Integer> afterChi = new ArrayList<>(handCards);
|
||||||
|
afterChi.remove(Integer.valueOf(remove1));
|
||||||
|
afterChi.remove(Integer.valueOf(remove2));
|
||||||
|
Collections.sort(afterChi);
|
||||||
|
|
||||||
|
boolean needs258 = !checkSuitCount(afterChi);
|
||||||
|
int targetSizeAfterDiscard = afterChi.size() - 1;
|
||||||
|
|
||||||
|
int maxTingCount = 0;
|
||||||
|
Set<Integer> uniqueCards = new HashSet<>(afterChi);
|
||||||
|
|
||||||
|
for (int discardCard : uniqueCards) {
|
||||||
|
List<Integer> afterDiscard = new ArrayList<>(afterChi);
|
||||||
|
afterDiscard.remove(Integer.valueOf(discardCard));
|
||||||
|
Collections.sort(afterDiscard);
|
||||||
|
|
||||||
|
if (afterDiscard.size() == targetSizeAfterDiscard) {
|
||||||
|
int tingCount = countTingCards(afterDiscard, needs258);
|
||||||
|
maxTingCount = Math.max(maxTingCount, tingCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxTingCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增:统计听牌数量
|
||||||
|
public static int countTingCards(List<Integer> hand, boolean needs258) {
|
||||||
|
if (hand.size() % 3 != 1) return 0; // 听牌时手牌应该是3n+1张
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tingCards.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改后的 TingChiOption 类
|
||||||
|
static class TingChiOption {
|
||||||
|
int[] chiPair;
|
||||||
|
int tingCount;
|
||||||
|
Set<Integer> tingCards;
|
||||||
|
int bestDiscard;
|
||||||
|
|
||||||
|
TingChiOption(int[] chiPair, int tingCount, Set<Integer> tingCards, int bestDiscard) {
|
||||||
|
this.chiPair = chiPair;
|
||||||
|
this.tingCount = tingCount;
|
||||||
|
this.tingCards = tingCards;
|
||||||
|
this.bestDiscard = bestDiscard;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 新增:获取吃牌后的听牌结果
|
||||||
|
public static TingResult getTingResultAfterChi(List<Integer> handCards, int chiCard,
|
||||||
|
int remove1, int remove2) {
|
||||||
|
// 模拟吃牌
|
||||||
|
List<Integer> afterChi = new ArrayList<>(handCards);
|
||||||
|
afterChi.remove(Integer.valueOf(remove1));
|
||||||
|
afterChi.remove(Integer.valueOf(remove2));
|
||||||
|
Collections.sort(afterChi);
|
||||||
|
|
||||||
|
boolean needs258 = !checkSuitCount(afterChi);
|
||||||
|
int targetSizeAfterDiscard = afterChi.size() - 1;
|
||||||
|
|
||||||
|
TingResult bestResult = null;
|
||||||
|
Set<Integer> uniqueCards = new HashSet<>(afterChi);
|
||||||
|
|
||||||
|
for (int discardCard : uniqueCards) {
|
||||||
|
List<Integer> afterDiscard = new ArrayList<>(afterChi);
|
||||||
|
afterDiscard.remove(Integer.valueOf(discardCard));
|
||||||
|
Collections.sort(afterDiscard);
|
||||||
|
|
||||||
|
if (afterDiscard.size() == targetSizeAfterDiscard) {
|
||||||
|
Set<Integer> tingCards = getTingCards(afterDiscard, needs258);
|
||||||
|
int tingCount = tingCards.size();
|
||||||
|
|
||||||
|
if (tingCount > 0) {
|
||||||
|
if (bestResult == null || tingCount > bestResult.tingCount) {
|
||||||
|
bestResult = new TingResult(tingCount, tingCards, discardCard);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bestResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增:获取具体的听牌集合
|
||||||
|
public static Set<Integer> getTingCards(List<Integer> hand, boolean needs258) {
|
||||||
|
if (hand.size() % 3 != 1) return new HashSet<>(); // 听牌时手牌应该是3n+1张
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tingCards;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增:格式化牌显示
|
||||||
|
public static String formatCards(Set<Integer> cards) {
|
||||||
|
List<String> cardStrs = new ArrayList<>();
|
||||||
|
for (int card : cards) {
|
||||||
|
int type = card / 100;
|
||||||
|
int value = card % 100;
|
||||||
|
cardStrs.add(value + getTypeName(type));
|
||||||
|
}
|
||||||
|
return String.join(", ", cardStrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增:记录听牌结果的类
|
||||||
|
static class TingResult {
|
||||||
|
int tingCount;
|
||||||
|
Set<Integer> tingCards;
|
||||||
|
int discardCard; // 打哪张牌能达到这个听牌
|
||||||
|
|
||||||
|
TingResult(int tingCount, Set<Integer> tingCards, int discardCard) {
|
||||||
|
this.tingCount = tingCount;
|
||||||
|
this.tingCards = tingCards;
|
||||||
|
this.discardCard = discardCard;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 检查吃牌并打出一张后是否能听牌 - 优化版
|
||||||
|
*/
|
||||||
|
public static boolean canTingAfterChiAndDiscard(List<Integer> handCards, int chiCard,
|
||||||
|
int remove1, int remove2) {
|
||||||
|
// 1. 模拟吃牌:移除两张牌,吃的牌不加入手牌
|
||||||
|
List<Integer> afterChi = new ArrayList<>(handCards);
|
||||||
|
afterChi.remove(Integer.valueOf(remove1));
|
||||||
|
afterChi.remove(Integer.valueOf(remove2));
|
||||||
|
Collections.sort(afterChi);
|
||||||
|
|
||||||
|
System.out.println(" 吃后手牌(" + afterChi.size() + "张): " + convertToReadable(afterChi));
|
||||||
|
|
||||||
|
// 2. 检查是否需要258将(花色是否>=10张)
|
||||||
|
System.out.println(chiCard);
|
||||||
|
System.out.println(afterChi);
|
||||||
|
boolean needs258 = !checkSuitCount(afterChi);
|
||||||
|
if (needs258) {
|
||||||
|
System.out.println(" 花色牌数不足10张,需要258做将");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 根据手牌数量确定目标手牌数
|
||||||
|
int originalSize = handCards.size();
|
||||||
|
int targetSizeAfterDiscard;
|
||||||
|
|
||||||
|
if (originalSize == 13) {
|
||||||
|
// 标准情况:13张手牌 -> 吃后11张 -> 打后10张
|
||||||
|
targetSizeAfterDiscard = 10;
|
||||||
|
} else if (originalSize == 14) {
|
||||||
|
// 你的例子:14张手牌 -> 吃后12张 -> 打后11张
|
||||||
|
targetSizeAfterDiscard = 11;
|
||||||
|
} else {
|
||||||
|
// 其他情况,根据吃后手牌数减1
|
||||||
|
targetSizeAfterDiscard = afterChi.size() - 1;
|
||||||
|
System.out.println(" 非标准手牌数,目标手牌数: " + targetSizeAfterDiscard);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 尝试打每一张不同的牌
|
||||||
|
Set<Integer> uniqueCards = new HashSet<>(afterChi);
|
||||||
|
boolean foundTing = false;
|
||||||
|
for (int discardCard : uniqueCards) {
|
||||||
|
List<Integer> afterDiscard = new ArrayList<>(afterChi);
|
||||||
|
afterDiscard.remove(Integer.valueOf(discardCard));
|
||||||
|
Collections.sort(afterDiscard);
|
||||||
|
|
||||||
|
int discardType = discardCard / 100;
|
||||||
|
int discardValue = discardCard % 100;
|
||||||
|
|
||||||
|
System.out.println("---打牌-----" + discardValue);
|
||||||
|
|
||||||
|
System.out.print("\n 打" + discardValue + getTypeName(discardType) +
|
||||||
|
" → 剩余" + afterDiscard.size() + "张: " +
|
||||||
|
convertToReadable(afterDiscard));
|
||||||
|
|
||||||
|
// 5. 检查打牌后是否能听牌
|
||||||
|
boolean canTing = checkCanTing(afterDiscard, needs258, targetSizeAfterDiscard);
|
||||||
|
if (canTing) {
|
||||||
|
HuNanChangSha.isTinChi = true;
|
||||||
|
ChangShaSuanFaTest.isChi=true;
|
||||||
|
System.out.println(" ✓ 听牌!");
|
||||||
|
foundTing = true;
|
||||||
|
|
||||||
|
// 分析听牌详情
|
||||||
|
analyzeTingDetails(afterDiscard, needs258);
|
||||||
|
} else {
|
||||||
|
// // 如果不能听牌,检查是否是好牌型
|
||||||
|
// System.out.print(" [手牌分析中...]");
|
||||||
|
//
|
||||||
|
// if (isGoodHandForTing(afterDiscard, needs258, targetSizeAfterDiscard)) {
|
||||||
|
// System.out.println(" ✓ 好牌型(接近听牌)");
|
||||||
|
// foundTing = true;
|
||||||
|
// } else {
|
||||||
|
System.out.println(" ✗ 不听");
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return foundTing;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查手牌是否能听牌
|
||||||
|
*/
|
||||||
|
public static boolean checkCanTing(List<Integer> hand, boolean needs258, int targetSize) {
|
||||||
|
if (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将要求)
|
||||||
|
*/
|
||||||
|
public static 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分析听牌详情
|
||||||
|
*/
|
||||||
|
public static 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<>();
|
||||||
|
|
||||||
|
// 比较当前数量和上次数量
|
||||||
|
if (tingCards.size() > lastTingCount) {
|
||||||
|
isMoreThanLast = true;
|
||||||
|
// System.out.print("(比上次多" + (tingCards.size() - lastTingCount) + "张) ");
|
||||||
|
} else if (tingCards.size() < lastTingCount) {
|
||||||
|
// System.out.print("(比上次少" + (lastTingCount - tingCards.size()) + "张) ");
|
||||||
|
} else {
|
||||||
|
// System.out.print("(与上次相同) ");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int card : tingCards) {
|
||||||
|
int type = card / 100;
|
||||||
|
int value = card % 100;
|
||||||
|
tingCardStrs.add(value + getTypeName(type));
|
||||||
|
|
||||||
|
}
|
||||||
|
System.out.println(String.join(", ", tingCardStrs));
|
||||||
|
|
||||||
|
// 更新上次听牌数量
|
||||||
|
lastTingCount = tingCards.size();
|
||||||
|
|
||||||
|
// 分析听牌类型
|
||||||
|
analyzeTingType(tingCards);
|
||||||
|
} else {
|
||||||
|
System.out.println(" 无听牌");
|
||||||
|
// 比较当前数量和上次数量
|
||||||
|
if (0 > lastTingCount) {
|
||||||
|
isMoreThanLast = true;
|
||||||
|
}
|
||||||
|
// 更新上次听牌数量
|
||||||
|
lastTingCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 修改 analyzeTingType 方法以接收 Set<Integer>
|
||||||
|
public static void analyzeTingType(Set<Integer> tingCards) {
|
||||||
|
if (tingCards.size() == 1) {
|
||||||
|
System.out.println("听牌类型: 单吊");
|
||||||
|
} else if (tingCards.size() == 2) {
|
||||||
|
List<Integer> tingList = new ArrayList<>(tingCards);
|
||||||
|
int type1 = tingList.get(0) / 100;
|
||||||
|
int type2 = tingList.get(1) / 100;
|
||||||
|
if (type1 == type2) {
|
||||||
|
System.out.println("听牌类型: 对倒");
|
||||||
|
} else {
|
||||||
|
System.out.println("听牌类型: 双面听");
|
||||||
|
}
|
||||||
|
} else if (tingCards.size() >= 3) {
|
||||||
|
System.out.println("听牌类型: 多面听(" + tingCards.size() + "张)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查手牌花色牌数是否>=10张
|
||||||
|
*/
|
||||||
|
public 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否有某种花色>=10张
|
||||||
|
for (int count : suitCount.values()) {
|
||||||
|
if (count >= 10) {
|
||||||
|
return true; // 有花色>=10张,不需要258将
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false; // 没有花色>=10张,需要258将
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查七对子
|
||||||
|
*/
|
||||||
|
public 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将)
|
||||||
|
*/
|
||||||
|
public 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将)
|
||||||
|
*/
|
||||||
|
public static boolean checkNormalHu(List<Integer> handCards) {
|
||||||
|
return checkNormalHuRecursive(new ArrayList<>(handCards), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查牌是否能组成顺子或刻子
|
||||||
|
*/
|
||||||
|
public 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有麻将牌
|
||||||
|
*/
|
||||||
|
public static 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换为可读格式
|
||||||
|
*/
|
||||||
|
public 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取牌型名称
|
||||||
|
*/
|
||||||
|
public static String getTypeName(int type) {
|
||||||
|
switch (type) {
|
||||||
|
case 1:
|
||||||
|
return "万";
|
||||||
|
case 2:
|
||||||
|
return "筒";
|
||||||
|
case 3:
|
||||||
|
return "条";
|
||||||
|
default:
|
||||||
|
return "字";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static boolean isMoreThanLast() {
|
||||||
|
return isMoreThanLast;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,404 @@
|
||||||
|
package com.robotcm.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 "字";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,424 @@
|
||||||
|
package com.robotcm.taurus.util;
|
||||||
|
|
||||||
|
import com.robotcm.hunan.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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,735 @@
|
||||||
|
package com.robotcm.taurus.util;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class TingPaiChecker {
|
||||||
|
|
||||||
|
// 长沙麻将特殊规则:二五八将
|
||||||
|
private static final Set<Integer> JIANG_PAIS;
|
||||||
|
|
||||||
|
static {
|
||||||
|
Set<Integer> jiangSet = new HashSet<>();
|
||||||
|
jiangSet.add(2);
|
||||||
|
jiangSet.add(5);
|
||||||
|
jiangSet.add(8);
|
||||||
|
JIANG_PAIS = Collections.unmodifiableSet(jiangSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断手牌是否听牌(主要入口方法)
|
||||||
|
* @param handCards 手牌列表(13张或14张)
|
||||||
|
* @return TingResult 听牌结果对象
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* 判断手牌是否听牌(主要入口方法)
|
||||||
|
*/
|
||||||
|
public static TingResult checkTingPai(List<Integer> handCards) {
|
||||||
|
TingResult result = new TingResult();
|
||||||
|
|
||||||
|
if (handCards == null || handCards.isEmpty()) {
|
||||||
|
result.setMessage("手牌为空");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int handSize = handCards.size();
|
||||||
|
System.out.println("手牌数量: " + handSize + "张");
|
||||||
|
System.out.println("手牌: " + convertToReadable(handCards));
|
||||||
|
|
||||||
|
// === 新增:处理四张相同牌的情况 ===
|
||||||
|
// 统计每种牌的数量
|
||||||
|
Map<Integer, Integer> countMap = new HashMap<>();
|
||||||
|
for (int card : handCards) {
|
||||||
|
countMap.put(card, countMap.getOrDefault(card, 0) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 找出所有四张相同的牌
|
||||||
|
List<Integer> cardsWithFour = new ArrayList<>();
|
||||||
|
for (Map.Entry<Integer, Integer> entry : countMap.entrySet()) {
|
||||||
|
if (entry.getValue() == 4) {
|
||||||
|
cardsWithFour.add(entry.getKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 如果有四张相同的牌,去掉一张(选择其中一种处理)
|
||||||
|
List<Integer> processedHandCards = new ArrayList<>(handCards);
|
||||||
|
if (!cardsWithFour.isEmpty()) {
|
||||||
|
// 只处理第一种四张相同的牌(如果有多个,实际麻将中不会出现这种情况)
|
||||||
|
int cardToRemove = cardsWithFour.get(0);
|
||||||
|
System.out.println("检测到四张相同的牌: " + formatCard(cardToRemove) + ",去掉一张");
|
||||||
|
|
||||||
|
// 去掉一张
|
||||||
|
processedHandCards.remove(Integer.valueOf(cardToRemove));
|
||||||
|
System.out.println("处理后手牌: " + convertToReadable(processedHandCards));
|
||||||
|
System.out.println("处理后数量: " + processedHandCards.size() + "张");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. 检查手牌数量是否合法
|
||||||
|
int processedSize = processedHandCards.size();
|
||||||
|
|
||||||
|
if (processedSize != 13 && processedSize != 14) {
|
||||||
|
result.setMessage("手牌数量必须是13张(未听牌)或14张(已胡牌)");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 如果手牌14张,先检查是否已经胡牌
|
||||||
|
if (processedSize == 14) {
|
||||||
|
HuResult huResult = checkAllHuTypes(processedHandCards);
|
||||||
|
if (huResult.canHu) {
|
||||||
|
result.setAlreadyHu(true);
|
||||||
|
result.setMessage("已胡牌 - " + huResult.huType);
|
||||||
|
result.setHuType(huResult.huType);
|
||||||
|
System.out.println("✓ 已胡牌 (" + huResult.huType + ")");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 检查是否听牌
|
||||||
|
List<TingCardInfo> tingCards = findTingCardsWithType(processedHandCards);
|
||||||
|
|
||||||
|
if (!tingCards.isEmpty()) {
|
||||||
|
result.setTingPai(true);
|
||||||
|
result.setTingCardsInfo(tingCards);
|
||||||
|
result.setMessage("听牌,可听" + tingCards.size() + "张牌");
|
||||||
|
System.out.println("✓ 听牌");
|
||||||
|
System.out.println("听牌数: " + tingCards.size() + "张");
|
||||||
|
|
||||||
|
// 按胡牌类型分组显示
|
||||||
|
Map<String, List<TingCardInfo>> groupedByType = new HashMap<>();
|
||||||
|
for (TingCardInfo info : tingCards) {
|
||||||
|
groupedByType.computeIfAbsent(info.huType, k -> new ArrayList<>()).add(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Map.Entry<String, List<TingCardInfo>> entry : groupedByType.entrySet()) {
|
||||||
|
System.out.print(" " + entry.getKey() + "听牌: ");
|
||||||
|
List<String> cardStrs = new ArrayList<>();
|
||||||
|
for (TingCardInfo info : entry.getValue()) {
|
||||||
|
cardStrs.add(formatCard(info.card));
|
||||||
|
}
|
||||||
|
System.out.println(String.join(" ", cardStrs));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result.setTingPai(false);
|
||||||
|
result.setMessage("未听牌");
|
||||||
|
System.out.println("✗ 未听牌");
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 检查所有胡牌类型
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* 检查所有胡牌类型
|
||||||
|
*/
|
||||||
|
private static HuResult checkAllHuTypes(List<Integer> handCards) {
|
||||||
|
// 按优先级检查
|
||||||
|
HuResult pengPengHu = checkPengPengHu(handCards);
|
||||||
|
if (pengPengHu.canHu) {
|
||||||
|
return pengPengHu;
|
||||||
|
}
|
||||||
|
|
||||||
|
HuResult qingYiSe = checkQingYiSe(handCards);
|
||||||
|
if (qingYiSe.canHu) {
|
||||||
|
return qingYiSe;
|
||||||
|
}
|
||||||
|
|
||||||
|
HuResult normalHu = checkNormalHuWith258(handCards);
|
||||||
|
if (normalHu.canHu) {
|
||||||
|
return normalHu;
|
||||||
|
}
|
||||||
|
|
||||||
|
HuResult qiDui = checkQiDui(handCards);
|
||||||
|
if (qiDui.canHu) {
|
||||||
|
return qiDui;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new HuResult(false, "");
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 检查碰碰胡(不需要258将)
|
||||||
|
* 碰碰胡规则:4组刻子+1对将(对子可以是任意牌)
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* 修复的碰碰胡检查
|
||||||
|
*/
|
||||||
|
private static HuResult checkPengPengHu(List<Integer> handCards) {
|
||||||
|
if (handCards.size() != 14) return new HuResult(false, "");
|
||||||
|
|
||||||
|
List<Integer> sorted = new ArrayList<>(handCards);
|
||||||
|
Collections.sort(sorted);
|
||||||
|
|
||||||
|
// 统计每种牌的数量
|
||||||
|
Map<Integer, Integer> countMap = new HashMap<>();
|
||||||
|
for (int card : sorted) {
|
||||||
|
countMap.put(card, countMap.getOrDefault(card, 0) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int keziCount = 0; // 刻子数量
|
||||||
|
int duiziCount = 0; // 对子数量
|
||||||
|
int singleCount = 0; // 单张数量
|
||||||
|
|
||||||
|
for (Map.Entry<Integer, Integer> entry : countMap.entrySet()) {
|
||||||
|
int count = entry.getValue();
|
||||||
|
|
||||||
|
if (count == 3) {
|
||||||
|
keziCount++;
|
||||||
|
} else if (count == 2) {
|
||||||
|
duiziCount++;
|
||||||
|
} else if (count == 4) {
|
||||||
|
// 杠可以看作一个刻子
|
||||||
|
keziCount++;
|
||||||
|
} else if (count == 1) {
|
||||||
|
singleCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 碰碰胡条件:4组刻子 + 1对将,不能有单张
|
||||||
|
if (keziCount == 4 && duiziCount == 1 && singleCount == 0) {
|
||||||
|
// 检查是否清一色
|
||||||
|
boolean isQingYiSe = checkIfQingYiSe(handCards);
|
||||||
|
if (isQingYiSe) {
|
||||||
|
return new HuResult(true, "清一色碰碰胡");
|
||||||
|
}
|
||||||
|
return new HuResult(true, "碰碰胡");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new HuResult(false, "");
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 检查清一色(不需要258将)
|
||||||
|
* 清一色规则:所有牌都是同一花色,可以是任意胡牌牌型
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* 检查清一色
|
||||||
|
*/
|
||||||
|
private static HuResult checkQingYiSe(List<Integer> handCards) {
|
||||||
|
if (handCards.size() != 14) return new HuResult(false, "");
|
||||||
|
|
||||||
|
// 检查是否清一色
|
||||||
|
if (!checkIfQingYiSe(handCards)) {
|
||||||
|
return new HuResult(false, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清一色只需要能胡牌即可,不需要258将
|
||||||
|
if (canHuWithout258(handCards)) {
|
||||||
|
return new HuResult(true, "清一色");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new HuResult(false, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否清一色
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static boolean checkIfQingYiSe(List<Integer> handCards) {
|
||||||
|
if (handCards.isEmpty()) return false;
|
||||||
|
|
||||||
|
int firstType = getCardType(handCards.get(0));
|
||||||
|
for (int card : handCards) {
|
||||||
|
int type = getCardType(card);
|
||||||
|
// 字牌不算清一色
|
||||||
|
if (type >= 4 || type != firstType) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 普通胡牌(不需要258将)
|
||||||
|
*/
|
||||||
|
private static boolean canHuWithout258(List<Integer> handCards) {
|
||||||
|
return canHuRecursive(new ArrayList<>(handCards), false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查普通胡牌(必须有258将)
|
||||||
|
*/
|
||||||
|
private static HuResult checkNormalHuWith258(List<Integer> handCards) {
|
||||||
|
if (handCards.size() != 14) return new HuResult(false, "");
|
||||||
|
|
||||||
|
List<Integer> sorted = new ArrayList<>(handCards);
|
||||||
|
Collections.sort(sorted);
|
||||||
|
|
||||||
|
// 先检查是否可能胡牌(基础条件)
|
||||||
|
if (!canHuRecursive(sorted, false, true)) {
|
||||||
|
return new HuResult(false, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new HuResult(true, "普通胡牌");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查七对子
|
||||||
|
*/
|
||||||
|
private static HuResult checkQiDui(List<Integer> handCards) {
|
||||||
|
if (handCards.size() != 14) return new HuResult(false, "");
|
||||||
|
|
||||||
|
Map<Integer, Integer> countMap = new HashMap<>();
|
||||||
|
for (int card : handCards) {
|
||||||
|
countMap.put(card, countMap.getOrDefault(card, 0) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否都是对子
|
||||||
|
int duiziCount = 0;
|
||||||
|
for (int count : countMap.values()) {
|
||||||
|
if (count == 2) {
|
||||||
|
duiziCount++;
|
||||||
|
} else if (count == 4) {
|
||||||
|
// 4张相同可以看作2个对子
|
||||||
|
duiziCount += 2;
|
||||||
|
} else {
|
||||||
|
return new HuResult(false, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (duiziCount == 7) {
|
||||||
|
boolean isQingYiSe = checkIfQingYiSe(handCards);
|
||||||
|
if (isQingYiSe) {
|
||||||
|
return new HuResult(true, "清一色七对");
|
||||||
|
}
|
||||||
|
return new HuResult(true, "七对子");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new HuResult(false, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 递归胡牌检查
|
||||||
|
*/
|
||||||
|
private static boolean canHuRecursive(List<Integer> handCards, boolean hasJiang, boolean require258) {
|
||||||
|
if (handCards.isEmpty()) {
|
||||||
|
return hasJiang;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Integer> sorted = new ArrayList<>(handCards);
|
||||||
|
Collections.sort(sorted);
|
||||||
|
|
||||||
|
int firstCard = sorted.get(0);
|
||||||
|
int count = Collections.frequency(sorted, firstCard);
|
||||||
|
|
||||||
|
// 1. 尝试作为刻子
|
||||||
|
if (count >= 3) {
|
||||||
|
List<Integer> remaining = new ArrayList<>(sorted);
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
remaining.remove(Integer.valueOf(firstCard));
|
||||||
|
}
|
||||||
|
if (canHuRecursive(remaining, hasJiang, require258)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 尝试作为顺子(只有万筒条)
|
||||||
|
int type = getCardType(firstCard);
|
||||||
|
int value = getCardValue(firstCard);
|
||||||
|
|
||||||
|
if (type <= 3 && value <= 7) {
|
||||||
|
int secondCard = firstCard + 1;
|
||||||
|
int thirdCard = firstCard + 2;
|
||||||
|
|
||||||
|
if (sorted.contains(secondCard) && sorted.contains(thirdCard)) {
|
||||||
|
List<Integer> remaining = new ArrayList<>(sorted);
|
||||||
|
remaining.remove(Integer.valueOf(firstCard));
|
||||||
|
remaining.remove(Integer.valueOf(secondCard));
|
||||||
|
remaining.remove(Integer.valueOf(thirdCard));
|
||||||
|
if (canHuRecursive(remaining, hasJiang, require258)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 尝试作为将
|
||||||
|
if (!hasJiang && count >= 2) {
|
||||||
|
if (require258) {
|
||||||
|
int jiangValue = getCardValue(firstCard);
|
||||||
|
if (!JIANG_PAIS.contains(jiangValue)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Integer> remaining = new ArrayList<>(sorted);
|
||||||
|
remaining.remove(Integer.valueOf(firstCard));
|
||||||
|
remaining.remove(Integer.valueOf(firstCard));
|
||||||
|
if (canHuRecursive(remaining, true, require258)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找所有可能的听牌(带胡牌类型)
|
||||||
|
*/
|
||||||
|
private static List<TingCardInfo> findTingCardsWithType(List<Integer> handCards) {
|
||||||
|
List<TingCardInfo> tingCards = new ArrayList<>();
|
||||||
|
|
||||||
|
// 只检查合理的候选牌
|
||||||
|
Set<Integer> possibleHuCards = getReasonableHuCards(handCards);
|
||||||
|
|
||||||
|
System.out.println("候选胡牌(" + possibleHuCards.size() + "张): " +
|
||||||
|
convertToReadable(new ArrayList<>(possibleHuCards)));
|
||||||
|
|
||||||
|
for (int huCard : possibleHuCards) {
|
||||||
|
List<Integer> testHand = new ArrayList<>(handCards);
|
||||||
|
testHand.add(huCard);
|
||||||
|
Collections.sort(testHand);
|
||||||
|
|
||||||
|
HuResult result = checkAllHuTypes(testHand);
|
||||||
|
if (result.canHu) {
|
||||||
|
tingCards.add(new TingCardInfo(huCard, result.huType));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tingCards;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取合理的候选胡牌(优化版)
|
||||||
|
*/
|
||||||
|
private static Set<Integer> getReasonableHuCards(List<Integer> handCards) {
|
||||||
|
Set<Integer> possibleCards = new HashSet<>();
|
||||||
|
|
||||||
|
// 1. 手牌中已有的牌(优先考虑)
|
||||||
|
for (int card : handCards) {
|
||||||
|
possibleCards.add(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 手牌的相邻牌(万筒条)
|
||||||
|
for (int card : handCards) {
|
||||||
|
int type = getCardType(card);
|
||||||
|
if (type <= 3) {
|
||||||
|
int value = getCardValue(card);
|
||||||
|
if (value > 1) possibleCards.add(card - 1);
|
||||||
|
if (value < 9) possibleCards.add(card + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 258将牌(重要)
|
||||||
|
for (int type = 1; type <= 3; type++) {
|
||||||
|
for (int jiang : JIANG_PAIS) {
|
||||||
|
possibleCards.add(type * 100 + jiang);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return possibleCards;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查剩余牌是否能全部组成顺子或刻子
|
||||||
|
*/
|
||||||
|
private boolean canFormAllMelds(List<Integer> cards) {
|
||||||
|
if (cards.isEmpty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return dfsFormMelds(new ArrayList<>(cards));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean dfsFormMelds(List<Integer> cards) {
|
||||||
|
if (cards.isEmpty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Collections.sort(cards);
|
||||||
|
int firstCard = cards.get(0);
|
||||||
|
int count = Collections.frequency(cards, firstCard);
|
||||||
|
|
||||||
|
// 尝试刻子
|
||||||
|
if (count >= 3) {
|
||||||
|
List<Integer> newCards = new ArrayList<>(cards);
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
newCards.remove(Integer.valueOf(firstCard));
|
||||||
|
}
|
||||||
|
if (dfsFormMelds(newCards)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试顺子
|
||||||
|
int type = getCardType(firstCard);
|
||||||
|
int value = getCardValue(firstCard);
|
||||||
|
|
||||||
|
if (type <= 3 && value <= 7) {
|
||||||
|
int secondCard = firstCard + 1;
|
||||||
|
int thirdCard = firstCard + 2;
|
||||||
|
|
||||||
|
if (cards.contains(secondCard) && cards.contains(thirdCard)) {
|
||||||
|
List<Integer> newCards = new ArrayList<>(cards);
|
||||||
|
newCards.remove(Integer.valueOf(firstCard));
|
||||||
|
newCards.remove(Integer.valueOf(secondCard));
|
||||||
|
newCards.remove(Integer.valueOf(thirdCard));
|
||||||
|
if (dfsFormMelds(newCards)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取牌值
|
||||||
|
*/
|
||||||
|
private static int getCardValue(int card) {
|
||||||
|
return card % 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取牌类型
|
||||||
|
*/
|
||||||
|
private static int getCardType(int card) {
|
||||||
|
return card / 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化单张牌
|
||||||
|
*/
|
||||||
|
private static String formatCard(int card) {
|
||||||
|
int type = getCardType(card);
|
||||||
|
int value = getCardValue(card);
|
||||||
|
switch (type) {
|
||||||
|
case 1:
|
||||||
|
return value + "万";
|
||||||
|
case 2:
|
||||||
|
return value + "筒";
|
||||||
|
case 3:
|
||||||
|
return value + "条";
|
||||||
|
default:
|
||||||
|
return "字牌" + value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换为可读格式
|
||||||
|
*/
|
||||||
|
private static String convertToReadable(List<Integer> cards) {
|
||||||
|
if (cards == null || cards.isEmpty()) {
|
||||||
|
return "空";
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> cardStrs = new ArrayList<>();
|
||||||
|
for (int card : cards) {
|
||||||
|
cardStrs.add(formatCard(card));
|
||||||
|
}
|
||||||
|
return String.join(" ", cardStrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 胡牌结果类
|
||||||
|
*/
|
||||||
|
private static class HuResult {
|
||||||
|
boolean canHu;
|
||||||
|
String huType;
|
||||||
|
|
||||||
|
HuResult(boolean canHu, String huType) {
|
||||||
|
this.canHu = canHu;
|
||||||
|
this.huType = huType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 听牌信息类
|
||||||
|
*/
|
||||||
|
public static class TingCardInfo {
|
||||||
|
int card;
|
||||||
|
String huType;
|
||||||
|
|
||||||
|
public TingCardInfo(int card, String huType) {
|
||||||
|
this.card = card;
|
||||||
|
this.huType = huType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCard() {
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHuType() {
|
||||||
|
return huType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 听牌结果类
|
||||||
|
*/
|
||||||
|
public static class TingResult {
|
||||||
|
private boolean isTingPai;
|
||||||
|
private boolean isAlreadyHu;
|
||||||
|
private String huType;
|
||||||
|
private List<TingCardInfo> tingCardsInfo;
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
public TingResult() {
|
||||||
|
this.tingCardsInfo = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isTingPai() {
|
||||||
|
return isTingPai;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTingPai(boolean tingPai) {
|
||||||
|
isTingPai = tingPai;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAlreadyHu() {
|
||||||
|
return isAlreadyHu;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAlreadyHu(boolean alreadyHu) {
|
||||||
|
isAlreadyHu = alreadyHu;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHuType() {
|
||||||
|
return huType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHuType(String huType) {
|
||||||
|
this.huType = huType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<TingCardInfo> getTingCardsInfo() {
|
||||||
|
return tingCardsInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTingCardsInfo(List<TingCardInfo> tingCardsInfo) {
|
||||||
|
this.tingCardsInfo = tingCardsInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMessage(String message) {
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("听牌分析结果:\n");
|
||||||
|
sb.append("状态: ").append(message).append("\n");
|
||||||
|
|
||||||
|
if (isAlreadyHu) {
|
||||||
|
sb.append("🎉 已胡牌 (").append(huType).append(")\n");
|
||||||
|
} else if (isTingPai) {
|
||||||
|
sb.append("✅ 听牌状态\n");
|
||||||
|
sb.append("可胡牌: ").append(tingCardsInfo.size()).append("张\n");
|
||||||
|
|
||||||
|
// 按胡牌类型分组显示
|
||||||
|
Map<String, List<TingCardInfo>> grouped = new HashMap<>();
|
||||||
|
for (TingCardInfo info : tingCardsInfo) {
|
||||||
|
grouped.computeIfAbsent(info.huType, k -> new ArrayList<>()).add(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Map.Entry<String, List<TingCardInfo>> entry : grouped.entrySet()) {
|
||||||
|
sb.append(entry.getKey()).append(":\n");
|
||||||
|
// 按花色分组
|
||||||
|
Map<Integer, List<Integer>> typeGrouped = new HashMap<>();
|
||||||
|
for (TingCardInfo info : entry.getValue()) {
|
||||||
|
int type = info.card / 100;
|
||||||
|
typeGrouped.computeIfAbsent(type, k -> new ArrayList<>()).add(info.card);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Map.Entry<Integer, List<Integer>> typeEntry : typeGrouped.entrySet()) {
|
||||||
|
String typeName = getTypeName(typeEntry.getKey());
|
||||||
|
List<Integer> cards = typeEntry.getValue();
|
||||||
|
Collections.sort(cards);
|
||||||
|
|
||||||
|
List<String> valueStrs = new ArrayList<>();
|
||||||
|
for (int card : cards) {
|
||||||
|
valueStrs.add(String.valueOf(card % 10));
|
||||||
|
}
|
||||||
|
sb.append(" ").append(typeName).append(": ").append(String.join(" ", valueStrs)).append("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sb.append("❌ 未听牌\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getTypeName(int type) {
|
||||||
|
switch (type) {
|
||||||
|
case 1:
|
||||||
|
return "万";
|
||||||
|
case 2:
|
||||||
|
return "筒";
|
||||||
|
case 3:
|
||||||
|
return "条";
|
||||||
|
default:
|
||||||
|
return "字牌";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试方法
|
||||||
|
*/
|
||||||
|
public static void main(String[] args) {
|
||||||
|
TingPaiChecker checker = new TingPaiChecker();
|
||||||
|
|
||||||
|
System.out.println("=== 长沙麻将听牌分析测试(含大胡) ===\n");
|
||||||
|
|
||||||
|
//// // 测试1:碰碰胡
|
||||||
|
// System.out.println("测试1:碰碰胡");
|
||||||
|
// List<Integer> hand1 = Arrays.asList(
|
||||||
|
// 203, 101, 101, // 1万刻子
|
||||||
|
// 102, 102, 102, // 2万刻子
|
||||||
|
// 103, 103, 103, // 3万刻子
|
||||||
|
// 104, 104, 104, // 4万刻子
|
||||||
|
// 203 // 5万对子(将)
|
||||||
|
// );
|
||||||
|
// TingResult result1 = checker.checkTingPai(hand1);
|
||||||
|
// System.out.println(result1);
|
||||||
|
|
||||||
|
// // 测试2:清一色
|
||||||
|
// System.out.println("\n测试2:清一色");
|
||||||
|
List<Integer> hand2 = Arrays.asList(
|
||||||
|
208, 208, 207, 207, 207, 205, 204, 104, 103, 103, 102, 102, 101 // 7万对子(将)
|
||||||
|
);
|
||||||
|
TingResult result2 = checker.checkTingPai(hand2);
|
||||||
|
System.out.println(result2.isTingPai);
|
||||||
|
//
|
||||||
|
// // 测试3:清一色碰碰胡
|
||||||
|
// System.out.println("\n测试3:清一色碰碰胡");
|
||||||
|
// List<Integer> hand3 = Arrays.asList(
|
||||||
|
// 101, 101, 101, // 1万刻子
|
||||||
|
// 102, 102, 102, // 2万刻子
|
||||||
|
// 103, 103, 103, // 3万刻子
|
||||||
|
// 104, 104, 104, // 4万刻子
|
||||||
|
// 105, 105 // 5万对子(将)
|
||||||
|
// );
|
||||||
|
// TingResult result3 = checker.checkTingPai(hand3);
|
||||||
|
// System.out.println(result3);
|
||||||
|
|
||||||
|
// 测试4:普通胡牌(258将)
|
||||||
|
// System.out.println("\n测试4:普通胡牌(258将)");
|
||||||
|
// List<Integer> hand4 = Arrays.asList(
|
||||||
|
// 101, 101, 101, // 1万刻子
|
||||||
|
// 102, 103, 104, // 1-2-3万顺子
|
||||||
|
// 201, // 2筒对子(将)
|
||||||
|
// 302, 303, 304, // 2-3-4条顺子
|
||||||
|
// 305, 306, 307 // 5-6-7条顺子
|
||||||
|
// );
|
||||||
|
// TingResult result4 = checker.checkTingPai(hand4);
|
||||||
|
// System.out.println(result4);
|
||||||
|
//
|
||||||
|
// // 测试5:七对子
|
||||||
|
// System.out.println("\n测试5:七对子");
|
||||||
|
// List<Integer> hand5 = Arrays.asList(
|
||||||
|
// 101, 101, // 1万对子
|
||||||
|
// 102, 102, // 2万对子
|
||||||
|
// 103, 103, // 3万对子
|
||||||
|
// 201, 201, // 1筒对子
|
||||||
|
// 202, 202, // 2筒对子
|
||||||
|
// 301, 301, // 1条对子
|
||||||
|
// 302, 302 // 2条对子
|
||||||
|
// );
|
||||||
|
// TingResult result5 = checker.checkTingPai(hand5);
|
||||||
|
// System.out.println(result5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,293 @@
|
||||||
|
package com.robotcm.taurus.util;
|
||||||
|
|
||||||
|
import com.game.Util;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class TryPutCard {
|
||||||
|
public int pair_count;
|
||||||
|
public Stack<List<Integer>> stack;
|
||||||
|
public List<Integer> cardList;
|
||||||
|
public boolean jiang = true;
|
||||||
|
public int zhong_count;
|
||||||
|
public int zhongid = 0;
|
||||||
|
|
||||||
|
private void push(List<Integer> cardGroup) {
|
||||||
|
this.stack.push(cardGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void rollBack() {
|
||||||
|
List<Integer> cardGroup = this.stack.pop();
|
||||||
|
for (Integer card : cardGroup) {
|
||||||
|
if (card == zhongid) {
|
||||||
|
this.zhong_count += 1;
|
||||||
|
} else {
|
||||||
|
this.cardList.add(card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Collections.sort(this.cardList);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean tryShunzi(int card) {
|
||||||
|
if (card % 100 > 7) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Util.checkCard(card + 1, this.cardList) && Util.checkCard(card + 2, this.cardList)) {
|
||||||
|
Util.removeCard(this.cardList, card, 1);
|
||||||
|
Util.removeCard(this.cardList, card + 1, 1);
|
||||||
|
Util.removeCard(this.cardList, card + 2, 1);
|
||||||
|
List<Integer> cardGroup = new ArrayList<Integer>();
|
||||||
|
cardGroup.add(card);
|
||||||
|
cardGroup.add(card + 1);
|
||||||
|
cardGroup.add(card + 2);
|
||||||
|
this.push(cardGroup);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean tryKezi(int card) {
|
||||||
|
if (Util.checkCardAndRomve(card, this.cardList, 3)) {
|
||||||
|
// CardUtil.removeCard(this.cardList, card, 3);
|
||||||
|
List<Integer> cardGroup = new ArrayList<Integer>();
|
||||||
|
cardGroup.add(card);
|
||||||
|
cardGroup.add(card);
|
||||||
|
cardGroup.add(card);
|
||||||
|
this.push(cardGroup);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean tryPair(int card) {
|
||||||
|
if (this.pair_count > 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (jiang) {
|
||||||
|
if (!(card % 100 == 2 || card % 100 == 5 || card % 100 == 8)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Util.checkCardAndRomve(card, this.cardList, 2)) {
|
||||||
|
// CardUtil.removeCard(this.cardList, card, 2);
|
||||||
|
List<Integer> cardGroup = new ArrayList<Integer>();
|
||||||
|
cardGroup.add(card);
|
||||||
|
cardGroup.add(card);
|
||||||
|
this.push(cardGroup);
|
||||||
|
this.pair_count = 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean tryKezi1Zhong(int card) {
|
||||||
|
if (this.zhong_count >= 1 && Util.checkCardAndRomve(card, this.cardList, 2)) {
|
||||||
|
// CardUtil.removeCard(this.cardList, card, 2);
|
||||||
|
List<Integer> cardGroup = new ArrayList<Integer>();
|
||||||
|
cardGroup.add(card);
|
||||||
|
cardGroup.add(card);
|
||||||
|
cardGroup.add(this.zhongid);
|
||||||
|
this.zhong_count -= 1;
|
||||||
|
this.push(cardGroup);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean tryShunzi1Zhong(int card) {
|
||||||
|
if (card % 100 > 8) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.zhong_count < 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Util.checkCard(card + 1, this.cardList)) {
|
||||||
|
Util.removeCard(cardList, card, 1);
|
||||||
|
Util.removeCard(cardList, card + 1, 1);
|
||||||
|
this.zhong_count -= 1;
|
||||||
|
List<Integer> cardGroup = new ArrayList<Integer>();
|
||||||
|
cardGroup.add(card);
|
||||||
|
cardGroup.add(card + 1);
|
||||||
|
cardGroup.add(this.zhongid);
|
||||||
|
this.push(cardGroup);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Util.checkCard(card + 2, this.cardList) && ((card + 1) % 100 != 0)) {
|
||||||
|
Util.removeCard(cardList, card, 1);
|
||||||
|
Util.removeCard(cardList, card + 2, 1);
|
||||||
|
this.zhong_count -= 1;
|
||||||
|
List<Integer> cardGroup = new ArrayList<Integer>();
|
||||||
|
cardGroup.add(card);
|
||||||
|
cardGroup.add(this.zhongid);
|
||||||
|
cardGroup.add(card + 2);
|
||||||
|
this.push(cardGroup);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean tryPair1Zhong(int card) {
|
||||||
|
if (this.pair_count > 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (jiang) {
|
||||||
|
if (!(card % 100 == 2 || card % 100 == 5 || card % 100 == 8)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.zhong_count < 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Util.removeCard(cardList, card, 1);
|
||||||
|
List<Integer> cardGroup = new ArrayList<Integer>();
|
||||||
|
cardGroup.add(card);
|
||||||
|
cardGroup.add(this.zhongid);
|
||||||
|
this.push(cardGroup);
|
||||||
|
this.zhong_count -= 1;
|
||||||
|
this.pair_count = 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int trypushcard(List<Integer> handcards,int card){
|
||||||
|
this.cardList = handcards;
|
||||||
|
this.cardList.add(card);
|
||||||
|
tryinert();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int tryinert(){
|
||||||
|
if (this.cardList.size() == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int activeCard = this.cardList.get(0);
|
||||||
|
|
||||||
|
//判断是否能组合才对子
|
||||||
|
if (tryPair(activeCard))
|
||||||
|
{
|
||||||
|
if (tryinert()==0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
this.pair_count = 0;
|
||||||
|
this.rollBack();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean tryWin() {
|
||||||
|
if (this.cardList.size() == 0 && this.pair_count == 1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.cardList.size() == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int activeCard = this.cardList.get(0);
|
||||||
|
|
||||||
|
if (tryPair(activeCard)) {
|
||||||
|
if (tryWin()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
this.pair_count = 0;
|
||||||
|
this.rollBack();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tryKezi(activeCard)) {
|
||||||
|
if (tryWin()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
this.rollBack();
|
||||||
|
|
||||||
|
}
|
||||||
|
if (tryShunzi(activeCard)) {
|
||||||
|
if (tryWin()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
this.rollBack();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tryKezi1Zhong(activeCard)) {
|
||||||
|
if (tryWin()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
this.rollBack();
|
||||||
|
|
||||||
|
}
|
||||||
|
if (tryShunzi1Zhong(activeCard)) {
|
||||||
|
if (tryWin()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
this.rollBack();
|
||||||
|
}
|
||||||
|
if (tryPair1Zhong(activeCard)) {
|
||||||
|
if (tryWin()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
this.pair_count = 0;
|
||||||
|
this.rollBack();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
|
||||||
|
long time = System.currentTimeMillis();
|
||||||
|
List<Integer> abc = Arrays.asList(204, 206, 205, 205, 203, 109, 108, 107, 107, 106, 103, 103, 102, 101);
|
||||||
|
TryPutCard win = new TryPutCard();
|
||||||
|
Map<Integer,Integer> bast = new HashMap<>();
|
||||||
|
for(int i=0;i<abc.size();i++){
|
||||||
|
int tmpcard = abc.get(i);
|
||||||
|
abc.remove(i);
|
||||||
|
int per = win.trypushcard(abc,tmpcard);
|
||||||
|
bast.put(tmpcard,per);
|
||||||
|
}
|
||||||
|
System.out.println(bast);
|
||||||
|
|
||||||
|
|
||||||
|
// for (int i = 0; i < 1000000; ++i) {
|
||||||
|
/* ArrayList<Integer> cardInhand = new ArrayList<Integer>();
|
||||||
|
cardInhand.add(102);
|
||||||
|
cardInhand.add(102);
|
||||||
|
cardInhand.add(204);
|
||||||
|
cardInhand.add(204);
|
||||||
|
cardInhand.add(206);
|
||||||
|
cardInhand.add(206);
|
||||||
|
|
||||||
|
cardInhand.add(305);
|
||||||
|
cardInhand.add(305);
|
||||||
|
cardInhand.add(305);
|
||||||
|
cardInhand.add(306);
|
||||||
|
cardInhand.add(306);
|
||||||
|
cardInhand.add(307);
|
||||||
|
|
||||||
|
cardInhand.add(307);
|
||||||
|
// cardInhand.add(301);
|
||||||
|
// cardInhand.add(301);
|
||||||
|
// cardInhand.add(108);
|
||||||
|
|
||||||
|
// cardInhand.contains(204);
|
||||||
|
// Util.checkCard(204, cardInhand);
|
||||||
|
// CardUtil.checkCardAndRomve(204, cardInhand, 2);
|
||||||
|
|
||||||
|
// if(Util.checkCard(204, cardInhand)) {
|
||||||
|
// CardUtil.removeCard(cardInhand, 204, 2);
|
||||||
|
// }
|
||||||
|
TryPutCard win = new TryPutCard(cardInhand, 0);
|
||||||
|
// win.tryWin();*/
|
||||||
|
// Paixing.tingKongCheck(new ArrayList<>(), cardInhand, 301);
|
||||||
|
// }
|
||||||
|
//Map<Integer, WinCardType> map = new HashMap<Integer, WinCardType>();
|
||||||
|
//Paixing.checkWin(map, new ArrayList<>(), cardInhand, 305, room.difen_score);
|
||||||
|
|
||||||
|
System.out.println(System.currentTimeMillis() - time);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,274 @@
|
||||||
|
package com.robotcm.taurus.util;
|
||||||
|
|
||||||
|
import com.game.Util;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Stack;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public class WinCard {
|
||||||
|
public int pair_count;
|
||||||
|
public Stack<List<Integer>> stack;
|
||||||
|
public List<Integer> cardList;
|
||||||
|
public boolean jiang = true;
|
||||||
|
public int zhong_count;
|
||||||
|
public int zhongid = 0;
|
||||||
|
|
||||||
|
private void push(List<Integer> cardGroup) {
|
||||||
|
this.stack.push(cardGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void rollBack() {
|
||||||
|
List<Integer> cardGroup = this.stack.pop();
|
||||||
|
for (Integer card : cardGroup) {
|
||||||
|
if (card == zhongid) {
|
||||||
|
this.zhong_count += 1;
|
||||||
|
} else {
|
||||||
|
this.cardList.add(card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Collections.sort(this.cardList);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean tryShunzi(int card) {
|
||||||
|
if (card % 100 > 7) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Util.checkCard(card + 1, this.cardList) && Util.checkCard(card + 2, this.cardList)) {
|
||||||
|
Util.removeCard(this.cardList, card, 1);
|
||||||
|
Util.removeCard(this.cardList, card + 1, 1);
|
||||||
|
Util.removeCard(this.cardList, card + 2, 1);
|
||||||
|
List<Integer> cardGroup = new ArrayList<Integer>();
|
||||||
|
cardGroup.add(card);
|
||||||
|
cardGroup.add(card + 1);
|
||||||
|
cardGroup.add(card + 2);
|
||||||
|
this.push(cardGroup);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean tryKezi(int card) {
|
||||||
|
if (Util.checkCardAndRomve(card, this.cardList, 3)) {
|
||||||
|
// CardUtil.removeCard(this.cardList, card, 3);
|
||||||
|
List<Integer> cardGroup = new ArrayList<Integer>();
|
||||||
|
cardGroup.add(card);
|
||||||
|
cardGroup.add(card);
|
||||||
|
cardGroup.add(card);
|
||||||
|
this.push(cardGroup);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean tryPair(int card) {
|
||||||
|
if (this.pair_count > 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (jiang) {
|
||||||
|
if (!(card % 100 == 2 || card % 100 == 5 || card % 100 == 8)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Util.checkCardAndRomve(card, this.cardList, 2)) {
|
||||||
|
// CardUtil.removeCard(this.cardList, card, 2);
|
||||||
|
List<Integer> cardGroup = new ArrayList<Integer>();
|
||||||
|
cardGroup.add(card);
|
||||||
|
cardGroup.add(card);
|
||||||
|
this.push(cardGroup);
|
||||||
|
this.pair_count = 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean tryKezi1Zhong(int card) {
|
||||||
|
if (this.zhong_count >= 1 && Util.checkCardAndRomve(card, this.cardList, 2)) {
|
||||||
|
// CardUtil.removeCard(this.cardList, card, 2);
|
||||||
|
List<Integer> cardGroup = new ArrayList<Integer>();
|
||||||
|
cardGroup.add(card);
|
||||||
|
cardGroup.add(card);
|
||||||
|
cardGroup.add(this.zhongid);
|
||||||
|
this.zhong_count -= 1;
|
||||||
|
this.push(cardGroup);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean tryShunzi1Zhong(int card) {
|
||||||
|
if (card % 100 > 8) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.zhong_count < 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Util.checkCard(card + 1, this.cardList)) {
|
||||||
|
Util.removeCard(cardList, card, 1);
|
||||||
|
Util.removeCard(cardList, card + 1, 1);
|
||||||
|
this.zhong_count -= 1;
|
||||||
|
List<Integer> cardGroup = new ArrayList<Integer>();
|
||||||
|
cardGroup.add(card);
|
||||||
|
cardGroup.add(card + 1);
|
||||||
|
cardGroup.add(this.zhongid);
|
||||||
|
this.push(cardGroup);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Util.checkCard(card + 2, this.cardList) && ((card + 1) % 100 != 0)) {
|
||||||
|
Util.removeCard(cardList, card, 1);
|
||||||
|
Util.removeCard(cardList, card + 2, 1);
|
||||||
|
this.zhong_count -= 1;
|
||||||
|
List<Integer> cardGroup = new ArrayList<Integer>();
|
||||||
|
cardGroup.add(card);
|
||||||
|
cardGroup.add(this.zhongid);
|
||||||
|
cardGroup.add(card + 2);
|
||||||
|
this.push(cardGroup);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean tryPair1Zhong(int card) {
|
||||||
|
if (this.pair_count > 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (jiang) {
|
||||||
|
if (!(card % 100 == 2 || card % 100 == 5 || card % 100 == 8)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.zhong_count < 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Util.removeCard(cardList, card, 1);
|
||||||
|
List<Integer> cardGroup = new ArrayList<Integer>();
|
||||||
|
cardGroup.add(card);
|
||||||
|
cardGroup.add(this.zhongid);
|
||||||
|
this.push(cardGroup);
|
||||||
|
this.zhong_count -= 1;
|
||||||
|
this.pair_count = 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean tryWin() {
|
||||||
|
if (this.cardList.size() == 0 && this.pair_count == 1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.cardList.size() == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int activeCard = this.cardList.get(0);
|
||||||
|
|
||||||
|
if (tryPair(activeCard)) {
|
||||||
|
if (tryWin()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
this.pair_count = 0;
|
||||||
|
this.rollBack();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tryKezi(activeCard)) {
|
||||||
|
if (tryWin()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
this.rollBack();
|
||||||
|
|
||||||
|
}
|
||||||
|
if (tryShunzi(activeCard)) {
|
||||||
|
if (tryWin()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
this.rollBack();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tryKezi1Zhong(activeCard)) {
|
||||||
|
if (tryWin()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
this.rollBack();
|
||||||
|
|
||||||
|
}
|
||||||
|
if (tryShunzi1Zhong(activeCard)) {
|
||||||
|
if (tryWin()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
this.rollBack();
|
||||||
|
}
|
||||||
|
if (tryPair1Zhong(activeCard)) {
|
||||||
|
if (tryWin()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
this.pair_count = 0;
|
||||||
|
this.rollBack();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WinCard(List<Integer> cardInhand, int addCard) {
|
||||||
|
this.stack = new Stack<List<Integer>>();
|
||||||
|
this.cardList = new ArrayList<Integer>();
|
||||||
|
this.cardList.addAll(cardInhand);
|
||||||
|
if (addCard == 0) {
|
||||||
|
this.zhongid = 0;
|
||||||
|
this.zhong_count = 1;
|
||||||
|
} else {
|
||||||
|
this.cardList.add(addCard);
|
||||||
|
}
|
||||||
|
|
||||||
|
Collections.sort(this.cardList);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
|
||||||
|
long time = System.currentTimeMillis();
|
||||||
|
// for (int i = 0; i < 1000000; ++i) {
|
||||||
|
ArrayList<Integer> cardInhand = new ArrayList<Integer>();
|
||||||
|
cardInhand.add(207);
|
||||||
|
cardInhand.add(207);
|
||||||
|
cardInhand.add(202);
|
||||||
|
cardInhand.add(202);
|
||||||
|
cardInhand.add(202);
|
||||||
|
cardInhand.add(109);
|
||||||
|
|
||||||
|
cardInhand.add(109);
|
||||||
|
cardInhand.add(109);
|
||||||
|
cardInhand.add(104);
|
||||||
|
cardInhand.add(104);
|
||||||
|
cardInhand.add(102);
|
||||||
|
cardInhand.add(102);
|
||||||
|
|
||||||
|
cardInhand.add(102);
|
||||||
|
// cardInhand.add(301);
|
||||||
|
// cardInhand.add(301);
|
||||||
|
// cardInhand.add(108);
|
||||||
|
|
||||||
|
// cardInhand.contains(204);
|
||||||
|
// Util.checkCard(204, cardInhand);
|
||||||
|
// CardUtil.checkCardAndRomve(204, cardInhand, 2);
|
||||||
|
|
||||||
|
// if(Util.checkCard(204, cardInhand)) {
|
||||||
|
// CardUtil.removeCard(cardInhand, 204, 2);
|
||||||
|
// }
|
||||||
|
WinCard win = new WinCard(cardInhand, 104);
|
||||||
|
boolean c = win.tryWin();
|
||||||
|
System.out.println(c);
|
||||||
|
// Paixing.tingKongCheck(new ArrayList<>(), cardInhand, 301);
|
||||||
|
// }
|
||||||
|
//Map<Integer, WinCardType> map = new HashMap<Integer, WinCardType>();
|
||||||
|
//Paixing.checkWin(map, new ArrayList<>(), cardInhand, 305, room.difen_score);
|
||||||
|
|
||||||
|
System.out.println(System.currentTimeMillis() - time);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,87 @@
|
||||||
|
package com.robotcm.taurus.util;
|
||||||
|
|
||||||
|
import com.taurus.core.entity.ITArray;
|
||||||
|
import com.taurus.core.entity.ITObject;
|
||||||
|
import com.taurus.core.entity.TObject;
|
||||||
|
|
||||||
|
public class WinCardType {
|
||||||
|
|
||||||
|
public static final int QIXIAODUI = 1; //七小对多少分?
|
||||||
|
|
||||||
|
public static final int JIANGJIANGHU = 2; //将将胡多少分?
|
||||||
|
|
||||||
|
public static final int QINGYISE = 3; //清一色多少分?
|
||||||
|
|
||||||
|
public static final int PENGPENGHU = 4; //碰碰胡多少分?
|
||||||
|
|
||||||
|
public static final int GANGSHANGHUA = 5; //杠上花多少分?
|
||||||
|
|
||||||
|
public static final int GANGSHANGPAO = 6; //杠上炮多少分?
|
||||||
|
|
||||||
|
public static final int QUANQIUREN = 7; //全求人多少分?
|
||||||
|
|
||||||
|
public static final int HAIDI = 8; //海底多少分?
|
||||||
|
|
||||||
|
public static final int HAIDIPAO = 9; //海底炮多少分?
|
||||||
|
|
||||||
|
public static final int QIANG_GANG_HU =10; //抢杠胡?
|
||||||
|
|
||||||
|
public static final int XIAOHU = 11;
|
||||||
|
|
||||||
|
public final static int TYPE_TIANHU = 12;
|
||||||
|
|
||||||
|
public final static int TYPE_TIANTINGHU = 13;
|
||||||
|
|
||||||
|
public final static int TYPE_MENQING = 14;
|
||||||
|
|
||||||
|
public final static int TYPE_LIULIUSHUN = 21;
|
||||||
|
|
||||||
|
public final static int TYPE_DASIXI = 22;
|
||||||
|
|
||||||
|
public final static int TYPE_QUEYISE = 23;
|
||||||
|
|
||||||
|
public final static int TYPE_BANBANHU = 24;
|
||||||
|
|
||||||
|
public final static int TYPE_JIEJIEGAO = 25;
|
||||||
|
|
||||||
|
public final static int TYPE_SANTONG = 26;
|
||||||
|
|
||||||
|
|
||||||
|
public final static int TYPE_YIZHIHUA = 27;
|
||||||
|
|
||||||
|
public final static int WIN_TYPE_SCORE = 7;
|
||||||
|
public final static int WIN_PING_TYPE_SCORE = 3;
|
||||||
|
|
||||||
|
public int type;
|
||||||
|
|
||||||
|
public int value;
|
||||||
|
|
||||||
|
public int score;
|
||||||
|
|
||||||
|
public ITArray opcard;
|
||||||
|
|
||||||
|
public WinCardType(int type, int value, int score) {
|
||||||
|
this.type = type;
|
||||||
|
this.value = value;
|
||||||
|
this.score = score;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ITObject toTObject() {
|
||||||
|
return toTObject(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ITObject toTObject(boolean is_opcard) {
|
||||||
|
ITObject obj = TObject.newInstance();
|
||||||
|
obj.putInt("type", type);
|
||||||
|
obj.putInt("value", value);
|
||||||
|
if(is_opcard&&opcard!=null) {
|
||||||
|
obj.putTArray("opcard", opcard);
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return "type:" + type + " value:" + value;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,955 @@
|
||||||
|
package com.robotcm.taurus.util;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class ZhuanZhuanSuanFaTest {
|
||||||
|
private static final int HONG_ZHONG = 412;
|
||||||
|
|
||||||
|
// 权重配置
|
||||||
|
private static final int WEIGHT_COMPLETED_MELD = 70; // 已完成的面子权重
|
||||||
|
private static final int WEIGHT_PAIR = 60; // 对子权重
|
||||||
|
private static final int WEIGHT_POTENTIAL_TRIPLET = 50;
|
||||||
|
private static final int WEIGHT_POTENTIAL_PAIR = 40;
|
||||||
|
private static final int WEIGHT_CLOSE_ADJACENT = 60; // 相邻牌权重
|
||||||
|
private static final int WEIGHT_FAR_ADJACENT = 25; // 远邻牌权重
|
||||||
|
private static final int WEIGHT_MIDDLE_RANK = 25; // 中张牌权重
|
||||||
|
private static final int PENALTY_EDGE_RANK = 15; // 边张惩罚
|
||||||
|
private static final int PENALTY_ISOLATED = 40; // 孤张惩罚
|
||||||
|
|
||||||
|
public String outCardSuanFa(List<Integer> cardInhand, int card) {
|
||||||
|
List<Integer> handCards = new ArrayList<>(cardInhand);
|
||||||
|
handCards.sort(Integer::compareTo);
|
||||||
|
System.out.println("排序后机器人手牌" + handCards);
|
||||||
|
|
||||||
|
// 分析手牌结构
|
||||||
|
HandAnalysis analysis = analyzeHand(handCards);
|
||||||
|
|
||||||
|
System.out.println("机器人 zhuanZhuanCount 转转数量 " + analysis.zhuanZhaunCount);
|
||||||
|
System.out.println(" 按花色分组的计数 " + analysis.cardCounts);
|
||||||
|
System.out.println("机器人 已完成的刻子、顺子 " + analysis.completedMelds);
|
||||||
|
System.out.println("机器人 对子 " + analysis.pairs);
|
||||||
|
System.out.println("机器人 孤张牌 " + analysis.isolatedCards);
|
||||||
|
System.out.println("机器人 每张牌的有用程度 " + analysis.cardUsefulness);
|
||||||
|
System.out.println("机器人 按花色分组的手牌" + analysis.cardsBySuit);
|
||||||
|
System.out.println("机器人 向听数 " + analysis.shantenCount);
|
||||||
|
|
||||||
|
// 计算每张牌的出牌优先级
|
||||||
|
Map<Integer, Integer> discardPriority = calculateDiscardPriority(handCards, analysis);
|
||||||
|
|
||||||
|
// 选择要打出的牌
|
||||||
|
int cardToDiscard = selectCardToDiscard(handCards, discardPriority);
|
||||||
|
System.out.println("最终打出牌 " + cardToDiscard);
|
||||||
|
|
||||||
|
return String.valueOf(cardToDiscard);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class HandAnalysis {
|
||||||
|
int zhuanZhaunCount = 0;
|
||||||
|
Map<Integer, Integer> cardCounts = new HashMap<>(); // 按花色分组的计数
|
||||||
|
List<List<Integer>> completedMelds = new ArrayList<>(); // 已完成的刻子/顺子
|
||||||
|
List<Integer> pairs = new ArrayList<>(); //对子
|
||||||
|
List<Integer> isolatedCards = new ArrayList<>(); // 孤张牌
|
||||||
|
Map<Integer, Integer> cardUsefulness = new HashMap<>(); // 每张牌的有用程度
|
||||||
|
Map<Integer, List<Integer>> cardsBySuit = new HashMap<>(); // 按花色分组的手牌
|
||||||
|
|
||||||
|
// 新增字段
|
||||||
|
int pairCount = 0; // 对子数量
|
||||||
|
int meldCount = 0; // 面子数量
|
||||||
|
int shantenCount = Integer.MAX_VALUE; // 向听数
|
||||||
|
boolean isTingPai = false; // 是否听牌
|
||||||
|
Set<Integer> tingCards = new HashSet<>(); // 可听的牌组
|
||||||
|
boolean hasLongQiDuiPotential = false; // 七对潜力
|
||||||
|
Set<Integer> usedInMelds = new HashSet<>(); // 已用于面子的牌
|
||||||
|
Set<Integer> usedInPairs = new HashSet<>(); // 已用于对子的牌
|
||||||
|
}
|
||||||
|
|
||||||
|
private HandAnalysis analyzeHand(List<Integer> handCards) {
|
||||||
|
HandAnalysis analysis = new HandAnalysis();
|
||||||
|
|
||||||
|
// 分离红中和普通牌,并按花色分组
|
||||||
|
for (int card : handCards) {
|
||||||
|
if (card == HONG_ZHONG) {
|
||||||
|
analysis.zhuanZhaunCount++;
|
||||||
|
} else {
|
||||||
|
int suit = card / 100;
|
||||||
|
analysis.cardCounts.put(card, analysis.cardCounts.getOrDefault(card, 0) + 1);
|
||||||
|
|
||||||
|
// 按花色分组
|
||||||
|
analysis.cardsBySuit.computeIfAbsent(suit, k -> new ArrayList<>()).add(card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
analyzeCardPatterns(analysis);
|
||||||
|
|
||||||
|
// 计算向听数
|
||||||
|
calculateShantenCount(analysis);
|
||||||
|
|
||||||
|
// 检查七对潜力
|
||||||
|
checkLongQiDuiPotential(analysis);
|
||||||
|
|
||||||
|
// 检查是否听牌
|
||||||
|
checkTingPai(analysis);
|
||||||
|
|
||||||
|
return analysis;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void analyzeCardPatterns(HandAnalysis analysis) {
|
||||||
|
// 初始化可组成面子的牌集合
|
||||||
|
Set<Integer> usedInMelds = new HashSet<>();
|
||||||
|
Set<Integer> usedInPairs = new HashSet<>();
|
||||||
|
|
||||||
|
// 对每种花色分别分析
|
||||||
|
for (List<Integer> suitCards : new ArrayList<>(analysis.cardsBySuit.values())) {
|
||||||
|
List<Integer> cardsCopy = new ArrayList<>(suitCards);
|
||||||
|
cardsCopy.sort(Integer::compareTo);
|
||||||
|
|
||||||
|
// 先找刻子
|
||||||
|
findTripletsOptimized(cardsCopy, analysis, usedInMelds);
|
||||||
|
// 再找顺子
|
||||||
|
findSequencesOptimized(cardsCopy, analysis, usedInMelds);
|
||||||
|
// 找对子
|
||||||
|
findPairsOptimized(cardsCopy, analysis, usedInPairs);
|
||||||
|
// 剩余的是孤张
|
||||||
|
analysis.isolatedCards.addAll(cardsCopy);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存已使用的牌信息
|
||||||
|
analysis.usedInMelds = usedInMelds;
|
||||||
|
analysis.usedInPairs = usedInPairs;
|
||||||
|
|
||||||
|
// 更新面子数量和对子数量
|
||||||
|
analysis.meldCount = analysis.completedMelds.size();
|
||||||
|
analysis.pairCount = analysis.pairs.size();
|
||||||
|
|
||||||
|
// 分析每张牌的价值
|
||||||
|
analyzeCardUsefulness(analysis);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 优化的刻子查找方法
|
||||||
|
private void findTripletsOptimized(List<Integer> cards, HandAnalysis analysis, Set<Integer> usedInMelds) {
|
||||||
|
Map<Integer, Integer> countMap = new HashMap<>();
|
||||||
|
for (int card : cards) {
|
||||||
|
countMap.put(card, countMap.getOrDefault(card, 0) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用新的列表来记录要移除的牌
|
||||||
|
List<Integer> cardsToRemove = new ArrayList<>();
|
||||||
|
|
||||||
|
for (int card : cards) {
|
||||||
|
if (countMap.getOrDefault(card, 0) >= 3 && !cardsToRemove.contains(card)) {
|
||||||
|
// 找到刻子
|
||||||
|
List<Integer> triplet = Arrays.asList(card, card, card);
|
||||||
|
analysis.completedMelds.add(triplet);
|
||||||
|
|
||||||
|
// 标记要移除的牌
|
||||||
|
cardsToRemove.add(card);
|
||||||
|
cardsToRemove.add(card);
|
||||||
|
cardsToRemove.add(card);
|
||||||
|
|
||||||
|
// 标记已使用的牌
|
||||||
|
usedInMelds.add(card);
|
||||||
|
|
||||||
|
// 更新计数
|
||||||
|
countMap.put(card, countMap.get(card) - 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 一次性移除所有标记的牌
|
||||||
|
cards.removeAll(cardsToRemove);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 优化的顺子查找方法
|
||||||
|
private void findSequencesOptimized(List<Integer> cards, HandAnalysis analysis, Set<Integer> usedInMelds) {
|
||||||
|
if (cards.size() < 3) return;
|
||||||
|
|
||||||
|
// 转换为点数并去重排序
|
||||||
|
Set<Integer> points = new HashSet<>();
|
||||||
|
for (int card : cards) {
|
||||||
|
points.add(card % 100); // 获取点数(1-9)
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Integer> sortedPoints = new ArrayList<>(points);
|
||||||
|
Collections.sort(sortedPoints);
|
||||||
|
|
||||||
|
for (int i = 0; i <= sortedPoints.size() - 3; i++) {
|
||||||
|
int p1 = sortedPoints.get(i);
|
||||||
|
int p2 = sortedPoints.get(i + 1);
|
||||||
|
int p3 = sortedPoints.get(i + 2);
|
||||||
|
|
||||||
|
if (p2 == p1 + 1 && p3 == p2 + 1) {
|
||||||
|
// 找到顺子模式,获取对应的牌
|
||||||
|
int suit = cards.get(0) / 100; // 所有牌都是同一花色
|
||||||
|
int card1 = suit * 100 + p1;
|
||||||
|
int card2 = suit * 100 + p2;
|
||||||
|
int card3 = suit * 100 + p3;
|
||||||
|
|
||||||
|
if (cards.contains(card1) && cards.contains(card2) && cards.contains(card3)) {
|
||||||
|
// 找到顺子
|
||||||
|
List<Integer> sequence = Arrays.asList(card1, card2, card3);
|
||||||
|
analysis.completedMelds.add(sequence);
|
||||||
|
|
||||||
|
// 移除这三张牌
|
||||||
|
cards.remove(Integer.valueOf(card1));
|
||||||
|
cards.remove(Integer.valueOf(card2));
|
||||||
|
cards.remove(Integer.valueOf(card3));
|
||||||
|
|
||||||
|
// 标记已使用的牌
|
||||||
|
usedInMelds.add(card1);
|
||||||
|
usedInMelds.add(card2);
|
||||||
|
usedInMelds.add(card3);
|
||||||
|
|
||||||
|
// 重新开始查找,因为牌列表改变了
|
||||||
|
findSequencesOptimized(cards, analysis, usedInMelds);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 优化的对子查找方法
|
||||||
|
private void findPairsOptimized(List<Integer> cards, HandAnalysis analysis, Set<Integer> usedInPairs) {
|
||||||
|
Map<Integer, Integer> countMap = new HashMap<>();
|
||||||
|
for (int card : cards) {
|
||||||
|
countMap.put(card, countMap.getOrDefault(card, 0) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用新的列表来记录要移除的牌
|
||||||
|
List<Integer> cardsToRemove = new ArrayList<>();
|
||||||
|
|
||||||
|
for (int card : cards) {
|
||||||
|
if (countMap.getOrDefault(card, 0) >= 2 && !cardsToRemove.contains(card)) {
|
||||||
|
analysis.pairs.add(card);
|
||||||
|
|
||||||
|
// 标记已使用的牌
|
||||||
|
usedInPairs.add(card);
|
||||||
|
|
||||||
|
// 标记要移除的牌
|
||||||
|
cardsToRemove.add(card);
|
||||||
|
cardsToRemove.add(card);
|
||||||
|
|
||||||
|
// 更新计数
|
||||||
|
countMap.put(card, countMap.get(card) - 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 一次性移除所有标记的牌
|
||||||
|
cards.removeAll(cardsToRemove);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算向听数
|
||||||
|
private void calculateShantenCount(HandAnalysis analysis) {
|
||||||
|
// 简化版向听数计算
|
||||||
|
int totalCards = analysis.zhuanZhaunCount;
|
||||||
|
for (List<Integer> suitCards : analysis.cardsBySuit.values()) {
|
||||||
|
totalCards += suitCards.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 基础向听数 = 理想牌型的面子数 - 当前面子数
|
||||||
|
int idealMelds = (totalCards + analysis.zhuanZhaunCount) / 3;
|
||||||
|
analysis.shantenCount = Math.max(0, idealMelds - analysis.meldCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查七对潜力
|
||||||
|
private void checkLongQiDuiPotential(HandAnalysis analysis) {
|
||||||
|
// 七对需要至少5个对子加上转转
|
||||||
|
if (analysis.pairCount >= 5 || analysis.zhuanZhaunCount >= 2) {
|
||||||
|
analysis.hasLongQiDuiPotential = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否听牌
|
||||||
|
private void checkTingPai(HandAnalysis analysis) {
|
||||||
|
// 检查基本听牌条件
|
||||||
|
int totalCards = analysis.zhuanZhaunCount;
|
||||||
|
for (List<Integer> suitCards : analysis.cardsBySuit.values()) {
|
||||||
|
totalCards += suitCards.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 标准麻将听牌条件:一对将牌 + 四面子,或七对子
|
||||||
|
boolean standardTing = (totalCards - 2) % 3 == 0 && analysis.shantenCount == 0;
|
||||||
|
boolean sevenPairsTing = analysis.hasLongQiDuiPotential && totalCards >= 12;
|
||||||
|
|
||||||
|
analysis.isTingPai = standardTing || sevenPairsTing;
|
||||||
|
|
||||||
|
// 如果听牌,尝试找出听的牌
|
||||||
|
if (analysis.isTingPai) {
|
||||||
|
findTingCards(analysis);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 找出可听的牌
|
||||||
|
private void findTingCards(HandAnalysis analysis) {
|
||||||
|
// 简化版听牌查找逻辑
|
||||||
|
for (int suit = 1; suit <= 3; suit++) { // 遍历万、饼、条
|
||||||
|
for (int rank = 1; rank <= 9; rank++) { // 遍历1-9
|
||||||
|
int testCard = suit * 100 + rank;
|
||||||
|
if (!analysis.cardCounts.containsKey(testCard) || analysis.cardCounts.get(testCard) < 4) {
|
||||||
|
// 模拟加入这张牌
|
||||||
|
analysis.tingCards.add(testCard);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分析摸牌后是否可听牌,并全面分析手牌结构
|
||||||
|
*/
|
||||||
|
public boolean analyzeDrawCard(List<Integer> currentHand, int drawnCard) {
|
||||||
|
// 创建包含摸牌后的手牌
|
||||||
|
List<Integer> newHand = new ArrayList<>(currentHand);
|
||||||
|
newHand.add(drawnCard);
|
||||||
|
newHand.sort(Integer::compareTo);
|
||||||
|
|
||||||
|
System.out.println("摸牌: " + drawnCard + ",摸牌后手牌: " + newHand);
|
||||||
|
|
||||||
|
// 分析新的手牌
|
||||||
|
HandAnalysis analysis = analyzeHand(newHand);
|
||||||
|
|
||||||
|
// 输出详细分析结果
|
||||||
|
System.out.println("摸牌后分析结果:");
|
||||||
|
System.out.println("- 是否听牌: " + analysis.isTingPai);
|
||||||
|
if (analysis.isTingPai) {
|
||||||
|
System.out.println("- 可听的牌组: " + analysis.tingCards);
|
||||||
|
}
|
||||||
|
System.out.println("- 转转数量: " + analysis.zhuanZhaunCount);
|
||||||
|
System.out.println("- 已完成的面子数: " + analysis.meldCount);
|
||||||
|
System.out.println("- 对子数量: " + analysis.pairCount);
|
||||||
|
System.out.println("- 孤张牌: " + analysis.isolatedCards);
|
||||||
|
System.out.println("- 向听数: " + analysis.shantenCount);
|
||||||
|
|
||||||
|
// 特别分析新摸的牌对牌型的影响
|
||||||
|
int newCardCount = 0;
|
||||||
|
for (int card : newHand) {
|
||||||
|
if (card == drawnCard) newCardCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("- 新摸牌 " + drawnCard + " 的数量: " + newCardCount);
|
||||||
|
|
||||||
|
// 分析新摸牌的潜力
|
||||||
|
int drawnCardPotential = calculateCardPotentialScore(drawnCard, analysis);
|
||||||
|
System.out.println("- 新摸牌的潜力分数: " + drawnCardPotential);
|
||||||
|
|
||||||
|
// 调用分析打出哪张牌可以听牌的方法
|
||||||
|
findDiscardToTing(newHand);
|
||||||
|
|
||||||
|
return analysis.isTingPai;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分析打出哪张牌可以听牌
|
||||||
|
*/
|
||||||
|
public Map<Integer, Set<Integer>> findDiscardToTing(List<Integer> currentHand) {
|
||||||
|
Map<Integer, Set<Integer>> discardToTingMap = new HashMap<>();
|
||||||
|
System.out.println("\n开始分析打出哪张牌可以听牌...");
|
||||||
|
|
||||||
|
// 对手牌进行深度复制,避免修改原手牌
|
||||||
|
List<Integer> handCopy = new ArrayList<>(currentHand);
|
||||||
|
handCopy.sort(Integer::compareTo);
|
||||||
|
|
||||||
|
// 统计每种牌的数量,避免重复分析相同的牌
|
||||||
|
Map<Integer, Integer> cardCounts = new HashMap<>();
|
||||||
|
for (int card : handCopy) {
|
||||||
|
cardCounts.put(card, cardCounts.getOrDefault(card, 0) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试打出每一种不同的牌
|
||||||
|
for (Map.Entry<Integer, Integer> entry : cardCounts.entrySet()) {
|
||||||
|
int cardToDiscard = entry.getKey();
|
||||||
|
|
||||||
|
// 转转通常不会打出,但仍然分析可能性
|
||||||
|
if (cardToDiscard == HONG_ZHONG) {
|
||||||
|
System.out.println("- 通常不建议打出转转: " + cardToDiscard);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 模拟打出这张牌
|
||||||
|
List<Integer> tempHand = new ArrayList<>(handCopy);
|
||||||
|
tempHand.remove(Integer.valueOf(cardToDiscard));
|
||||||
|
|
||||||
|
// 分析打出这张牌后的手牌是否听牌
|
||||||
|
HandAnalysis analysis = analyzeHand(tempHand);
|
||||||
|
|
||||||
|
// 如果听牌,记录这张牌和对应的听牌组
|
||||||
|
if (analysis.isTingPai && !analysis.tingCards.isEmpty()) {
|
||||||
|
discardToTingMap.put(cardToDiscard, analysis.tingCards);
|
||||||
|
System.out.println("- 打出牌: " + cardToDiscard + " 后可以听牌");
|
||||||
|
System.out.println(" 听的牌组: " + analysis.tingCards);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 输出分析结果
|
||||||
|
if (discardToTingMap.isEmpty()) {
|
||||||
|
System.out.println("- 当前没有可以打出后听牌的牌");
|
||||||
|
} else {
|
||||||
|
System.out.println("\n可打出后听牌的牌数量: " + discardToTingMap.size());
|
||||||
|
System.out.println("建议打出的牌及其对应的听牌组:");
|
||||||
|
for (Map.Entry<Integer, Set<Integer>> entry : discardToTingMap.entrySet()) {
|
||||||
|
System.out.println("- 牌: " + entry.getKey() + " -> 听牌组: " + entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return discardToTingMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算牌的潜力分数
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* 计算牌的潜力分数(增强版)
|
||||||
|
*/
|
||||||
|
private int calculateCardPotentialScore(int card, HandAnalysis analysis) {
|
||||||
|
int score = 0;
|
||||||
|
|
||||||
|
// 如果是转转,潜力很高
|
||||||
|
if (card == HONG_ZHONG) {
|
||||||
|
return 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
int suit = card / 100;
|
||||||
|
int rank = card % 100;
|
||||||
|
int cardCount = analysis.cardCounts.getOrDefault(card, 0);
|
||||||
|
|
||||||
|
// 1. 刻子潜力评估
|
||||||
|
if (cardCount >= 2) {
|
||||||
|
score += 30; // 两张相同的牌有刻子潜力
|
||||||
|
if (cardCount >= 3) {
|
||||||
|
score += 40; // 三张相同的牌刻子已完成
|
||||||
|
}
|
||||||
|
} else if (cardCount == 1) {
|
||||||
|
score += 10; // 单张牌的基础潜力
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 对子潜力
|
||||||
|
if (analysis.pairs.contains(card)) {
|
||||||
|
score += 25;
|
||||||
|
// 如果已经有多个对子,评估七对潜力
|
||||||
|
if (analysis.pairCount >= 5) {
|
||||||
|
score += 15; // 七对潜力加分
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 顺子潜力评估
|
||||||
|
// 检查两面搭(如34需要2或5)
|
||||||
|
int prevCard = suit * 100 + (rank - 1);
|
||||||
|
int nextCard = suit * 100 + (rank + 1);
|
||||||
|
boolean hasPrev = analysis.cardCounts.getOrDefault(prevCard, 0) > 0;
|
||||||
|
boolean hasNext = analysis.cardCounts.getOrDefault(nextCard, 0) > 0;
|
||||||
|
|
||||||
|
if (hasPrev && hasNext) {
|
||||||
|
score += 20; // 两面搭子
|
||||||
|
} else if (hasPrev || hasNext) {
|
||||||
|
score += 10; // 一面搭子
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 检查坎搭可能性(如2和4之间需要3)
|
||||||
|
if (rank >= 3 && rank <= 7) {
|
||||||
|
int prevPrevCard = suit * 100 + (rank - 2);
|
||||||
|
int nextNextCard = suit * 100 + (rank + 2);
|
||||||
|
|
||||||
|
if (analysis.cardCounts.getOrDefault(prevPrevCard, 0) > 0) {
|
||||||
|
score += 8; // 坎搭潜力
|
||||||
|
}
|
||||||
|
if (analysis.cardCounts.getOrDefault(nextNextCard, 0) > 0) {
|
||||||
|
score += 8; // 坎搭潜力
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 中间牌额外潜力
|
||||||
|
if (rank >= 3 && rank <= 7) {
|
||||||
|
score += 5; // 中张牌额外价值
|
||||||
|
}
|
||||||
|
// 边张减分
|
||||||
|
else if (rank == 1 || rank == 9) {
|
||||||
|
score -= 5; // 边张减分
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. 检查是否形成复合搭子(如334有两面搭和对子的复合)
|
||||||
|
if (cardCount >= 1) {
|
||||||
|
// 检查是否有相邻的对子
|
||||||
|
if (analysis.cardCounts.getOrDefault(prevCard, 0) >= 2) {
|
||||||
|
score += 8; // 相邻对子形成复合搭子
|
||||||
|
}
|
||||||
|
if (analysis.cardCounts.getOrDefault(nextCard, 0) >= 2) {
|
||||||
|
score += 8; // 相邻对子形成复合搭子
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否有相邻的单牌形成复合搭子(如334)
|
||||||
|
if (analysis.cardCounts.getOrDefault(prevCard, 0) == 1 && cardCount >= 2) {
|
||||||
|
score += 5;
|
||||||
|
}
|
||||||
|
if (analysis.cardCounts.getOrDefault(nextCard, 0) == 1 && cardCount >= 2) {
|
||||||
|
score += 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. 转转作为万能牌的影响
|
||||||
|
if (analysis.zhuanZhaunCount > 0) {
|
||||||
|
// 单张牌+转转可以形成对子
|
||||||
|
if (cardCount == 1) {
|
||||||
|
score += 10;
|
||||||
|
}
|
||||||
|
// 两张牌+转转可以形成刻子
|
||||||
|
if (cardCount == 2) {
|
||||||
|
score += 15;
|
||||||
|
}
|
||||||
|
// 转转可以填补顺子空缺
|
||||||
|
if (rank >= 2 && rank <= 8 && (!hasPrev || !hasNext)) {
|
||||||
|
score += 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8. 评估该牌对整体牌型的贡献
|
||||||
|
if (analysis.shantenCount <= 2 && !analysis.isolatedCards.contains(card)) {
|
||||||
|
score += 10; // 对接近听牌的牌型有贡献
|
||||||
|
}
|
||||||
|
|
||||||
|
// 9. 孤张牌减分
|
||||||
|
if (analysis.isolatedCards.contains(card)) {
|
||||||
|
score -= 15; // 孤张牌减分
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.max(0, score); // 确保分数不为负
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理牌数较少的花色
|
||||||
|
*/
|
||||||
|
private void handleSmallSuit(List<Integer> suitCards, HandAnalysis analysis) {
|
||||||
|
Map<Integer, Integer> countMap = new HashMap<>();
|
||||||
|
for (int card : suitCards) {
|
||||||
|
countMap.put(card, countMap.getOrDefault(card, 0) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 先找对子
|
||||||
|
for (Map.Entry<Integer, Integer> entry : countMap.entrySet()) {
|
||||||
|
if (entry.getValue() >= 2) {
|
||||||
|
analysis.pairs.add(entry.getKey());
|
||||||
|
|
||||||
|
// 移除对子
|
||||||
|
suitCards.remove(entry.getKey());
|
||||||
|
suitCards.remove(entry.getKey());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 剩余的都是孤张
|
||||||
|
analysis.isolatedCards.addAll(suitCards);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分析每张牌的有用程度(优化版)
|
||||||
|
*/
|
||||||
|
private void analyzeCardUsefulnessOptimized(HandAnalysis analysis) {
|
||||||
|
// 分析所有普通牌
|
||||||
|
for (List<Integer> suitCards : analysis.cardsBySuit.values()) {
|
||||||
|
for (int card : suitCards) {
|
||||||
|
int usefulness = calculateSingleCardUsefulness(card, suitCards, analysis);
|
||||||
|
analysis.cardUsefulness.put(card, usefulness);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重命名为analyzeCardUsefulness以保持向后兼容性
|
||||||
|
private void analyzeCardUsefulness(HandAnalysis analysis) {
|
||||||
|
analyzeCardUsefulnessOptimized(analysis);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int calculateSingleCardUsefulness(int card, List<Integer> sameSuitCards, HandAnalysis analysis) {
|
||||||
|
int usefulness = 0;
|
||||||
|
int suit = card / 100;
|
||||||
|
int rank = card % 100;
|
||||||
|
|
||||||
|
// 1. 检查是否在已完成的牌型中
|
||||||
|
for (List<Integer> meld : analysis.completedMelds) {
|
||||||
|
if (meld.contains(card)) {
|
||||||
|
usefulness += WEIGHT_COMPLETED_MELD;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 检查是否是对子
|
||||||
|
if (analysis.pairs.contains(card)) {
|
||||||
|
usefulness += WEIGHT_PAIR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 检查刻子潜力
|
||||||
|
int sameCardCount = Collections.frequency(sameSuitCards, card);
|
||||||
|
if (sameCardCount >= 2) {
|
||||||
|
usefulness += WEIGHT_POTENTIAL_TRIPLET;
|
||||||
|
} else if (sameCardCount == 1) {
|
||||||
|
usefulness += 15;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 检查顺子潜力
|
||||||
|
usefulness += checkSequencePotential(card, sameSuitCards);
|
||||||
|
|
||||||
|
// 5. 检查相邻牌潜力
|
||||||
|
usefulness += checkAdjacentCardPotential(card, sameSuitCards);
|
||||||
|
|
||||||
|
// 6. 中张牌价值更高(3-7)
|
||||||
|
if (rank >= 3 && rank <= 7) {
|
||||||
|
usefulness += WEIGHT_MIDDLE_RANK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. 边张牌价值较低(1,2,8,9)
|
||||||
|
if (rank == 1 || rank == 2 || rank == 8 || rank == 9) {
|
||||||
|
usefulness -= PENALTY_EDGE_RANK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8. 孤张牌惩罚
|
||||||
|
if (analysis.isolatedCards.contains(card)) {
|
||||||
|
usefulness -= PENALTY_ISOLATED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.max(usefulness, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 特别处理相邻牌的情况(优化版)
|
||||||
|
*/
|
||||||
|
private int checkAdjacentCardPotential(int card, List<Integer> sameSuitCards) {
|
||||||
|
int potential = 0;
|
||||||
|
int suit = card / 100;
|
||||||
|
int rank = card % 100;
|
||||||
|
|
||||||
|
// 检查前后相邻的牌
|
||||||
|
int[] closeAdjacent = {rank - 1, rank + 1}; // 紧邻的牌
|
||||||
|
int[] farAdjacent = {rank - 2, rank + 2}; // 隔一张的牌
|
||||||
|
|
||||||
|
// 紧邻的牌价值很高
|
||||||
|
for (int adjRank : closeAdjacent) {
|
||||||
|
if (adjRank >= 1 && adjRank <= 9) {
|
||||||
|
int adjCard = suit * 100 + adjRank;
|
||||||
|
if (sameSuitCards.contains(adjCard)) {
|
||||||
|
potential += WEIGHT_CLOSE_ADJACENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 隔一张的牌也有价值
|
||||||
|
for (int adjRank : farAdjacent) {
|
||||||
|
if (adjRank >= 1 && adjRank <= 9) {
|
||||||
|
int adjCard = suit * 100 + adjRank;
|
||||||
|
if (sameSuitCards.contains(adjCard)) {
|
||||||
|
potential += WEIGHT_FAR_ADJACENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return potential;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查顺子潜力(严格同花色)
|
||||||
|
*/
|
||||||
|
private int checkSequencePotential(int card, List<Integer> sameSuitCards) {
|
||||||
|
int potential = 0;
|
||||||
|
int rank = card % 100;
|
||||||
|
|
||||||
|
// 检查可能的顺子组合(必须是同花色)
|
||||||
|
int[][] sequencePatterns = {
|
||||||
|
{rank - 2, rank - 1}, // 缺前两张
|
||||||
|
{rank - 1, rank + 1}, // 缺中间一张
|
||||||
|
{rank + 1, rank + 2} // 缺后两张
|
||||||
|
};
|
||||||
|
|
||||||
|
int suit = card / 100;
|
||||||
|
|
||||||
|
for (int[] pattern : sequencePatterns) {
|
||||||
|
boolean validPattern = true;
|
||||||
|
for (int r : pattern) {
|
||||||
|
if (r < 1 || r > 9) {
|
||||||
|
validPattern = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (validPattern) {
|
||||||
|
int neededCard1 = suit * 100 + pattern[0];
|
||||||
|
int neededCard2 = suit * 100 + pattern[1];
|
||||||
|
|
||||||
|
if (sameSuitCards.contains(neededCard1) && sameSuitCards.contains(neededCard2)) {
|
||||||
|
potential += 30; // 已经有两张配套牌
|
||||||
|
} else if (sameSuitCards.contains(neededCard1) || sameSuitCards.contains(neededCard2)) {
|
||||||
|
potential += 15; // 有一张配套牌
|
||||||
|
} else {
|
||||||
|
potential += 5; // 有形成顺子的潜力
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return potential;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算每张牌的出牌优先级(增强版)
|
||||||
|
*/
|
||||||
|
private Map<Integer, Integer> calculateDiscardPriority(List<Integer> handCards, HandAnalysis analysis) {
|
||||||
|
Map<Integer, Integer> priority = new HashMap<>();
|
||||||
|
|
||||||
|
for (int card : handCards) {
|
||||||
|
if (card == HONG_ZHONG) {
|
||||||
|
priority.put(card, 1000); // 转转优先级很高(不应该打)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 听牌时,保护听牌组中的牌
|
||||||
|
if (analysis.isTingPai && analysis.tingCards.contains(card)) {
|
||||||
|
priority.put(card, 900); // 听牌组中的牌优先级很高
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取牌的有用程度
|
||||||
|
int usefulness = analysis.cardUsefulness.getOrDefault(card, 0);
|
||||||
|
|
||||||
|
// 计算牌的潜力分数
|
||||||
|
int potentialScore = calculateCardPotentialScore(card, analysis);
|
||||||
|
|
||||||
|
// 综合评估优先级:有用程度 + 潜力分数
|
||||||
|
int discardPriority = usefulness + potentialScore;
|
||||||
|
|
||||||
|
// 孤张牌更容易被打出(降低优先级数值)
|
||||||
|
if (analysis.isolatedCards.contains(card)) {
|
||||||
|
discardPriority = Math.max(1, discardPriority - PENALTY_ISOLATED);
|
||||||
|
|
||||||
|
// 特别处理:如果是完全孤立的边张牌(如1或9且没有相邻牌)
|
||||||
|
int rank = card % 100;
|
||||||
|
int suit = card / 100;
|
||||||
|
if ((rank == 1 || rank == 9) &&
|
||||||
|
analysis.cardCounts.getOrDefault(suit * 100 + (rank == 1 ? 2 : 8), 0) == 0) {
|
||||||
|
discardPriority = Math.max(1, discardPriority - 20); // 进一步降低优先级
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 边张牌(1和9)在不是对子或刻子的情况下优先级降低
|
||||||
|
int rank = card % 100;
|
||||||
|
if ((rank == 1 || rank == 9) && analysis.cardCounts.getOrDefault(card, 0) < 2) {
|
||||||
|
discardPriority = Math.max(1, discardPriority - 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
priority.put(card, discardPriority);
|
||||||
|
}
|
||||||
|
|
||||||
|
return priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int selectCardToDiscard(List<Integer> handCards, Map<Integer, Integer> priority) {
|
||||||
|
int minPriority = Integer.MAX_VALUE;
|
||||||
|
List<Integer> candidateCards = new ArrayList<>();
|
||||||
|
|
||||||
|
// 检查是否有可以打出后听牌的选项
|
||||||
|
Map<Integer, Set<Integer>> discardToTingMap = findDiscardToTing(handCards);
|
||||||
|
if (!discardToTingMap.isEmpty()) {
|
||||||
|
// 如果有多个可打出后听牌的选项,选择其中一个
|
||||||
|
Integer bestTingCard = discardToTingMap.keySet().iterator().next();
|
||||||
|
System.out.println("发现可以打出后听牌的牌: " + bestTingCard);
|
||||||
|
return bestTingCard;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 正常选择优先级最低的牌
|
||||||
|
for (int card : handCards) {
|
||||||
|
int cardPriority = priority.getOrDefault(card, 50);
|
||||||
|
|
||||||
|
// 选择优先级数值最小的牌(最应该打出的)
|
||||||
|
if (cardPriority < minPriority) {
|
||||||
|
minPriority = cardPriority;
|
||||||
|
candidateCards.clear();
|
||||||
|
candidateCards.add(card);
|
||||||
|
} else if (cardPriority == minPriority) {
|
||||||
|
candidateCards.add(card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!candidateCards.isEmpty()) {
|
||||||
|
// 优先打出数量较多的相同牌
|
||||||
|
if (candidateCards.size() > 1) {
|
||||||
|
int maxCount = 0;
|
||||||
|
int selectedCard = candidateCards.get(0);
|
||||||
|
Map<Integer, Integer> countMap = new HashMap<>();
|
||||||
|
|
||||||
|
for (int card : handCards) {
|
||||||
|
countMap.put(card, countMap.getOrDefault(card, 0) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int card : candidateCards) {
|
||||||
|
int count = countMap.getOrDefault(card, 0);
|
||||||
|
if (count > maxCount) {
|
||||||
|
maxCount = count;
|
||||||
|
selectedCard = card;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return selectedCard;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果只有一张候选牌,直接返回
|
||||||
|
return candidateCards.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return handCards.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否应该碰牌
|
||||||
|
*/
|
||||||
|
public boolean shouldPong(int proposedCard, List<Integer> currentHand) {
|
||||||
|
System.out.println("判断是否应该碰牌: " + proposedCard);
|
||||||
|
|
||||||
|
// 基础碰牌条件:手中已有两张相同的牌
|
||||||
|
int count = 0;
|
||||||
|
for (int card : currentHand) {
|
||||||
|
if (card == proposedCard) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count < 2) {
|
||||||
|
System.out.println("- 手中没有两张相同的牌,无法碰");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分析当前手牌状态
|
||||||
|
HandAnalysis analysis = analyzeHand(currentHand);
|
||||||
|
|
||||||
|
// 规则1:听牌时不能碰听牌组中的牌
|
||||||
|
if (analysis.isTingPai && analysis.tingCards.contains(proposedCard)) {
|
||||||
|
System.out.println("- 听牌时不能碰听牌组中的牌");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 规则2:有5个或更多对子时不能碰已成对子的牌
|
||||||
|
int pairCount = countPairs(currentHand);
|
||||||
|
if (pairCount >= 5) {
|
||||||
|
System.out.println("- 对子数量过多,不建议碰牌");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 规则3:顺子的情况下不能碰
|
||||||
|
if (isInSequence(proposedCard, currentHand)) {
|
||||||
|
System.out.println("- 形成顺子的情况下,不建议碰牌");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("- 可以碰牌");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算手牌中的对子数量
|
||||||
|
*/
|
||||||
|
private int countPairs(List<Integer> handCards) {
|
||||||
|
Map<Integer, Integer> countMap = new HashMap<>();
|
||||||
|
for (int card : handCards) {
|
||||||
|
countMap.put(card, countMap.getOrDefault(card, 0) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int pairCount = 0;
|
||||||
|
for (int count : countMap.values()) {
|
||||||
|
pairCount += count / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pairCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查牌是否在顺子中
|
||||||
|
*/
|
||||||
|
private boolean isInSequence(int card, List<Integer> handCards) {
|
||||||
|
int suit = card / 100;
|
||||||
|
int rank = card % 100;
|
||||||
|
|
||||||
|
// 检查是否有形成顺子的可能
|
||||||
|
boolean hasPrev = handCards.contains(suit * 100 + (rank - 1));
|
||||||
|
boolean hasNext = handCards.contains(suit * 100 + (rank + 1));
|
||||||
|
|
||||||
|
return hasPrev && hasNext;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否应该杠牌
|
||||||
|
*/
|
||||||
|
public boolean shouldGang(int proposedCard, List<Integer> currentHand, int gangType) {
|
||||||
|
System.out.println("判断是否应该杠牌: " + proposedCard + ",杠类型: " + gangType);
|
||||||
|
|
||||||
|
// 计算手牌中该牌的数量
|
||||||
|
int count = 0;
|
||||||
|
for (int card : currentHand) {
|
||||||
|
if (card == proposedCard) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查基础杠牌条件
|
||||||
|
// 类型2: 明杠(从他人处获得)
|
||||||
|
// 类型3: 吃杠(从他人处获得)
|
||||||
|
// 类型4: 自杠
|
||||||
|
// 类型5: 碰后补杠
|
||||||
|
|
||||||
|
boolean hasEnoughCards = false;
|
||||||
|
if (gangType == 2 || gangType == 3) {
|
||||||
|
// 明杠和吃杠需要手中已有三张
|
||||||
|
hasEnoughCards = (count >= 3);
|
||||||
|
} else if (gangType == 4 || gangType == 5) {
|
||||||
|
// 自杠和补杠需要手中有四张或之前已经碰了
|
||||||
|
hasEnoughCards = (count >= 4) || (count >= 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasEnoughCards) {
|
||||||
|
System.out.println("- 手中牌数量不足,无法杠");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分析当前手牌状态
|
||||||
|
HandAnalysis analysis = analyzeHand(currentHand);
|
||||||
|
|
||||||
|
// 规则1:听牌时优先考虑杠牌,但不杠听牌组中的牌
|
||||||
|
if (analysis.isTingPai && analysis.tingCards.contains(proposedCard)) {
|
||||||
|
System.out.println("- 听牌时不杠听牌组中的牌");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 规则2:检查杠后是否会破坏已有搭子
|
||||||
|
if (isInSequence(proposedCard, currentHand)) {
|
||||||
|
System.out.println("- 杠牌会破坏顺子搭子,不建议杠");
|
||||||
|
// 但是如果已经有刻子,杠牌还是可以考虑的
|
||||||
|
int tripletCount = 0;
|
||||||
|
for (List<Integer> meld : analysis.completedMelds) {
|
||||||
|
if (meld.contains(proposedCard) && meld.size() == 3) {
|
||||||
|
tripletCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tripletCount == 0) {
|
||||||
|
return false; // 没有刻子,不杠
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 规则3:转转优先杠牌,增加灵活性
|
||||||
|
if (proposedCard == HONG_ZHONG) {
|
||||||
|
System.out.println("- 转转优先杠,增加灵活性");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 规则4:如果牌局前期,有杠就杠增加得分机会
|
||||||
|
if (analysis.shantenCount >= 2) {
|
||||||
|
System.out.println("- 牌局前期,建议杠牌增加得分机会");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 规则5:如果牌局后期,即将听牌,谨慎杠牌
|
||||||
|
if (analysis.shantenCount == 1) {
|
||||||
|
System.out.println("- 牌局后期,谨慎杠牌,优先听牌");
|
||||||
|
// 计算杠牌后的手牌是否会变得更差
|
||||||
|
List<Integer> tempHand = new ArrayList<>(currentHand);
|
||||||
|
tempHand.remove(Integer.valueOf(proposedCard));
|
||||||
|
HandAnalysis afterGangAnalysis = analyzeHand(tempHand);
|
||||||
|
|
||||||
|
// 如果杠牌后向听数增加,不杠
|
||||||
|
if (afterGangAnalysis.shantenCount > analysis.shantenCount) {
|
||||||
|
System.out.println("- 杠牌后向听数增加,不建议杠");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("- 可以杠牌");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,940 @@
|
||||||
|
package com.robotcm.taurus.util;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class ai {
|
||||||
|
|
||||||
|
// 长沙麻将特殊规则:二五八将
|
||||||
|
private static final Set<Integer> JIANG_PAIS;
|
||||||
|
|
||||||
|
// 门子类型定义
|
||||||
|
private static final int DOOR_NONE = 0; // 无门子
|
||||||
|
private static final int DOOR_BIAN = 1; // 边张门子 (12/89)
|
||||||
|
private static final int DOOR_GE = 2; // 隔张门子 (13/79/24/68等)
|
||||||
|
private static final int DOOR_XIANGLIN = 3; // 相邻门子 (23/34/45/56/67/78等)
|
||||||
|
private static final int DOOR_DUIZI = 4; // 对子
|
||||||
|
|
||||||
|
public boolean isTinAi = false;
|
||||||
|
public boolean isTingpai = false;
|
||||||
|
|
||||||
|
static {
|
||||||
|
Set<Integer> jiangSet = new HashSet<>();
|
||||||
|
jiangSet.add(2);
|
||||||
|
jiangSet.add(5);
|
||||||
|
jiangSet.add(8);
|
||||||
|
JIANG_PAIS = Collections.unmodifiableSet(jiangSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 缓存优化
|
||||||
|
private Map<String, Double> scoreCache = new HashMap<>();
|
||||||
|
private Map<String, List<Integer>> tingCache = new HashMap<>();
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
System.out.println("=== 长沙麻将测试(含碰杠吃牌) ===");
|
||||||
|
ai ai1 = new ai();
|
||||||
|
|
||||||
|
// 测试1:普通手牌
|
||||||
|
System.out.println("\n=== 测试1:普通14张牌 ===");
|
||||||
|
PlayerState state1 = new PlayerState();
|
||||||
|
// state1.handCards = Arrays.asList(208, 204, 203, 202, 107, 106, 105, 103, 102, 101, 101, 208, 206, 207);
|
||||||
|
|
||||||
|
// test1.add(209);
|
||||||
|
// test1.add(208);
|
||||||
|
// test1.add(208);
|
||||||
|
// test1.add(207);
|
||||||
|
// test1.add(207);
|
||||||
|
// test1.add(206);
|
||||||
|
// test1.add(205);
|
||||||
|
// test1.add(205);
|
||||||
|
// test1.add(202);
|
||||||
|
// test1.add(201);
|
||||||
|
// test1.add(201);
|
||||||
|
// test1.add(107);
|
||||||
|
// test1.add(107);
|
||||||
|
// test1.add(104);
|
||||||
|
|
||||||
|
// state1.handCards = Arrays.asList(209,207,206,202,202,109,108,107,106,105,105,105,104,103);
|
||||||
|
|
||||||
|
state1.handCards = Arrays.asList(209, 209, 208, 207, 206, 205, 203, 202, 202, 202, 108, 105, 104, 103);
|
||||||
|
|
||||||
|
// state1.handCards = Arrays.asList(204, 206, 205, 205, 203, 109, 108, 107, 107, 106, 103, 103, 102, 101);
|
||||||
|
|
||||||
|
// state1.addChiGroup(Arrays.asList(107,108,106));
|
||||||
|
// state1.addPongGroup(Arrays.asList(109,109,109));
|
||||||
|
int discard1 = ai1.findBestDiscard(state1);
|
||||||
|
System.out.println("推荐出牌: " + ai1.formatCard(discard1));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主方法:根据完整玩家状态推荐出牌
|
||||||
|
*/
|
||||||
|
public int findBestDiscard(PlayerState state) {
|
||||||
|
System.out.println("\n=== 长沙麻将AI出牌分析 ===");
|
||||||
|
System.out.println("手牌(" + state.handCards.size() + "张): " + formatCards(state.handCards));
|
||||||
|
|
||||||
|
int pongGroups = state.getPongGroupCount();
|
||||||
|
int gangGroups = state.getGangGroupCount();
|
||||||
|
int chiGroups = state.getChiGroupCount();
|
||||||
|
|
||||||
|
System.out.println("碰牌组数: " + pongGroups +
|
||||||
|
",杠牌组数: " + gangGroups +
|
||||||
|
",吃牌组数: " + chiGroups);
|
||||||
|
|
||||||
|
// 清空缓存
|
||||||
|
scoreCache.clear();
|
||||||
|
tingCache.clear();
|
||||||
|
|
||||||
|
long startTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
// 获取可出的牌(手牌中的唯一牌)
|
||||||
|
Map<Integer, DiscardResult> results = new HashMap<>();
|
||||||
|
Set<Integer> uniqueCards = new HashSet<>(state.handCards);
|
||||||
|
|
||||||
|
// 如果只剩一张牌,直接返回
|
||||||
|
if (state.handCards.size() == 1) {
|
||||||
|
int onlyCard = state.handCards.get(0);
|
||||||
|
System.out.println("只剩一张牌,必须出: " + formatCard(onlyCard));
|
||||||
|
return onlyCard;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int card : uniqueCards) {
|
||||||
|
// 模拟打出一张牌后的状态
|
||||||
|
PlayerState newState = simulateDiscard(state, card);
|
||||||
|
DiscardResult result = evaluateDiscardWithState(card, state); // 传入原始状态
|
||||||
|
results.put(card, result);
|
||||||
|
|
||||||
|
System.out.printf("打出 %s: 评分=%.3f (听牌=%d张)%n",
|
||||||
|
formatCard(card), result.score, result.tingCards.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 找出最佳出牌 - 优先考虑听牌数量
|
||||||
|
int bestCard = -1;
|
||||||
|
double bestScore = Double.NEGATIVE_INFINITY;
|
||||||
|
int maxTingCount = 0;
|
||||||
|
|
||||||
|
// 首先找到最大听牌数量
|
||||||
|
for (DiscardResult result : results.values()) {
|
||||||
|
if (result.tingCards.size() > maxTingCount) {
|
||||||
|
maxTingCount = result.tingCards.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果有听牌,优先从听牌中选择
|
||||||
|
if (maxTingCount > 0) {
|
||||||
|
// 从所有听牌中选择评分最高的
|
||||||
|
for (Map.Entry<Integer, DiscardResult> entry : results.entrySet()) {
|
||||||
|
if (entry.getValue().tingCards.size() == maxTingCount) {
|
||||||
|
if (entry.getValue().score > bestScore) {
|
||||||
|
bestScore = entry.getValue().score;
|
||||||
|
bestCard = entry.getKey();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 没有听牌,选择评分最高的
|
||||||
|
for (Map.Entry<Integer, DiscardResult> entry : results.entrySet()) {
|
||||||
|
if (entry.getValue().score > bestScore) {
|
||||||
|
bestScore = entry.getValue().score;
|
||||||
|
bestCard = entry.getKey();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
long endTime = System.currentTimeMillis();
|
||||||
|
System.out.printf("分析耗时: %dms%n", (endTime - startTime));
|
||||||
|
|
||||||
|
if (bestCard != -1) {
|
||||||
|
DiscardResult bestResult = results.get(bestCard);
|
||||||
|
System.out.println("\n🎯 AI推荐出牌: " + formatCard(bestCard));
|
||||||
|
if (!bestResult.tingCards.isEmpty()) {
|
||||||
|
isTinAi = true;
|
||||||
|
isTingpai = true;
|
||||||
|
System.out.println("✅ 听牌,可听 " + bestResult.tingCards.size() + " 张牌:");
|
||||||
|
System.out.println("----牌组----" + bestResult.tingCards);
|
||||||
|
if (bestResult.tingCards.size() > 0) {
|
||||||
|
// 如果有ChangShaSuanFaTest类,需要处理
|
||||||
|
ChangShaSuanFaTest.tinCards.clear();
|
||||||
|
ChangShaSuanFaTest.tinCards.addAll(bestResult.tingCards);
|
||||||
|
}
|
||||||
|
List<String> tingCardsFormatted = bestResult.tingCards.stream()
|
||||||
|
.map(this::formatCard)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
System.out.println(" " + String.join(" ", tingCardsFormatted));
|
||||||
|
}
|
||||||
|
System.out.println("综合评分: " + String.format("%.3f", bestResult.score));
|
||||||
|
}
|
||||||
|
|
||||||
|
return bestCard;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模拟打出一张牌
|
||||||
|
*/
|
||||||
|
private PlayerState simulateDiscard(PlayerState originalState, int discardCard) {
|
||||||
|
PlayerState newState = new PlayerState();
|
||||||
|
|
||||||
|
// 复制手牌并移除要打的牌
|
||||||
|
newState.handCards = new ArrayList<>(originalState.handCards);
|
||||||
|
newState.handCards.remove((Integer) discardCard);
|
||||||
|
|
||||||
|
// 复制碰牌组
|
||||||
|
for (List<Integer> group : originalState.pongGroups) {
|
||||||
|
newState.pongGroups.add(new ArrayList<>(group));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 复制杠牌组
|
||||||
|
for (List<Integer> group : originalState.gangGroups) {
|
||||||
|
newState.gangGroups.add(new ArrayList<>(group));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 复制吃牌组
|
||||||
|
for (List<Integer> group : originalState.chiGroups) {
|
||||||
|
newState.chiGroups.add(new ArrayList<>(group));
|
||||||
|
}
|
||||||
|
|
||||||
|
newState.isZhuang = originalState.isZhuang;
|
||||||
|
|
||||||
|
return newState;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DiscardResult evaluateDiscardWithState(int discardedCard, PlayerState originalState) {
|
||||||
|
// 先模拟打出这张牌
|
||||||
|
PlayerState simulatedState = simulateDiscard(originalState, discardedCard);
|
||||||
|
List<Integer> hand = simulatedState.handCards;
|
||||||
|
double totalScore = 0.0;
|
||||||
|
|
||||||
|
// === 1. 基础分 ===
|
||||||
|
totalScore += 10.0;
|
||||||
|
|
||||||
|
// === 新增:花色集中度评估 ===
|
||||||
|
double suitConcentrationScore = evaluateSuitConcentration(discardedCard, hand);
|
||||||
|
totalScore += suitConcentrationScore;
|
||||||
|
|
||||||
|
// === 2. 门子类型评估 ===
|
||||||
|
System.out.println("\n=== 评估 " + formatCard(discardedCard) + " ===");
|
||||||
|
|
||||||
|
// 使用原始状态来获取对子信息
|
||||||
|
int originalCount = 0;
|
||||||
|
for (int c : originalState.handCards) {
|
||||||
|
if (c == discardedCard) originalCount++;
|
||||||
|
}
|
||||||
|
System.out.println(" [原始] " + formatCard(discardedCard) + " 在原始手牌中有 " + originalCount + " 张");
|
||||||
|
|
||||||
|
int doorType = getDoorType(discardedCard, hand, originalState); // 传入原始状态
|
||||||
|
double doorScore = evaluateDoorType(doorType, discardedCard);
|
||||||
|
totalScore += doorScore;
|
||||||
|
|
||||||
|
System.out.printf(" [门子] 类型=%s, 评分=%.1f%n",
|
||||||
|
getDoorTypeName(doorType), doorScore);
|
||||||
|
|
||||||
|
// === 3. 检查是否在完整顺子中 ===
|
||||||
|
if (isInCompleteSequence(discardedCard, originalState.handCards)) { // 使用原始状态
|
||||||
|
System.out.println(" [顺子] " + formatCard(discardedCard) + " 在完整顺子中");
|
||||||
|
|
||||||
|
if (originalCount > 1) {
|
||||||
|
System.out.println(" [冗余] " + formatCard(discardedCard) +
|
||||||
|
" 在顺子中且有多张,加30分鼓励打出");
|
||||||
|
totalScore += 30.0;
|
||||||
|
} else {
|
||||||
|
System.out.println(" [保留] " + formatCard(discardedCard) +
|
||||||
|
" 是顺子中的唯一一张,扣15分");
|
||||||
|
totalScore -= 15.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// === 4. 对子和刻子评估 ===
|
||||||
|
double pairScore = evaluatePairsAndPongsFinal(discardedCard, originalState); // 传入原始状态
|
||||||
|
totalScore += pairScore;
|
||||||
|
|
||||||
|
// === 5. 绝对孤张评估 ===
|
||||||
|
if (isAbsolutelyLonelyFinal(discardedCard, hand)) {
|
||||||
|
totalScore += 15.0;
|
||||||
|
System.out.println(" [孤张] " + formatCard(discardedCard) + " 是绝对孤张,加15分");
|
||||||
|
}
|
||||||
|
|
||||||
|
// === 6. 简单结构评估 ===
|
||||||
|
double structureScore = evaluateSimpleStructure(discardedCard, hand);
|
||||||
|
totalScore += structureScore;
|
||||||
|
|
||||||
|
// === 7. 听牌检查 ===
|
||||||
|
List<Integer> tingCards = checkTingCardsWithState(simulatedState);
|
||||||
|
if (!tingCards.isEmpty()) {
|
||||||
|
totalScore += 100.0;
|
||||||
|
totalScore += tingCards.size() * 50.0;
|
||||||
|
|
||||||
|
for (int tingCard : tingCards) {
|
||||||
|
int value = getCardValue(tingCard);
|
||||||
|
if (JIANG_PAIS.contains(value)) {
|
||||||
|
totalScore += 20.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.printf(" [总分] %.1f%n", totalScore);
|
||||||
|
return new DiscardResult(totalScore, tingCards, 0);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 花色集中度评估:优先打散张,保留集中花色的牌
|
||||||
|
*/
|
||||||
|
private double evaluateSuitConcentration(int card, List<Integer> hand) {
|
||||||
|
int type = getCardType(card);
|
||||||
|
int value = getCardValue(card);
|
||||||
|
|
||||||
|
// 统计该花色的牌数
|
||||||
|
int suitCount = 0;
|
||||||
|
for (int c : hand) {
|
||||||
|
if (getCardType(c) == type) {
|
||||||
|
suitCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double score = 0.0;
|
||||||
|
|
||||||
|
// 如果该花色牌很少(<=3张),优先打
|
||||||
|
if (suitCount <= 3) {
|
||||||
|
score += 20.0; // 鼓励打散张
|
||||||
|
}
|
||||||
|
// 如果该花色牌很多(>=8张),尽量不打
|
||||||
|
else if (suitCount >= 8) {
|
||||||
|
score -= 15.0; // 扣分,尽量不打集中花色的牌
|
||||||
|
}
|
||||||
|
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取门子类型(改进版)
|
||||||
|
*/
|
||||||
|
private int getDoorType(int card, List<Integer> hand, PlayerState state) {
|
||||||
|
int type = getCardType(card);
|
||||||
|
int value = getCardValue(card);
|
||||||
|
|
||||||
|
if (type > 3) return DOOR_NONE;
|
||||||
|
|
||||||
|
// 使用传入的状态来获取对子信息
|
||||||
|
int originalCount = 0;
|
||||||
|
for (int c : state.handCards) {
|
||||||
|
if (c == card) originalCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (originalCount >= 2) {
|
||||||
|
return DOOR_DUIZI;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查边张
|
||||||
|
if (value == 1 || value == 9) {
|
||||||
|
int adjacent = (value == 1) ? card + 1 : card - 1;
|
||||||
|
if (state.handCards.contains(adjacent)) {
|
||||||
|
return DOOR_BIAN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查相邻门子
|
||||||
|
for (int other : state.handCards) {
|
||||||
|
if (getCardType(other) == type) {
|
||||||
|
int otherValue = getCardValue(other);
|
||||||
|
int diff = Math.abs(otherValue - value);
|
||||||
|
|
||||||
|
if (diff == 1) {
|
||||||
|
// 排除12、21、89、98的情况(这些是边张)
|
||||||
|
if ((value == 1 && otherValue == 2) || (value == 2 && otherValue == 1) ||
|
||||||
|
(value == 8 && otherValue == 9) || (value == 9 && otherValue == 8)) {
|
||||||
|
return DOOR_BIAN;
|
||||||
|
}
|
||||||
|
return DOOR_XIANGLIN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查隔张
|
||||||
|
for (int other : state.handCards) {
|
||||||
|
if (getCardType(other) == type) {
|
||||||
|
int otherValue = getCardValue(other);
|
||||||
|
int diff = Math.abs(otherValue - value);
|
||||||
|
|
||||||
|
if (diff == 2) {
|
||||||
|
return DOOR_GE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return DOOR_NONE;
|
||||||
|
} /**
|
||||||
|
* 获取门子类型名称
|
||||||
|
*/
|
||||||
|
private String getDoorTypeName(int doorType) {
|
||||||
|
switch (doorType) {
|
||||||
|
case DOOR_BIAN: return "边张门子";
|
||||||
|
case DOOR_GE: return "隔张门子";
|
||||||
|
case DOOR_XIANGLIN: return "相邻门子";
|
||||||
|
case DOOR_DUIZI: return "对子";
|
||||||
|
case DOOR_NONE: return "无门子";
|
||||||
|
default: return "未知";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private double evaluateDoorType(int doorType, int card) {
|
||||||
|
double score = 0.0;
|
||||||
|
int value = getCardValue(card);
|
||||||
|
int type = getCardType(card);
|
||||||
|
|
||||||
|
switch (doorType) {
|
||||||
|
case DOOR_BIAN:
|
||||||
|
// 边张门子:12或89,依赖性强
|
||||||
|
score += 25.0; // 增加分值,更鼓励打边张
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DOOR_GE:
|
||||||
|
// 隔张门子:差1张才能形成顺子
|
||||||
|
if (value == 2 || value == 8) {
|
||||||
|
score += 5.0;
|
||||||
|
} else if (value == 3 || value == 7) {
|
||||||
|
score += 8.0;
|
||||||
|
} else {
|
||||||
|
score += 10.0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DOOR_XIANGLIN:
|
||||||
|
// 相邻门子:很容易形成顺子,应该保留
|
||||||
|
score -= 15.0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DOOR_DUIZI:
|
||||||
|
if (JIANG_PAIS.contains(value)) {
|
||||||
|
// 258将对子是好牌,不应该扣那么多分
|
||||||
|
score -= 20.0;
|
||||||
|
} else if (value == 1 || value == 9) {
|
||||||
|
// 边张对子(1万或9万):价值较低,鼓励打出
|
||||||
|
score += 10.0; // 改为加分,鼓励打出边张对子
|
||||||
|
} else if (value == 9 && type == 1) { // 9万对子
|
||||||
|
score -= 15.0;
|
||||||
|
} else {
|
||||||
|
score -= 10.0; // 中张对子
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DOOR_NONE:
|
||||||
|
// 绝对孤张:优先打
|
||||||
|
if (JIANG_PAIS.contains(value)) {
|
||||||
|
score += 20.0;
|
||||||
|
} else if (value == 1 || value == 9) {
|
||||||
|
// 边张孤张:19是边张,价值最低
|
||||||
|
score += 30.0; // 增加分值,更鼓励打边张孤张
|
||||||
|
} else {
|
||||||
|
score += 20.0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
private boolean isInCompleteSequence(int card, List<Integer> hand) {
|
||||||
|
int type = getCardType(card);
|
||||||
|
int value = getCardValue(card);
|
||||||
|
|
||||||
|
if (type > 3) return false;
|
||||||
|
|
||||||
|
// 统计该牌在手牌中的数量
|
||||||
|
int cardCount = Collections.frequency(hand, card);
|
||||||
|
|
||||||
|
// 如果该牌在手牌中,检查是否能形成顺子
|
||||||
|
if (cardCount > 0) {
|
||||||
|
// 检查是否能形成123顺子
|
||||||
|
if (value == 1) {
|
||||||
|
int card2 = card + 1;
|
||||||
|
int card3 = card + 2;
|
||||||
|
return hand.contains(card2) && hand.contains(card3);
|
||||||
|
}
|
||||||
|
// 检查是否能形成789顺子
|
||||||
|
else if (value == 9) {
|
||||||
|
int card7 = card - 2;
|
||||||
|
int card8 = card - 1;
|
||||||
|
return hand.contains(card7) && hand.contains(card8);
|
||||||
|
}
|
||||||
|
// 检查其他顺子情况
|
||||||
|
else if (value >= 2 && value <= 8) {
|
||||||
|
// 检查前顺子
|
||||||
|
int prev1 = card - 1;
|
||||||
|
int prev2 = card - 2;
|
||||||
|
if (value >= 3 && hand.contains(prev1) && hand.contains(prev2)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// 检查后顺子
|
||||||
|
int next1 = card + 1;
|
||||||
|
int next2 = card + 2;
|
||||||
|
if (value <= 7 && hand.contains(next1) && hand.contains(next2)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// 检查中间顺子
|
||||||
|
int prev = card - 1;
|
||||||
|
int next = card + 1;
|
||||||
|
if (hand.contains(prev) && hand.contains(next)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 简单结构评估(简化版)
|
||||||
|
*/
|
||||||
|
private double evaluateSimpleStructure(int card, List<Integer> hand) {
|
||||||
|
double score = 0.0;
|
||||||
|
int type = getCardType(card);
|
||||||
|
int value = getCardValue(card);
|
||||||
|
|
||||||
|
if (type > 3) return 0.0;
|
||||||
|
|
||||||
|
// 258将特殊处理
|
||||||
|
if (JIANG_PAIS.contains(value)) {
|
||||||
|
score -= 3.0; // 258将价值较高
|
||||||
|
}
|
||||||
|
|
||||||
|
// 边张扣分
|
||||||
|
if (value == 1 || value == 9) {
|
||||||
|
score -= 2.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否在顺子中间
|
||||||
|
*/
|
||||||
|
private boolean isInSequenceMiddle(int card, List<Integer> hand) {
|
||||||
|
int type = getCardType(card);
|
||||||
|
int value = getCardValue(card);
|
||||||
|
|
||||||
|
if (type > 3) return false;
|
||||||
|
|
||||||
|
// 检查前后是否能形成顺子
|
||||||
|
int prevCard = card - 1;
|
||||||
|
int nextCard = card + 1;
|
||||||
|
|
||||||
|
// 需要手牌中有前后两张牌
|
||||||
|
return hand.contains(prevCard) && hand.contains(nextCard);
|
||||||
|
}
|
||||||
|
|
||||||
|
private double evaluatePairsAndPongsFinal(int card, PlayerState state) {
|
||||||
|
// 获取原始手牌中该牌的数量
|
||||||
|
int originalCount = 0;
|
||||||
|
for (int c : state.handCards) {
|
||||||
|
if (c == card) originalCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println(" [数量] " + formatCard(card) + " 在原始手牌中有 " + originalCount + " 张");
|
||||||
|
|
||||||
|
// 如果有3张或以上,是刻子
|
||||||
|
if (originalCount >= 3) {
|
||||||
|
System.out.println(" [刻子] " + formatCard(card) + " 是刻子,扣50分");
|
||||||
|
return -50.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果有2张,是对子
|
||||||
|
if (originalCount == 2) {
|
||||||
|
int type = getCardType(card);
|
||||||
|
int value = getCardValue(card);
|
||||||
|
|
||||||
|
// 特别处理边张对子(1万或9万)
|
||||||
|
if (value == 1 || value == 9) {
|
||||||
|
// 边张对子价值最低,特别鼓励打出
|
||||||
|
System.out.println(" [边张对子] " + formatCard(card) + " 是边张对子,加60分鼓励打出");
|
||||||
|
return 60.0; // 增加边张对子的鼓励分
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查这个对子是否在顺子中且是多余的
|
||||||
|
if (isRedundantPairInSequence(card, state.handCards)) {
|
||||||
|
System.out.println(" [冗余] " + formatCard(card) + " 是顺子中的冗余对子,加40分鼓励打出");
|
||||||
|
return 40.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 258将的对子要保留
|
||||||
|
if (JIANG_PAIS.contains(value)) {
|
||||||
|
System.out.println(" [258将] " + formatCard(card) + " 是258将对子,扣30分");
|
||||||
|
return -30.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 普通对子
|
||||||
|
System.out.println(" [对子] " + formatCard(card) + " 是普通对子,扣10分");
|
||||||
|
return -10.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println(" [单张] " + formatCard(card) + " 是单张");
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
private boolean isRedundantPairInSequence(int card, List<Integer> originalHand) {
|
||||||
|
int type = getCardType(card);
|
||||||
|
int value = getCardValue(card);
|
||||||
|
|
||||||
|
if (type > 3) return false;
|
||||||
|
|
||||||
|
// 统计原始手牌中该牌的数量
|
||||||
|
int count = Collections.frequency(originalHand, card);
|
||||||
|
if (count < 2) return false; // 不是对子
|
||||||
|
|
||||||
|
System.out.println(" [检查冗余] " + formatCard(card) + " 是" + count + "张的对子");
|
||||||
|
|
||||||
|
// 特别处理边张(1万或9万)
|
||||||
|
if (value == 1) {
|
||||||
|
// 1万对子,检查是否能和2、3形成顺子
|
||||||
|
int card2 = card + 1;
|
||||||
|
int card3 = card + 2;
|
||||||
|
if (originalHand.contains(card2) && originalHand.contains(card3)) {
|
||||||
|
System.out.println(" [边张冗余] " + formatCard(card) + " 是1万且在1-2-3顺子中,应该优先打出");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (value == 9) {
|
||||||
|
// 9万对子,检查是否能和7、8形成顺子
|
||||||
|
int card7 = card - 2;
|
||||||
|
int card8 = card - 1;
|
||||||
|
if (originalHand.contains(card7) && originalHand.contains(card8)) {
|
||||||
|
System.out.println(" [边张冗余] " + formatCard(card) + " 是9万且在7-8-9顺子中,应该优先打出");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 对于3万,检查是否有1万和2万形成顺子
|
||||||
|
if (value == 3) {
|
||||||
|
int card1 = card - 2; // 1万
|
||||||
|
int card2 = card - 1; // 2万
|
||||||
|
|
||||||
|
boolean has1 = originalHand.contains(card1);
|
||||||
|
boolean has2 = originalHand.contains(card2);
|
||||||
|
|
||||||
|
System.out.println(" [顺子检查] 1万=" + has1 + ", 2万=" + has2);
|
||||||
|
|
||||||
|
// 如果有1万和2万,且3万有2张,那么多余的1张3万应该打出
|
||||||
|
if (has1 && has2) {
|
||||||
|
System.out.println(" [确定冗余] " + formatCard(card) + " 在1-2-3顺子中是多余的");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查其他顺子情况
|
||||||
|
if (value >= 1 && value <= 7) {
|
||||||
|
int next1 = card + 1;
|
||||||
|
int next2 = card + 2;
|
||||||
|
if (originalHand.contains(next1) && originalHand.contains(next2)) {
|
||||||
|
System.out.println(" [确定冗余] " + formatCard(card) + " 在" + value + "-" +
|
||||||
|
getCardValue(next1) + "-" + getCardValue(next2) + "顺子中是多余的");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value >= 2 && value <= 8) {
|
||||||
|
int prev = card - 1;
|
||||||
|
int next = card + 1;
|
||||||
|
if (originalHand.contains(prev) && originalHand.contains(next)) {
|
||||||
|
System.out.println(" [确定冗余] " + formatCard(card) + " 在" +
|
||||||
|
getCardValue(prev) + "-" + value + "-" + getCardValue(next) + "顺子中是多余的");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value >= 3 && value <= 9) {
|
||||||
|
int prev1 = card - 1;
|
||||||
|
int prev2 = card - 2;
|
||||||
|
if (originalHand.contains(prev1) && originalHand.contains(prev2)) {
|
||||||
|
System.out.println(" [确定冗余] " + formatCard(card) + " 在" +
|
||||||
|
getCardValue(prev2) + "-" + getCardValue(prev1) + "-" + value + "顺子中是多余的");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private boolean isAbsolutelyLonelyFinal(int card, List<Integer> hand) {
|
||||||
|
int type = getCardType(card);
|
||||||
|
int value = getCardValue(card);
|
||||||
|
|
||||||
|
if (type > 3) return false;
|
||||||
|
|
||||||
|
// 检查同花色牌
|
||||||
|
for (int other : hand) {
|
||||||
|
if (getCardType(other) == type) {
|
||||||
|
int diff = Math.abs(getCardValue(other) - value);
|
||||||
|
if (diff <= 2) { // 相邻或隔张
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 以下是原有方法,保持不变...
|
||||||
|
// checkTingCardsWithState, canHuWithState, canHuWith258, canFormMelds,
|
||||||
|
// getReasonableHuCards, getStateKey, getCardValue, getCardType,
|
||||||
|
// formatCard, formatCards
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查听牌情况
|
||||||
|
*/
|
||||||
|
public List<Integer> checkTingCardsWithState(PlayerState state) {
|
||||||
|
String stateKey = getStateKey(state);
|
||||||
|
if (tingCache.containsKey(stateKey)) {
|
||||||
|
return tingCache.get(stateKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Integer> tingCards = new ArrayList<>();
|
||||||
|
|
||||||
|
int pongGroups = state.getPongGroupCount();
|
||||||
|
int gangGroups = state.getGangGroupCount();
|
||||||
|
int chiGroups = state.getChiGroupCount();
|
||||||
|
|
||||||
|
// 计算已使用的牌数
|
||||||
|
int usedCardsCount = pongGroups * 3 + gangGroups * 4 + chiGroups * 3;
|
||||||
|
|
||||||
|
// 听牌时手牌数量应为:14 - 已使用的牌数 - 1(要胡的牌)
|
||||||
|
int expectedHandCount = 14 - usedCardsCount - 1;
|
||||||
|
|
||||||
|
if (state.handCards.size() != expectedHandCount) {
|
||||||
|
tingCache.put(stateKey, tingCards);
|
||||||
|
return tingCards;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取合理的胡牌候选
|
||||||
|
Set<Integer> possibleHuCards = getReasonableHuCards(state.handCards);
|
||||||
|
|
||||||
|
for (int huCard : possibleHuCards) {
|
||||||
|
if (canHuWithState(state, huCard)) {
|
||||||
|
tingCards.add(huCard);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tingCache.put(stateKey, tingCards);
|
||||||
|
return tingCards;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 胡牌检查
|
||||||
|
*/
|
||||||
|
private boolean canHuWithState(PlayerState state, int huCard) {
|
||||||
|
int totalHandCards = state.handCards.size() + 1;
|
||||||
|
int pongCardsCount = state.getPongGroupCount() * 3;
|
||||||
|
int gangCardsCount = state.getGangGroupCount() * 4;
|
||||||
|
int chiCardsCount = state.getChiGroupCount() * 3;
|
||||||
|
int totalCards = totalHandCards + pongCardsCount + gangCardsCount + chiCardsCount;
|
||||||
|
|
||||||
|
if (totalCards != 14) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Integer> remainingHand = new ArrayList<>(state.handCards);
|
||||||
|
remainingHand.add(huCard);
|
||||||
|
Collections.sort(remainingHand);
|
||||||
|
|
||||||
|
return canHuWith258(remainingHand);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查手牌是否能胡牌(考虑258将)
|
||||||
|
*/
|
||||||
|
private boolean canHuWith258(List<Integer> hand) {
|
||||||
|
if (hand.size() != 2 && hand.size() != 5 && hand.size() != 8 && hand.size() != 11 && hand.size() != 14) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Integer> sortedHand = new ArrayList<>(hand);
|
||||||
|
Collections.sort(sortedHand);
|
||||||
|
|
||||||
|
// 尝试所有可能的将牌
|
||||||
|
for (int i = 0; i < sortedHand.size() - 1; i++) {
|
||||||
|
if (sortedHand.get(i).equals(sortedHand.get(i + 1))) {
|
||||||
|
int cardValue = getCardValue(sortedHand.get(i));
|
||||||
|
if (JIANG_PAIS.contains(cardValue)) {
|
||||||
|
List<Integer> tempHand = new ArrayList<>(sortedHand);
|
||||||
|
tempHand.remove(i);
|
||||||
|
tempHand.remove(i);
|
||||||
|
|
||||||
|
if (canFormMelds(tempHand)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否能组成全部顺子/刻子
|
||||||
|
*/
|
||||||
|
private boolean canFormMelds(List<Integer> hand) {
|
||||||
|
if (hand.isEmpty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Integer> sorted = new ArrayList<>(hand);
|
||||||
|
Collections.sort(sorted);
|
||||||
|
|
||||||
|
// 尝试刻子
|
||||||
|
if (sorted.size() >= 3 &&
|
||||||
|
sorted.get(0).equals(sorted.get(1)) &&
|
||||||
|
sorted.get(1).equals(sorted.get(2))) {
|
||||||
|
List<Integer> newHand = new ArrayList<>(sorted);
|
||||||
|
newHand.remove(0);
|
||||||
|
newHand.remove(0);
|
||||||
|
newHand.remove(0);
|
||||||
|
if (canFormMelds(newHand)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试顺子
|
||||||
|
if (sorted.size() >= 3) {
|
||||||
|
int first = sorted.get(0);
|
||||||
|
int type = getCardType(first);
|
||||||
|
|
||||||
|
if (type <= 3) {
|
||||||
|
int second = first + 1;
|
||||||
|
int third = first + 2;
|
||||||
|
|
||||||
|
if (sorted.contains(second) && sorted.contains(third)) {
|
||||||
|
List<Integer> newHand = new ArrayList<>(sorted);
|
||||||
|
newHand.remove((Integer) first);
|
||||||
|
newHand.remove((Integer) second);
|
||||||
|
newHand.remove((Integer) third);
|
||||||
|
if (canFormMelds(newHand)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取合理的胡牌候选
|
||||||
|
*/
|
||||||
|
private Set<Integer> getReasonableHuCards(List<Integer> hand) {
|
||||||
|
Set<Integer> possibleCards = new HashSet<>();
|
||||||
|
|
||||||
|
// 现有牌和相邻牌
|
||||||
|
for (int card : hand) {
|
||||||
|
possibleCards.add(card);
|
||||||
|
int type = getCardType(card);
|
||||||
|
if (type <= 3) {
|
||||||
|
int value = getCardValue(card);
|
||||||
|
if (value > 1) possibleCards.add(card - 1);
|
||||||
|
if (value < 9) possibleCards.add(card + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 所有258牌
|
||||||
|
for (int type = 1; type <= 3; type++) {
|
||||||
|
for (int jiang : JIANG_PAIS) {
|
||||||
|
possibleCards.add(type * 100 + jiang);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 限制数量
|
||||||
|
if (possibleCards.size() > 15) {
|
||||||
|
List<Integer> list = new ArrayList<>(possibleCards);
|
||||||
|
Collections.shuffle(list);
|
||||||
|
possibleCards = new HashSet<>(list.subList(0, 15));
|
||||||
|
}
|
||||||
|
|
||||||
|
return possibleCards;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成状态key
|
||||||
|
*/
|
||||||
|
private String getStateKey(PlayerState state) {
|
||||||
|
List<Integer> sortedHand = new ArrayList<>(state.handCards);
|
||||||
|
Collections.sort(sortedHand);
|
||||||
|
|
||||||
|
List<String> pongKeys = new ArrayList<>();
|
||||||
|
for (List<Integer> group : state.pongGroups) {
|
||||||
|
List<Integer> sorted = new ArrayList<>(group);
|
||||||
|
Collections.sort(sorted);
|
||||||
|
pongKeys.add(sorted.toString());
|
||||||
|
}
|
||||||
|
Collections.sort(pongKeys);
|
||||||
|
|
||||||
|
List<String> gangKeys = new ArrayList<>();
|
||||||
|
for (List<Integer> group : state.gangGroups) {
|
||||||
|
List<Integer> sorted = new ArrayList<>(group);
|
||||||
|
Collections.sort(sorted);
|
||||||
|
gangKeys.add(sorted.toString());
|
||||||
|
}
|
||||||
|
Collections.sort(gangKeys);
|
||||||
|
|
||||||
|
List<String> chiKeys = new ArrayList<>();
|
||||||
|
for (List<Integer> group : state.chiGroups) {
|
||||||
|
List<Integer> sorted = new ArrayList<>(group);
|
||||||
|
Collections.sort(sorted);
|
||||||
|
chiKeys.add(sorted.toString());
|
||||||
|
}
|
||||||
|
Collections.sort(chiKeys);
|
||||||
|
|
||||||
|
return sortedHand.toString() + "|" +
|
||||||
|
pongKeys.toString() + "|" +
|
||||||
|
gangKeys.toString() + "|" +
|
||||||
|
chiKeys.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取牌值
|
||||||
|
*/
|
||||||
|
private int getCardValue(int card) {
|
||||||
|
return card % 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取牌类型
|
||||||
|
*/
|
||||||
|
private int getCardType(int card) {
|
||||||
|
return card / 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化单张牌
|
||||||
|
*/
|
||||||
|
String formatCard(int card) {
|
||||||
|
int type = card / 100;
|
||||||
|
int value = card % 10;
|
||||||
|
switch (type) {
|
||||||
|
case 1:
|
||||||
|
return value + "万";
|
||||||
|
case 2:
|
||||||
|
return value + "筒";
|
||||||
|
case 3:
|
||||||
|
return value + "条";
|
||||||
|
default:
|
||||||
|
return "字牌";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化牌列表
|
||||||
|
*/
|
||||||
|
private String formatCards(List<Integer> cards) {
|
||||||
|
return cards.stream()
|
||||||
|
.map(this::formatCard)
|
||||||
|
.collect(Collectors.joining(" "));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 出牌结果类
|
||||||
|
*/
|
||||||
|
private static class DiscardResult {
|
||||||
|
double score;
|
||||||
|
List<Integer> tingCards;
|
||||||
|
int searchDepth;
|
||||||
|
|
||||||
|
DiscardResult(double score, List<Integer> tingCards, int searchDepth) {
|
||||||
|
this.score = score;
|
||||||
|
this.tingCards = tingCards;
|
||||||
|
this.searchDepth = searchDepth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,51 @@
|
||||||
|
package com.robotcm.taurus.util;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class test1 {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
ai ai = new ai();
|
||||||
|
PlayerState playerState = new PlayerState();
|
||||||
|
playerState.handCards = Arrays.asList(205,207,206,203,203,203,202,201,108,107,106,102,102,103);
|
||||||
|
|
||||||
|
List<Integer> integers = ai.checkTingCardsWithState(playerState);
|
||||||
|
System.out.println(integers);
|
||||||
|
// // 手动构造15张牌,包含飞机555666
|
||||||
|
// List<CardObj> cardInhand = new ArrayList<>();
|
||||||
|
//
|
||||||
|
// // 飞机部分:555666
|
||||||
|
// cardInhand.add(new CardObj(213)); // 红桃5
|
||||||
|
// cardInhand.add(new CardObj(113)); // 方块5
|
||||||
|
// cardInhand.add(new CardObj(112)); // 梅花5
|
||||||
|
// cardInhand.add(new CardObj(211)); // 红桃6
|
||||||
|
// cardInhand.add(new CardObj(109)); // 方块6
|
||||||
|
// cardInhand.add(new CardObj(109)); // 梅花6
|
||||||
|
//
|
||||||
|
// // 带牌部分:小单牌和小对子
|
||||||
|
// cardInhand.add(new CardObj(209)); // 红桃7 (小单牌)
|
||||||
|
// cardInhand.add(new CardObj(107)); // 红桃8 (小单牌)
|
||||||
|
// cardInhand.add(new CardObj(106)); // 红桃9 (小单牌)
|
||||||
|
// cardInhand.add(new CardObj(105)); // 红桃10 (小单牌)
|
||||||
|
//
|
||||||
|
// // 其他牌
|
||||||
|
// cardInhand.add(new CardObj(205)); // 红桃J
|
||||||
|
// cardInhand.add(new CardObj(104)); // 方块J (形成对子)
|
||||||
|
// cardInhand.add(new CardObj(105)); // 红桃Q
|
||||||
|
// cardInhand.add(new CardObj(205)); // 红桃K
|
||||||
|
// cardInhand.add(new CardObj(303)); // 红桃A
|
||||||
|
//
|
||||||
|
// System.out.println("构造的手牌: " + cardInhand);
|
||||||
|
// System.out.println("手牌数量: " + cardInhand.size());
|
||||||
|
//
|
||||||
|
// // 调用智能出牌方法
|
||||||
|
// ITArray itArray = test.intelligentPaoDeKuaiOutCard(cardInhand, null, null);
|
||||||
|
// System.out.println("打出牌" + itArray);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,252 @@
|
||||||
|
package com.robotcm.taurus.util;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class test99 {
|
||||||
|
|
||||||
|
// 长沙麻将特殊规则:二五八将
|
||||||
|
private static final Set<Integer> JIANG_PAIS;
|
||||||
|
|
||||||
|
static {
|
||||||
|
Set<Integer> jiangSet = new HashSet<>();
|
||||||
|
jiangSet.add(2);
|
||||||
|
jiangSet.add(5);
|
||||||
|
jiangSet.add(8);
|
||||||
|
JIANG_PAIS = Collections.unmodifiableSet(jiangSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分析14张手牌的最佳打牌策略
|
||||||
|
*/
|
||||||
|
public static void analyzeHandCards(List<Integer> handCards) {
|
||||||
|
if (handCards == null || handCards.size() != 14) {
|
||||||
|
System.out.println("错误:手牌必须是14张");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("当前手牌(14张): " + convertToReadable(handCards));
|
||||||
|
System.out.println("======================================");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 2. 分析手牌结构
|
||||||
|
List<Integer> sortedHand = new ArrayList<>(handCards);
|
||||||
|
Collections.sort(sortedHand);
|
||||||
|
|
||||||
|
System.out.println("\n=== 手牌结构分析 ===");
|
||||||
|
|
||||||
|
// 找出所有刻子
|
||||||
|
List<List<Integer>> kezis = findKezis(sortedHand);
|
||||||
|
System.out.println("刻子(" + kezis.size() + "组):");
|
||||||
|
for (List<Integer> kezi : kezis) {
|
||||||
|
System.out.println(" " + convertToReadable(kezi));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 找出所有顺子
|
||||||
|
List<List<Integer>> shunzis = findShunzis(sortedHand);
|
||||||
|
System.out.println("\n顺子(" + shunzis.size() + "组):");
|
||||||
|
for (List<Integer> shunzi : shunzis) {
|
||||||
|
System.out.println(" " + convertToReadable(shunzi));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 找出所有258将牌
|
||||||
|
List<Integer> jiangCandidates = findJiangCandidates(sortedHand);
|
||||||
|
System.out.println("\n258将牌候选(" + jiangCandidates.size() + "张):");
|
||||||
|
System.out.println(" " + convertToReadable(jiangCandidates));
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 找出所有刻子
|
||||||
|
*/
|
||||||
|
private static List<List<Integer>> findKezis(List<Integer> handCards) {
|
||||||
|
List<List<Integer>> kezis = new ArrayList<>();
|
||||||
|
List<Integer> temp = new ArrayList<>(handCards);
|
||||||
|
|
||||||
|
while (!temp.isEmpty()) {
|
||||||
|
int card = temp.get(0);
|
||||||
|
int count = Collections.frequency(temp, card);
|
||||||
|
|
||||||
|
if (count >= 3) {
|
||||||
|
List<Integer> kezi = new ArrayList<>();
|
||||||
|
kezi.add(card);
|
||||||
|
kezi.add(card);
|
||||||
|
kezi.add(card);
|
||||||
|
kezis.add(kezi);
|
||||||
|
|
||||||
|
// 移除这3张牌
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
temp.remove(Integer.valueOf(card));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
temp.remove(Integer.valueOf(card));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return kezis;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 找出所有顺子
|
||||||
|
*/
|
||||||
|
private static List<List<Integer>> findShunzis(List<Integer> handCards) {
|
||||||
|
List<List<Integer>> shunzis = new ArrayList<>();
|
||||||
|
List<Integer> temp = new ArrayList<>(handCards);
|
||||||
|
|
||||||
|
// 先移除已找到的刻子
|
||||||
|
List<List<Integer>> kezis = findKezis(handCards);
|
||||||
|
for (List<Integer> kezi : kezis) {
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
temp.remove(Integer.valueOf(kezi.get(0)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 找顺子
|
||||||
|
Collections.sort(temp);
|
||||||
|
|
||||||
|
for (int i = 0; i < temp.size(); i++) {
|
||||||
|
int card1 = temp.get(i);
|
||||||
|
int type = getCardType(card1);
|
||||||
|
int value = getCardValue(card1);
|
||||||
|
|
||||||
|
if (type <= 3 && value <= 7) {
|
||||||
|
int card2 = card1 + 1;
|
||||||
|
int card3 = card1 + 2;
|
||||||
|
|
||||||
|
if (temp.contains(card2) && temp.contains(card3)) {
|
||||||
|
List<Integer> shunzi = new ArrayList<>();
|
||||||
|
shunzi.add(card1);
|
||||||
|
shunzi.add(card2);
|
||||||
|
shunzi.add(card3);
|
||||||
|
shunzis.add(shunzi);
|
||||||
|
|
||||||
|
// 移除这3张牌
|
||||||
|
temp.remove(Integer.valueOf(card1));
|
||||||
|
temp.remove(Integer.valueOf(card2));
|
||||||
|
temp.remove(Integer.valueOf(card3));
|
||||||
|
i--; // 因为移除了元素
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return shunzis;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 找出258将牌候选
|
||||||
|
*/
|
||||||
|
private static List<Integer> findJiangCandidates(List<Integer> handCards) {
|
||||||
|
List<Integer> jiangs = new ArrayList<>();
|
||||||
|
|
||||||
|
for (int card : handCards) {
|
||||||
|
int value = getCardValue(card);
|
||||||
|
if (JIANG_PAIS.contains(value)) {
|
||||||
|
jiangs.add(card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return jiangs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否能组成顺子
|
||||||
|
*/
|
||||||
|
private static boolean canFormShunzi(int card1, int card2, int card3) {
|
||||||
|
if (getCardType(card1) != getCardType(card2) ||
|
||||||
|
getCardType(card1) != getCardType(card3)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Integer> values = Arrays.asList(
|
||||||
|
getCardValue(card1),
|
||||||
|
getCardValue(card2),
|
||||||
|
getCardValue(card3)
|
||||||
|
);
|
||||||
|
Collections.sort(values);
|
||||||
|
|
||||||
|
return values.get(0) + 1 == values.get(1) &&
|
||||||
|
values.get(1) + 1 == values.get(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取牌值
|
||||||
|
*/
|
||||||
|
private static int getCardValue(int card) {
|
||||||
|
return card % 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取牌类型
|
||||||
|
*/
|
||||||
|
private static int getCardType(int card) {
|
||||||
|
return card / 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化单张牌
|
||||||
|
*/
|
||||||
|
private static String formatCard(int card) {
|
||||||
|
int type = getCardType(card);
|
||||||
|
int value = getCardValue(card);
|
||||||
|
switch (type) {
|
||||||
|
case 1:
|
||||||
|
return value + "万";
|
||||||
|
case 2:
|
||||||
|
return value + "筒";
|
||||||
|
case 3:
|
||||||
|
return value + "条";
|
||||||
|
default:
|
||||||
|
return "字牌" + value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换为可读格式
|
||||||
|
*/
|
||||||
|
private static String convertToReadable(List<Integer> cards) {
|
||||||
|
if (cards == null || cards.isEmpty()) {
|
||||||
|
return "空";
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> cardStrs = new ArrayList<>();
|
||||||
|
for (int card : cards) {
|
||||||
|
cardStrs.add(formatCard(card));
|
||||||
|
}
|
||||||
|
return String.join(" ", cardStrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试方法
|
||||||
|
*/
|
||||||
|
public static void main(String[] args) {
|
||||||
|
System.out.println("=== 长沙麻将手牌分析 ===\n");
|
||||||
|
|
||||||
|
// 测试你提供的例子
|
||||||
|
System.out.println("测试例子:201,202,203, 101,102,103, 303,303,303,105,108,307,309,308");
|
||||||
|
List<Integer> hand = Arrays.asList(
|
||||||
|
201, 202, 203, // 1筒2筒3筒顺子
|
||||||
|
101, 102, 103, // 1万2万3万顺子
|
||||||
|
303, 303, 303, // 3条刻子
|
||||||
|
105, // 5万
|
||||||
|
108, // 8万
|
||||||
|
307, // 7条
|
||||||
|
309, // 9条
|
||||||
|
308 // 8条
|
||||||
|
);
|
||||||
|
|
||||||
|
analyzeHandCards(hand);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,462 @@
|
||||||
|
package com.robotcm.taurus.util;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class zuixin {
|
||||||
|
|
||||||
|
// 长沙麻将特殊规则:二五八将
|
||||||
|
private static final Set<Integer> JIANG_PAIS;
|
||||||
|
|
||||||
|
public boolean isTinAi = false;
|
||||||
|
|
||||||
|
static {
|
||||||
|
Set<Integer> jiangSet = new HashSet<>();
|
||||||
|
jiangSet.add(2);
|
||||||
|
jiangSet.add(5);
|
||||||
|
jiangSet.add(8);
|
||||||
|
JIANG_PAIS = Collections.unmodifiableSet(jiangSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
System.out.println("=== 长沙麻将测试(含碰杠吃牌) ===");
|
||||||
|
zuixin ai = new zuixin();
|
||||||
|
|
||||||
|
// 测试1:普通手牌
|
||||||
|
System.out.println("\n=== 测试1:普通14张牌 ===");
|
||||||
|
PlayerState state1 = new PlayerState();
|
||||||
|
state1.handCards = Arrays.asList(204, 206, 205, 205, 203, 109, 108, 107, 107, 106, 103, 102, 101, 101);
|
||||||
|
int discard1 = ai.findBestDiscard(state1);
|
||||||
|
System.out.println("推荐出牌: " + ai.formatCard(discard1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主方法:根据完整玩家状态推荐出牌
|
||||||
|
*/
|
||||||
|
public int findBestDiscard(PlayerState state) {
|
||||||
|
System.out.println("\n=== 长沙麻将AI出牌分析 ===");
|
||||||
|
System.out.println("手牌(" + state.handCards.size() + "张): " + formatCards(state.handCards));
|
||||||
|
|
||||||
|
// 分析手牌结构
|
||||||
|
analyzeHandStructure(state.handCards);
|
||||||
|
|
||||||
|
long startTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
// 获取可出的牌(手牌中的唯一牌)
|
||||||
|
Map<Integer, Double> results = new HashMap<>();
|
||||||
|
Set<Integer> uniqueCards = new HashSet<>(state.handCards);
|
||||||
|
|
||||||
|
// 如果只剩一张牌,直接返回
|
||||||
|
if (state.handCards.size() == 1) {
|
||||||
|
int onlyCard = state.handCards.get(0);
|
||||||
|
System.out.println("只剩一张牌,必须出: " + formatCard(onlyCard));
|
||||||
|
return onlyCard;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int card : uniqueCards) {
|
||||||
|
// 评估打出这张牌的得分
|
||||||
|
double score = evaluateDiscard(card, state);
|
||||||
|
results.put(card, score);
|
||||||
|
|
||||||
|
System.out.printf("打出 %s: 评分=%.1f%n", formatCard(card), score);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 找出最佳出牌
|
||||||
|
int bestCard = -1;
|
||||||
|
double bestScore = Double.NEGATIVE_INFINITY;
|
||||||
|
|
||||||
|
for (Map.Entry<Integer, Double> entry : results.entrySet()) {
|
||||||
|
if (entry.getValue() > bestScore) {
|
||||||
|
bestScore = entry.getValue();
|
||||||
|
bestCard = entry.getKey();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
long endTime = System.currentTimeMillis();
|
||||||
|
System.out.printf("\n分析耗时: %dms%n", (endTime - startTime));
|
||||||
|
|
||||||
|
if (bestCard != -1) {
|
||||||
|
System.out.println("\n🎯 AI推荐出牌: " + formatCard(bestCard));
|
||||||
|
}
|
||||||
|
|
||||||
|
return bestCard;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分析手牌结构
|
||||||
|
*/
|
||||||
|
private void analyzeHandStructure(List<Integer> hand) {
|
||||||
|
System.out.println("\n=== 手牌结构分析 ===");
|
||||||
|
|
||||||
|
// 按花色分组
|
||||||
|
Map<Integer, List<Integer>> suitGroups = new HashMap<>();
|
||||||
|
for (int card : hand) {
|
||||||
|
int suit = getCardType(card);
|
||||||
|
suitGroups.putIfAbsent(suit, new ArrayList<>());
|
||||||
|
suitGroups.get(suit).add(getCardValue(card));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分析每个花色
|
||||||
|
for (Map.Entry<Integer, List<Integer>> entry : suitGroups.entrySet()) {
|
||||||
|
int suit = entry.getKey();
|
||||||
|
List<Integer> values = entry.getValue();
|
||||||
|
Collections.sort(values);
|
||||||
|
|
||||||
|
System.out.printf(" %s: %s%n",
|
||||||
|
getSuitName(suit),
|
||||||
|
values.stream().map(String::valueOf).collect(Collectors.joining(" ")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 评估打出某张牌的得分
|
||||||
|
*/
|
||||||
|
private double evaluateDiscard(int card, PlayerState state) {
|
||||||
|
List<Integer> hand = state.handCards;
|
||||||
|
int type = getCardType(card);
|
||||||
|
int value = getCardValue(card);
|
||||||
|
double score = 0.0;
|
||||||
|
|
||||||
|
System.out.printf("\n=== 评估 %s ===%n", formatCard(card));
|
||||||
|
|
||||||
|
// 1. 检查原始数量
|
||||||
|
int originalCount = Collections.frequency(hand, card);
|
||||||
|
System.out.printf(" 原始数量: %d张%n", originalCount);
|
||||||
|
|
||||||
|
// 2. 258将牌保护(非常重要)
|
||||||
|
if (JIANG_PAIS.contains(value)) {
|
||||||
|
System.out.println(" 258将牌: 扣30分");
|
||||||
|
score -= 30.0;
|
||||||
|
|
||||||
|
// 258将对子特别重要
|
||||||
|
if (originalCount >= 2) {
|
||||||
|
System.out.println(" 258将对子: 额外扣20分");
|
||||||
|
score -= 20.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 检查是否是对子/刻子(非258将)
|
||||||
|
if (originalCount >= 2 && !JIANG_PAIS.contains(value)) {
|
||||||
|
if (originalCount >= 3) {
|
||||||
|
System.out.println(" 刻子: 扣40分");
|
||||||
|
score -= 40.0;
|
||||||
|
} else {
|
||||||
|
// 检查是否冗余对子
|
||||||
|
boolean isRedundant = isRedundantPairInCurrentHand(card, hand);
|
||||||
|
if (isRedundant) {
|
||||||
|
System.out.println(" 冗余对子: 加25分鼓励打出");
|
||||||
|
score += 25.0;
|
||||||
|
} else {
|
||||||
|
System.out.println(" 对子: 扣15分");
|
||||||
|
score -= 15.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 检查这张牌的搭子情况
|
||||||
|
DaziInfo daziInfo = analyzeDazi(card, hand);
|
||||||
|
System.out.printf(" 搭子分析: %s%n", daziInfo.getDescription());
|
||||||
|
|
||||||
|
// 根据搭子情况调整分数
|
||||||
|
if (daziInfo.isLonely()) {
|
||||||
|
System.out.println(" 完全孤立: 加20分");
|
||||||
|
score += 20.0;
|
||||||
|
} else if (daziInfo.getGoodDaziCount() == 0) {
|
||||||
|
System.out.println(" 无好搭子: 加15分");
|
||||||
|
score += 15.0;
|
||||||
|
} else if (daziInfo.getGoodDaziCount() == 1) {
|
||||||
|
System.out.println(" 有1个好搭子: 加5分");
|
||||||
|
score += 5.0;
|
||||||
|
} else {
|
||||||
|
System.out.printf(" 有%d个好搭子: 扣15分(保留价值高)%n", daziInfo.getGoodDaziCount());
|
||||||
|
score -= 15.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 边张评估
|
||||||
|
if (value == 1 || value == 9) {
|
||||||
|
System.out.println(" 边张: 加15分(利用率低)");
|
||||||
|
score += 15.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. 检查是否在完整顺子中且多余
|
||||||
|
if (isInCompleteSequenceAndRedundant(card, hand)) {
|
||||||
|
System.out.println(" 顺子中多余: 加20分鼓励打出");
|
||||||
|
score += 20.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. 花色分布
|
||||||
|
int suitCount = countSuitCards(type, hand);
|
||||||
|
if (suitCount <= 2) {
|
||||||
|
System.out.printf(" 花色孤立(只有%d张): 加10分%n", suitCount);
|
||||||
|
score += 10.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 搭子信息类
|
||||||
|
*/
|
||||||
|
private static class DaziInfo {
|
||||||
|
private boolean lonely = true;
|
||||||
|
private int goodDaziCount = 0;
|
||||||
|
private List<String> descriptions = new ArrayList<>();
|
||||||
|
|
||||||
|
public void addGoodDazi(String desc) {
|
||||||
|
goodDaziCount++;
|
||||||
|
descriptions.add(desc);
|
||||||
|
lonely = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addBadDazi(String desc) {
|
||||||
|
descriptions.add(desc);
|
||||||
|
lonely = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLonely() {
|
||||||
|
return lonely;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getGoodDaziCount() {
|
||||||
|
return goodDaziCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
if (lonely) return "完全孤立";
|
||||||
|
return String.join(", ", descriptions) + " (好搭子:" + goodDaziCount + "个)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分析搭子情况
|
||||||
|
*/
|
||||||
|
private DaziInfo analyzeDazi(int card, List<Integer> hand) {
|
||||||
|
int type = getCardType(card);
|
||||||
|
int value = getCardValue(card);
|
||||||
|
DaziInfo info = new DaziInfo();
|
||||||
|
|
||||||
|
// 获取同花色牌(不包括要评估的这张牌本身)
|
||||||
|
List<Integer> sameSuit = hand.stream()
|
||||||
|
.filter(c -> getCardType(c) == type && c != card)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
if (sameSuit.isEmpty()) {
|
||||||
|
return info; // 完全孤立
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查相邻牌
|
||||||
|
boolean hasNeighbor = false;
|
||||||
|
for (int other : sameSuit) {
|
||||||
|
int otherValue = getCardValue(other);
|
||||||
|
int diff = Math.abs(otherValue - value);
|
||||||
|
|
||||||
|
if (diff == 1) {
|
||||||
|
hasNeighbor = true;
|
||||||
|
// 相邻搭子是好搭子
|
||||||
|
int min = Math.min(value, otherValue);
|
||||||
|
int max = Math.max(value, otherValue);
|
||||||
|
info.addGoodDazi(formatCard(type * 100 + min) + formatCard(type * 100 + max));
|
||||||
|
} else if (diff == 2) {
|
||||||
|
// 隔张搭子,检查是否有中间牌
|
||||||
|
int middleValue = Math.min(value, otherValue) + 1;
|
||||||
|
int middleCard = type * 100 + middleValue;
|
||||||
|
if (hand.contains(middleCard)) {
|
||||||
|
// 实际能形成顺子,是好搭子
|
||||||
|
info.addGoodDazi("顺子" + formatCard(type * 100 + Math.min(value, otherValue)) +
|
||||||
|
formatCard(middleCard) +
|
||||||
|
formatCard(type * 100 + Math.max(value, otherValue)));
|
||||||
|
} else {
|
||||||
|
// 单纯的隔张搭子,质量一般
|
||||||
|
info.addBadDazi("隔张" + formatCard(type * 100 + Math.min(value, otherValue)) +
|
||||||
|
formatCard(type * 100 + Math.max(value, otherValue)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有相邻牌但有对子,对子也是好搭子
|
||||||
|
if (!hasNeighbor) {
|
||||||
|
int count = Collections.frequency(hand, card);
|
||||||
|
if (count >= 2) {
|
||||||
|
info.addGoodDazi("对子");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否冗余对子(在当前手牌中)
|
||||||
|
*/
|
||||||
|
private boolean isRedundantPairInCurrentHand(int card, List<Integer> hand) {
|
||||||
|
int type = getCardType(card);
|
||||||
|
int value = getCardValue(card);
|
||||||
|
|
||||||
|
// 检查是否在顺子中
|
||||||
|
if (value >= 3) {
|
||||||
|
// 检查是否能形成前顺子
|
||||||
|
int prev1 = type * 100 + (value - 1);
|
||||||
|
int prev2 = type * 100 + (value - 2);
|
||||||
|
if (hand.contains(prev1) && hand.contains(prev2)) {
|
||||||
|
// 比如3万对子,且有12万
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value <= 7) {
|
||||||
|
// 检查是否能形成后顺子
|
||||||
|
int next1 = type * 100 + (value + 1);
|
||||||
|
int next2 = type * 100 + (value + 2);
|
||||||
|
if (hand.contains(next1) && hand.contains(next2)) {
|
||||||
|
// 比如7万对子,且有89万
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value >= 2 && value <= 8) {
|
||||||
|
// 检查是否能形成中顺子
|
||||||
|
int prev = type * 100 + (value - 1);
|
||||||
|
int next = type * 100 + (value + 1);
|
||||||
|
if (hand.contains(prev) && hand.contains(next)) {
|
||||||
|
// 比如4万对子,且有3万和5万
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否在完整顺子中且多余
|
||||||
|
*/
|
||||||
|
private boolean isInCompleteSequenceAndRedundant(int card, List<Integer> hand) {
|
||||||
|
int type = getCardType(card);
|
||||||
|
int value = getCardValue(card);
|
||||||
|
int count = Collections.frequency(hand, card);
|
||||||
|
|
||||||
|
if (count <= 1) return false; // 只有一张,不多余
|
||||||
|
|
||||||
|
// 检查是否能形成顺子
|
||||||
|
if (value >= 1 && value <= 7) {
|
||||||
|
int next1 = type * 100 + (value + 1);
|
||||||
|
int next2 = type * 100 + (value + 2);
|
||||||
|
if (hand.contains(next1) && hand.contains(next2)) {
|
||||||
|
// 比如有2张1万,且有2万和3万
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value >= 3 && value <= 9) {
|
||||||
|
int prev1 = type * 100 + (value - 1);
|
||||||
|
int prev2 = type * 100 + (value - 2);
|
||||||
|
if (hand.contains(prev1) && hand.contains(prev2)) {
|
||||||
|
// 比如有2张3万,且有1万和2万
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统计同花色的牌数
|
||||||
|
*/
|
||||||
|
private int countSuitCards(int suit, List<Integer> hand) {
|
||||||
|
int count = 0;
|
||||||
|
for (int card : hand) {
|
||||||
|
if (getCardType(card) == suit) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getSuitName(int suit) {
|
||||||
|
switch(suit) {
|
||||||
|
case 1: return "万";
|
||||||
|
case 2: return "筒";
|
||||||
|
case 3: return "条";
|
||||||
|
default: return "字牌";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取牌值
|
||||||
|
*/
|
||||||
|
private int getCardValue(int card) {
|
||||||
|
return card % 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取牌类型
|
||||||
|
*/
|
||||||
|
private int getCardType(int card) {
|
||||||
|
return card / 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化单张牌
|
||||||
|
*/
|
||||||
|
String formatCard(int card) {
|
||||||
|
int type = card / 100;
|
||||||
|
int value = card % 10;
|
||||||
|
switch (type) {
|
||||||
|
case 1:
|
||||||
|
return value + "万";
|
||||||
|
case 2:
|
||||||
|
return value + "筒";
|
||||||
|
case 3:
|
||||||
|
return value + "条";
|
||||||
|
default:
|
||||||
|
return "字牌";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化牌列表
|
||||||
|
*/
|
||||||
|
private String formatCards(List<Integer> cards) {
|
||||||
|
return cards.stream()
|
||||||
|
.sorted()
|
||||||
|
.map(this::formatCard)
|
||||||
|
.collect(Collectors.joining(" "));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 玩家状态类
|
||||||
|
*/
|
||||||
|
public static class PlayerState {
|
||||||
|
public List<Integer> handCards = new ArrayList<>();
|
||||||
|
public List<List<Integer>> pongGroups = new ArrayList<>();
|
||||||
|
public List<List<Integer>> gangGroups = new ArrayList<>();
|
||||||
|
public List<List<Integer>> chiGroups = new ArrayList<>();
|
||||||
|
public boolean isZhuang = false;
|
||||||
|
|
||||||
|
public void addPongGroup(List<Integer> group) {
|
||||||
|
pongGroups.add(new ArrayList<>(group));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addGangGroup(List<Integer> group) {
|
||||||
|
gangGroups.add(new ArrayList<>(group));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addChiGroup(List<Integer> group) {
|
||||||
|
chiGroups.add(new ArrayList<>(group));
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPongGroupCount() {
|
||||||
|
return pongGroups.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getGangGroupCount() {
|
||||||
|
return gangGroups.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getChiGroupCount() {
|
||||||
|
return chiGroups.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTotalGroupCount() {
|
||||||
|
return getPongGroupCount() + getGangGroupCount() + getChiGroupCount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue