< Summary

Class:IADS
Assembly:bamlab.micromissiles
File(s):/github/workspace/Assets/Scripts/IADS/IADS.cs
Covered lines:49
Uncovered lines:42
Coverable lines:91
Total lines:146
Line coverage:53.8% (49 of 91)
Covered branches:0
Total branches:0
Covered methods:12
Total methods:15
Method coverage:80% (12 of 15)

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
IADS()0%110100%
Awake()0%3.333066.67%
Start()0%110100%
OnDestroy()0%6200%
RegisterSimulationStarted()0%110100%
RegisterSimulationEnded()0%220100%
RegisterNewLauncher(...)0%220100%
RegisterNewThreat(...)0%220100%
HierarchyManager()0%5.44055.56%
BuildHierarchy()0%6200%
AssignSubInterceptor(...)0%11.466046.67%
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  // The IADS only manages the launchers in the top level of the interceptor hierarchy.
 114  private List<IHierarchical> _launchers = new List<IHierarchical>();
 15
 16  // Coroutine to perform the maintain the agent hierarchy.
 17  private Coroutine _hierarchyCoroutine;
 18
 19  // List of threats waiting to be incorporated into the hierarchy.
 120  private List<IHierarchical> _newThreats = new List<IHierarchical>();
 21
 95722  public static IADS Instance { get; private set; }
 23
 108724  public IReadOnlyList<IHierarchical> Launchers => _launchers.AsReadOnly();
 25
 126  private void Awake() {
 127    if (Instance != null && Instance != this) {
 028      Destroy(gameObject);
 129    } else {
 130      Instance = this;
 131    }
 132  }
 33
 134  private void Start() {
 135    SimManager.Instance.OnSimulationStarted += RegisterSimulationStarted;
 136    SimManager.Instance.OnSimulationEnded += RegisterSimulationEnded;
 137    SimManager.Instance.OnNewLauncher += RegisterNewLauncher;
 138    SimManager.Instance.OnNewThreat += RegisterNewThreat;
 139  }
 40
 041  private void OnDestroy() {
 042    if (_hierarchyCoroutine != null) {
 043      StopCoroutine(_hierarchyCoroutine);
 044      _hierarchyCoroutine = null;
 045    }
 046  }
 47
 1248  private void RegisterSimulationStarted() {
 1249    _hierarchyCoroutine = StartCoroutine(HierarchyManager(_hierarchyUpdatePeriod));
 1250  }
 51
 1152  private void RegisterSimulationEnded() {
 2253    if (_hierarchyCoroutine != null) {
 1154      StopCoroutine(_hierarchyCoroutine);
 1155      _hierarchyCoroutine = null;
 1156    }
 1157    _launchers.Clear();
 1158    _newThreats.Clear();
 1159  }
 60
 1461  public void RegisterNewLauncher(IInterceptor interceptor) {
 2862    if (interceptor.HierarchicalAgent != null) {
 1463      interceptor.OnAssignSubInterceptor += AssignSubInterceptor;
 1464      interceptor.OnReassignTarget += ReassignTarget;
 1465      _launchers.Add(interceptor.HierarchicalAgent);
 1466    }
 1467  }
 68
 95569  public void RegisterNewThreat(IThreat threat) {
 191070    if (threat.HierarchicalAgent != null) {
 95571      _newThreats.Add(threat.HierarchicalAgent);
 95572    }
 95573  }
 74
 1275  private IEnumerator HierarchyManager(float period) {
 2476    while (true) {
 1277      if (_newThreats.Count != 0) {
 078        BuildHierarchy();
 079      }
 1280      yield return new WaitForSeconds(period);
 081    }
 82  }
 83
 084  private void BuildHierarchy() {
 85    // TODO(titan): The clustering algorithm should be aware of the capacity of the launcher.
 086    var swarmClusterer = new KMeansClusterer(Mathf.RoundToInt(_launchers.Count / _coverageFactor));
 087    List<Cluster> swarms = swarmClusterer.Cluster(_newThreats);
 088    _newThreats.Clear();
 89
 90    // Assign one swarm to each launcher.
 091    var swarmToLauncherAssignment =
 92        new MinDistanceAssignment(Assignment.Assignment_EvenAssignment_Assign);
 093    List<AssignmentItem> swarmToLauncherAssignments =
 94        swarmToLauncherAssignment.Assign(_launchers, swarms);
 095    void AssignTarget(IHierarchical hierarchical, IHierarchical target) {
 096      hierarchical.Target = target;
 097      foreach (var subHierarchical in hierarchical.ActiveSubHierarchicals) {
 098        AssignTarget(subHierarchical, target);
 099      }
 0100    }
 0101    foreach (var assignment in swarmToLauncherAssignments) {
 102      // Assign the swarm as the target to the launcher.
 0103      assignment.First.Target = assignment.Second;
 104      // Assign the launcher as the target to all threats within the swarm.
 105      // TODO(titan): The threats would normally target the aircraft carrier within the strike
 106      // group.
 0107      AssignTarget(assignment.Second, assignment.First);
 0108    }
 0109  }
 110
 132111  private void AssignSubInterceptor(IInterceptor subInterceptor) {
 132112    if (subInterceptor.CapacityRemaining <= 0) {
 0113      return;
 114    }
 115
 116    // Pass the sub-interceptor through all the launchers in order of increasing distance between
 117    // the sub-interceptor and the launcher's target.
 132118    var sortedLaunchers =
 188119        Launchers.Where(launcher => launcher.Target != null && !launcher.Target.IsTerminated)
 120            .OrderBy(launcher =>
 0121                         Vector3.Distance(subInterceptor.Position, launcher.Target.Position));
 396122    foreach (var launcher in sortedLaunchers) {
 0123      if (launcher.AssignNewTarget(subInterceptor.HierarchicalAgent,
 0124                                   subInterceptor.CapacityRemaining)) {
 0125        break;
 126      }
 0127    }
 132128  }
 129
 0130  private void ReassignTarget(IHierarchical target) {
 131    // Assign the closest launcher with non-zero remaining capacity to pursue the target.
 0132    var closestLauncher =
 133        Launchers
 0134            .Select(launcher => new {
 135              Hierarchical = launcher,
 136              Interceptor = (launcher as HierarchicalAgent)?.Agent as IInterceptor,
 137            })
 0138            .Where(launcher => launcher.Interceptor?.CapacityPlannedRemaining > 0)
 0139            .OrderBy(launcher => Vector3.Distance(target.Position, launcher.Hierarchical.Position))
 140            .FirstOrDefault();
 0141    if (closestLauncher == null) {
 0142      return;
 143    }
 0144    closestLauncher.Interceptor.ReassignTarget(target);
 0145  }
 146}