fix(rules): stop auto-directed movement cutting corners
PF1e (CRB, Measuring Distance): "You can't move diagonally past a corner (even by taking a 5-foot step)". _best_move_step (the greedy descent behind the auto-directed move/run/5-foot-step) now enforces the same diagonal_allowed corner check _step5_to and _best_flee_step already apply. tests/test_combat.py::test_ranged_no_line_of_effect_moves_around_wall pinned the old corner-cutting step (0,1)->(1,2) past the wall at (0,2); re-scripted for the legal (0,1)->(1,1) path (wall cover now applies). Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -378,6 +378,37 @@ def test_step5_diagonal_with_clear_flanks_is_allowed() -> None:
|
||||
assert eng._states[0].pos == (1, 1)
|
||||
|
||||
|
||||
def test_auto_move_does_not_cut_wall_corner() -> None:
|
||||
# SRD Measuring Distance: "You can't move diagonally past a corner."
|
||||
# Wall at (1, 0) flanks the (0,0)->(1,1) diagonal: the auto-directed
|
||||
# move must walk around it (via (0, 1)), never through the corner.
|
||||
def policy(_engine: CombatEngine, state: CombatantState) -> tuple[Action, ...]:
|
||||
if state.combatant.id == "hero":
|
||||
return (Action(kind="move", target_id="foe"),)
|
||||
return _WAIT
|
||||
|
||||
eng = _hero_vs_foe(policy, [10, 5], round_cap=1, foe_pos=(2, 2), grid=_corner_grid())
|
||||
t = eng.run().transcript
|
||||
move_line = next(ln for ln in t if "hero: move" in ln)
|
||||
assert "(0,0)->(1,1)" not in move_line # no corner cut
|
||||
assert "(0,0)->(0,1)" in move_line # went around the wall
|
||||
|
||||
|
||||
def test_auto_step5_does_not_cut_wall_corner() -> None:
|
||||
# The auto 5-foot step takes its single step from the same greedy walk,
|
||||
# so it too must refuse the corner-cutting diagonal toward the foe.
|
||||
def policy(_engine: CombatEngine, state: CombatantState) -> tuple[Action, ...]:
|
||||
if state.combatant.id == "hero":
|
||||
return (Action(kind="5foot_step", target_id="foe"),)
|
||||
return _WAIT
|
||||
|
||||
eng = _hero_vs_foe(policy, [10, 5], round_cap=1, foe_pos=(2, 2), grid=_corner_grid())
|
||||
t = eng.run().transcript
|
||||
assert "round 1 hero: 5ft step (0,0)->(1,1)" not in t
|
||||
assert "round 1 hero: 5ft step (0,0)->(0,1)" in t
|
||||
assert eng._states[0].pos == (0, 1)
|
||||
|
||||
|
||||
# ── (e) U1 preserved: neither target nor destination → missing_target ─────────
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user