火力规则:装备匹配规则实现
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
package com.solution.rule.config;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 装备匹配规则默认参数,与 {@code resources/rules/rule.drl} 中 {@code buildParam} 键保持一致。
|
||||
*/
|
||||
public final class FireRuleMatchDefaultParams {
|
||||
|
||||
private FireRuleMatchDefaultParams() {
|
||||
}
|
||||
|
||||
public static Map<String, Object> defaults() {
|
||||
Map<String, Object> param = new HashMap<>();
|
||||
param.put("weight", 1);
|
||||
param.put("minSelectedScore", 1);
|
||||
param.put("tankScore", 1);
|
||||
param.put("airScore", 2);
|
||||
param.put("groundScore", 1);
|
||||
param.put("missileScore", 1);
|
||||
param.put("airTaskScore", 10);
|
||||
param.put("bluePlatformKeywords_air", "F-16,J-10,F-35");
|
||||
param.put("redPreferredWhenBlueAir", "防空,导弹,无人机,直升机,空空");
|
||||
param.put("redPreferredWhenGround", "远火,榴弹,炮,火箭");
|
||||
param.put("airTaskKeywords", "空中,制空,拦截,空战");
|
||||
param.put("groundTaskKeywords", "地面,突击,登陆");
|
||||
param.put("tankKeywords", "坦克,装甲");
|
||||
param.put("missileKeywords", "导弹,火箭弹,巡航");
|
||||
param.put("tieBreak", "equipmentId");
|
||||
return param;
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import com.solution.rule.domain.ultimately.dto.FireRuleInputDTO;
|
||||
import com.solution.rule.domain.ultimately.dto.FireRuleInputForceSideDTO;
|
||||
import com.solution.rule.domain.ultimately.dto.FireRuleInputRedWeaponElementDTO;
|
||||
import com.solution.rule.domain.ultimately.dto.FireRuleTaskInputDTO;
|
||||
import com.solution.rule.config.FireRuleMatchDefaultParams;
|
||||
import com.solution.rule.domain.ultimately.fact.DroolsFact;
|
||||
import com.solution.rule.domain.ultimately.vo.FireRuleOutputVO;
|
||||
import com.solution.rule.domain.vo.ComponentCountVO;
|
||||
@@ -27,6 +28,7 @@ import com.solution.rule.strategy.SceneStrategy;
|
||||
import com.solution.rule.strategy.SceneStrategyFactory;
|
||||
import org.kie.api.KieBase;
|
||||
import org.kie.api.runtime.KieSession;
|
||||
import org.kie.api.runtime.rule.FactHandle;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@@ -197,7 +199,7 @@ public class FireRuleServiceImpl implements FireRuleService {
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public FireRuleOutputVO rule(FireRuleInputDTO task) {
|
||||
public FireRuleOutputVO rule(FireRuleInputDTO task) {
|
||||
if(ObjectUtil.isEmpty(task)){
|
||||
throw new RuntimeException(ExceptionConstants.PARAMETER_EXCEPTION);
|
||||
}
|
||||
@@ -207,10 +209,9 @@ public class FireRuleServiceImpl implements FireRuleService {
|
||||
}
|
||||
//创建KieSession
|
||||
KieSession kieSession = kieBase.newKieSession();
|
||||
//设置Drools全局变量
|
||||
Map<String, Object> globalParams = new HashMap<>();
|
||||
// globalParams.putAll(FireRuleMatchDefaultParams.defaults());
|
||||
kieSession.setGlobal("globalParams", globalParams);
|
||||
kieSession.insert(globalParams);
|
||||
//获取红方阵营id
|
||||
String redObjectHandleId = getObjectHandle(task);
|
||||
if(ObjectUtil.isEmpty(redObjectHandleId)){
|
||||
@@ -229,16 +230,25 @@ public class FireRuleServiceImpl implements FireRuleService {
|
||||
fireRuleOutputVO.setSourceFile(task.getSourceFile());
|
||||
|
||||
DroolsFact droolsFact = new DroolsFact();
|
||||
droolsFact.setRedWeapons(redWeapons);
|
||||
|
||||
// droolsFact.getFireRuleOutputVO().setRedWeapons(redWeapons);
|
||||
droolsFact.setRedWeapons(new ArrayList<>(redWeapons));
|
||||
droolsFact.setFireRuleOutputVO(fireRuleOutputVO);
|
||||
for (FireRuleTaskInputDTO fireRuleTaskInputDTO : tasks) {
|
||||
droolsFact.setTask(fireRuleTaskInputDTO);
|
||||
kieSession.insert(droolsFact);
|
||||
|
||||
FactHandle droolsFactHandle = null;
|
||||
try {
|
||||
for (FireRuleTaskInputDTO fireRuleTaskInputDTO : tasks) {
|
||||
droolsFact.setTask(fireRuleTaskInputDTO);
|
||||
if (droolsFactHandle == null) {
|
||||
droolsFactHandle = kieSession.insert(droolsFact);
|
||||
} else {
|
||||
kieSession.update(droolsFactHandle, droolsFact);
|
||||
}
|
||||
kieSession.fireAllRules();
|
||||
}
|
||||
} finally {
|
||||
kieSession.dispose();
|
||||
}
|
||||
|
||||
return droolsFact.getFireRuleOutputVO();
|
||||
return fireRuleOutputVO;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
package com.solution.rule.utils;
|
||||
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.solution.rule.domain.ultimately.dto.FireRuleInputRedSubComponentsDTO;
|
||||
import com.solution.rule.domain.ultimately.vo.FireRuleLauncherConfigurationVO;
|
||||
import com.solution.rule.domain.ultimately.vo.FireRuleMissionListItemVO;
|
||||
import com.solution.rule.domain.ultimately.vo.FireRuleMountedWeaponRefVO;
|
||||
import com.solution.rule.domain.ultimately.vo.FireRuleRedSubComponentsVO;
|
||||
import com.solution.rule.domain.ultimately.vo.FireRuleRedWeaponEquipmentVO;
|
||||
import com.solution.rule.domain.ultimately.vo.FireRuleRedWeaponSlotVO;
|
||||
import com.solution.rule.domain.ultimately.vo.FireRuleSceneTaskNodeVO;
|
||||
import com.solution.rule.domain.ultimately.vo.FireRuleSceneTaskPayloadVO;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 将入参中的红方装备 {@code SubComponents}(DTO)转为输出 VO,保持与原始 JSON 结构一致。
|
||||
*/
|
||||
public final class FireRuleRedWeaponOutputFillHelper {
|
||||
|
||||
private static final ObjectMapper MAPPER = new ObjectMapper()
|
||||
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
|
||||
private FireRuleRedWeaponOutputFillHelper() {
|
||||
}
|
||||
|
||||
/**
|
||||
* DTO 与 VO 字段/JsonProperty 对齐,通过 Jackson 树转换实现原样输出。
|
||||
*/
|
||||
public static FireRuleRedSubComponentsVO toOutputSubComponents(FireRuleInputRedSubComponentsDTO src) {
|
||||
if (src == null) {
|
||||
return null;
|
||||
}
|
||||
return MAPPER.convertValue(src, FireRuleRedSubComponentsVO.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将输出 redWeapons 单项映射为 Tasks 节点(一个装备 -> 一个任务节点)。
|
||||
*
|
||||
* <p>字段映射约定:</p>
|
||||
* <ul>
|
||||
* <li>drawName = Name + \"打击任务\"</li>
|
||||
* <li>side = OwnerForceSide(按你的要求直接复制)</li>
|
||||
* <li>id 优先 EquipmentID,其次 PlatID,最后用 index 兜底</li>
|
||||
* <li>task.payload.weaponId = EquipmentID</li>
|
||||
* <li>task.payload.sideId = OwnerForceSide</li>
|
||||
* <li>missionList:尽量从 SubComponents.weapon[*] 中提取 mountedWeapon/name/number/launcherType</li>
|
||||
* </ul>
|
||||
*/
|
||||
public static FireRuleSceneTaskNodeVO toTaskNode(FireRuleRedWeaponEquipmentVO redWeapon, int index) {
|
||||
FireRuleSceneTaskNodeVO node = new FireRuleSceneTaskNodeVO();
|
||||
if (redWeapon == null) {
|
||||
node.setId("redWeaponTask_" + index);
|
||||
node.setDrawName("打击任务");
|
||||
return node;
|
||||
}
|
||||
|
||||
String name = nz(redWeapon.getName());
|
||||
node.setName(name);
|
||||
node.setDrawName(name + "打击任务");
|
||||
node.setId(buildNodeId(redWeapon, index));
|
||||
node.setSide(redWeapon.getOwnerForceSide());
|
||||
// 按你的要求写死 Tasks 节点的展示字段
|
||||
node.setColor("rgb(220, 39, 39)");
|
||||
node.setDataType("taskPlane");
|
||||
node.setGroupType("tasks");
|
||||
node.setShow(true);
|
||||
node.setIsSelected(false);
|
||||
node.setSort((long) index);
|
||||
|
||||
FireRuleSceneTaskPayloadVO payload = new FireRuleSceneTaskPayloadVO();
|
||||
payload.setName(name);
|
||||
payload.setWeaponId(redWeapon.getEquipmentId());
|
||||
payload.setSideId(redWeapon.getOwnerForceSide());
|
||||
payload.setMissionList(buildMissionList(redWeapon));
|
||||
node.setTask(payload);
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量转换:redWeapons 有几个就生成几个 Tasks。
|
||||
*/
|
||||
public static List<FireRuleSceneTaskNodeVO> toTaskNodes(List<FireRuleRedWeaponEquipmentVO> redWeapons) {
|
||||
if (redWeapons == null || redWeapons.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<FireRuleSceneTaskNodeVO> list = new ArrayList<>(redWeapons.size());
|
||||
for (int i = 0; i < redWeapons.size(); i++) {
|
||||
list.add(toTaskNode(redWeapons.get(i), i));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private static List<FireRuleMissionListItemVO> buildMissionList(FireRuleRedWeaponEquipmentVO redWeapon) {
|
||||
if (redWeapon == null || redWeapon.getSubComponents() == null || redWeapon.getSubComponents().getWeapon() == null) {
|
||||
return null;
|
||||
}
|
||||
List<FireRuleMissionListItemVO> result = new ArrayList<>();
|
||||
for (FireRuleRedWeaponSlotVO slot : redWeapon.getSubComponents().getWeapon()) {
|
||||
if (slot == null) {
|
||||
continue;
|
||||
}
|
||||
FireRuleLauncherConfigurationVO cfg = slot.getConfiguration();
|
||||
FireRuleMountedWeaponRefVO mounted = cfg != null ? cfg.getMountedWeapon() : null;
|
||||
|
||||
FireRuleMissionListItemVO item = new FireRuleMissionListItemVO();
|
||||
// label:优先挂载武器名,其次 deviceName
|
||||
String label = mounted != null ? mounted.getName() : null;
|
||||
if (isBlank(label)) {
|
||||
label = slot.getDeviceName();
|
||||
}
|
||||
item.setLabel(label);
|
||||
item.setNumber(cfg != null ? cfg.getNumber() : null);
|
||||
item.setValue(mounted != null ? mounted.getName() : null);
|
||||
item.setLauncherType(extractLauncherType(slot.getTwiceModified()));
|
||||
result.add(item);
|
||||
}
|
||||
return result.isEmpty() ? null : result;
|
||||
}
|
||||
|
||||
private static String extractLauncherType(Map<String, Object> twiceModified) {
|
||||
if (twiceModified == null || twiceModified.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
Object v = twiceModified.get("launcherType");
|
||||
return v == null ? null : String.valueOf(v);
|
||||
}
|
||||
|
||||
private static String buildNodeId(FireRuleRedWeaponEquipmentVO redWeapon, int index) {
|
||||
String id = nz(redWeapon.getEquipmentId());
|
||||
if (!id.isEmpty()) {
|
||||
return id;
|
||||
}
|
||||
id = nz(redWeapon.getPlatId());
|
||||
if (!id.isEmpty()) {
|
||||
return id;
|
||||
}
|
||||
return "redWeaponTask_" + index;
|
||||
}
|
||||
|
||||
private static String nz(String s) {
|
||||
return s == null ? "" : s;
|
||||
}
|
||||
|
||||
private static boolean isBlank(String s) {
|
||||
return s == null || s.trim().isEmpty();
|
||||
}
|
||||
}
|
||||
@@ -1,34 +1,326 @@
|
||||
package com.solution.rule.utils;
|
||||
|
||||
import com.solution.rule.domain.ultimately.dto.FireRuleInputRedWeaponElementDTO;
|
||||
import com.solution.rule.domain.ultimately.dto.FireRuleTaskInputDTO;
|
||||
import com.solution.rule.domain.ultimately.dto.FireRuleTaskWeaponDTO;
|
||||
import com.solution.rule.domain.ultimately.dto.FireRuleWeaponComponentDTO;
|
||||
import com.solution.rule.domain.ultimately.fact.DroolsFact;
|
||||
import com.solution.rule.domain.ultimately.vo.FireRuleOutputVO;
|
||||
import com.solution.rule.domain.ultimately.vo.FireRuleRedWeaponEquipmentVO;
|
||||
import com.solution.rule.domain.ultimately.vo.FireRuleTaskInputVO;
|
||||
import com.solution.rule.domain.ultimately.vo.FireRuleTaskWeaponVO;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 规则函数方法
|
||||
* Drools 规则调用的装备匹配逻辑。所有业务词均来自 globalParams(由 rule.drl 的 buildParam 注入),本类不写死中文业务词。
|
||||
* <p>
|
||||
* <b>总分公式(每件候选红装)</b>
|
||||
* <pre>
|
||||
* score(red) = scoreRuleSlots(...) + scoreLegacyLayer(...)
|
||||
*
|
||||
* scoreRuleSlots = Σ(i=1..ruleSlotCount) [ containsAny(blueBlob, blueRuleKeywords_i)
|
||||
* ∧ containsAny(redBlob, redRuleKeywords_i)
|
||||
* ? ruleScore_i * weight : 0 ]
|
||||
*
|
||||
* scoreLegacyLayer = ①空中平台 + ②空中任务 + ③地面任务 + ④坦克 + ⑤导弹 五段条件之和,每段若满足均为「对应 *Score * weight」
|
||||
* (五段含义见 scoreLegacyLayer 方法注释与 rule.drl 中各键说明)。
|
||||
* </pre>
|
||||
* <p>
|
||||
* <b>文本串定义</b>
|
||||
* <ul>
|
||||
* <li>blueBlob:{@link #buildBlueTextBlob} — 蓝方任务 drawName、dataType、taskWeapons 及组件名拼接成一大串,用于关键词包含判断。</li>
|
||||
* <li>redBlob:{@link #buildRedTextBlob} — 红装 name、platformType、supportType 拼接。</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* <b>选优与输出</b>
|
||||
* <ul>
|
||||
* <li>在 redWeapons 池中对每件算 score,取最大值;若并列,{@link #compareRedForTieBreak}(由 tieBreak 参数控制,默认 equipmentId 小者优先)。</li>
|
||||
* <li>若 maxScore < minSelectedScore:不追加 fireRuleInputs;仍将当前池映射到 FireRuleOutputVO.redWeapons。</li>
|
||||
* <li>否则:池中 remove 选中项;fireRuleInputs 追加一行,drawName += outputDrawNameSuffix,taskWeapons 一条映射自选中红装;redWeapons 为剩余池。</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class RuleFunction {
|
||||
public final class RuleFunction {
|
||||
|
||||
private RuleFunction() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 装备匹配
|
||||
* 单轮规则执行:对当前 DroolsFact 的一条蓝方任务,从 redWeapons 中选一件装备并写回输出。
|
||||
* <p>
|
||||
* 参数 p(globalParams)中各键含义与运算见 {@link RuleFunction} 类注释及 resources/rules/rule.drl 内 buildParam 行内注释。
|
||||
*/
|
||||
public static void equipmentRule(DroolsFact task, Map globalParams){
|
||||
//空中总分数
|
||||
Integer airScore = 0;
|
||||
//反坦克总分数
|
||||
Integer antitankScore = 0;
|
||||
//远程打击总分数
|
||||
Integer remoteScore = 0;
|
||||
@SuppressWarnings("rawtypes")
|
||||
public static void equipmentRule(DroolsFact fact, Map globalParams) {
|
||||
if (fact == null || fact.getTask() == null) {
|
||||
return;
|
||||
}
|
||||
Map<String, Object> p = castParams(globalParams);
|
||||
FireRuleTaskInputDTO blue = fact.getTask();
|
||||
List<FireRuleInputRedWeaponElementDTO> pool = fact.getRedWeapons();
|
||||
if (pool == null) {
|
||||
pool = new ArrayList<>();
|
||||
fact.setRedWeapons(pool);
|
||||
}
|
||||
|
||||
FireRuleTaskInputDTO blueTask = task.getTask();
|
||||
// weight、minSelectedScore:见 rule.drl 注释
|
||||
String blueBlob = buildBlueTextBlob(blue);
|
||||
int weight = readInt(p, "weight", 1);
|
||||
int minScore = readInt(p, "minSelectedScore", 1);
|
||||
|
||||
//权重因子
|
||||
Integer weight = (Integer) globalParams.get("weight");
|
||||
String drawName = blueTask.getDrawName();
|
||||
if("".contains(drawName)){
|
||||
int bestIndex = -1;
|
||||
int bestScore = Integer.MIN_VALUE;
|
||||
|
||||
for (int i = 0; i < pool.size(); i++) {
|
||||
FireRuleInputRedWeaponElementDTO red = pool.get(i);
|
||||
String redBlob = buildRedTextBlob(red);
|
||||
int score = scoreRuleSlots(blueBlob, redBlob, p, weight)
|
||||
+ scoreLegacyLayer(blueBlob, redBlob, p, weight);
|
||||
if (score > bestScore) {
|
||||
bestScore = score;
|
||||
bestIndex = i;
|
||||
} else if (score == bestScore && bestIndex >= 0) {
|
||||
// 并列:compareRedForTieBreak(a,b)>0 表示当前 best 的 equipmentId 比候选 red 大,应换成更小的 id
|
||||
if (compareRedForTieBreak(pool.get(bestIndex), red, p) > 0) {
|
||||
bestIndex = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FireRuleOutputVO out = fact.getFireRuleOutputVO();
|
||||
if (out == null) {
|
||||
out = new FireRuleOutputVO();
|
||||
fact.setFireRuleOutputVO(out);
|
||||
}
|
||||
|
||||
// 未达门槛或池空:不写 fireRuleInputs,仅同步「剩余池」到输出 redWeapons
|
||||
if (bestIndex < 0 || pool.isEmpty() || bestScore < minScore) {
|
||||
out.setRedWeapons(convertPoolToEquipmentVoList(pool));
|
||||
// Tasks 由最终输出 redWeapons 一一生成(一个装备 -> 一个任务)
|
||||
out.setTasks(FireRuleRedWeaponOutputFillHelper.toTaskNodes(out.getRedWeapons()));
|
||||
return;
|
||||
}
|
||||
|
||||
FireRuleInputRedWeaponElementDTO chosen = pool.remove(bestIndex);
|
||||
|
||||
out.setRedWeapons(convertPoolToEquipmentVoList(pool));
|
||||
// Tasks 由最终输出 redWeapons 一一生成(一个装备 -> 一个任务)
|
||||
out.setTasks(FireRuleRedWeaponOutputFillHelper.toTaskNodes(out.getRedWeapons()));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Map<String, Object> castParams(Map raw) {
|
||||
return raw == null ? new java.util.HashMap<>() : (Map<String, Object>) raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* 蓝方侧用于「关键词包含」判断的合并文本(空格分隔各字段)。
|
||||
*/
|
||||
private static String buildBlueTextBlob(FireRuleTaskInputDTO task) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
append(sb, task.getDrawName());
|
||||
append(sb, task.getDataType());
|
||||
if (task.getTaskWeapons() != null) {
|
||||
for (FireRuleTaskWeaponDTO w : task.getTaskWeapons()) {
|
||||
if (w == null) {
|
||||
continue;
|
||||
}
|
||||
append(sb, w.getName());
|
||||
append(sb, w.getSupportType());
|
||||
append(sb, w.getEquipmentId());
|
||||
if (w.getComponents() != null) {
|
||||
for (FireRuleWeaponComponentDTO c : w.getComponents()) {
|
||||
if (c == null) {
|
||||
continue;
|
||||
}
|
||||
append(sb, c.getDeviceName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 红方侧用于「关键词包含」判断的合并文本。
|
||||
*/
|
||||
private static String buildRedTextBlob(FireRuleInputRedWeaponElementDTO red) {
|
||||
if (red == null) {
|
||||
return "";
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
append(sb, red.getName());
|
||||
append(sb, red.getPlatformType());
|
||||
append(sb, red.getSupportType());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static void append(StringBuilder sb, String s) {
|
||||
if (s != null && !s.isEmpty()) {
|
||||
sb.append(s).append(' ');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 规则槽得分:对 i=1..ruleSlotCount,若 blueBlob 命中 blueRuleKeywords_i 且 redBlob 命中 redRuleKeywords_i,
|
||||
* 则累加 ruleScore_i * weight。关键词为英文逗号分隔,任一词作为子串出现在文本中即命中。
|
||||
*/
|
||||
private static int scoreRuleSlots(String blueBlob, String redBlob, Map<String, Object> p, int weight) {
|
||||
int n = readInt(p, "ruleSlotCount", 0);
|
||||
int sum = 0;
|
||||
for (int i = 1; i <= n; i++) {
|
||||
String bk = str(p, "blueRuleKeywords_" + i, "");
|
||||
String rk = str(p, "redRuleKeywords_" + i, "");
|
||||
if (bk.isEmpty() || rk.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (containsAny(blueBlob, bk) && containsAny(redBlob, rk)) {
|
||||
sum += readInt(p, "ruleScore_" + i, 0) * weight;
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* 兼容层得分:五段独立条件,可叠加。每段均为「蓝关键词命中 ∧ 红关键词命中 → 加 对应分数 * weight」。
|
||||
* <ul>
|
||||
* <li>① bluePlatformKeywords_air + redPreferredWhenBlueAir → airScore</li>
|
||||
* <li>② airTaskKeywords + redPreferredWhenBlueAir → airTaskScore</li>
|
||||
* <li>③ groundTaskKeywords + redPreferredWhenGround → groundScore</li>
|
||||
* <li>④ tankKeywords + redMatchKeywords_tank → tankScore</li>
|
||||
* <li>⑤ missileKeywords + redMatchKeywords_missile → missileScore</li>
|
||||
* </ul>
|
||||
* 键名与 rule.drl 中 param.put 一致。
|
||||
*/
|
||||
private static int scoreLegacyLayer(String blueBlob, String redBlob, Map<String, Object> p, int weight) {
|
||||
int s = 0;
|
||||
if (containsAny(blueBlob, str(p, "bluePlatformKeywords_air", ""))
|
||||
&& containsAny(redBlob, str(p, "redPreferredWhenBlueAir", ""))) {
|
||||
s += readInt(p, "airScore", 0) * weight;
|
||||
}
|
||||
if (containsAny(blueBlob, str(p, "airTaskKeywords", ""))
|
||||
&& containsAny(redBlob, str(p, "redPreferredWhenBlueAir", ""))) {
|
||||
s += readInt(p, "airTaskScore", 0) * weight;
|
||||
}
|
||||
if (containsAny(blueBlob, str(p, "groundTaskKeywords", ""))
|
||||
&& containsAny(redBlob, str(p, "redPreferredWhenGround", ""))) {
|
||||
s += readInt(p, "groundScore", 0) * weight;
|
||||
}
|
||||
if (containsAny(blueBlob, str(p, "tankKeywords", ""))
|
||||
&& containsAny(redBlob, str(p, "redMatchKeywords_tank", ""))) {
|
||||
s += readInt(p, "tankScore", 0) * weight;
|
||||
}
|
||||
if (containsAny(blueBlob, str(p, "missileKeywords", ""))
|
||||
&& containsAny(redBlob, str(p, "redMatchKeywords_missile", ""))) {
|
||||
s += readInt(p, "missileScore", 0) * weight;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* 并列时比较两件红装:tieBreak=equipmentId 时返回 id 字典序比较结果(>0 表示 a 的 id 大于 b,应选 b)。
|
||||
*/
|
||||
private static int compareRedForTieBreak(
|
||||
FireRuleInputRedWeaponElementDTO a,
|
||||
FireRuleInputRedWeaponElementDTO b,
|
||||
Map<String, Object> p) {
|
||||
if (a == null) {
|
||||
return b == null ? 0 : 1;
|
||||
}
|
||||
if (b == null) {
|
||||
return -1;
|
||||
}
|
||||
String mode = str(p, "tieBreak", "equipmentId");
|
||||
if ("equipmentId".equals(mode)) {
|
||||
String ida = nz(a.getEquipmentId());
|
||||
String idb = nz(b.getEquipmentId());
|
||||
return ida.compareTo(idb);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* commaKeywords:英文逗号分隔;若 text 包含其中任一词(trim 后非空)则命中。
|
||||
*/
|
||||
private static boolean containsAny(String text, String commaKeywords) {
|
||||
if (text == null || text.isEmpty() || commaKeywords == null || commaKeywords.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
for (String part : commaKeywords.split(",")) {
|
||||
String k = part.trim();
|
||||
if (!k.isEmpty() && text.contains(k)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static String str(Map<String, Object> p, String key, String def) {
|
||||
Object v = p.get(key);
|
||||
return v == null ? def : String.valueOf(v);
|
||||
}
|
||||
|
||||
private static int readInt(Map<String, Object> p, String key, int def) {
|
||||
Object v = p.get(key);
|
||||
if (v == null) {
|
||||
return def;
|
||||
}
|
||||
if (v instanceof Number) {
|
||||
return ((Number) v).intValue();
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(String.valueOf(v).trim());
|
||||
} catch (NumberFormatException e) {
|
||||
return def;
|
||||
}
|
||||
}
|
||||
|
||||
private static String nz(String s) {
|
||||
return s == null ? "" : s;
|
||||
}
|
||||
|
||||
private static FireRuleTaskWeaponVO toTaskWeaponVo(FireRuleInputRedWeaponElementDTO r) {
|
||||
FireRuleTaskWeaponVO w = new FireRuleTaskWeaponVO();
|
||||
if (r != null) {
|
||||
w.setEquipmentId(r.getEquipmentId());
|
||||
w.setName(r.getName());
|
||||
w.setSupportType(r.getSupportType());
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
private static List<FireRuleRedWeaponEquipmentVO> convertPoolToEquipmentVoList(
|
||||
List<FireRuleInputRedWeaponElementDTO> pool) {
|
||||
List<FireRuleRedWeaponEquipmentVO> list = new ArrayList<>();
|
||||
if (pool == null) {
|
||||
return list;
|
||||
}
|
||||
for (FireRuleInputRedWeaponElementDTO e : pool) {
|
||||
list.add(toRedEquipmentVo(e));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private static FireRuleRedWeaponEquipmentVO toRedEquipmentVo(FireRuleInputRedWeaponElementDTO src) {
|
||||
if (src == null) {
|
||||
return null;
|
||||
}
|
||||
FireRuleRedWeaponEquipmentVO vo = new FireRuleRedWeaponEquipmentVO();
|
||||
vo.setSupportType(src.getSupportType());
|
||||
vo.setTroopsDetail(src.getTroopsDetail());
|
||||
vo.setPlatformType(src.getPlatformType());
|
||||
vo.setIsStrikeTarget(src.getIsStrikeTarget());
|
||||
vo.setIsReconTarget(src.getIsReconTarget());
|
||||
vo.setIsInterferenceTarget(src.getIsInterferenceTarget());
|
||||
vo.setIsDefendImportantPlace(src.getIsDefendImportantPlace());
|
||||
vo.setGroupType(src.getGroupType());
|
||||
vo.setEquipmentId(src.getEquipmentId());
|
||||
vo.setName(src.getName());
|
||||
vo.setOwnerForceSide(src.getOwnerForceSide());
|
||||
vo.setPlatId(src.getPlatId());
|
||||
vo.setSubComponents(FireRuleRedWeaponOutputFillHelper.toOutputSubComponents(src.getSubComponents()));
|
||||
return vo;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user