mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2026-08-29 13:04:59 +00:00
The cardinal-jump side-wall gate previously demanded BOTH lateral columns be passable along the trajectory, and the landing-overshoot gate rejected jumps with a wall one block past the landing on the takeoff axis. Both rules rejected feasible jumps in the live world: breaking a single head-height block in a corridor with a continuous wall on one side could leave a +1 ascend cardinal sprint jump as the only reachable route, but the planner returned no path. Side-wall check now accepts when at least one lateral side is passable. The bot footprint (0.6m centred) stays >=0.2m clear of an adjacent wall under on-axis yaw, so a single-side wall does not contact the arc; only a fully-walled tunnel is rejected so the executor's 5-degree yaw drift has bail-out room. Landing-overshoot check is now a no-op. The LandingRecovery brake profile keeps cardinal-jump overshoot under 0.3m so the footprint stays inside the landing block when the brake engages, making the "wall one cell past landing" check a false positive in practice. Updated the conflicting Rejects2x1GapWhenSideWallNarrowsLanding test to assert the new accept-with-single-side-wall behaviour, added a fully-walled-tunnel rejection test to guard the bail-out lower bound, and added a regression covering the live "+1 ascend over a broken head-height block in a single-walled corridor" scenario. Made-with: Cursor
410 lines
13 KiB
C#
410 lines
13 KiB
C#
using System;
|
|
using MinecraftClient.Pathing.Core;
|
|
|
|
namespace MinecraftClient.Pathing.Moves;
|
|
|
|
internal static class ParkourFeasibility
|
|
{
|
|
public static bool IsSidewallProfile(int xOffset, int zOffset, int yDelta)
|
|
{
|
|
int absX = Math.Abs(xOffset);
|
|
int absZ = Math.Abs(zOffset);
|
|
int major = Math.Max(absX, absZ);
|
|
int minor = Math.Min(absX, absZ);
|
|
|
|
return minor == 1
|
|
&& major >= 2
|
|
&& major <= 5
|
|
&& yDelta is >= -2 and <= 1;
|
|
}
|
|
|
|
public static void GetSidewallAxes(int xOffset, int zOffset, out int forwardX, out int forwardZ, out int lateralX, out int lateralZ)
|
|
{
|
|
if (Math.Abs(xOffset) > Math.Abs(zOffset))
|
|
{
|
|
forwardX = Math.Sign(xOffset);
|
|
forwardZ = 0;
|
|
lateralX = 0;
|
|
lateralZ = Math.Sign(zOffset);
|
|
}
|
|
else
|
|
{
|
|
forwardX = 0;
|
|
forwardZ = Math.Sign(zOffset);
|
|
lateralX = Math.Sign(xOffset);
|
|
lateralZ = 0;
|
|
}
|
|
}
|
|
|
|
public static bool HasRunUp(
|
|
CalculationContext ctx,
|
|
int x,
|
|
int y,
|
|
int z,
|
|
int xOffset,
|
|
int zOffset,
|
|
int yDelta)
|
|
{
|
|
double horiz = Math.Sqrt(xOffset * xOffset + zOffset * zOffset);
|
|
bool carriedEntry = ctx.PreviousMoveType is MoveType.Parkour or MoveType.Descend;
|
|
|
|
// Sprint-momentum carry semantics for flat (yDelta == 0) parkour:
|
|
// * Parkour-carry: the previous move's airborne sprint is preserved
|
|
// cleanly through landing, so a chained 5 c2c flat parkour can
|
|
// fire immediately from the takeoff edge.
|
|
// * Descend-carry: the previous Descend often overshoots the
|
|
// takeoff block by ~0.5 m (the bot lands inside the takeoff
|
|
// block but already past the leading edge), eating the runway
|
|
// the SprintJumpTemplate needs to spin sprint back up. In
|
|
// practice this lets the bot launch with sub-12-tick momentum
|
|
// and short-fall the 5 c2c gap by ~0.7 m. Treat Descend-carry
|
|
// as a cold start for flat run-up so the planner inserts an
|
|
// explicit traverse runway or picks a shorter parkour.
|
|
bool parkourCarry = ctx.PreviousMoveType == MoveType.Parkour;
|
|
double threshold = yDelta switch
|
|
{
|
|
> 0 when carriedEntry => 4.5,
|
|
> 0 => 2.5,
|
|
< 0 when carriedEntry => 5.5,
|
|
< 0 => 3.5,
|
|
_ when parkourCarry => 5.5,
|
|
_ => 3.5,
|
|
};
|
|
if (horiz < threshold)
|
|
return true;
|
|
|
|
if (carriedEntry && yDelta < 0)
|
|
return true;
|
|
|
|
int xSign = Math.Sign(xOffset);
|
|
int zSign = Math.Sign(zOffset);
|
|
|
|
// Long flat sprint parkour (5 c2c, horiz~5) requires the player to be
|
|
// launched at full vanilla sprint velocity (~12 momentum ticks). A
|
|
// standing-jump cold start only reaches gap=3 (=4 c2c). Without a
|
|
// clean Parkour-carry, one back block of runway is not enough to
|
|
// spin sprint up; demand at least two aligned back blocks so the
|
|
// executor has a real run-up window.
|
|
// tools/sim_jump_reach.py "Standing sprint jump (0t momentum)" matrix
|
|
// shows gap=4 dy=0 is unreachable, while 12t-momentum gap=4 reaches
|
|
// 5.1075 m.
|
|
int requiredBackBlocks = (yDelta == 0 && !parkourCarry && horiz >= 4.5) ? 2 : 1;
|
|
|
|
for (int i = 1; i <= requiredBackBlocks; i++)
|
|
{
|
|
int backX = x - xSign * i;
|
|
int backZ = z - zSign * i;
|
|
if (!ctx.CanWalkOn(backX, y - 1, backZ))
|
|
return false;
|
|
if (!IsColumnPassable(ctx, backX, y, backZ))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public static bool TryGetRequiredStaticEntryRunupSteps(
|
|
MoveType previousMoveType,
|
|
int xOffset,
|
|
int zOffset,
|
|
int yDelta,
|
|
out int requiredSteps)
|
|
{
|
|
requiredSteps = 0;
|
|
|
|
if (previousMoveType is MoveType.Parkour or MoveType.Descend)
|
|
return false;
|
|
|
|
int major = Math.Max(Math.Abs(xOffset), Math.Abs(zOffset));
|
|
if (yDelta == -1 && major == 5)
|
|
{
|
|
requiredSteps = 1;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public static bool HasPreparedRunup(
|
|
EntryPreparationState state,
|
|
int x,
|
|
int y,
|
|
int z,
|
|
int forwardX,
|
|
int forwardZ,
|
|
int requiredSteps)
|
|
{
|
|
return state.Kind == EntryPreparationKind.SidewallRunup
|
|
&& state.IsPrepared
|
|
&& state.OriginX == x
|
|
&& state.OriginY == y
|
|
&& state.OriginZ == z
|
|
&& state.ForwardX == forwardX
|
|
&& state.ForwardZ == forwardZ
|
|
&& state.RequiredSteps == requiredSteps;
|
|
}
|
|
|
|
public static bool HasDiagonalShoulderClearance(
|
|
CalculationContext ctx,
|
|
int x,
|
|
int y,
|
|
int z,
|
|
int xOffset,
|
|
int zOffset)
|
|
{
|
|
if (xOffset == 0 || zOffset == 0)
|
|
return true;
|
|
|
|
return IsColumnPassable(ctx, x + Math.Sign(xOffset), y, z)
|
|
&& IsColumnPassable(ctx, x, y, z + Math.Sign(zOffset));
|
|
}
|
|
|
|
public static bool HasLandingOvershootClearance(
|
|
CalculationContext ctx,
|
|
int destX,
|
|
int destY,
|
|
int destZ,
|
|
int xSign,
|
|
int zSign)
|
|
{
|
|
// The original check rejected jumps whose landing column had a wall
|
|
// immediately past it (on the same axis as the takeoff). Empirically
|
|
// the runtime brake during LandingRecovery shrinks the overshoot
|
|
// distance to <0.3 m for cardinal sprint jumps, so the bot's
|
|
// footprint stays within the landing block when the brake kicks in.
|
|
// Rejecting feasible cardinal jumps because a wall lies one block
|
|
// beyond the landing prevented routes through narrow tunnels with
|
|
// bookend walls (e.g. a 2 c2c +1 ascend out of a dead-end alcove).
|
|
// Defer to the executor's deceleration profile and accept the move.
|
|
_ = ctx;
|
|
_ = destX;
|
|
_ = destY;
|
|
_ = destZ;
|
|
_ = xSign;
|
|
_ = zSign;
|
|
return true;
|
|
}
|
|
|
|
public static bool HasCardinalSideClearance(
|
|
CalculationContext ctx,
|
|
int x,
|
|
int y,
|
|
int z,
|
|
int xOffset,
|
|
int zOffset)
|
|
{
|
|
if ((xOffset == 0) == (zOffset == 0))
|
|
return true;
|
|
|
|
// For a cardinal sprint jump the bot's footprint (0.6 m wide centred
|
|
// on the takeoff/landing axis) stays at least 0.2 m clear of the
|
|
// adjacent z±1 / x±1 columns when yaw is on-axis, so geometrically
|
|
// a wall on ONE side cannot block the arc. The original check
|
|
// demanded BOTH sides be passable, which rejected feasible jumps
|
|
// along single-walled corridors (very common when leaping over a
|
|
// head-height obstruction next to a continuous wall).
|
|
//
|
|
// Accept the jump as long as at least one lateral side is open
|
|
// along the entire trajectory. A fully-walled tunnel (both sides
|
|
// blocked at any step) is still rejected because the executor's
|
|
// 5-degree yaw tolerance can drift the bot up to ~0.17 m laterally
|
|
// and we want some bail-out margin if it overshoots toward a wall.
|
|
if (xOffset != 0)
|
|
{
|
|
int xSign = Math.Sign(xOffset);
|
|
for (int step = 1; step <= Math.Abs(xOffset); step++)
|
|
{
|
|
int gx = x + xSign * step;
|
|
if (!IsColumnPassable(ctx, gx, y, z - 1)
|
|
&& !IsColumnPassable(ctx, gx, y, z + 1))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
int zSign = Math.Sign(zOffset);
|
|
for (int step = 1; step <= Math.Abs(zOffset); step++)
|
|
{
|
|
int gz = z + zSign * step;
|
|
if (!IsColumnPassable(ctx, x - 1, y, gz)
|
|
&& !IsColumnPassable(ctx, x + 1, y, gz))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public static bool HasIntermediateLandingConflict(
|
|
CalculationContext ctx,
|
|
int x,
|
|
int y,
|
|
int z,
|
|
int xOffset,
|
|
int zOffset,
|
|
int yDelta)
|
|
{
|
|
if (yDelta >= 0)
|
|
return false;
|
|
|
|
bool cardinal = (xOffset == 0) != (zOffset == 0);
|
|
int distance = Math.Max(Math.Abs(xOffset), Math.Abs(zOffset));
|
|
if (!cardinal || distance < 6)
|
|
return false;
|
|
|
|
int destY = y + yDelta;
|
|
int xSign = Math.Sign(xOffset);
|
|
int zSign = Math.Sign(zOffset);
|
|
|
|
for (int step = 1; step < distance; step++)
|
|
{
|
|
int gx = x + (xOffset != 0 ? xSign * step : 0);
|
|
int gz = z + (zOffset != 0 ? zSign * step : 0);
|
|
|
|
for (int candidateY = y - 1; candidateY >= destY; candidateY--)
|
|
{
|
|
if (ctx.CanWalkOn(gx, candidateY - 1, gz) && IsColumnPassable(ctx, gx, candidateY, gz))
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public static bool HasDominantAxisRunUp(
|
|
CalculationContext ctx,
|
|
int x,
|
|
int y,
|
|
int z,
|
|
int forwardX,
|
|
int forwardZ,
|
|
int xOffset,
|
|
int zOffset,
|
|
int yDelta)
|
|
{
|
|
int major = Math.Max(Math.Abs(xOffset), Math.Abs(zOffset));
|
|
int maxMajor = yDelta switch
|
|
{
|
|
> 0 => 3,
|
|
< 0 => 5,
|
|
_ => 4,
|
|
};
|
|
|
|
if (major > maxMajor)
|
|
return false;
|
|
|
|
bool carriedEntry = ctx.PreviousMoveType is MoveType.Parkour or MoveType.Descend;
|
|
if (carriedEntry)
|
|
return true;
|
|
|
|
// Cold-start sprint-jump reaches ~3.1-3.5 blocks horizontally without
|
|
// any pre-existing momentum, so short sidewall jumps remain feasible
|
|
// from a lone overhang block even when no 2-block runway is available
|
|
// behind the start (matches the staircase/step-pyramid cases seen in
|
|
// the wild, and Baritone's MomentumBehavior.ALLOWED contract).
|
|
double horiz = Math.Sqrt((xOffset * xOffset) + (zOffset * zOffset));
|
|
double coldStartReach = yDelta switch
|
|
{
|
|
> 0 => 2.5,
|
|
< 0 => 3.3,
|
|
_ => 3.2,
|
|
};
|
|
if (horiz <= coldStartReach)
|
|
return true;
|
|
|
|
for (int i = 1; i <= 2; i++)
|
|
{
|
|
int rx = x - (forwardX * i);
|
|
int rz = z - (forwardZ * i);
|
|
if (!ctx.CanWalkOn(rx, y - 1, rz) || !IsColumnPassable(ctx, rx, y, rz))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public static bool HasSidewallArcClearance(
|
|
CalculationContext ctx,
|
|
int x,
|
|
int y,
|
|
int z,
|
|
int forwardX,
|
|
int forwardZ,
|
|
int lateralX,
|
|
int lateralZ,
|
|
int xOffset,
|
|
int zOffset,
|
|
int yDelta)
|
|
{
|
|
int major = Math.Max(Math.Abs(xOffset), Math.Abs(zOffset));
|
|
int insideWallDepth = 0;
|
|
|
|
// Probe up to MaxProbeDepth cells along the forward axis at the lateral
|
|
// column to measure how thick the inner wall is. A 1- or 2-thick wall
|
|
// was the original supported case; thicker walls (3) still let the
|
|
// sidewall arc play out because the wall only provides lateral support
|
|
// during the sprint-jump — the player brushes the wall longer but the
|
|
// forward reach is unchanged. Walls thicker than MaxProbeDepth are
|
|
// rejected because they either bury the landing column or leave no
|
|
// open air for the arc to complete.
|
|
const int MaxProbeDepth = 3;
|
|
for (int step = 0; step < MaxProbeDepth; step++)
|
|
{
|
|
int wx = x + lateralX + (forwardX * step);
|
|
int wz = z + lateralZ + (forwardZ * step);
|
|
if (ctx.CanWalkThrough(wx, y, wz) && ctx.CanWalkThrough(wx, y + 1, wz))
|
|
break;
|
|
insideWallDepth++;
|
|
}
|
|
|
|
if (insideWallDepth is < 1 or > MaxProbeDepth)
|
|
return false;
|
|
|
|
for (int step = 1; step <= major; step++)
|
|
{
|
|
int cx = x + (forwardX * step);
|
|
int cz = z + (forwardZ * step);
|
|
if (!IsColumnPassable(ctx, cx, y, cz))
|
|
return false;
|
|
}
|
|
|
|
int outsideX = x - lateralX;
|
|
int outsideZ = z - lateralZ;
|
|
return IsColumnPassable(ctx, outsideX, y, outsideZ);
|
|
}
|
|
|
|
public static bool HasSidewallLandingClearance(
|
|
CalculationContext ctx,
|
|
int destX,
|
|
int destY,
|
|
int destZ,
|
|
int forwardX,
|
|
int forwardZ,
|
|
int lateralX,
|
|
int lateralZ)
|
|
{
|
|
if (!ctx.CanWalkOn(destX, destY - 1, destZ))
|
|
return false;
|
|
|
|
if (!IsColumnPassable(ctx, destX, destY, destZ))
|
|
return false;
|
|
|
|
if (!IsColumnPassable(ctx, destX + forwardX, destY, destZ + forwardZ))
|
|
return false;
|
|
|
|
if (!IsColumnPassable(ctx, destX - lateralX, destY, destZ - lateralZ))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
private static bool IsColumnPassable(CalculationContext ctx, int x, int y, int z)
|
|
{
|
|
return ctx.CanWalkThrough(x, y, z)
|
|
&& ctx.CanWalkThrough(x, y + 1, z);
|
|
}
|
|
}
|