RecommendMultiGroup: 多编队火力分配 + 专一弹药优先 + 130测试通过
This commit is contained in:
parent
12cb6576fe
commit
6030b8de07
@ -33,8 +33,52 @@ namespace CounterDrone.Core.Algorithms
|
|||||||
public float InterceptProbability { get; set; }
|
public float InterceptProbability { get; set; }
|
||||||
public string SummaryRationale { get; set; } = string.Empty;
|
public string SummaryRationale { get; set; } = string.Empty;
|
||||||
|
|
||||||
/// <summary>由推荐方案生成的发射计划,仿真器直接执行</summary>
|
|
||||||
public List<FireEvent> FireSchedule { get; set; } = new();
|
public List<FireEvent> FireSchedule { get; set; } = new();
|
||||||
|
|
||||||
|
/// <summary>此方案占用的平台起始索引(多编队合并用)</summary>
|
||||||
|
public int PlatformOffset { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>多编队推荐结果</summary>
|
||||||
|
public class MultiGroupRecommendation
|
||||||
|
{
|
||||||
|
/// <summary>每组的独立方案</summary>
|
||||||
|
public List<DefenseSolution> GroupSolutions { get; set; } = new();
|
||||||
|
/// <summary>合并后的总发射计划</summary>
|
||||||
|
public List<FireEvent> MergedFireSchedule { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>火力单元(装备编组)</summary>
|
||||||
|
public class FireUnit
|
||||||
|
{
|
||||||
|
public string GroupId { get; set; } = string.Empty;
|
||||||
|
public AerosolType LoadedAmmo { get; set; }
|
||||||
|
public int PlatformCount { get; set; }
|
||||||
|
public float PositionX, PositionY, PositionZ;
|
||||||
|
public float MuzzleVelocity { get; set; } = 800f;
|
||||||
|
public float Cooldown { get; set; } = 5f;
|
||||||
|
public float AmmoChangeTime { get; set; } = 300f;
|
||||||
|
// 弹药库:可以装填的弹药类型
|
||||||
|
public List<AerosolType> AvailableAmmo { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>无人机编队(多编队推荐输入)</summary>
|
||||||
|
public class DroneGroup
|
||||||
|
{
|
||||||
|
public string GroupId { get; set; } = string.Empty;
|
||||||
|
public TargetConfig Target { get; set; } = new();
|
||||||
|
public RoutePlan Route { get; set; } = new();
|
||||||
|
public List<Waypoint> Waypoints { get; set; } = new();
|
||||||
|
|
||||||
|
/// <summary>预计到达航路中点的时间(秒)</summary>
|
||||||
|
public float GetArrivalTime()
|
||||||
|
{
|
||||||
|
if (Waypoints.Count < 2) return 0;
|
||||||
|
var start = new Vector3((float)Waypoints[0].PosX, (float)Waypoints[0].PosY, (float)Waypoints[0].PosZ);
|
||||||
|
var end = new Vector3((float)Waypoints[^1].PosX, (float)Waypoints[^1].PosY, (float)Waypoints[^1].PosZ);
|
||||||
|
var speed = (float)Target.TypicalSpeed / 3.6f;
|
||||||
|
return start.DistanceTo(end) / speed / 2f;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class RecommendedPlatform
|
public class RecommendedPlatform
|
||||||
|
|||||||
@ -219,5 +219,62 @@ namespace CounterDrone.Core.Algorithms
|
|||||||
var sigma = (sY + sZ) / 2f;
|
var sigma = (sY + sZ) / 2f;
|
||||||
return rPhase2 + sigma * (float)Math.Sqrt(2f * Math.Log(peakC / threshold));
|
return rPhase2 + sigma * (float)Math.Sqrt(2f * Math.Log(peakC / threshold));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>多编队推荐 — 每组独立方案,按时间分配火力单元,合并发射计划</summary>
|
||||||
|
public MultiGroupRecommendation RecommendMultiGroup(
|
||||||
|
List<DroneGroup> droneGroups, List<FireUnit> fireUnits, CombatScene environment)
|
||||||
|
{
|
||||||
|
var result = new MultiGroupRecommendation();
|
||||||
|
if (droneGroups.Count == 0 || fireUnits.Count == 0) return result;
|
||||||
|
|
||||||
|
var sorted = droneGroups.OrderBy(g => g.GetArrivalTime()).ToList();
|
||||||
|
var usedUntil = new Dictionary<string, float>();
|
||||||
|
int offset = 0;
|
||||||
|
|
||||||
|
foreach (var group in sorted)
|
||||||
|
{
|
||||||
|
var powerType = (PowerType)group.Target.PowerType;
|
||||||
|
var neededAmmo = MatchTable.GetValueOrDefault(powerType, AerosolType.InertGas);
|
||||||
|
|
||||||
|
FireUnit assigned = null;
|
||||||
|
// 优先匹配专一弹药的火力单元,避免多用途单元被抢占
|
||||||
|
var candidates = fireUnits
|
||||||
|
.Where(u => u.AvailableAmmo.Contains(neededAmmo))
|
||||||
|
.OrderBy(u => u.AvailableAmmo.Count) // 少弹种的优先
|
||||||
|
.ThenBy(u => usedUntil.ContainsKey(u.GroupId) ? 1 : 0); // 不忙的优先
|
||||||
|
|
||||||
|
foreach (var unit in candidates)
|
||||||
|
{
|
||||||
|
if (!unit.AvailableAmmo.Contains(neededAmmo)) continue;
|
||||||
|
if (!usedUntil.TryGetValue(unit.GroupId, out var busyUntil))
|
||||||
|
{ assigned = unit; break; }
|
||||||
|
if (unit.LoadedAmmo == neededAmmo && busyUntil <= group.GetArrivalTime())
|
||||||
|
{ assigned = unit; break; }
|
||||||
|
if (busyUntil + unit.AmmoChangeTime <= group.GetArrivalTime())
|
||||||
|
{ assigned = unit; break; }
|
||||||
|
}
|
||||||
|
if (assigned == null) continue;
|
||||||
|
|
||||||
|
var threat = new ThreatProfile
|
||||||
|
{
|
||||||
|
Environment = environment,
|
||||||
|
Targets = new List<TargetConfig> { group.Target },
|
||||||
|
Route = group.Route,
|
||||||
|
Waypoints = group.Waypoints,
|
||||||
|
};
|
||||||
|
var sol = Recommend(threat).Best;
|
||||||
|
foreach (var fe in sol.FireSchedule) fe.PlatformIndex += offset;
|
||||||
|
|
||||||
|
result.GroupSolutions.Add(sol);
|
||||||
|
result.MergedFireSchedule.AddRange(sol.FireSchedule);
|
||||||
|
|
||||||
|
assigned.LoadedAmmo = sol.RecommendedAerosolType;
|
||||||
|
usedUntil[assigned.GroupId] = (sol.FireSchedule.Count > 0
|
||||||
|
? sol.FireSchedule.Max(f => f.FireTime) : group.GetArrivalTime()) + assigned.Cooldown;
|
||||||
|
offset += sol.Platforms.Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
using CounterDrone.Core.Models;
|
using CounterDrone.Core.Models;
|
||||||
|
|
||||||
namespace CounterDrone.Core.Algorithms
|
namespace CounterDrone.Core.Algorithms
|
||||||
@ -6,5 +7,11 @@ namespace CounterDrone.Core.Algorithms
|
|||||||
public interface IDefenseAdvisor
|
public interface IDefenseAdvisor
|
||||||
{
|
{
|
||||||
DefenseRecommendation Recommend(ThreatProfile threat);
|
DefenseRecommendation Recommend(ThreatProfile threat);
|
||||||
|
|
||||||
|
/// <summary>多编队推荐 — 为每组分配火力单元,合并发射计划</summary>
|
||||||
|
MultiGroupRecommendation RecommendMultiGroup(
|
||||||
|
List<DroneGroup> droneGroups,
|
||||||
|
List<FireUnit> fireUnits,
|
||||||
|
CombatScene environment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
47
test/unit/CounterDrone.Core.Tests/MultiGroupAdvisorTests.cs
Normal file
47
test/unit/CounterDrone.Core.Tests/MultiGroupAdvisorTests.cs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using CounterDrone.Core.Algorithms;
|
||||||
|
using CounterDrone.Core.Models;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace CounterDrone.Core.Tests
|
||||||
|
{
|
||||||
|
public class MultiGroupAdvisorTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void TwoSeparateUnits_TwoGroups_BothGetSolutions()
|
||||||
|
{
|
||||||
|
var ammoCatalog = DefaultAmmunition.GetAll();
|
||||||
|
Console.WriteLine($"Ammo: inert={ammoCatalog[0].BurstChargeKg}kg, active={ammoCatalog[1].BurstChargeKg}kg");
|
||||||
|
|
||||||
|
var advisor = new DefaultDefenseAdvisor(ammoCatalog);
|
||||||
|
|
||||||
|
var g1 = new DroneGroup
|
||||||
|
{
|
||||||
|
GroupId = "g1", Target = new TargetConfig { PowerType = (int)PowerType.Piston, Quantity = 1, TypicalSpeed = 200, TypicalAltitude = 500 },
|
||||||
|
Route = new RoutePlan { FormationMode = (int)FormationMode.Single },
|
||||||
|
Waypoints = new List<Waypoint> { new() { PosX = 0, PosY = 500, PosZ = 0 }, new() { PosX = 20000, PosY = 500, PosZ = 0 } },
|
||||||
|
};
|
||||||
|
var g2 = new DroneGroup
|
||||||
|
{
|
||||||
|
GroupId = "g2", Target = new TargetConfig { PowerType = (int)PowerType.Jet, Quantity = 1, TypicalSpeed = 500, TypicalAltitude = 800 },
|
||||||
|
Route = new RoutePlan { FormationMode = (int)FormationMode.Single },
|
||||||
|
Waypoints = new List<Waypoint> { new() { PosX = 0, PosY = 800, PosZ = 0 }, new() { PosX = 30000, PosY = 800, PosZ = 0 } },
|
||||||
|
};
|
||||||
|
|
||||||
|
var u1 = new FireUnit { GroupId = "u1", AvailableAmmo = new() { AerosolType.InertGas, AerosolType.ActiveMaterial }, PlatformCount = 5, MuzzleVelocity = 800 };
|
||||||
|
var u2 = new FireUnit { GroupId = "u2", AvailableAmmo = new() { AerosolType.ActiveMaterial }, PlatformCount = 4, MuzzleVelocity = 800 };
|
||||||
|
|
||||||
|
var rec = advisor.RecommendMultiGroup(
|
||||||
|
new List<DroneGroup> { g1, g2 },
|
||||||
|
new List<FireUnit> { u1, u2 },
|
||||||
|
new CombatScene { WindSpeed = 3 });
|
||||||
|
|
||||||
|
var msg = $"Solutions={rec.GroupSolutions.Count}, Merged={rec.MergedFireSchedule.Count}";
|
||||||
|
foreach (var s in rec.GroupSolutions)
|
||||||
|
msg += $" | {s.RecommendedAerosolType}: plat={s.Platforms.Count} fire={s.FireSchedule.Count}";
|
||||||
|
Assert.True(rec.GroupSolutions.Count == 2, msg);
|
||||||
|
Assert.NotEmpty(rec.MergedFireSchedule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user