diff --git a/Directory.Build.props b/Directory.Build.props
index 7ed6fb6e8..190e89780 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -6,8 +6,8 @@
45c2f4006f5f7dab357426cabd3197e8d0a17f66
$(CULTMATH_ROOT)
$(CultLibRoot)
-
- 252e226c51826d626f4113673f31d96577b67ec9
+
+ 9c1b06cd703bfa44352050e819b0d0659cf5978a
C:\Program Files\Git\cmd\git.exe
git
diff --git a/Packages/manifest.json b/Packages/manifest.json
index a04f36e20..2dd2a3bb3 100644
--- a/Packages/manifest.json
+++ b/Packages/manifest.json
@@ -54,6 +54,6 @@
"com.unity.modules.xr": "1.0.0",
"org.gamecult.caching.unity": "https://github.com/GameCult/CultLib.git?path=/src/GameCult.Unity/Assets/Caching#caching-unity-v1.4.0",
"org.gamecult.cultlib": "https://github.com/GameCult/CultLib.git?path=/unity/org.gamecult.cultlib#cultlib-unity-v1.0.60",
- "org.gamecult.cultmath": "https://github.com/GameCult/CultLib.git?path=/packages/cultmath/unity/org.gamecult.cultmath#cultmath-unity-v0.4.0"
+ "org.gamecult.cultmath": "https://github.com/GameCult/CultLib.git?path=/packages/cultmath/unity/org.gamecult.cultmath#cultmath-unity-v0.4.1"
}
}
diff --git a/Packages/packages-lock.json b/Packages/packages-lock.json
index f4c128708..5d26bd587 100644
--- a/Packages/packages-lock.json
+++ b/Packages/packages-lock.json
@@ -272,11 +272,11 @@
"hash": "45c2f4006f5f7dab357426cabd3197e8d0a17f66"
},
"org.gamecult.cultmath": {
- "version": "https://github.com/GameCult/CultLib.git?path=/packages/cultmath/unity/org.gamecult.cultmath#cultmath-unity-v0.4.0",
+ "version": "https://github.com/GameCult/CultLib.git?path=/packages/cultmath/unity/org.gamecult.cultmath#cultmath-unity-v0.4.1",
"depth": 0,
"source": "git",
"dependencies": {},
- "hash": "252e226c51826d626f4113673f31d96577b67ec9"
+ "hash": "9c1b06cd703bfa44352050e819b0d0659cf5978a"
},
"com.unity.modules.accessibility": {
"version": "1.0.0",
diff --git a/tests/Aetheria.Shared.Tests/ThrustAllocatorTests.cs b/tests/Aetheria.Shared.Tests/ThrustAllocatorTests.cs
index 7e36f5569..228b2e630 100644
--- a/tests/Aetheria.Shared.Tests/ThrustAllocatorTests.cs
+++ b/tests/Aetheria.Shared.Tests/ThrustAllocatorTests.cs
@@ -265,6 +265,39 @@ public class ThrustAllocatorTests
}
}
+ // A cruiser-like hull, n thrusters each pushing along one body axis with a lever-arm yaw (the finding's fixture
+ // generator: Synthetic(8, 7005)).
+ static float3[] Synthetic(int n, int seed)
+ {
+ var rng = new System.Random(seed);
+ var cols = new float3[n];
+ for (var i = 0; i < n; i++)
+ {
+ var a = 20f + 40f * (float)rng.NextDouble();
+ var yaw = (float)(rng.NextDouble() * 2 - 1) * 120f;
+ var dir = rng.Next(10);
+ cols[i] = dir < 4 ? Col(0, a, yaw) : dir < 6 ? Col(0, -a, yaw) : dir < 8 ? Col(a, 0, yaw) : Col(-a, 0, yaw);
+ }
+ return cols;
+ }
+
+ // Finding solver-converged-early: on this hull a fresh allocator's eighth intent of a seeded random walk stopped at a
+ // non-optimal point (throttle 7 at 1.0 against .922 warm). The answer is the same from a cold and from a warm start.
+ [Fact]
+ public void ColdAndWarmStartsAgreeOnTheHullThatStoppedEarly()
+ {
+ var hull = Synthetic(8, 7005);
+ var rng = new System.Random(5);
+ var warm = new ThrustAllocator();
+ for (var step = 0; step < 8; step++)
+ {
+ float x = (float)rng.NextDouble() * 2 - 1, y = (float)rng.NextDouble() * 2 - 1, turn = (float)rng.NextDouble() * 2 - 1;
+ var fromWarm = Solve(warm, hull, x, y, turn);
+ var fromCold = Solve(hull, x, y, turn);
+ for (var i = 0; i < hull.Length; i++) Assert.True(MathF.Abs(fromWarm[i] - fromCold[i]) <= 1e-5f, $"step {step}: throttle {i} {fromCold[i]} cold vs {fromWarm[i]} warm");
+ }
+ }
+
[Fact]
public void EqualThrustersShareTheDemand()
{