feat: add sidewall parkour planner support

This commit is contained in:
BruceChen 2026-04-18 16:26:15 +00:00
parent 724880f928
commit e23037a897
5 changed files with 342 additions and 0 deletions

View file

@ -207,6 +207,35 @@ public sealed class LivePathingRegressionTests
Assert.Empty(PathSegmentBuilder.FromPath(result.Path));
}
[Theory]
[MemberData(nameof(SidewallParkourScenarioBuilder.AcceptedCases), MemberType = typeof(SidewallParkourScenarioBuilder))]
public void AStar_SidewallAcceptedCases_PlanThroughAllThreeJumps(string scenarioId, int gap, int deltaY, int wallOffset)
{
PathingExecutionScenario scenario = SidewallParkourScenarioBuilder.Create(scenarioId, gap, deltaY, wallOffset);
PathResult result = PathingScenarioRunner.PlanOnly(scenario);
List<PathSegment> segments = PathSegmentBuilder.FromPath(result.Path);
Assert.Equal(PathStatus.Success, result.Status);
Assert.Equal(3, segments.FindAll(segment => segment.MoveType == MoveType.Parkour).Count);
Assert.All(
segments.Where(segment => segment.MoveType == MoveType.Parkour),
segment => Assert.Equal(ParkourProfile.Sidewall, segment.ParkourProfile));
Assert.Equal(scenario.Goal.X + 0.5, segments[^1].End.X);
Assert.Equal(scenario.Goal.Y, segments[^1].End.Y);
Assert.Equal(scenario.Goal.Z + 0.5, segments[^1].End.Z);
}
[Theory]
[MemberData(nameof(SidewallParkourScenarioBuilder.RejectedCases), MemberType = typeof(SidewallParkourScenarioBuilder))]
public void AStar_SidewallRejectedCases_RejectBeforeExecution(string scenarioId, int gap, int deltaY, int wallOffset)
{
PathingExecutionScenario scenario = SidewallParkourScenarioBuilder.Create(scenarioId, gap, deltaY, wallOffset);
PathResult result = PathingScenarioRunner.PlanOnly(scenario);
Assert.Equal(PathStatus.Failed, result.Status);
Assert.Empty(PathSegmentBuilder.FromPath(result.Path));
}
[Fact]
public void AStar_LiveCoordinateLinearDescendGap3DyMinus2_PlansThroughAllThreeJumps()
{

View file

@ -0,0 +1,51 @@
using MinecraftClient.Mapping;
using MinecraftClient.Pathing.Core;
using MinecraftClient.Pathing.Moves.Impl;
using MinecraftClient.Tests.Pathing.Execution;
using Xunit;
namespace MinecraftClient.Tests.Pathing.Moves;
public sealed class MoveSidewallParkourTests
{
[Theory]
[InlineData("sidewall-flat-gap2-wo0", 2, 0, 0)]
[InlineData("sidewall-flat-gap3-wo1", 3, 0, 1)]
[InlineData("sidewall-ascend-gap2-dy+1-wo0", 2, 1, 0)]
[InlineData("sidewall-ascend-gap3-dy+1-wo1", 3, 1, 1)]
[InlineData("sidewall-descend-gap2-dy-1-wo0", 2, -1, 0)]
[InlineData("sidewall-descend-gap3-dy-1-wo1", 3, -1, 1)]
[InlineData("sidewall-descend-gap2-dy-2-wo0", 2, -2, 0)]
[InlineData("sidewall-descend-gap3-dy-2-wo1", 3, -2, 1)]
public void Calculate_AcceptsTheoryAllowedCases(string scenarioId, int gap, int deltaY, int wallOffset)
{
World world = SidewallParkourScenarioBuilder.BuildWorld(gap, deltaY, wallOffset);
var ctx = new CalculationContext(world, allowParkour: true, allowParkourAscend: true);
var move = new MoveSidewallParkour(xOffset: -1, zOffset: gap, yDelta: deltaY);
MoveResult result = default;
move.Calculate(ctx, 100, 80, 100, ref result);
Assert.False(result.IsImpossible, scenarioId);
Assert.Equal(ParkourProfile.Sidewall, result.ParkourProfile);
}
[Theory]
[InlineData("sidewall-flat-gap5-wo0", 5, 0, 0)]
[InlineData("sidewall-flat-gap5-wo1", 5, 0, 1)]
[InlineData("sidewall-ascend-gap4-dy+1-wo0", 4, 1, 0)]
[InlineData("sidewall-ascend-gap4-dy+1-wo1", 4, 1, 1)]
[InlineData("sidewall-descend-gap6-dy-1-wo0", 6, -1, 0)]
[InlineData("sidewall-descend-gap6-dy-2-wo1", 6, -2, 1)]
public void Calculate_RejectsTheoryForbiddenCases(string scenarioId, int gap, int deltaY, int wallOffset)
{
World world = SidewallParkourScenarioBuilder.BuildWorld(gap, deltaY, wallOffset);
var ctx = new CalculationContext(world, allowParkour: true, allowParkourAscend: true);
var move = new MoveSidewallParkour(xOffset: -1, zOffset: gap, yDelta: deltaY);
MoveResult result = default;
move.Calculate(ctx, 100, 80, 100, ref result);
Assert.True(result.IsImpossible, scenarioId);
}
}