Add Suitability.Gaussian for optimum-tolerance bell curves
CI / build-test (push) Successful in 1m28s
CI / build-test (push) Successful in 1m28s
A small AI helper computing how well a value matches a preferred optimum: a Gaussian bell in [0,1], 1 at the optimum, e^-0.5 one tolerance away, over an arbitrary input scale (unlike ResponseCurve's monotonic [0,1] shaping). The building block for environment-suitability growth. Covered by tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
a684c23cd9
commit
d0104df304
@@ -0,0 +1,26 @@
|
||||
namespace MrGameEng.AI;
|
||||
|
||||
/// <summary>
|
||||
/// Suitability curves: how well an environmental value matches a preferred optimum. Unlike
|
||||
/// <see cref="ResponseCurve"/> (monotonic shaping over <c>[0,1]</c>), these are bell shapes around an
|
||||
/// optimum on an arbitrary scale — the building block for "this organism likes ~18°C, tolerates ±12°".
|
||||
/// Pure and GPU-free.
|
||||
/// </summary>
|
||||
public static class Suitability
|
||||
{
|
||||
/// <summary>
|
||||
/// Gaussian bell in <c>[0, 1]</c>: 1 when <paramref name="value"/> equals <paramref name="optimum"/>,
|
||||
/// falling off as it departs, reaching <c>e^-0.5 ≈ 0.607</c> one <paramref name="tolerance"/> away.
|
||||
/// A non-positive <paramref name="tolerance"/> degenerates to an exact match (1 at the optimum, else 0).
|
||||
/// </summary>
|
||||
public static float Gaussian(float value, float optimum, float tolerance)
|
||||
{
|
||||
if (tolerance <= 0f)
|
||||
{
|
||||
return value == optimum ? 1f : 0f;
|
||||
}
|
||||
|
||||
var z = (value - optimum) / tolerance;
|
||||
return MathF.Exp(-0.5f * z * z);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
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));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user