diff --git a/src/CounterDrone.Core/Algorithms/AlgorithmFactory.cs b/src/CounterDrone.Core/Algorithms/AlgorithmFactory.cs index ff8eb02..fb9c208 100644 --- a/src/CounterDrone.Core/Algorithms/AlgorithmFactory.cs +++ b/src/CounterDrone.Core/Algorithms/AlgorithmFactory.cs @@ -10,7 +10,6 @@ namespace CounterDrone.Core.Algorithms { [typeof(ICloudDispersionModel)] = () => new GaussianPuffDispersion(), [typeof(IDamageModel)] = () => new DamageModelRouter(), - [typeof(IDefensePlanner)] = () => new DefaultDefensePlanner(null), }; public static void Register(Func factory) diff --git a/src/CounterDrone.Core/Algorithms/DefaultDefensePlanner.cs b/src/CounterDrone.Core/Algorithms/DefaultDefensePlanner.cs index d6a2e61..5f42f63 100644 --- a/src/CounterDrone.Core/Algorithms/DefaultDefensePlanner.cs +++ b/src/CounterDrone.Core/Algorithms/DefaultDefensePlanner.cs @@ -12,7 +12,9 @@ namespace CounterDrone.Core.Algorithms public DefaultDefensePlanner(List ammoCatalog) { - _ammoCatalog = ammoCatalog ?? new List(); + _ammoCatalog = ammoCatalog ?? throw new ArgumentNullException(nameof(ammoCatalog)); + if (_ammoCatalog.Count == 0) + throw new ArgumentException("弹药规格目录不能为空"); } private static readonly Dictionary MatchTable = new() @@ -60,6 +62,17 @@ namespace CounterDrone.Core.Algorithms var sorted = threats.OrderByDescending(t => t.Priority).ToList(); // Step 2-4: 贪心分配求解 + // 预先检查:所有需要的弹药类型都在目录中 + var neededTypes = sorted + .Select(t => MatchAmmo((PowerType)t.Target.PowerType)) + .Distinct() + .ToList(); + foreach (var t in neededTypes) + { + if (!_ammoCatalog.Any(a => a.AerosolType == (int)t)) + throw new InvalidOperationException($"弹药规格目录中缺少类型: {t}"); + } + result.Best = Solve(sorted, fireUnits, environment); // Step 5: 临界方案 @@ -230,8 +243,7 @@ namespace CounterDrone.Core.Algorithms { var candidates = new List(); var neededAmmo = MatchAmmo((PowerType)threat.Target.PowerType); - var ammo = _ammoCatalog.FirstOrDefault(a => a.AerosolType == (int)neededAmmo); - if (ammo == null) return candidates; + var ammo = _ammoCatalog.First(a => a.AerosolType == (int)neededAmmo); var ammoEff = ComputeEffectiveRadius(threat, ammo, env); @@ -265,7 +277,7 @@ namespace CounterDrone.Core.Algorithms if (unit.MuzzleVelocity <= 0) return null; float maxRange = unit.MuzzleVelocity * unit.MuzzleVelocity / 9.81f; - if (dist > maxRange * 0.8f) return null; + if (dist > maxRange) return null; float shellTime = dist / unit.MuzzleVelocity; float interceptTime = threat.ArrivalTime; @@ -302,7 +314,7 @@ namespace CounterDrone.Core.Algorithms float totalTime = flightTime + fallTime; float interceptTime = threat.ArrivalTime; - if (totalTime > interceptTime * 1.5f) return null; + if (totalTime > interceptTime) return null; float avgSpeed = (float)threat.Target.TypicalSpeed / 3.6f; float neededExposure = ammoType == AerosolType.ActiveMaterial ? 2f : 6f; @@ -365,11 +377,9 @@ namespace CounterDrone.Core.Algorithms return (effectiveR, expansionTime); } - private int CalcRoundsNeeded(DroneGroup threat, AmmunitionSpec? ammo, + private int CalcRoundsNeeded(DroneGroup threat, AmmunitionSpec ammo, CombatScene env, bool isAirBased) { - if (ammo == null) return 1; - float avgSpeed = (float)threat.Target.TypicalSpeed / 3.6f; var (effectiveR, _) = ComputeEffectiveRadius(threat, ammo, env); float spacing = effectiveR * 1.5f; @@ -383,10 +393,8 @@ namespace CounterDrone.Core.Algorithms } private float ComputeInterceptProbability(DroneGroup threat, - AmmunitionSpec? ammo, int rounds, CombatScene env) + AmmunitionSpec ammo, int rounds, CombatScene env) { - if (ammo == null) return 0f; - float avgSpeed = (float)threat.Target.TypicalSpeed / 3.6f; var (effectiveR, _) = ComputeEffectiveRadius(threat, ammo, env); float spacing = effectiveR * 1.5f; @@ -404,10 +412,9 @@ namespace CounterDrone.Core.Algorithms // ═══════════════════════════════════════════════ private List GenerateFireEventsAt(DroneGroup threat, FireUnit unit, - AerosolType ammoType, AmmunitionSpec? ammo, CombatScene env, float targetOffset) + AerosolType ammoType, AmmunitionSpec ammo, CombatScene env, float targetOffset) { var events = new List(); - if (ammo == null) return events; var mid = ThreatMidpoint(threat); var (effectiveR, expansionTime) = ComputeEffectiveRadius(threat, ammo, env); diff --git a/src/CounterDrone.Core/Simulation/SimulationEngine.cs b/src/CounterDrone.Core/Simulation/SimulationEngine.cs index 284761e..0faa539 100644 --- a/src/CounterDrone.Core/Simulation/SimulationEngine.cs +++ b/src/CounterDrone.Core/Simulation/SimulationEngine.cs @@ -53,13 +53,13 @@ namespace CounterDrone.Core.Simulation private int _entityCounter; public SimulationEngine(IScenarioService scenarioService, FrameDataStore frameStore, - IDamageModel damageModel, IPathProvider paths, IDefensePlanner? planner = null) + IDamageModel damageModel, IPathProvider paths, IDefensePlanner planner) { _scenarioService = scenarioService; _frameStore = frameStore; _damageModel = damageModel; _paths = paths; - _planner = planner ?? new DefaultDefensePlanner(new List()); + _planner = planner; } public void SetFireSchedule(List schedule) diff --git a/src/Unity/Assets/Scripts/Managers/SimulationRunner.cs b/src/Unity/Assets/Scripts/Managers/SimulationRunner.cs index c3d471b..a09eb50 100644 --- a/src/Unity/Assets/Scripts/Managers/SimulationRunner.cs +++ b/src/Unity/Assets/Scripts/Managers/SimulationRunner.cs @@ -47,7 +47,7 @@ namespace CounterDrone.Unity new RoutePlanRepository(_db), new WaypointRepository(_db)); _frameStore = new FrameDataStore(_paths); - _engine = new SimulationEngine(_scenario, _frameStore, new DamageModelRouter(), _paths); + _engine = new SimulationEngine(_scenario, _frameStore, new DamageModelRouter(), _paths, new DefaultDefensePlanner(DefaultAmmunition.GetAll())); } public void LoadAndStart(string taskId) diff --git a/test/unit/CounterDrone.Core.Tests/AlgorithmFactoryTests.cs b/test/unit/CounterDrone.Core.Tests/AlgorithmFactoryTests.cs index 6dd0054..3edd513 100644 --- a/test/unit/CounterDrone.Core.Tests/AlgorithmFactoryTests.cs +++ b/test/unit/CounterDrone.Core.Tests/AlgorithmFactoryTests.cs @@ -13,9 +13,6 @@ namespace CounterDrone.Core.Tests var damage = AlgorithmFactory.Create(); Assert.IsType(damage); - - var planner = AlgorithmFactory.Create(); - Assert.IsType(planner); } [Fact] diff --git a/test/unit/CounterDrone.Core.Tests/EdgeCaseTests.cs b/test/unit/CounterDrone.Core.Tests/EdgeCaseTests.cs index 78ba518..109c3f2 100644 --- a/test/unit/CounterDrone.Core.Tests/EdgeCaseTests.cs +++ b/test/unit/CounterDrone.Core.Tests/EdgeCaseTests.cs @@ -65,7 +65,7 @@ namespace CounterDrone.Core.Tests }); var engine = new SimulationEngine(_scenario, new FrameDataStore(new TestPathProvider(_testDir)), - new DamageModelRouter(), new TestPathProvider(_testDir)); + new DamageModelRouter(), new TestPathProvider(_testDir), new DefaultDefensePlanner(DefaultAmmunition.GetAll())); engine.Initialize(_taskId); engine.TimeScale = 4f; @@ -107,7 +107,7 @@ namespace CounterDrone.Core.Tests }); var engine = new SimulationEngine(_scenario, new FrameDataStore(new TestPathProvider(_testDir)), - new DamageModelRouter(), new TestPathProvider(_testDir)); + new DamageModelRouter(), new TestPathProvider(_testDir), new DefaultDefensePlanner(DefaultAmmunition.GetAll())); engine.Initialize(_taskId); engine.TimeScale = 4f; @@ -148,7 +148,7 @@ namespace CounterDrone.Core.Tests }); var engine = new SimulationEngine(_scenario, new FrameDataStore(new TestPathProvider(_testDir)), - new DamageModelRouter(), new TestPathProvider(_testDir)); + new DamageModelRouter(), new TestPathProvider(_testDir), new DefaultDefensePlanner(DefaultAmmunition.GetAll())); engine.Initialize(_taskId); engine.TimeScale = 4f; diff --git a/test/unit/CounterDrone.Core.Tests/FullPipelineTests.cs b/test/unit/CounterDrone.Core.Tests/FullPipelineTests.cs index a6246a5..739adb4 100644 --- a/test/unit/CounterDrone.Core.Tests/FullPipelineTests.cs +++ b/test/unit/CounterDrone.Core.Tests/FullPipelineTests.cs @@ -49,7 +49,7 @@ namespace CounterDrone.Core.Tests private SimulationEngine RunSimulation(int maxTicks, float tickDt = 1f / 20f) { - var engine = new SimulationEngine(_scenario, _frameStore, new DamageModelRouter(), _paths); + var engine = new SimulationEngine(_scenario, _frameStore, new DamageModelRouter(), _paths, new DefaultDefensePlanner(_ammoCatalog)); engine.Initialize(_taskId); engine.TimeScale = 8f; // 8倍速加速 diff --git a/test/unit/CounterDrone.Core.Tests/SimulationEngineTests.cs b/test/unit/CounterDrone.Core.Tests/SimulationEngineTests.cs index deb0de9..3047d25 100644 --- a/test/unit/CounterDrone.Core.Tests/SimulationEngineTests.cs +++ b/test/unit/CounterDrone.Core.Tests/SimulationEngineTests.cs @@ -36,7 +36,7 @@ namespace CounterDrone.Core.Tests new RoutePlanRepository(_mainDb), new WaypointRepository(_mainDb)); _engine = new SimulationEngine(_scenarioService, new FrameDataStore(paths), - new DamageModelRouter(), paths); + new DamageModelRouter(), paths, new DefaultDefensePlanner(DefaultAmmunition.GetAll())); } public void Dispose()