Minecraft-Console-Client/MinecraftClient/Pathing/Moves/JumpDescriptor.cs
BruceChen d002930a6a pathing: unify jump moves into MoveJump + IMoveExpander
Replace seven hand-written IMove classes (MoveTraverse, MoveDiagonal,
MoveAscend, MoveDiagonalAscend, MoveDiagonalDescend, MoveParkour,
MoveSidewallParkour) with a single MoveJump driven by a JumpDescriptor
(XOffset, ZOffset, YDelta, JumpFlavor). JumpFeasibility is the single
source of truth for the physics/cost rules of every jump-family move.

A* no longer iterates a flat IMove[]. The Calculate loop now drives
an IMoveExpander[] that writes into a stackalloc Span<MoveNeighbor>,
eliminating per-iteration heap traffic. JumpExpander enumerates every
jump-family descriptor dynamically; LegacyMoveExpander wraps the
remaining dynamic-landing moves (MoveDescend, MoveSprintDescend,
MoveClimb, MoveFall) so callers that still pass a custom IMove[]
keep working.

Add two O(1) short-circuits at the top of JumpExpander.Expand:
- Hoist the per-node parkour preconditions (AllowParkour + CanSprint,
  standing block climbability, feet-liquid, head clearance at y+2)
  so ~170 SprintJump + Sidewall descriptors never call JumpFeasibility
  when the node cannot take off at all.
- Precompute an 8-way "first step has no floor" table indexed by
  (sign(dx), sign(dz)) so SprintJump descriptors in a direction that
  has a walkable floor underneath are dropped without Evaluate.
- Add a conservative "any cardinal wall at y or y+1" probe that skips
  all 112 Sidewall descriptors when no wall exists adjacent to the
  takeoff.

Move tests switch to the new MoveJump.* factory methods. Behavior is
verified by the existing test suite: the 21 pre-existing baseline
failures are preserved exactly, 0 regressions introduced.

Made-with: Cursor
2026-04-19 17:03:26 +00:00

56 lines
1.8 KiB
C#

namespace MinecraftClient.Pathing.Moves;
/// <summary>
/// Kind of jump-family move. Each flavor selects a different evaluator path
/// inside <see cref="JumpFeasibility"/> while sharing low-level primitives
/// (head clearance, destination clearance, flight-path sweep, cost model).
/// </summary>
public enum JumpFlavor
{
/// <summary>
/// Single-block cardinal or diagonal walk at the same Y. No jump input.
/// Covers the old <c>MoveTraverse</c> and <c>MoveDiagonal</c>.
/// </summary>
Walk,
/// <summary>
/// Single-block vertical step (dy = +1 up or dy = -1 down), cardinal or
/// diagonal. Covers <c>MoveAscend</c>, <c>MoveDiagonalAscend</c>, and
/// <c>MoveDiagonalDescend</c>.
/// </summary>
Step,
/// <summary>
/// Multi-block sprint jump, cardinal or diagonal. Covers <c>MoveParkour</c>.
/// </summary>
SprintJump,
/// <summary>
/// Dominant-axis sprint jump that uses an inner wall for support. Covers
/// <c>MoveSidewallParkour</c>.
/// </summary>
Sidewall,
}
/// <summary>
/// Fully describes a single jump-family move (Walk / Step / SprintJump /
/// Sidewall). All geometry that downstream planners or templates need can be
/// derived from this value, so A* only needs to enumerate descriptors rather
/// than hard-coded IMove subclasses.
/// </summary>
public readonly record struct JumpDescriptor(
int XOffset,
int ZOffset,
int YDelta,
JumpFlavor Flavor)
{
public bool IsCardinal => (XOffset == 0) != (ZOffset == 0);
public bool IsDiagonal => XOffset != 0 && ZOffset != 0;
public int HorizontalMajor
=> System.Math.Max(System.Math.Abs(XOffset), System.Math.Abs(ZOffset));
public int HorizontalMinor
=> System.Math.Min(System.Math.Abs(XOffset), System.Math.Abs(ZOffset));
}