Files
auto-solution/auto-solution-rule/src/main/resources/rules/rule.drl
2026-04-15 15:30:20 +08:00

354 lines
19 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package rules;
import com.solution.rule.domain.ultimately.fact.DroolsFact;
import java.util.Map;
import static com.solution.rule.utils.RuleFunction.equipmentRule;
import static com.solution.rule.utils.RuleFunction.target;
import static com.solution.rule.utils.RuleFunction.position;
import static com.solution.rule.utils.RuleFunction.trackRoute;
import static com.solution.rule.utils.RuleFunction.groupFormation;
global java.util.Map globalParams;
/**
* 构建装备匹配所需的全部可调参数(会 merge 进 globalParams覆盖 Java 侧同名默认值)。
*
* ========== 总体运算逻辑(与 RuleFunction.equipmentRule 一致)==========
* 1先拼「蓝方文本串」blueBlob任务 drawName、dataType、taskWeapons 下各武器的 name/supportType/equipmentId、组件 deviceName。
* 2对每个红方装备拼「红方文本串」redBlobname、platform_type、SupportType。
* 3每件红装得分 score = 规则槽得分(scoreRuleSlots) + 兼容层得分(scoreLegacyLayer),均为整数。
* - 规则槽:对 i=1..ruleSlotCount若 blueBlob 命中 blueRuleKeywords_i 且 redBlob 命中 redRuleKeywords_i
* 则加上 ruleScore_i * weight。
* - 兼容层:多组「蓝关键词 + 红关键词 + 对应分数」,见下方各键说明;每组条件同时满足则加上 对应Score * weight。
* 4在池中取 score 最大者;若多人并列,由 tieBreak 决定(见 tieBreak
* 5若 maxScore < minSelectedScore视为未匹配不往 fireRuleInputs 追加行redWeapons 输出仍为当前池。
* 6若匹配成功从池中 remove 该件fireRuleInputs 追加一行drawName 后接 outputDrawNameSuffixtaskWeapons 填选中红装映射。
*
* 关键词格式英文逗号分隔子串包含即算命中contains不要求整词匹配。
*/
function Map buildParam(){
Map param = new java.util.HashMap();
// ---------- 全局倍率与门槛 ----------
// weight对上述所有「基础分数」的统一乘数规则槽的 ruleScore_i、兼容层的 *Score 都会乘 weight
param.put("weight", 1);
// minSelectedScore单件红装总分达到该值及以上才会被选中并写入 fireRuleInputs否则本任务视为未匹配到装备。
param.put("minSelectedScore", 1);
// tieBreak并列最高分时的决胜方式。当前实现仅支持 "equipmentId":装备 ID 字典序更小的优先。
param.put("tieBreak", "equipmentId");
// outputDrawNameSuffix匹配成功写入 fireRuleInputs 时,在蓝方原 drawName 后面拼接的后缀(如「打击任务」)。
param.put("outputDrawNameSuffix", "打击任务");
// ---------- 规则槽(可配置条数,便于只改本文件而不改 Java----------
// ruleSlotCount启用几条槽规则第 i 条使用 blueRuleKeywords_i、redRuleKeywords_i、ruleScore_i。
param.put("ruleSlotCount", 3);
// 槽1蓝方文本中出现任一子串 且 红方文本中出现任一子串 → 加 ruleScore_1 * weight。
param.put("blueRuleKeywords_1", "F-16,F-35");
param.put("redRuleKeywords_1", "防空,导弹,无人机");
param.put("ruleScore_1", 5);
param.put("blueRuleKeywords_2", "坦克,装甲");
param.put("redRuleKeywords_2", "反坦克");
param.put("ruleScore_2", 4);
param.put("blueRuleKeywords_3", "地面,突击");
param.put("redRuleKeywords_3", "远火,榴弹,炮");
param.put("ruleScore_3", 2);
// ---------- 兼容层:按「场景关键词」配对加分(与 RuleFunction.scoreLegacyLayer 一一对应)----------
// 以下每组均为:蓝方文本命中第一列关键词 且 红方文本命中第二列关键词 → 加 第三列分数 * weight。
//
// ① 空中平台类:蓝方像「对空/机型」且红方像对空装备 → + airScore * weight
param.put("bluePlatformKeywords_air", "F-16,J-10,F-35");
param.put("redPreferredWhenBlueAir", "防空,导弹,无人机,直升机,空空");
param.put("airScore", 2);
// ② 任务文案像空中任务 且 红方偏好对空 → + airTaskScore * weight可与①叠加
param.put("airTaskKeywords", "空中,制空,拦截,空战");
param.put("airTaskScore", 10);
// ③ 任务文案像地面任务 且 红方偏好地面火力 → + groundScore * weight
param.put("groundTaskKeywords", "地面,突击,登陆");
param.put("redPreferredWhenGround", "远火,榴弹,炮,火箭");
param.put("groundScore", 1);
// ④ 蓝方像坦克/装甲目标 且 红方文本命中 redMatchKeywords_tank → + tankScore * weight
param.put("tankKeywords", "坦克,装甲");
param.put("redMatchKeywords_tank", "反坦克");
param.put("tankScore", 1);
// ⑤ 蓝方像导弹类 且 红方文本命中 redMatchKeywords_missile → + missileScore * weight
param.put("missileKeywords", "导弹,火箭弹,巡航");
param.put("redMatchKeywords_missile", "防空,导弹,导弹发射");
param.put("missileScore", 1);
// ===================== 目标分配参数(写入 Tasks.task.execute =====================
// executeTypeDefault生成 execute[0] 的类型字段 取值strike_test/assault
param.put("executeTypeDefault", "assault");
// targetPickModeroundRobin(稳定轮询) / random(伪随机但同输入稳定)
param.put("targetPickMode", "roundRobin");
// minTargetsPerRed / maxTargetsPerRedCap每个红方任务最少/最多分配的目标数
param.put("minTargetsPerRed", 1);
param.put("maxTargetsPerRedCap", 3);
// radToTargetsCsvsuccessTargetRad(命中率) -> 每红装目标数 的映射(阈值:目标数),按阈值从大到小匹配
// 例0.8:1,0.5:2,0.2:3 表示 successTargetRad>=0.8 分1个>=0.5 分2个>=0.2 分3个
param.put("radToTargetsCsv", "0.8:1,0.5:2,0.2:3");
// rangeParseRegex从 attDefaultValue/attExplain 中提取射程数值的正则取第1个数字
param.put("rangeParseRegex", "(\\\\d+(?:\\\\.\\\\d+)?)");
// rangeUnit提取数值的单位km/m二选一
param.put("rangeUnit", "km");
// minRangeToAllowAssignKm若解析到的蓝方射程小于该值则该蓝方装备不参与被分配无法解析则忽略此过滤
param.put("minRangeToAllowAssignKm", 0);
// ===================== 低命中率补拿装备参数 =====================
// redHitRateThreshold红方装备命中率阈值低于该值时触发补拿
param.put("redHitRateThreshold", 0.6);
// maxExtraWeaponsPerTask每条蓝方任务最多补拿几件红装
param.put("maxExtraWeaponsPerTask", 2);
// maxSupplementRounds补拿循环最大轮次防死循环
param.put("maxSupplementRounds", 2);
// extraPickMinScore补拿时红装最低匹配分
param.put("extraPickMinScore", 1);
// ===================== 阵位规则参数(写入 SubComponents.platform[].positions =====================
// positionRuleEnabled是否启用阵位规则。true=执行阵位生成false=跳过,不改 platform.positions。
param.put("positionRuleEnabled", true);
// positionAnchorMode锚点模式。当前实现使用 hybrid蓝方 taskWeapons.coordinate 的中心点作为主锚点)。
param.put("positionAnchorMode", "hybrid");
// trackPointDirectionMode航向计算模式。
// - head2next取 trackPoints[0] -> trackPoints[1] 作为方向(默认)
// - tail2prev取倒数第二个 -> 最后一个点作为方向
param.put("trackPointDirectionMode", "head2next");
// fallbackBearingDeg当 trackPoints 缺失或无法计算方位时使用该默认方位角0-360
param.put("fallbackBearingDeg", 0);
// deployDistanceKmMin部署距离下限km。最终距离不会小于该值。
param.put("deployDistanceKmMin", 8);
// deployDistanceKmMax部署距离上限km。最终距离不会大于该值。
param.put("deployDistanceKmMax", 30);
// deployDistanceKmDefault默认部署距离km
// 当 distanceByPlatformCsv 未命中任何关键词时,使用该值。
param.put("deployDistanceKmDefault", 15);
// distanceByPlatformCsv按“关键词”覆盖部署距离km不写死具体类型完全由业务配置。
// 格式:关键词:距离,关键词:距离(示例:防空:18,反坦克:10,迫击炮:8
// 匹配范围:红方装备 Name / Platform_type 文本包含关键词即命中。
// 优先级:命中后覆盖 deployDistanceKmDefault但最终仍受 deployDistanceKmMin 与 deployDistanceKmMax 约束。
param.put("distanceByPlatformCsv", "");
// formationType编队样式可选 line / wedge / circle。
param.put("formationType", "line");
// formationSpacingMeters编队间距影响同一红装下 platform[] 点位离散程度。
// 说明Java 侧会与 minInterPlatformDistanceMeters 比较,取更大值,避免平台重叠过近。
param.put("formationSpacingMeters", 300);
// formationHeadingOffsetDeg编队相对主航向的偏转角主要用于 wedge/circle 的分散方向。
param.put("formationHeadingOffsetDeg", 15);
// defaultDeployHeight默认部署高度用于 positions 第3位高度值基线。
param.put("defaultDeployHeight", 30);
// heightFollowBlueRatio高度跟随蓝方比例>=0
// 计算方式:高度 = defaultDeployHeight + 蓝方锚点平均高度 * heightFollowBlueRatio。
// 0 表示不跟随蓝方高度,仅使用默认高度。
param.put("heightFollowBlueRatio", 0.0);
// enableWarZoneClamp是否启用作战区约束。true=超出 warZoneLocation 时回拉到区内。
param.put("enableWarZoneClamp", true);
// warZoneClampMode作战区约束模式。当前实现使用 nearestInside沿锚点到目标点方向二分回拉到区内
param.put("warZoneClampMode", "nearestInside");
// minInterPlatformDistanceMeters平台最小间距下限用于抑制平台点位过度重叠。
param.put("minInterPlatformDistanceMeters", 80);
// ===================== 航迹规则参数(写入 TrackParam 动态 key + execute.targetList.moveRouteId =====================
// trackRuleEnabled是否启用航迹生成与 moveRouteId 绑定。
param.put("trackRuleEnabled", true);
// trackRouteAlgorithm航迹变形算法。followBlue(默认) / shortestPath / flank / jam
param.put("trackRouteAlgorithm", "followBlue");
// trackRouteNameSuffix航迹名称 = 红方任务 drawName + 此后缀(默认「航迹」→ 如 xxx打击任务航迹
param.put("trackRouteNameSuffix", "航迹");
// trackAirDataTypeCsv蓝方 dataType 命中任一子串(忽略大小写)则 TrackType=routeLineAir
param.put("trackAirDataTypeCsv", "taskPlane,air,plane,flight");
// trackAirKeywordsCsv蓝方 drawName 或红方 name/platformType 命中任一子串则视为飞行航迹
param.put("trackAirKeywordsCsv", "机,飞,空,J-,F-,无人机,直升机");
// trackGroundTrackType非飞行类时 TrackType 取值(可先占位,后续再接地面路网)
param.put("trackGroundTrackType", "routeLineGround");
// 航迹侧向算法复用上方「阵位规则」中的 trackPointDirectionModehead2next / tail2prev缺省回退见 trackFallbackBearingDeg
param.put("trackFallbackBearingDeg", 0);
// enableTrackWarZoneClamp航迹点是否约束在 warZoneLocation 多边形内
param.put("enableTrackWarZoneClamp", true);
// trackExtraNodesMax在蓝方航迹点基础上最多额外插入的点数0=与蓝方点数持平;>0 时在中段均匀插值)
param.put("trackExtraNodesMax", 0);
// shortestPath相邻两点间直线插值分段数>=1越大折线越平滑非真实路网最短路径
param.put("trackShortPathSegments", 3);
// flank侧向偏移距离trackFlankSideModealternate / left / right
param.put("trackFlankOffsetMeters", 800);
param.put("trackFlankSideMode", "alternate");
// jam正弦扰动振幅、沿航迹起伏周期数越大摆动越密
param.put("trackJamWobbleMeters", 400);
param.put("trackJamSegments", 4);
// ===================== 编组规则参数(写入 TrackParam.Groups + wingmanData =====================
// groupRuleEnabled是否生成编组节点。
param.put("groupRuleEnabled", true);
// groupDrawNameSuffixdrawName = 领队装备 Name + 此后缀(默认「编组」)
param.put("groupDrawNameSuffix", "编组");
// groupDrawNameWithIndex是否在 drawName 末尾追加序号(避免多组同名),如 J15编组1
param.put("groupDrawNameWithIndex", false);
// groupFormationModeonePerRed(每件红装一组) / clusterByCount(按固定人数分组) / singleGroup(全部一组)
param.put("groupFormationMode", "onePerRed");
// groupClusterSizeclusterByCount 模式下每组人数上限(>=1
param.put("groupClusterSize", 3);
// groupLeaderPickModebyHitRateThenId(命中率高优先,平手 equipmentId 小) / byId(equipmentId 字典序最小)
param.put("groupLeaderPickMode", "byHitRateThenId");
// groupMinMembersForWingman组内人数达到该值才生成 wingmanData否则仅 leader 壳,僚机列表为空)
param.put("groupMinMembersForWingman", 2);
// wingman 几何参数(相对蓝方主航向 mainBearing 叠加)
param.put("wingmanDistanceBaseMeters", 100);
param.put("wingmanDistanceStepMeters", 50);
param.put("wingmanAngleBaseDeg", 50);
param.put("wingmanAngleStepDeg", 15);
param.put("wingmanAltBaseMeters", 40);
param.put("wingmanAltScale", 1.0);
// ===== 以下由规则配置表自动同步生成(请勿手改 param.put 段) =====
param.put("groupRuleEnabled", true);
param.put("groupDrawNameSuffix", "编组");
param.put("groupDrawNameWithIndex", false);
param.put("groupFormationMode", "onePerRed");
param.put("groupClusterSize", 3);
param.put("groupLeaderPickMode", "byHitRateThenId");
param.put("groupMinMembersForWingman", 2);
param.put("wingmanDistanceBaseMeters", 100);
param.put("wingmanDistanceStepMeters", 50);
param.put("wingmanAngleBaseDeg", 50);
param.put("wingmanAngleStepDeg", 15);
param.put("wingmanAltBaseMeters", 40);
param.put("wingmanAltScale", 1.0);
param.put("enableTrackWarZoneClamp", true);
param.put("trackRuleEnabled", true);
param.put("trackRouteAlgorithm", "followBlue");
param.put("trackRouteNameSuffix", "航迹");
param.put("trackAirDataTypeCsv", "taskPlane,air,plane,flight");
param.put("trackAirKeywordsCsv", "机,飞,空,J-,F-,无人机,直升机");
param.put("trackGroundTrackType", "routeLineGround");
param.put("trackFallbackBearingDeg", 0);
param.put("trackExtraNodesMax", 0);
param.put("trackShortPathSegments", 3);
param.put("trackFlankOffsetMeters", 800);
param.put("trackFlankSideMode", "alternate");
param.put("trackJamWobbleMeters", 400);
param.put("trackJamSegments", 4);
param.put("enableWarZoneClamp", true);
param.put("positionRuleEnabled", true);
param.put("positionAnchorMode", "hybrid");
param.put("trackPointDirectionMode", "head2next");
param.put("fallbackBearingDeg", 0);
param.put("deployDistanceKmMin", 8);
param.put("deployDistanceKmMax", 30);
param.put("deployDistanceKmDefault", 15);
param.put("distanceByPlatformCsv", "");
param.put("formationType", "line");
param.put("formationSpacingMeters", 300);
param.put("formationHeadingOffsetDeg", 15);
param.put("defaultDeployHeight", 30);
param.put("heightFollowBlueRatio", 0.0);
param.put("warZoneClampMode", "nearestInside");
param.put("minInterPlatformDistanceMeters", 80);
param.put("redHitRateThreshold", 0.6);
param.put("maxExtraWeaponsPerTask", 2);
param.put("maxSupplementRounds", 2);
param.put("extraPickMinScore", 1);
param.put("executeTypeDefault", "assault");
param.put("targetPickMode", "roundRobin");
param.put("minTargetsPerRed", 1);
param.put("maxTargetsPerRedCap", 3);
param.put("radToTargetsCsv", "0.8:1,0.5:2,0.2:3");
param.put("rangeParseRegex", "(\\\\d+(?:\\\\.\\\\d+)?)");
param.put("rangeUnit", "km");
param.put("minRangeToAllowAssignKm", 0);
param.put("weight", 1);
param.put("minSelectedScore", 1);
param.put("tieBreak", "equipmentId");
param.put("outputDrawNameSuffix", "打击任务");
param.put("ruleSlotCount", 3);
param.put("blueRuleKeywords_1", "F-16,F-35");
param.put("redRuleKeywords_1", "防空,导弹,无人机");
param.put("ruleScore_1", 5);
param.put("blueRuleKeywords_2", "坦克,装甲");
param.put("redRuleKeywords_2", "反坦克");
param.put("ruleScore_2", 4);
param.put("blueRuleKeywords_3", "地面,突击");
param.put("redRuleKeywords_3", "远火,榴弹,炮");
param.put("ruleScore_3", 2);
param.put("bluePlatformKeywords_air", "F-16,J-10,F-35");
param.put("redPreferredWhenBlueAir", "防空,导弹,无人机,直升机,空空");
param.put("airScore", 2);
param.put("airTaskKeywords", "空中,制空,拦截,空战");
param.put("airTaskScore", 10);
param.put("groundTaskKeywords", "地面,突击,登陆");
param.put("redPreferredWhenGround", "远火,榴弹,炮,火箭");
param.put("groundScore", 1);
param.put("tankKeywords", "坦克,装甲");
param.put("redMatchKeywords_tank", "反坦克");
param.put("tankScore", 1);
param.put("missileKeywords", "导弹,火箭弹,巡航");
param.put("redMatchKeywords_missile", "防空,导弹,导弹发射");
param.put("missileScore", 1);
return param;
}
function void mergeDefaultParams(Map current){
Map defaults = buildParam();
for (Object k : defaults.keySet()) {
if (!current.containsKey(k)) {
current.put(k, defaults.get(k));
}
}
}
rule "装备匹配"
salience 100
when
$fact : DroolsFact(task != null)
then
// 以本文件 buildParam 为真源覆盖同名键,再执行 Java 侧匹配逻辑
// globalParams.putAll(buildParam());
mergeDefaultParams(globalParams);
equipmentRule($fact, globalParams);
end
rule "目标匹配"
salience 90
when
$fact : DroolsFact(task != null)
then
// 显式目标分配规则:填充 Tasks.task.execute.targetList[*].targetId
// globalParams.putAll(buildParam());
mergeDefaultParams(globalParams);
target($fact, globalParams);
end
rule "阵位匹配"
salience 80
when
$fact : DroolsFact(task != null)
then
// 显式阵位规则:填充 redWeapons.SubComponents.platform[].positions
// globalParams.putAll(buildParam());
mergeDefaultParams(globalParams);
position($fact, globalParams);
end
rule "航迹匹配"
salience 70
when
$fact : DroolsFact(task != null)
then
// 显式航迹规则:填充 TrackParam 下各航迹 id并绑定 execute[0].targetList[*].moveRouteId
// globalParams.putAll(buildParam());
mergeDefaultParams(globalParams);
trackRoute($fact, globalParams);
end
rule "编组匹配"
salience 60
when
$fact : DroolsFact(task != null)
then
// 显式编组规则:填充 TrackParam.GroupsgroupType=addGroup与 wingmanData
// globalParams.putAll(buildParam());
mergeDefaultParams(globalParams);
groupFormation($fact, globalParams);
end