mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2026-08-15 13:04:36 +00:00
Theory simulator: - Add 2D side-wall jump physics with yaw sweep for worst-case margin - Generate sidewall theory cases (flat/ascend/descend, wall_offset 0/1) - Add momentum-capabilities.json with band compression and max_reach - Extend models, capabilities, canonical, and renderers for sidewall Full-coverage parkour test suite (tools/test-parkour.py): - Derive test matrix from momentum-capabilities.json - Build linear/neo/ceiling courses via RCON with 7-block clear margin - Use /goto for pathfinding, parse A* and PathMgr log output - Stop-at-first-failure per (family, subfamily, dy, ceil, wo) group - Hierarchical --filter (e.g. linear/flat, ceiling/headhitter/ceil2.5) - Exclude sidewall from default matrix (identical max_reach to linear) Pathing execution fixes: - Align parkour contracts and timing budgets with live test results - Fix jump-entry yaw snapping for grounded handoffs - Template helper and sprint jump template refinements Made-with: Cursor
126 lines
4.5 KiB
Python
126 lines
4.5 KiB
Python
import unittest
|
|
from pathlib import Path
|
|
|
|
from tools.pathing_theory.primitives import (
|
|
can_reach_gap,
|
|
can_reach_gap_with_side_wall,
|
|
get_landing,
|
|
)
|
|
from tools.pathing_theory.simulator import build_theory_cases
|
|
|
|
|
|
class PathingTheoryMatrixTests(unittest.TestCase):
|
|
def test_build_theory_cases_returns_first_wave_families(self) -> None:
|
|
cases = build_theory_cases()
|
|
families = {(case.family, case.subfamily) for case in cases}
|
|
|
|
self.assertIn(("linear", "flat"), families)
|
|
self.assertIn(("linear", "ascend"), families)
|
|
self.assertIn(("linear", "descend"), families)
|
|
self.assertIn(("neo", "neo"), families)
|
|
self.assertIn(("ceiling", "headhitter"), families)
|
|
self.assertIn(("sidewall", "flat"), families)
|
|
self.assertIn(("sidewall", "ascend"), families)
|
|
self.assertIn(("sidewall", "descend"), families)
|
|
|
|
linear_boundary = next(
|
|
case
|
|
for case in cases
|
|
if case.case_id == "linear-flat-sprint-mm12-gap5-dy0p0"
|
|
)
|
|
self.assertFalse(linear_boundary.expected_reachable)
|
|
self.assertTrue(
|
|
linear_boundary.margin is None or linear_boundary.margin < 0.0
|
|
)
|
|
|
|
def test_walk_momentum_does_not_turn_gap4_ascend_into_reachable(self) -> None:
|
|
ok, landing_x, needed_x = can_reach_gap(
|
|
gap_blocks=4,
|
|
dy=1.0,
|
|
sprint=False,
|
|
momentum_ticks=12,
|
|
)
|
|
|
|
self.assertFalse(ok)
|
|
self.assertTrue(landing_x is None or landing_x < needed_x)
|
|
|
|
def test_walk_momentum_can_reach_gap2_ascend_with_edge_takeoff_support(self) -> None:
|
|
ok, landing_x, needed_x = can_reach_gap(
|
|
gap_blocks=2,
|
|
dy=1.0,
|
|
sprint=False,
|
|
momentum_ticks=12,
|
|
)
|
|
|
|
self.assertTrue(ok)
|
|
self.assertIsNotNone(landing_x)
|
|
self.assertGreaterEqual(landing_x, needed_x)
|
|
|
|
def test_walk_gap3_ascend_does_not_snap_up_after_falling_below_platform(self) -> None:
|
|
landing = get_landing(
|
|
sprint=False,
|
|
target_y=1.0,
|
|
landing_x_start=3.5,
|
|
momentum_ticks=12,
|
|
)
|
|
|
|
self.assertIsNone(landing)
|
|
|
|
def test_sprint_with_run_up_still_treats_flat_gap5_as_unreachable(self) -> None:
|
|
ok, landing_x, needed_x = can_reach_gap(
|
|
gap_blocks=5,
|
|
dy=0.0,
|
|
sprint=True,
|
|
momentum_ticks=12,
|
|
)
|
|
|
|
self.assertFalse(ok)
|
|
self.assertTrue(landing_x is None or landing_x < needed_x)
|
|
|
|
def test_sidewall_wo0_margin_is_less_or_equal_to_linear(self) -> None:
|
|
"""Side wall should never make a jump easier than the open-air linear case."""
|
|
cases = build_theory_cases()
|
|
linear_by_key: dict[tuple, float | None] = {}
|
|
for c in cases:
|
|
if c.family == "linear":
|
|
key = (c.subfamily, c.movement_mode, c.momentum_ticks,
|
|
c.gap_blocks, c.delta_y)
|
|
linear_by_key[key] = c.margin
|
|
|
|
for c in cases:
|
|
if c.family != "sidewall" or c.wall_offset != 0:
|
|
continue
|
|
key = (c.subfamily, c.movement_mode, c.momentum_ticks,
|
|
c.gap_blocks, c.delta_y)
|
|
lin_margin = linear_by_key.get(key)
|
|
if lin_margin is None or c.margin is None:
|
|
continue
|
|
self.assertLessEqual(
|
|
c.margin, lin_margin + 1e-9,
|
|
f"sidewall margin {c.margin} > linear margin {lin_margin} "
|
|
f"for {c.case_id}",
|
|
)
|
|
|
|
def test_sidewall_walk_descend_gap3_mm0_is_unreachable_wo0(self) -> None:
|
|
"""The tight walk descend dy=-2 gap3 mm0 should flip to unreachable with wall."""
|
|
ok, _, _ = can_reach_gap_with_side_wall(
|
|
gap_blocks=3, dy=-2.0, wall_offset=0, sprint=False, momentum_ticks=0,
|
|
)
|
|
self.assertFalse(ok)
|
|
|
|
def test_sidewall_sprint_flat_gap4_mm12_is_still_reachable_wo0(self) -> None:
|
|
"""Sprint flat gap4 with plenty of margin should survive the wall penalty."""
|
|
ok, landing_x, needed_x = can_reach_gap_with_side_wall(
|
|
gap_blocks=4, dy=0.0, wall_offset=0, sprint=True, momentum_ticks=12,
|
|
)
|
|
self.assertTrue(ok)
|
|
self.assertIsNotNone(landing_x)
|
|
self.assertGreater(landing_x, needed_x)
|
|
|
|
def test_theory_markdown_mentions_canonical_live_coverage(self) -> None:
|
|
markdown = Path("tools/pathing_data/theory-matrix.md").read_text(encoding="utf-8")
|
|
self.assertIn("Canonical live coverage", markdown)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|