using NUnit.Framework; using ProjectM.Simulation; using Unity.Mathematics; namespace ProjectM.Tests { /// /// Pure-function tests for — resolving a map node into a and /// scattering points within a room's shape. Pins the plan mapping, the depth/type difficulty ramp, the per-type /// node density, and (the swept-scatter safety net) that every scattered point lies within its shape footprint. /// public class RoomLayoutMathTests { [Test] public void Plan_CopiesNodeFields_AndResolvesArchetype() { var node = new RunMapNode { RoomType = RoomTypeId.Reward, Biome = RoomBiomeId.Cavern, ShapeId = RoomShapeId.Wide, NextMask = 1, }; var plan = RoomLayoutMath.Plan(node, layer: 3, roomCount: 8); Assert.AreEqual(RoomTypeId.Reward, plan.RoomType); Assert.AreEqual(RoomBiomeId.Cavern, plan.Biome); Assert.AreEqual(RoomShapeId.Wide, plan.ShapeId); Assert.AreEqual(RoomLayoutMath.ShapeRadius(RoomShapeId.Wide), plan.Radius); Assert.AreEqual(RoomLayoutMath.BaseNodeCount(RoomTypeId.Reward), plan.NodeCount); Assert.AreEqual(RoomLayoutMath.DifficultyEpoch(3, RoomTypeId.Reward), plan.DifficultyEpoch); } [Test] public void DifficultyEpoch_DeeperIsHarder_EliteAndBossBump() { Assert.AreEqual(1, RoomLayoutMath.DifficultyEpoch(0, RoomTypeId.Combat), "layer 0 floors at 1"); Assert.Greater(RoomLayoutMath.DifficultyEpoch(5, RoomTypeId.Combat), RoomLayoutMath.DifficultyEpoch(2, RoomTypeId.Combat), "deeper is harder"); Assert.AreEqual(RoomLayoutMath.DifficultyEpoch(4, RoomTypeId.Combat) + 2, RoomLayoutMath.DifficultyEpoch(4, RoomTypeId.Elite), "Elite +2"); Assert.AreEqual(RoomLayoutMath.DifficultyEpoch(4, RoomTypeId.Combat) + 3, RoomLayoutMath.DifficultyEpoch(4, RoomTypeId.Boss), "Boss +3"); } [Test] public void BaseNodeCount_RewardDense_BossMinimal() { Assert.Greater(RoomLayoutMath.BaseNodeCount(RoomTypeId.Reward), RoomLayoutMath.BaseNodeCount(RoomTypeId.Combat), "reward rooms are resource-dense"); Assert.AreEqual(1, RoomLayoutMath.BaseNodeCount(RoomTypeId.Boss), "boss room is minimal"); Assert.GreaterOrEqual(RoomLayoutMath.BaseNodeCount(RoomTypeId.Combat), 1); } [Test] public void ShapeRadius_AllShapesPositive() { for (byte s = 0; s < RoomShapeId.Count; s++) Assert.Greater(RoomLayoutMath.ShapeRadius(s), 0f, $"shape {s}"); } [Test] public void ScatterInShape_AlwaysWithinShapeFootprint() { var center = new float3(1000f, 1f, 5f); // offset origin (expedition region) + nonzero Y preserved for (byte shape = 0; shape < RoomShapeId.Count; shape++) { var rng = new Random(9871u + shape); for (int i = 0; i < 500; i++) { float3 p = RoomLayoutMath.ScatterInShape(shape, center, i, 500, ref rng); Assert.AreEqual(center.y, p.y, 1e-4f, $"shape {shape}: Y preserved"); Assert.IsTrue(RoomLayoutMath.ContainsPoint(shape, center, p), $"shape {shape}: scattered point {p} escaped the footprint"); } } } [Test] public void ScatterInShape_Deterministic_ForSameSeedSequence() { var center = new float3(0f, 0f, 0f); var a = new Random(4242u); var b = new Random(4242u); for (int i = 0; i < 50; i++) { float3 pa = RoomLayoutMath.ScatterInShape(RoomShapeId.Cross, center, i, 50, ref a); float3 pb = RoomLayoutMath.ScatterInShape(RoomShapeId.Cross, center, i, 50, ref b); Assert.AreEqual(pa.x, pb.x, 1e-6f); Assert.AreEqual(pa.z, pb.z, 1e-6f); } } } }