< Summary

Class:IADS
Assembly:bamlab.micromissiles
File(s):/github/workspace/Assets/Scripts/IADS/IADS.cs
Covered lines:0
Uncovered lines:104
Coverable lines:104
Total lines:169
Line coverage:0% (0 of 104)
Covered branches:0
Total branches:0
Covered methods:0
Total methods:17
Method coverage:0% (0 of 17)

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
IADS()0%2100%
Awake()0%12300%
Start()0%2100%
OnDestroy()0%6200%
RegisterSimulationStarted()0%2100%
RegisterSimulationEnded()0%6200%
RegisterNewAsset(...)0%6200%
RegisterNewLauncher(...)0%6200%
RegisterNewThreat(...)0%6200%
HierarchyManager()0%20400%
BuildHierarchy()0%30500%
AssignSubInterceptor(...)0%42600%
ReassignTarget(...)0%20400%

File(s)

/github/workspace/Assets/Scripts/IADS/IADS.cs

#LineLine coverage
 1using System.Collections;
 2using System.Collections.Generic;
 3using System.Linq;
 4using UnityEngine;
 5
 6// The Integrated Air Defense System (IADS) manages the air defense strategy.
 7// It implements the singleton pattern to ensure that only one instance exists.
 8public class IADS : MonoBehaviour {
 9  // Hierarchy parameters.
 10  private const float _hierarchyUpdatePeriod = 5f;
 11  private const float _coverageFactor = 1f;
 12
 13  // List of assets.
 014  private List<IHierarchical> _assets = new List<IHierarchical>();
 15
 16  // The IADS only manages the launchers at the top level of the interceptor hierarchy.
 017  private List<IHierarchical> _launchers = new List<IHierarchical>();
 18
 19  // Coroutine to perform the maintain the agent hierarchy.
 20  private Coroutine _hierarchyCoroutine;
 21
 22  // List of threats waiting to be incorporated into the hierarchy.
 023  private List<IHierarchical> _newThreats = new List<IHierarchical>();
 24
 025  public static IADS Instance { get; private set; }
 26
 027  public IReadOnlyList<IHierarchical> Assets => _assets.AsReadOnly();
 28
 029  public IReadOnlyList<IHierarchical> Launchers => _launchers.AsReadOnly();
 30
 031  private void Awake() {
 032    if (Instance != null && Instance != this) {
 033      Destroy(gameObject);
 034    } else {
 035      Instance = this;
 036    }
 037  }
 38
 039  private void Start() {
 040    SimManager.Instance.OnSimulationStarted += RegisterSimulationStarted;
 041    SimManager.Instance.OnSimulationEnded += RegisterSimulationEnded;
 042    SimManager.Instance.OnNewAsset += RegisterNewAsset;
 043    SimManager.Instance.OnNewLauncher += RegisterNewLauncher;
 044    SimManager.Instance.OnNewThreat += RegisterNewThreat;
 045  }
 46
 047  private void OnDestroy() {
 048    if (_hierarchyCoroutine != null) {
 049      StopCoroutine(_hierarchyCoroutine);
 050      _hierarchyCoroutine = null;
 051    }
 052  }
 53
 054  private void RegisterSimulationStarted() {
 055    _hierarchyCoroutine = StartCoroutine(HierarchyManager(_hierarchyUpdatePeriod));
 056  }
 57
 058  private void RegisterSimulationEnded() {
 059    if (_hierarchyCoroutine != null) {
 060      StopCoroutine(_hierarchyCoroutine);
 061      _hierarchyCoroutine = null;
 062    }
 063    _assets.Clear();
 064    _launchers.Clear();
 065    _newThreats.Clear();
 066  }
 67
 068  public void RegisterNewAsset(IInterceptor asset) {
 069    if (asset.HierarchicalAgent != null) {
 070      _assets.Add(asset.HierarchicalAgent);
 071    }
 072  }
 73
 074  public void RegisterNewLauncher(IInterceptor launcher) {
 075    if (launcher.HierarchicalAgent != null) {
 076      launcher.OnAssignSubInterceptor += AssignSubInterceptor;
 077      launcher.OnReassignTarget += ReassignTarget;
 078      _launchers.Add(launcher.HierarchicalAgent);
 079    }
 080  }
 81
 082  public void RegisterNewThreat(IThreat threat) {
 083    if (threat.HierarchicalAgent != null) {
 084      _newThreats.Add(threat.HierarchicalAgent);
 085    }
 086  }
 87
 088  private IEnumerator HierarchyManager(float period) {
 089    while (true) {
 090      if (_newThreats.Count != 0) {
 091        BuildHierarchy();
 092      }
 093      yield return new WaitForSeconds(period);
 094    }
 95  }
 96
 097  private void BuildHierarchy() {
 098    if (_newThreats.Count == 0 || _launchers.Count == 0) {
 099      return;
 100    }
 101
 102    // TODO(titan): The clustering algorithm should be aware of the capacity of the launcher.
 0103    var swarmClusterer = new KMeansClusterer(Mathf.RoundToInt(_launchers.Count / _coverageFactor));
 0104    List<Cluster> swarms = swarmClusterer.Cluster(_newThreats);
 0105    _newThreats.Clear();
 106
 107    // Assign one swarm to each launcher.
 0108    var swarmToLauncherAssignment =
 109        new MinDistanceAssignment(Assignment.Assignment_EvenAssignment_Assign);
 0110    List<AssignmentItem> swarmToLauncherAssignments =
 111        swarmToLauncherAssignment.Assign(_launchers, swarms);
 0112    void AssignTarget(IHierarchical hierarchical, IHierarchical target) {
 0113      hierarchical.Target = target;
 0114      foreach (var subHierarchical in hierarchical.ActiveSubHierarchicals) {
 0115        AssignTarget(subHierarchical, target);
 0116      }
 0117    }
 0118    foreach (var assignment in swarmToLauncherAssignments) {
 119      // Assign the swarm as the target to the launcher.
 0120      assignment.First.Target = assignment.Second;
 121
 122      // Find the asset closest to each swarm and assign it as the target to all threats within the
 123      // swarm. If there are no assets, the threats will target the launcher.
 124      // TODO(titan): Move threat coordination into a separate module as the IADS should only manage
 125      // the defense strategy.
 0126      var closestAsset =
 0127          _assets.OrderBy(asset => Vector3.Distance(assignment.Second.Position, asset.Position))
 128              .FirstOrDefault();
 0129      AssignTarget(assignment.Second, closestAsset ?? assignment.First);
 0130    }
 0131  }
 132
 0133  private void AssignSubInterceptor(IInterceptor subInterceptor) {
 0134    if (subInterceptor.CapacityRemaining <= 0) {
 0135      return;
 136    }
 137
 138    // Pass the sub-interceptor through all the launchers in order of increasing distance between
 139    // the sub-interceptor and the launcher's target.
 0140    var sortedLaunchers =
 0141        Launchers.Where(launcher => launcher.Target != null && !launcher.Target.IsTerminated)
 142            .OrderBy(launcher =>
 0143                         Vector3.Distance(subInterceptor.Position, launcher.Target.Position));
 0144    foreach (var launcher in sortedLaunchers) {
 0145      IHierarchical target = launcher.FindNewTarget(subInterceptor.HierarchicalAgent,
 146                                                    subInterceptor.CapacityRemaining);
 0147      if (subInterceptor.EvaluateReassignedTarget(target)) {
 0148        break;
 149      }
 0150    }
 0151  }
 152
 0153  private void ReassignTarget(IHierarchical target) {
 154    // Assign the closest launcher with non-zero remaining capacity to pursue the target.
 0155    var closestLauncher =
 156        Launchers
 0157            .Select(launcher => new {
 158              Hierarchical = launcher,
 159              Interceptor = (launcher as HierarchicalAgent)?.Agent as IInterceptor,
 160            })
 0161            .Where(launcher => launcher.Interceptor?.CapacityPlannedRemaining > 0)
 0162            .OrderBy(launcher => Vector3.Distance(target.Position, launcher.Hierarchical.Position))
 163            .FirstOrDefault();
 0164    if (closestLauncher == null) {
 0165      return;
 166    }
 0167    closestLauncher.Interceptor.ReassignTarget(target);
 0168  }
 169}