using NUnit.Framework; using ProjectM.Simulation; using Unity.Mathematics; namespace ProjectM.Tests { /// /// Pure-function tests for (no ECS world), mirroring /// StatMathTests. Pins the M4 co-op deterministic spawn spread: stable, distinct per-NetworkId ring /// positions so players never stack on connect. /// public class PlayerSpawnRingTests { const float R = 2.5f; const int Slots = 4; const float Eps = 1e-4f; [Test] public void Deterministic_SameInputs_SameOffset() { var a = PlayerSpawnMath.SpawnOffset(2, R, Slots); var b = PlayerSpawnMath.SpawnOffset(2, R, Slots); Assert.AreEqual(a.x, b.x, Eps); Assert.AreEqual(a.y, b.y, Eps); Assert.AreEqual(a.z, b.z, Eps); } [Test] public void FirstFour_Land_On_Cardinal_Ring_Slots() { // NetworkIds start at 1; slots=4 -> 0, 90, 180, 270 degrees on a ring of radius R. AssertXz(PlayerSpawnMath.SpawnOffset(1, R, Slots), R, 0f); AssertXz(PlayerSpawnMath.SpawnOffset(2, R, Slots), 0f, R); AssertXz(PlayerSpawnMath.SpawnOffset(3, R, Slots), -R, 0f); AssertXz(PlayerSpawnMath.SpawnOffset(4, R, Slots), 0f, -R); } [Test] public void Distinct_NetworkIds_Give_Distinct_Positions() { var seen = new System.Collections.Generic.List(); for (int id = 1; id <= 8; id++) { var p = PlayerSpawnMath.SpawnOffset(id, R, Slots); foreach (var q in seen) Assert.Greater(math.distance(p, q), 1e-3f, $"id {id} collides with an earlier slot"); seen.Add(p); } } [Test] public void FifthPlayer_Spills_To_Outer_Ring() { // idx 4 with slots 4 -> ring 1, slot 0 -> radius doubled, angle 0. AssertXz(PlayerSpawnMath.SpawnOffset(5, R, Slots), 2f * R, 0f); } [Test] public void NonPositiveRadius_Returns_Zero() { Assert.AreEqual(0f, math.length(PlayerSpawnMath.SpawnOffset(3, 0f, Slots)), Eps); Assert.AreEqual(0f, math.length(PlayerSpawnMath.SpawnOffset(3, -1f, Slots)), Eps); } [Test] public void DegenerateSlots_DoNotThrow_AndStayDistinct() { // slots < 1 is clamped to 1 (every player on its own concentric ring). var a = PlayerSpawnMath.SpawnOffset(1, R, 0); var b = PlayerSpawnMath.SpawnOffset(2, R, 0); Assert.Greater(math.distance(a, b), 1e-3f); } [Test] public void Offset_Is_Planar_NoVerticalComponent() { for (int id = 1; id <= 6; id++) Assert.AreEqual(0f, PlayerSpawnMath.SpawnOffset(id, R, Slots).y, Eps); } static void AssertXz(float3 p, float x, float z) { Assert.AreEqual(x, p.x, Eps); Assert.AreEqual(z, p.z, Eps); } } }