test: add sidewall scenario builder fixtures

This commit is contained in:
BruceChen 2026-04-18 15:18:55 +00:00
parent 6cc4c6a6a9
commit 056d7afef1
2 changed files with 136 additions and 0 deletions

View file

@ -0,0 +1,89 @@
using MinecraftClient.Mapping;
using MinecraftClient.Pathing.Goals;
namespace MinecraftClient.Tests.Pathing.Execution;
public static class SidewallParkourScenarioBuilder
{
private const int SegmentCount = 3;
private const int BaseX = 100;
private const int BaseY = 80;
private const int BaseZ = 100;
private const int FloorY = BaseY - 1;
public static IEnumerable<object[]> AcceptedCases()
{
yield return ["sidewall-flat-gap2-wo0", 2, 0, 0];
yield return ["sidewall-flat-gap3-wo1", 3, 0, 1];
yield return ["sidewall-ascend-gap2-dy+1-wo0", 2, 1, 0];
yield return ["sidewall-ascend-gap3-dy+1-wo1", 3, 1, 1];
yield return ["sidewall-descend-gap2-dy-1-wo0", 2, -1, 0];
yield return ["sidewall-descend-gap3-dy-1-wo1", 3, -1, 1];
yield return ["sidewall-descend-gap2-dy-2-wo0", 2, -2, 0];
yield return ["sidewall-descend-gap3-dy-2-wo1", 3, -2, 1];
}
public static IEnumerable<object[]> RejectedCases()
{
yield return ["sidewall-flat-gap5-wo0", 5, 0, 0];
yield return ["sidewall-flat-gap5-wo1", 5, 0, 1];
yield return ["sidewall-ascend-gap4-dy+1-wo0", 4, 1, 0];
yield return ["sidewall-ascend-gap4-dy+1-wo1", 4, 1, 1];
yield return ["sidewall-descend-gap6-dy-1-wo0", 6, -1, 0];
yield return ["sidewall-descend-gap6-dy-1-wo1", 6, -1, 1];
yield return ["sidewall-descend-gap6-dy-2-wo0", 6, -2, 0];
yield return ["sidewall-descend-gap6-dy-2-wo1", 6, -2, 1];
}
internal static PathingExecutionScenario Create(string scenarioId, int gap, int deltaY, int wallOffset, int maxExecutionTicks = 700)
{
return new PathingExecutionScenario
{
Id = scenarioId,
BuildWorld = () => BuildWorld(gap, deltaY, wallOffset),
Start = new Location(BaseX + 0.5, BaseY, BaseZ + 0.5),
Goal = new GoalBlock(BaseX - SegmentCount, FloorY + (deltaY * SegmentCount) + 1, BaseZ + (gap * SegmentCount)),
StartYaw = 0f,
MaxExecutionTicks = maxExecutionTicks,
};
}
internal static World BuildWorld(int gap, int deltaY, int wallOffset)
{
int maxZ = BaseZ + (gap * SegmentCount);
World world = FlatWorldTestBuilder.CreateStoneFloor(floorY: 0, min: 80, max: maxZ + 8);
FlatWorldTestBuilder.ClearBox(world, 90, 70, 90, 110, 96, maxZ + 8);
int curX = BaseX;
int curY = FloorY;
int curZ = BaseZ;
FlatWorldTestBuilder.FillSolid(world, curX, curY, curZ - 2, curX, curY, curZ);
for (int segment = 0; segment < SegmentCount; segment++)
{
int landY = curY + deltaY;
int wallX = curX - 1;
FlatWorldTestBuilder.FillSolid(
world,
wallX,
Math.Min(curY, landY) - 1,
curZ,
wallX,
Math.Max(curY, landY) + 7,
curZ + wallOffset);
int landX = curX - 1;
int landZ = curZ + gap;
FlatWorldTestBuilder.SetSolid(world, landX, landY, landZ);
curX = landX;
curY = landY;
curZ = landZ;
}
return world;
}
}

View file

@ -0,0 +1,47 @@
using MinecraftClient.Mapping;
using Xunit;
namespace MinecraftClient.Tests.Pathing.Execution;
public sealed class SidewallParkourScenarioBuilderTests
{
[Fact]
public void BuildWorld_FlatGap2Wo0_MatchesLiveRouteGeometry()
{
World world = SidewallParkourScenarioBuilder.BuildWorld(gap: 2, deltaY: 0, wallOffset: 0);
Assert.Equal(Material.Stone, world.GetBlock(new Location(100, 79, 98)).Type);
Assert.Equal(Material.Stone, world.GetBlock(new Location(100, 79, 99)).Type);
Assert.Equal(Material.Stone, world.GetBlock(new Location(100, 79, 100)).Type);
Assert.Equal(Material.Stone, world.GetBlock(new Location(99, 78, 100)).Type);
Assert.Equal(Material.Stone, world.GetBlock(new Location(99, 79, 102)).Type);
Assert.Equal(Material.Air, world.GetBlock(new Location(100, 79, 101)).Type);
}
[Fact]
public void BuildWorld_FlatGap3Wo1_ExtendsWallByTwoBlocksAlongRunwaySide()
{
World world = SidewallParkourScenarioBuilder.BuildWorld(gap: 3, deltaY: 0, wallOffset: 1);
Assert.Equal(Material.Stone, world.GetBlock(new Location(99, 78, 100)).Type);
Assert.Equal(Material.Stone, world.GetBlock(new Location(99, 78, 101)).Type);
Assert.Equal(Material.Air, world.GetBlock(new Location(99, 78, 102)).Type);
Assert.Equal(Material.Stone, world.GetBlock(new Location(99, 79, 103)).Type);
}
[Fact]
public void Create_FlatGap2Wo0_UsesSameStartAndGoalAsLiveHarness()
{
PathingExecutionScenario scenario = SidewallParkourScenarioBuilder.Create(
"sidewall-flat-gap2-wo0",
gap: 2,
deltaY: 0,
wallOffset: 0);
Assert.Equal(new Location(100.5, 80, 100.5), scenario.Start);
Assert.Equal(97, scenario.Goal.X);
Assert.Equal(80, scenario.Goal.Y);
Assert.Equal(106, scenario.Goal.Z);
Assert.Equal(0f, scenario.StartYaw);
}
}