< Summary

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

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%
RegisterNewLauncher(...)0%6200%
RegisterNewThreat(...)0%6200%
HierarchyManager()0%20400%
BuildHierarchy()0%6200%
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  // The IADS only manages the launchers in the top level of the interceptor hierarchy.
 014  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.
 020  private List<IHierarchical> _newThreats = new List<IHierarchical>();
 21
 022  public static IADS Instance { get; private set; }
 23
 024  public IReadOnlyList<IHierarchical> Launchers => _launchers.AsReadOnly();
 25
 026  private void Awake() {
 027    if (Instance != null && Instance != this) {
 028      Destroy(gameObject);
 029    } else {
 030      Instance = this;
 031    }
 032  }
 33
 034  private void Start() {
 035    SimManager.Instance.OnSimulationStarted += RegisterSimulationStarted;
 036    SimManager.Instance.OnSimulationEnded += RegisterSimulationEnded;
 037    SimManager.Instance.OnNewLauncher += RegisterNewLauncher;
 038    SimManager.Instance.OnNewThreat += RegisterNewThreat;
 039  }
 40
 041  private void OnDestroy() {
 042    if (_hierarchyCoroutine != null) {
 043      StopCoroutine(_hierarchyCoroutine);
 044      _hierarchyCoroutine = null;
 045    }
 046  }
 47
 048  private void RegisterSimulationStarted() {
 049    _hierarchyCoroutine = StartCoroutine(HierarchyManager(_hierarchyUpdatePeriod));
 050  }
 51
 052  private void RegisterSimulationEnded() {
 053    if (_hierarchyCoroutine != null) {
 054      StopCoroutine(_hierarchyCoroutine);
 055      _hierarchyCoroutine = null;
 056    }
 057    _launchers.Clear();
 058    _newThreats.Clear();
 059  }
 60
 061  public void RegisterNewLauncher(IInterceptor interceptor) {
 062    if (interceptor.HierarchicalAgent != null) {
 063      interceptor.OnAssignSubInterceptor += AssignSubInterceptor;
 064      interceptor.OnReassignTarget += ReassignTarget;
 065      _launchers.Add(interceptor.HierarchicalAgent);
 066    }
 067  }
 68
 069  public void RegisterNewThreat(IThreat threat) {
 070    if (threat.HierarchicalAgent != null) {
 071      _newThreats.Add(threat.HierarchicalAgent);
 072    }
 073  }
 74
 075  private IEnumerator HierarchyManager(float period) {
 076    while (true) {
 077      if (_newThreats.Count != 0) {
 078        BuildHierarchy();
 079      }
 080      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
 0111  private void AssignSubInterceptor(IInterceptor subInterceptor) {
 0112    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.
 0118    var sortedLaunchers =
 0119        Launchers.Where(launcher => launcher.Target != null && !launcher.Target.IsTerminated)
 120            .OrderBy(launcher =>
 0121                         Vector3.Distance(subInterceptor.Position, launcher.Target.Position));
 0122    foreach (var launcher in sortedLaunchers) {
 0123      if (launcher.AssignNewTarget(subInterceptor.HierarchicalAgent,
 0124                                   subInterceptor.CapacityRemaining)) {
 0125        break;
 126      }
 0127    }
 0128  }
 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}