diff --git a/src/MrGameEng.Simulation/AI/Suitability.cs b/src/MrGameEng.Simulation/AI/Suitability.cs
new file mode 100644
index 0000000..557a164
--- /dev/null
+++ b/src/MrGameEng.Simulation/AI/Suitability.cs
@@ -0,0 +1,26 @@
+namespace MrGameEng.AI;
+
+///
+/// Suitability curves: how well an environmental value matches a preferred optimum. Unlike
+/// (monotonic shaping over [0,1]), 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.
+///
+public static class Suitability
+{
+ ///
+ /// Gaussian bell in [0, 1]: 1 when equals ,
+ /// falling off as it departs, reaching e^-0.5 ≈ 0.607 one away.
+ /// A non-positive degenerates to an exact match (1 at the optimum, else 0).
+ ///
+ 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);
+ }
+}
diff --git a/tests/MrGameEng.Simulation.Tests/AI/SuitabilityTests.cs b/tests/MrGameEng.Simulation.Tests/AI/SuitabilityTests.cs
new file mode 100644
index 0000000..d61fe99
--- /dev/null
+++ b/tests/MrGameEng.Simulation.Tests/AI/SuitabilityTests.cs
@@ -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));
+ }
+}