using Theriapolis.Core.Util; using Xunit; namespace Theriapolis.Tests.Determinism; /// /// Phase 0 smoke tests: two SeededRng instances with the same seed must produce /// identical outputs, and different seeds must diverge. /// public sealed class SeededRngTests { [Fact] public void SameSeed_ProducesSameSequence() { var a = new SeededRng(123); var b = new SeededRng(123); for (int i = 0; i < 1000; i++) Assert.Equal(a.NextUInt64(), b.NextUInt64()); } [Fact] public void DifferentSeeds_ProduceDifferentSequences() { var a = new SeededRng(123); var b = new SeededRng(456); bool anyDifferent = false; for (int i = 0; i < 10; i++) if (a.NextUInt64() != b.NextUInt64()) { anyDifferent = true; break; } Assert.True(anyDifferent, "Different seeds should produce different sequences."); } [Fact] public void ZeroSeed_DoesNotGetStuck() { var rng = new SeededRng(0); ulong prev = rng.NextUInt64(); bool moved = false; for (int i = 0; i < 5; i++) { ulong next = rng.NextUInt64(); if (next != prev) { moved = true; break; } prev = next; } Assert.True(moved, "RNG must advance even from seed 0."); } [Fact] public void NextFloat_InRange() { var rng = new SeededRng(999); for (int i = 0; i < 10_000; i++) { float f = rng.NextFloat(); Assert.True(f >= 0f && f < 1f, $"float {f} out of [0,1)"); } } [Fact] public void ForSubsystem_DifferentTagsProduceDifferentStreams() { var a = SeededRng.ForSubsystem(0xDEADBEEF, Theriapolis.Core.C.RNG_TERRAIN); var b = SeededRng.ForSubsystem(0xDEADBEEF, Theriapolis.Core.C.RNG_MOISTURE); Assert.NotEqual(a.NextUInt64(), b.NextUInt64()); } [Fact] public void Seed123_FirstValue_IsReproducible() { // Two independent instances must produce the exact same first value. var x = new SeededRng(123).NextUInt64(); var y = new SeededRng(123).NextUInt64(); Assert.Equal(x, y); } }