Files
Project-M/Assets/_Project/Tests/EditMode/RoomLayoutMathTests.cs
T
2026-07-02 20:41:43 -07:00

94 lines
4.1 KiB
C#

using NUnit.Framework;
using ProjectM.Simulation;
using Unity.Mathematics;
namespace ProjectM.Tests
{
/// <summary>
/// Pure-function tests for <see cref="RoomLayoutMath"/> — resolving a map node into a <see cref="RoomPlan"/> 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.
/// </summary>
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);
}
}
}
}