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,101 @@
|
||||
using Theriapolis.Core;
|
||||
using Theriapolis.Core.World;
|
||||
using Theriapolis.Core.World.Generation;
|
||||
using Xunit;
|
||||
|
||||
namespace Theriapolis.Tests.Worldgen;
|
||||
|
||||
/// <summary>
|
||||
/// Linear feature exclusion (Addendum A §2): no parallel river+rail or rail+road
|
||||
/// on the same non-settlement tile. Road+river is allowed — it represents a
|
||||
/// bridge crossing (see ValidationPassStage.CheckLinearExclusion).
|
||||
/// </summary>
|
||||
public sealed class LinearFeatureTests : IClassFixture<WorldCache>
|
||||
{
|
||||
private readonly WorldCache _cache;
|
||||
|
||||
public LinearFeatureTests(WorldCache cache) => _cache = cache;
|
||||
|
||||
[Theory]
|
||||
[InlineData(0xCAFEBABEUL)]
|
||||
[InlineData(0x11223344UL)]
|
||||
[InlineData(0xDEADBEEFUL)]
|
||||
[InlineData(0xFEEDFACEUL)]
|
||||
[InlineData(0xABCDEF00UL)]
|
||||
[InlineData(0x00112233UL)]
|
||||
[InlineData(0x99AABBCCUL)]
|
||||
[InlineData(0x12345678UL)]
|
||||
[InlineData(0x87654321UL)]
|
||||
[InlineData(0x0DEADC0DUL)]
|
||||
public void NoParallelLinearFeatures_OnNonSettlementTiles(ulong seed)
|
||||
{
|
||||
var ctx = _cache.Get(seed);
|
||||
var world = ctx.World;
|
||||
int W = C.WORLD_WIDTH_TILES;
|
||||
int H = C.WORLD_HEIGHT_TILES;
|
||||
|
||||
int violations = 0;
|
||||
for (int y = 0; y < H; y++)
|
||||
for (int x = 0; x < W; x++)
|
||||
{
|
||||
ref var tile = ref world.TileAt(x, y);
|
||||
if ((tile.Features & FeatureFlags.IsSettlement) != 0) continue;
|
||||
|
||||
bool hasRiver = (tile.Features & FeatureFlags.HasRiver) != 0;
|
||||
bool hasRail = (tile.Features & FeatureFlags.HasRail) != 0;
|
||||
bool hasRoad = (tile.Features & FeatureFlags.HasRoad) != 0;
|
||||
|
||||
// River + Rail parallel
|
||||
if (hasRiver && hasRail &&
|
||||
tile.RiverFlowDir != Theriapolis.Core.Util.Dir.None &&
|
||||
tile.RailDir != Theriapolis.Core.Util.Dir.None &&
|
||||
Theriapolis.Core.Util.Dir.IsParallel(tile.RiverFlowDir, tile.RailDir))
|
||||
violations++;
|
||||
|
||||
// Rail + Road (always a violation outside settlements)
|
||||
if (hasRail && hasRoad)
|
||||
violations++;
|
||||
}
|
||||
|
||||
Assert.Equal(0, violations);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RoadNetwork_HasAtLeastOneRoad()
|
||||
{
|
||||
var ctx = _cache.Get(0xCAFEBABEUL);
|
||||
Assert.NotEmpty(ctx.World.Roads);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RailNetwork_HasAtLeastOneRailLine()
|
||||
{
|
||||
if (!C.ENABLE_RAIL) return; // rail subsystem disabled; nothing to assert
|
||||
var ctx = _cache.Get(0xCAFEBABEUL);
|
||||
Assert.NotEmpty(ctx.World.Rails);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Roads_AreInWorldPixelSpace()
|
||||
{
|
||||
var ctx = _cache.Get(0xCAFEBABEUL);
|
||||
float maxCoord = C.WORLD_WIDTH_TILES * C.WORLD_TILE_PIXELS;
|
||||
foreach (var road in ctx.World.Roads)
|
||||
foreach (var pt in road.Points)
|
||||
{
|
||||
Assert.InRange(pt.X, 0f, maxCoord);
|
||||
Assert.InRange(pt.Y, 0f, maxCoord);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ValidationPassHash_RecordsZeroViolations()
|
||||
{
|
||||
var ctx = _cache.Get(0xCAFEBABEUL);
|
||||
if (!ctx.World.StageHashes.TryGetValue("ValidationPass", out ulong vhash))
|
||||
return; // stage didn't record hash — skip
|
||||
|
||||
int violations = (int)(vhash / 1000);
|
||||
Assert.Equal(0, violations);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user