Assignment system overhaul to fix many bugs
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using UnityEngine;
|
||||
using System.Linq;
|
||||
using System.Diagnostics.Contracts;
|
||||
|
||||
// The assignment class is an interface for assigning a threat to each interceptor.
|
||||
public interface IAssignment {
|
||||
@@ -8,39 +11,30 @@ public interface IAssignment {
|
||||
// The first element corresponds to the interceptor index, and the second element
|
||||
// corresponds to the threat index.
|
||||
public struct AssignmentItem {
|
||||
public int InterceptorIndex;
|
||||
public int ThreatIndex;
|
||||
public Interceptor Interceptor;
|
||||
public Threat Threat;
|
||||
|
||||
public AssignmentItem(int missileIndex, int threatIndex) {
|
||||
InterceptorIndex = missileIndex;
|
||||
ThreatIndex = threatIndex;
|
||||
public AssignmentItem(Interceptor interceptor, Threat threat) {
|
||||
Interceptor = interceptor;
|
||||
Threat = threat;
|
||||
}
|
||||
}
|
||||
|
||||
// A list containing the interceptor-target assignments.
|
||||
|
||||
// Assign a target to each interceptor that has not been assigned a target yet.
|
||||
public abstract IEnumerable<AssignmentItem> Assign(List<Agent> missiles, List<Agent> targets);
|
||||
[Pure]
|
||||
public abstract IEnumerable<AssignmentItem> Assign(in IReadOnlyList<Interceptor> interceptors, in IReadOnlyList<ThreatData> threatTable);
|
||||
|
||||
// Get the list of assignable interceptor indices.
|
||||
protected static List<int> GetAssignableInterceptorIndices(List<Agent> missiles) {
|
||||
List<int> assignableInterceptorIndices = new List<int>();
|
||||
for (int missileIndex = 0; missileIndex < missiles.Count; missileIndex++) {
|
||||
if (missiles[missileIndex].IsAssignable()) {
|
||||
assignableInterceptorIndices.Add(missileIndex);
|
||||
}
|
||||
}
|
||||
return assignableInterceptorIndices;
|
||||
[Pure]
|
||||
protected static List<Interceptor> GetAssignableInterceptors(in IReadOnlyList<Interceptor> interceptors) {
|
||||
return interceptors.Where(interceptor => interceptor.IsAssignable()).ToList();
|
||||
}
|
||||
|
||||
// Get the list of active target indices.
|
||||
protected static List<int> GetActiveThreatIndices(List<Agent> threats) {
|
||||
List<int> activeThreatIndices = new List<int>();
|
||||
for (int threatIndex = 0; threatIndex < threats.Count; threatIndex++) {
|
||||
if (!threats[threatIndex].IsHit()) {
|
||||
activeThreatIndices.Add(threatIndex);
|
||||
}
|
||||
}
|
||||
return activeThreatIndices;
|
||||
// Get the list of active threats.
|
||||
[Pure]
|
||||
protected static List<ThreatData> GetActiveThreats(in IReadOnlyList<ThreatData> threats) {
|
||||
return threats.Where(t => t.Status != ThreatStatus.DESTROYED).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,39 +1,43 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
// The round-robin assignment class assigns missiles to the targets in a
|
||||
// round-robin order.
|
||||
using System.Diagnostics.Contracts;
|
||||
// The round-robin assignment class assigns interceptors to the targets in a
|
||||
// round-robin order using the new paradigm.
|
||||
public class RoundRobinAssignment : IAssignment {
|
||||
// Previous target index that was assigned.
|
||||
private int prevTargetIndex = -1;
|
||||
// Previous target index that was assigned.
|
||||
private int prevTargetIndex = -1;
|
||||
|
||||
// Assign a target to each interceptor that has not been assigned a target yet.
|
||||
public IEnumerable<IAssignment.AssignmentItem> Assign(List<Agent> missiles, List<Agent> targets) {
|
||||
List<IAssignment.AssignmentItem> assignments = new List<IAssignment.AssignmentItem>();
|
||||
List<int> assignableInterceptorIndices = IAssignment.GetAssignableInterceptorIndices(missiles);
|
||||
if (assignableInterceptorIndices.Count == 0) {
|
||||
return assignments;
|
||||
// Assign a target to each interceptor that has not been assigned a target yet.
|
||||
[Pure]
|
||||
public IEnumerable<IAssignment.AssignmentItem> Assign(in IReadOnlyList<Interceptor> interceptors, in IReadOnlyList<ThreatData> targets) {
|
||||
List<IAssignment.AssignmentItem> assignments = new List<IAssignment.AssignmentItem>();
|
||||
|
||||
// Get the list of interceptors that are available for assignment.
|
||||
List<Interceptor> assignableInterceptors = IAssignment.GetAssignableInterceptors(interceptors);
|
||||
if (assignableInterceptors.Count == 0) {
|
||||
return assignments;
|
||||
}
|
||||
|
||||
// Get the list of active threats that need to be addressed.
|
||||
List<ThreatData> activeThreats = IAssignment.GetActiveThreats(targets);
|
||||
if (activeThreats.Count == 0) {
|
||||
return assignments;
|
||||
}
|
||||
|
||||
// Perform round-robin assignment.
|
||||
foreach (Interceptor interceptor in assignableInterceptors) {
|
||||
// Determine the next target index in a round-robin fashion.
|
||||
int nextTargetIndex = (prevTargetIndex + 1) % activeThreats.Count;
|
||||
ThreatData selectedThreat = activeThreats[nextTargetIndex];
|
||||
|
||||
// Assign the interceptor to the selected threat.
|
||||
assignments.Add(new IAssignment.AssignmentItem(interceptor, selectedThreat.Threat));
|
||||
|
||||
// Update the previous target index.
|
||||
prevTargetIndex = nextTargetIndex;
|
||||
}
|
||||
|
||||
return assignments;
|
||||
}
|
||||
|
||||
List<int> activeThreatIndices = IAssignment.GetActiveThreatIndices(targets);
|
||||
if (activeThreatIndices.Count == 0) {
|
||||
return assignments;
|
||||
}
|
||||
|
||||
foreach (int missileIndex in assignableInterceptorIndices) {
|
||||
int nextActiveTargetIndex = activeThreatIndices.FindIndex(index => index > prevTargetIndex);
|
||||
|
||||
if (nextActiveTargetIndex == -1) {
|
||||
nextActiveTargetIndex = 0;
|
||||
}
|
||||
|
||||
int nextTargetIndex = activeThreatIndices[nextActiveTargetIndex];
|
||||
assignments.Add(new IAssignment.AssignmentItem(missileIndex, nextTargetIndex));
|
||||
prevTargetIndex = nextTargetIndex;
|
||||
}
|
||||
|
||||
return assignments;
|
||||
}
|
||||
}
|
||||
@@ -3,70 +3,64 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Unity.VisualScripting;
|
||||
using UnityEngine;
|
||||
|
||||
// The threat assignment class assigns missiles to the targets based
|
||||
using System.Diagnostics.Contracts;
|
||||
// The threat assignment class assigns interceptors to the targets based
|
||||
// on the threat level of the targets.
|
||||
public class ThreatAssignment : IAssignment {
|
||||
// Assign a target to each interceptor that has not been assigned a target yet.
|
||||
public IEnumerable<IAssignment.AssignmentItem> Assign(List<Agent> missiles, List<Agent> targets) {
|
||||
[Pure]
|
||||
public IEnumerable<IAssignment.AssignmentItem> Assign(in IReadOnlyList<Interceptor> interceptors, in IReadOnlyList<ThreatData> targets) {
|
||||
List<IAssignment.AssignmentItem> assignments = new List<IAssignment.AssignmentItem>();
|
||||
|
||||
List<int> assignableInterceptorIndices = IAssignment.GetAssignableInterceptorIndices(missiles);
|
||||
if (assignableInterceptorIndices.Count == 0) {
|
||||
List<Interceptor> assignableInterceptors = IAssignment.GetAssignableInterceptors(interceptors);
|
||||
if (assignableInterceptors.Count == 0) {
|
||||
Debug.LogWarning("No assignable interceptors found");
|
||||
return assignments;
|
||||
}
|
||||
|
||||
List<int> activeThreatIndices = IAssignment.GetActiveThreatIndices(targets);
|
||||
if (activeThreatIndices.Count == 0) {
|
||||
List<ThreatData> activeThreats = IAssignment.GetActiveThreats(targets);
|
||||
if (activeThreats.Count == 0) {
|
||||
Debug.LogWarning("No active threats found");
|
||||
return assignments;
|
||||
}
|
||||
|
||||
Vector3 positionToDefend = Vector3.zero;
|
||||
List<ThreatInfo> threatInfos =
|
||||
CalculateThreatLevels(targets, activeThreatIndices, positionToDefend);
|
||||
CalculateThreatLevels(activeThreats, positionToDefend);
|
||||
|
||||
foreach (int missileIndex in assignableInterceptorIndices) {
|
||||
if (missiles[missileIndex].HasAssignedTarget())
|
||||
continue;
|
||||
if (threatInfos.Count == 0)
|
||||
break;
|
||||
|
||||
// Find the optimal target for this interceptor based on distance and threat
|
||||
ThreatInfo optimalTarget = null;
|
||||
float optimalScore = float.MinValue;
|
||||
|
||||
foreach (ThreatInfo threat in threatInfos) {
|
||||
float distance = Vector3.Distance(missiles[missileIndex].transform.position,
|
||||
targets[threat.TargetIndex].transform.position);
|
||||
float score = threat.ThreatLevel / distance; // Balance threat level with proximity
|
||||
|
||||
if (score > optimalScore) {
|
||||
optimalScore = score;
|
||||
optimalTarget = threat;
|
||||
// Sort ThreatInfo first by ThreatData.Status (UNASSIGNED first, then ASSIGNED)
|
||||
// Within each group, order by ThreatLevel descending
|
||||
threatInfos = threatInfos.OrderByDescending(t => t.ThreatData.Status == ThreatStatus.UNASSIGNED)
|
||||
.ThenByDescending(t => t.ThreatLevel)
|
||||
.ToList();
|
||||
|
||||
var assignableInterceptorsEnumerator = assignableInterceptors.GetEnumerator();
|
||||
if (assignableInterceptorsEnumerator.MoveNext()) // Move to the first element
|
||||
{
|
||||
foreach (ThreatInfo threatInfo in threatInfos) {
|
||||
assignments.Add(new IAssignment.AssignmentItem(assignableInterceptorsEnumerator.Current, threatInfo.ThreatData.Threat));
|
||||
if (!assignableInterceptorsEnumerator.MoveNext()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (optimalTarget != null) {
|
||||
assignments.Add(new IAssignment.AssignmentItem(missileIndex, optimalTarget.TargetIndex));
|
||||
threatInfos.Remove(optimalTarget);
|
||||
}
|
||||
}
|
||||
return assignments;
|
||||
}
|
||||
|
||||
private List<ThreatInfo> CalculateThreatLevels(List<Agent> targets, List<int> activeThreatIndices,
|
||||
Vector3 missilesMeanPosition) {
|
||||
|
||||
private List<ThreatInfo> CalculateThreatLevels(List<ThreatData> threatTable,
|
||||
Vector3 defensePosition) {
|
||||
List<ThreatInfo> threatInfos = new List<ThreatInfo>();
|
||||
|
||||
foreach (int targetIndex in activeThreatIndices) {
|
||||
Agent target = targets[targetIndex];
|
||||
float distanceToMean = Vector3.Distance(target.transform.position, missilesMeanPosition);
|
||||
float velocityMagnitude = target.GetVelocity().magnitude;
|
||||
foreach (ThreatData threatData in threatTable) {
|
||||
Threat threat = threatData.Threat;
|
||||
float distanceToMean = Vector3.Distance(threat.transform.position, defensePosition);
|
||||
float velocityMagnitude = threat.GetVelocity().magnitude;
|
||||
|
||||
// Calculate threat level based on proximity and velocity
|
||||
float threatLevel = (1 / distanceToMean) * velocityMagnitude;
|
||||
|
||||
threatInfos.Add(new ThreatInfo(targetIndex, threatLevel));
|
||||
threatInfos.Add(new ThreatInfo(threatData, threatLevel));
|
||||
}
|
||||
|
||||
// Sort threats in descending order
|
||||
@@ -74,11 +68,11 @@ public class ThreatAssignment : IAssignment {
|
||||
}
|
||||
|
||||
private class ThreatInfo {
|
||||
public int TargetIndex { get; }
|
||||
public ThreatData ThreatData { get; }
|
||||
public float ThreatLevel { get; }
|
||||
|
||||
public ThreatInfo(int targetIndex, float threatLevel) {
|
||||
TargetIndex = targetIndex;
|
||||
public ThreatInfo(ThreatData threatData, float threatLevel) {
|
||||
ThreatData = threatData;
|
||||
ThreatLevel = threatLevel;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user