diff --git a/tests/testEndGen.cpp b/tests/testEndGen.cpp new file mode 100644 index 0000000..3f7e2f5 --- /dev/null +++ b/tests/testEndGen.cpp @@ -0,0 +1,156 @@ +#include "testHarness.h" +#include "vf/EndGen.h" + +#include +#include + +using namespace vf; + +namespace { + +float distanceFromOrigin(int gx, int gz) { + float x = static_cast(gx); + float z = static_cast(gz); + return std::sqrt(x * x + z * z); +} + +bool locateOuterIsland(const EndGenerator& generator, int& outX, int& outZ) { + const EndGenerator::Params& params = generator.settings(); + for (int gx = params.outerDistance; gx < params.outerDistance + 2000; gx += 4) { + for (int gz = -600; gz <= 600; gz += 4) { + if (generator.biomeAt(gx, gz) != EndBiome::OuterIsland) continue; + outX = gx; + outZ = gz; + return true; + } + } + return false; +} + +int tallyBlocks(const World& world, Block block, int minX, int minZ, int span, int ceilingY) { + int total = 0; + for (int gx = minX; gx < minX + span; ++gx) + for (int gz = minZ; gz < minZ + span; ++gz) + for (int gy = 0; gy < ceilingY; ++gy) + if (world.blockAt(gx, gy, gz) == block) ++total; + return total; +} + +} // namespace + +VF_TEST(endChunkGenerationRepeatsExactlyOnTheSameSeed) { + EndGenerator generator; + Chunk first, second; + generator.generateChunk(first, {1, 3, -1}); + generator.generateChunk(second, {1, 3, -1}); + CHECK(first.raw() == second.raw()); + + EndGenerator::Params other; + other.seed = 24601u; + EndGenerator reseeded(other); + Chunk different; + reseeded.generateChunk(different, {1, 3, -1}); + CHECK(first.raw() != different.raw()); +} + +VF_TEST(endColumnIsByteIdenticalAcrossTwoRuns) { + EndGenerator generator; + World first, second; + generator.generateColumn(first, 2, -2); + generator.generateColumn(second, 2, -2); + + CHECK_EQ(first.chunkCount(), second.chunkCount()); + int mismatches = 0; + for (const auto& entry : first.allChunks()) { + const Chunk* twin = second.chunkAt(entry.first); + if (!twin || twin->raw() != entry.second->raw()) ++mismatches; + } + CHECK_EQ(mismatches, 0); +} + +VF_TEST(endCentralIslandIsSurroundedByVoid) { + EndGenerator generator; + const EndGenerator::Params& params = generator.settings(); + + CHECK(!generator.isVoid(0, 0)); + CHECK_EQ(generator.biomeAt(0, 0), EndBiome::CentralIsland); + + int solidNearCentre = 0; + for (int gx = -40; gx <= 40; gx += 5) + for (int gz = -40; gz <= 40; gz += 5) + if (!generator.isVoid(gx, gz)) ++solidNearCentre; + CHECK(solidNearCentre > 200); + + int intruders = 0; + int checkedRing = 0; + for (int gx = -600; gx <= 600; gx += 9) { + for (int gz = -600; gz <= 600; gz += 9) { + float distance = distanceFromOrigin(gx, gz); + if (distance <= params.centralRadius + params.edgeAmplitude) continue; + if (distance >= static_cast(params.outerDistance)) continue; + ++checkedRing; + if (!generator.isVoid(gx, gz)) ++intruders; + if (generator.biomeAt(gx, gz) != EndBiome::Void) ++intruders; + if (generator.islandAt(gx, gz).valid()) ++intruders; + } + } + CHECK(checkedRing > 1000); + CHECK_EQ(intruders, 0); +} + +VF_TEST(endIslandEdgeIsRaggedNotACleanCircle) { + EndGenerator generator; + const EndGenerator::Params& params = generator.settings(); + + int inside = 0; + int outside = 0; + for (int step = 0; step < 360; step += 3) { + float angle = 3.14159265f * static_cast(step) / 180.0f; + int gx = static_cast(std::lround(std::cos(angle) * params.centralRadius)); + int gz = static_cast(std::lround(std::sin(angle) * params.centralRadius)); + if (generator.isVoid(gx, gz)) ++outside; + else ++inside; + } + CHECK(inside > 0); + CHECK(outside > 0); +} + +VF_TEST(endTerrainIsEndStoneUnderAnEmptySky) { + EndGenerator generator; + World world; + generator.generateColumn(world, 0, 0); + + int surface = generator.surfaceHeight(8, 8); + CHECK(surface > generator.settings().floorY); + CHECK_EQ(world.blockAt(8, surface, 8), Block::EndStone); + CHECK(generator.isStone(8, surface, 8)); + CHECK(!generator.isStone(8, surface + 1, 8)); + + int deepBottom = surface; + while (generator.isStone(8, deepBottom - 1, 8)) --deepBottom; + CHECK(deepBottom > generator.settings().floorY); + CHECK_EQ(world.blockAt(8, deepBottom - 1, 8), Block::Air); + CHECK_EQ(world.blockAt(8, 120, 8), Block::Air); +} + +VF_TEST(voidColumnsReportNoSurfaceAtAll) { + EndGenerator generator; + CHECK(generator.isVoid(300, 300)); + CHECK_EQ(generator.surfaceHeight(300, 300), generator.settings().floorY - 1); + CHECK(!generator.isStone(300, 52, 300)); + CHECK_EQ(generator.biomeAt(300, 300), EndBiome::Void); + + World world; + generator.generateColumn(world, 20, 20); + CHECK_EQ(tallyBlocks(world, Block::EndStone, 320, 320, 16, 128), 0); +} + +VF_TEST(obsidianPillarsRingTheCentreDeterministically) { + EndGenerator generator; + const EndGenerator::Params& params = generator.settings(); + std::vector ring = generator.pillars(); + std::vector again = generator.pillars(); + + CHECK_EQ(ring.size(), static_cast(params.pillarCount)); + CHECK_EQ(again.size(), ring.size()); +