Initial commit: Theriapolis baseline at port/godot branch point
Captures the pre-Godot-port state of the codebase. This is the rollback anchor for the Godot port (M0 of theriapolis-rpg-implementation-plan-godot-port.md). All Phase 0 through Phase 6.5 work is included; Phase 7 is in flight. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
using Theriapolis.Core;
|
||||
using Xunit;
|
||||
|
||||
namespace Theriapolis.Tests.Dungeons;
|
||||
|
||||
/// <summary>
|
||||
/// Phase 7 M0 — schema integrity tests for the Phase 7 constants. These
|
||||
/// guard against silent regressions in:
|
||||
/// - <see cref="C.SAVE_SCHEMA_VERSION"/> (must == 8 at Phase 7 ship)
|
||||
/// - The 4 new RNG sub-streams (must be unique vs every existing stream)
|
||||
/// - Dungeon size bands (must be a coherent ladder)
|
||||
/// - Movement-cost multipliers (must be ≥ 1.0; squeezing must dominate)
|
||||
/// </summary>
|
||||
public sealed class Phase7ConstantsTests
|
||||
{
|
||||
[Fact]
|
||||
public void SaveSchemaVersion_IsEight()
|
||||
{
|
||||
Assert.Equal(8, C.SAVE_SCHEMA_VERSION);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DungeonRngSubStreams_AreDistinctFromAllExistingStreams()
|
||||
{
|
||||
// Collect every named ulong RNG sub-stream by reflection. Each
|
||||
// must be unique — a collision means two independent streams share
|
||||
// a seed, breaking the dice contract.
|
||||
var fields = typeof(C).GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static)
|
||||
.Where(f => f.IsLiteral && f.FieldType == typeof(ulong))
|
||||
.ToArray();
|
||||
var seen = new Dictionary<ulong, string>();
|
||||
foreach (var f in fields)
|
||||
{
|
||||
ulong value = (ulong)f.GetRawConstantValue()!;
|
||||
if (seen.TryGetValue(value, out var prior))
|
||||
Assert.Fail($"RNG sub-stream collision: {f.Name} == {prior} ({value:X})");
|
||||
seen[value] = f.Name;
|
||||
}
|
||||
// Belt-and-braces: assert the four Phase 7 streams exist.
|
||||
Assert.Contains(C.RNG_DUNGEON_LAYOUT, seen.Keys);
|
||||
Assert.Contains(C.RNG_ROOM_PICK, seen.Keys);
|
||||
Assert.Contains(C.RNG_DUNGEON_POPULATE, seen.Keys);
|
||||
Assert.Contains(C.RNG_DUNGEON_LOOT, seen.Keys);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DungeonSizeBands_FormCoherentLadder()
|
||||
{
|
||||
Assert.True(C.DUNGEON_SMALL_ROOMS_MIN <= C.DUNGEON_SMALL_ROOMS_MAX);
|
||||
Assert.True(C.DUNGEON_MED_ROOMS_MIN <= C.DUNGEON_MED_ROOMS_MAX);
|
||||
Assert.True(C.DUNGEON_LARGE_ROOMS_MIN <= C.DUNGEON_LARGE_ROOMS_MAX);
|
||||
// Ladders don't overlap — a small dungeon's max < medium's min.
|
||||
Assert.True(C.DUNGEON_SMALL_ROOMS_MAX < C.DUNGEON_MED_ROOMS_MIN);
|
||||
Assert.True(C.DUNGEON_MED_ROOMS_MAX < C.DUNGEON_LARGE_ROOMS_MIN);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MovementCostMultipliers_AreOrdered()
|
||||
{
|
||||
Assert.True(C.MOVE_COST_MISMATCH_LIGHT >= 1.0f,
|
||||
"Mismatch must never give a speed bonus.");
|
||||
Assert.True(C.MOVE_COST_MISMATCH_LIGHT < C.MOVE_COST_MISMATCH_MED);
|
||||
Assert.True(C.MOVE_COST_MISMATCH_MED < C.MOVE_COST_MISMATCH_HEAVY);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LockAndTrapDcs_AreOrdered()
|
||||
{
|
||||
Assert.True(C.LOCK_DC_TRIVIAL < C.LOCK_DC_EASY);
|
||||
Assert.True(C.LOCK_DC_EASY < C.LOCK_DC_MEDIUM);
|
||||
Assert.True(C.LOCK_DC_MEDIUM < C.LOCK_DC_HARD);
|
||||
Assert.True(C.TRAP_DC_TRIVIAL < C.TRAP_DC_EASY);
|
||||
Assert.True(C.TRAP_DC_EASY < C.TRAP_DC_MEDIUM);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user