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,104 @@
|
||||
using Theriapolis.Core;
|
||||
using Theriapolis.Core.World;
|
||||
using Theriapolis.Core.World.Generation;
|
||||
using Theriapolis.Core.World.Polylines;
|
||||
using Xunit;
|
||||
|
||||
namespace Theriapolis.Tests.Worldgen;
|
||||
|
||||
/// <summary>
|
||||
/// Hydrology correctness: rivers must be generated, endpoints must reach water.
|
||||
/// </summary>
|
||||
public sealed class HydrologyTests : IClassFixture<WorldCache>
|
||||
{
|
||||
private const ulong TestSeed = 0xCAFEBABEUL;
|
||||
|
||||
// HydrologyGen is stage 10 → 0-based index 9 (fast-path for hydrology-only tests).
|
||||
private const int HydrologyStageIndex = 9;
|
||||
|
||||
private readonly WorldCache _cache;
|
||||
|
||||
public HydrologyTests(WorldCache cache) => _cache = cache;
|
||||
|
||||
[Fact]
|
||||
public void Pipeline_GeneratesAtLeastOneRiver()
|
||||
{
|
||||
var ctx = _cache.GetThrough(TestSeed, HydrologyStageIndex);
|
||||
Assert.NotEmpty(ctx.World.Rivers);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AllRivers_HaveAtLeastTwoPoints()
|
||||
{
|
||||
var ctx = _cache.GetThrough(TestSeed, HydrologyStageIndex);
|
||||
foreach (var river in ctx.World.Rivers)
|
||||
Assert.True(river.Points.Count >= 2,
|
||||
$"River {river.Id} has fewer than 2 points");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RiverPolylines_AreInWorldPixelSpace()
|
||||
{
|
||||
var ctx = _cache.GetThrough(TestSeed, HydrologyStageIndex);
|
||||
float maxCoord = C.WORLD_WIDTH_TILES * C.WORLD_TILE_PIXELS;
|
||||
foreach (var river in ctx.World.Rivers)
|
||||
foreach (var pt in river.Points)
|
||||
{
|
||||
Assert.True(pt.X >= 0 && pt.X <= maxCoord,
|
||||
$"River {river.Id} point X={pt.X} out of world-pixel range");
|
||||
Assert.True(pt.Y >= 0 && pt.Y <= maxCoord,
|
||||
$"River {river.Id} point Y={pt.Y} out of world-pixel range");
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0xCAFEBABEUL)]
|
||||
[InlineData(0x12345678UL)]
|
||||
[InlineData(0xDEADBEEFUL)]
|
||||
public void RiverEndpoints_DrainToWaterOrWorldEdge(ulong seed)
|
||||
{
|
||||
var ctx = _cache.GetThrough(seed, HydrologyStageIndex);
|
||||
var world = ctx.World;
|
||||
int W = C.WORLD_WIDTH_TILES;
|
||||
int H = C.WORLD_HEIGHT_TILES;
|
||||
|
||||
int nonDrainingCount = 0;
|
||||
foreach (var river in world.Rivers)
|
||||
{
|
||||
if (river.Points.Count < 2) continue;
|
||||
var last = river.Points[^1];
|
||||
int lx = Math.Clamp((int)(last.X / C.WORLD_TILE_PIXELS), 0, W - 1);
|
||||
int ly = Math.Clamp((int)(last.Y / C.WORLD_TILE_PIXELS), 0, H - 1);
|
||||
|
||||
var biome = world.Tiles[lx, ly].Biome;
|
||||
bool atWater = biome == BiomeId.Ocean || biome == BiomeId.Wetland ||
|
||||
(world.Tiles[lx, ly].Features & FeatureFlags.HasRiver) != 0;
|
||||
bool atEdge = lx == 0 || ly == 0 || lx == W - 1 || ly == H - 1;
|
||||
|
||||
if (!atWater && !atEdge)
|
||||
nonDrainingCount++;
|
||||
}
|
||||
|
||||
// Allow up to 10% non-draining rivers (noise from complex terrain)
|
||||
double failRate = world.Rivers.Count > 0
|
||||
? (double)nonDrainingCount / world.Rivers.Count
|
||||
: 0.0;
|
||||
Assert.True(failRate <= 0.10,
|
||||
$"Seed {seed:X}: {nonDrainingCount}/{world.Rivers.Count} rivers do not drain to water ({failRate:P0})");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EncounterDensityMap_IsPopulated()
|
||||
{
|
||||
var ctx = _cache.Get(TestSeed);
|
||||
Assert.NotNull(ctx.World.EncounterDensity);
|
||||
|
||||
float sum = 0;
|
||||
int W = C.WORLD_WIDTH_TILES;
|
||||
int H = C.WORLD_HEIGHT_TILES;
|
||||
for (int y = 0; y < H; y++)
|
||||
for (int x = 0; x < W; x++)
|
||||
sum += ctx.World.EncounterDensity![x, y];
|
||||
Assert.True(sum > 0, "EncounterDensity map is all zeros");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user