Skip to content

Conversation

@tryuan99
Copy link
Member

This PR resolves #197. The problem with the previous interceptor reassignment occurs when a missile interceptor with capacity 1 is reassigned to a new threat that is selected from a leaf hierarchical's target cluster. Previously, this selection was based on the distance from the threat's position to the cluster's centroid (so all interceptors were assigned to the same threat). This PR changes this selection process to optimize for the intercept speed.

To clean up the code, calculating the fractional speed was refactored into its standalone static class FractionalSpeed. Furthermore, I modified the AssignNewTarget method to FindNewTarget to return a candidate threat. Each interceptor will then decide whether to accept the new threat or not in the EvaluateReassignedTarget method.

In this screenshot, you can see that most interceptors are reassigned to the next swarm of threats instead of forced to turn around to deal with the single missed threat from the first swarm. The reassigned targets are also spread out depending on the interceptor locations.

image

cc: @Joseph0120

@tryuan99 tryuan99 requested a review from daniellovell January 30, 2026 00:41
@stacklane-pr-stack-visualizer
Copy link

stacklane-pr-stack-visualizer bot commented Jan 30, 2026

🧱 Stack PR · Branch end (2 PRs in stack)

Stack Structure:

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request effectively refactors the fractional speed calculation into a dedicated utility, enhancing code reuse and clarity. The core change to optimize interceptor reassignment based on intercept speed is a significant improvement. However, I've identified a critical risk of a division-by-zero error in the new FractionalSpeed utility. Additionally, there are opportunities to improve the robustness of the new reassignment logic and fix a potential logic error in target selection. Please see the detailed comments for specific suggestions.

@coderabbitai
Copy link

coderabbitai bot commented Jan 30, 2026

📝 Walkthrough

Walkthrough

Refactors fractional speed computation into a new utility FractionalSpeed.Calculate(IAgent, Vector3) and replaces inlined calculations in MaxSpeedAssignment and SpeedEscapeDetector with calls to that method. Adds FractionalSpeed class and unit tests. Changes hierarchical target-assignment API: renames AssignNewTarget(IHierarchical,int) -> FindNewTarget(IHierarchical,int) returning IHierarchical and updates HierarchicalBase, IHierarchical, IADS, InterceptorBase. Adds IInterceptor.EvaluateReassignedTarget(IHierarchical) and implements reassignment-evaluation logic in InterceptorBase. Adjusts AgentBase max-normal-acceleration fallback behavior.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • daniellovell
🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 26.09% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main objective of the PR: optimizing interceptor reassignment for intercept speed instead of distance-based selection.
Description check ✅ Passed The description is clearly related to the changeset, explaining the problem being solved (interceptors converging on single threats), the solution (FractionalSpeed refactoring and FindNewTarget approach), and includes visual evidence of the improvement.
Linked Issues check ✅ Passed The PR successfully addresses issue #197 by refactoring threat selection from distance-based to intercept-speed-based optimization, distributing reassigned interceptors across multiple threats rather than converging on one.
Out of Scope Changes check ✅ Passed All changes are directly related to the reassignment optimization objective. The FractionalSpeed extraction, FindNewTarget refactoring, EvaluateReassignedTarget addition, and related updates across InterceptorBase, HierarchicalBase, and test files are all in scope.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch titan/reassignment_max_speed

Tip

🧪 Unit Test Generation v2 is now available!

We have significantly improved our unit test generation capabilities.

To enable: Add this to your .coderabbit.yaml configuration:

reviews:
  finishing_touches:
    unit_tests:
      enabled: true

Try it out by using the @coderabbitai generate unit tests command on your code files or under ✨ Finishing Touches on the walkthrough!

Have feedback? Share your thoughts on our Discord thread!


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
Assets/Scripts/Hierarchical/HierarchicalBase.cs (1)

202-218: Consider handling the edge case where assignments is empty.

MaxSpeedAssignment.Assign may return an empty list. While line 216-218 checks for assignments.Count != 1, it's worth noting that this also returns null if more than one assignment is returned, which seems intentional but could warrant a comment for clarity.

🤖 Fix all issues with AI agents
In `@Assets/Scripts/Hierarchical/HierarchicalBase.cs`:
- Around line 223-228: The current metric returns higher-is-better for
FractionalSpeed.Calculate but lower-is-better for Vector3.Distance while the
call uses OrderByDescending uniformly; update the sorting so the comparison
aligns with metric semantics—either (A) change the LINQ to choose
OrderByDescending(metric) when hierarchical is a HierarchicalAgent and
OrderBy(metric) when using the distance fallback, or (B) normalize metric so it
always yields higher-is-better (e.g., return -Vector3.Distance(...) for the
distance branch) and keep OrderByDescending; adjust the code around the metric
lambda, the ternary branch that checks hierarchical is HierarchicalAgent /
HierarchicalAgent hierarchicalAgent, and the filteredSubHierarchicals assignment
accordingly.

In `@Assets/Scripts/Utils/FractionalSpeed.cs`:
- Around line 8-14: distanceTimeConstant and minTurningRadius can become
Infinity/NaN when config values are missing or zero; update the calculation in
FractionalSpeed to guard denominators by clamping key inputs (e.g., result of
Constants.CalculateAirDensityAtAltitude(agent.Position.y),
agent.StaticConfig.LiftDragConfig?.DragCoefficient,
agent.StaticConfig.BodyConfig?.CrossSectionalArea) to a small positive epsilon
using Mathf.Max(..., epsilon) before dividing for distanceTimeConstant, and
check MaxNormalAcceleration() (or use Mathf.Max(agent.MaxNormalAcceleration(),
epsilon)) before computing minTurningRadius to avoid division-by-zero/NaN; keep
angleTimeConstant as-is but ensure any null coalescing uses safe defaults if
used elsewhere.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@Assets/Scripts/Agents/AgentBase.cs`:
- Around line 116-123: MaxNormalAcceleration can produce NaN when
MaxReferenceNormalAcceleration is missing (Infinity) and Speed==0 because 0 *
Infinity => NaN; update the MaxNormalAcceleration method to guard: detect when
maxReferenceNormalAcceleration is infinite (use
float.IsInfinity/Mathf.IsInfinity) or when Speed is approximately zero
(Mathf.Approximately) and return 0f immediately, otherwise compute the normal
acceleration as before; reference the MaxNormalAcceleration method and ensure
downstream callers like FractionalSpeed.Calculate / minTurningRadius get a
finite value.

In `@Assets/Tests/EditMode/FractionalSpeedTests.cs`:
- Around line 7-28: Add a [TearDown] method to destroy the test GameObject
created in SetUp: implement a TearDown method (e.g., TearDown or Cleanup) that
checks _agent and calls UnityEngine.Object.DestroyImmediate(_agent.gameObject)
(or Destroy if running play mode) and then sets _agent = null to avoid test
pollution; reference the existing SetUp, _agent, and AgentBase so the cleanup
targets the same object created in SetUp.
- Around line 30-46: The tests Calculate_DistanceOnly_ReturnsCorrectly and
Calculate_DistanceAndBearing_ReturnsCorrectly use Assert.AreEqual for float
comparisons which can fail due to floating-point precision; update both
assertions to use the Assert.AreEqual(expected, actual, delta) overload (or
Assert.AreApproximatelyEqual equivalent) with a small tolerance (e.g., 1e-6f or
1e-5f) when calling FractionalSpeed.Calculate(_agent, targetPosition) so the
expectedFractionalSpeed is compared within that tolerance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Reassignment] Interceptor reassignment tends to only target one threat

2 participants