Files
mrgameeng/tests/MrGameEng.Simulation.Tests/AI/SuitabilityTests.cs
T
Leonid PershinandClaude Opus 4.8 d360093be1
CI / build-test (push) Successful in 1m10s
Add Trapezoidal suitability function and corresponding tests
Introduced a new Trapezoid method in the Suitability class to model suitability with a hard tolerance band and a plateau. The method returns suitability values based on defined limits and optimal ranges. Added comprehensive unit tests to validate the functionality, including edge cases for flat plateaus, linear ramps, and hard limits.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 04:34:32 +03:00

93 lines
2.8 KiB
C#

using MrGameEng.AI;
using Xunit;
namespace MrGameEng.AI.Tests;
public class SuitabilityTests
{
[Fact]
public void Gaussian_PeaksAtOptimum()
{
Assert.Equal(1f, Suitability.Gaussian(18f, optimum: 18f, tolerance: 5f), 5);
}
[Fact]
public void Gaussian_IsSymmetricAroundOptimum()
{
var below = Suitability.Gaussian(13f, optimum: 18f, tolerance: 5f);
var above = Suitability.Gaussian(23f, optimum: 18f, tolerance: 5f);
Assert.Equal(below, above, 5);
}
[Fact]
public void Gaussian_AtOneToleranceAway_IsAboutPoint607()
{
Assert.Equal(MathF.Exp(-0.5f), Suitability.Gaussian(23f, 18f, 5f), 5);
}
[Fact]
public void Gaussian_FallsOffWithDistance()
{
var near = Suitability.Gaussian(20f, 18f, 5f);
var far = Suitability.Gaussian(30f, 18f, 5f);
Assert.True(far < near && far > 0f);
}
[Fact]
public void Gaussian_StaysWithinUnitRange()
{
for (var v = -50f; v <= 50f; v += 1f)
{
Assert.InRange(Suitability.Gaussian(v, 0f, 7f), 0f, 1f);
}
}
[Fact]
public void Gaussian_NonPositiveTolerance_IsExactMatch()
{
Assert.Equal(1f, Suitability.Gaussian(5f, 5f, 0f));
Assert.Equal(0f, Suitability.Gaussian(6f, 5f, 0f));
}
[Fact]
public void Trapezoid_IsFlatAcrossThePlateau()
{
Assert.Equal(1f, Suitability.Trapezoid(10f, 0f, 10f, 42f, 58f), 5);
Assert.Equal(1f, Suitability.Trapezoid(25f, 0f, 10f, 42f, 58f), 5);
Assert.Equal(1f, Suitability.Trapezoid(42f, 0f, 10f, 42f, 58f), 5);
}
[Fact]
public void Trapezoid_RampsLinearlyOnEachShoulder()
{
Assert.Equal(0.5f, Suitability.Trapezoid(5f, 0f, 10f, 42f, 58f), 5); // halfway up the cold ramp
Assert.Equal(0.5f, Suitability.Trapezoid(50f, 0f, 10f, 42f, 58f), 5); // halfway down the heat ramp
}
[Fact]
public void Trapezoid_IsZeroAtAndBeyondTheHardLimits()
{
Assert.Equal(0f, Suitability.Trapezoid(0f, 0f, 10f, 42f, 58f));
Assert.Equal(0f, Suitability.Trapezoid(-5f, 0f, 10f, 42f, 58f));
Assert.Equal(0f, Suitability.Trapezoid(58f, 0f, 10f, 42f, 58f));
Assert.Equal(0f, Suitability.Trapezoid(70f, 0f, 10f, 42f, 58f));
}
[Fact]
public void Trapezoid_DegenerateShoulder_IsAHardStep()
{
// optimalLow == min: full suitability immediately above the lower limit, zero at/below it.
Assert.Equal(1f, Suitability.Trapezoid(5f, 0f, 0f, 10f, 20f));
Assert.Equal(0f, Suitability.Trapezoid(0f, 0f, 0f, 10f, 20f));
}
[Fact]
public void Trapezoid_StaysWithinUnitRange()
{
for (var v = -50f; v <= 80f; v += 1f)
{
Assert.InRange(Suitability.Trapezoid(v, 0f, 10f, 42f, 58f), 0f, 1f);
}
}
}