fix(combat): Thread 5-10-5 diagonal parity through all budget-consuming paths

The greedy move/run/flee/charge descent helpers hardcoded diagonal parity 0,
letting a speed-30 walker cover 6 diagonal cells (cell-count budget). Under
5-10-5 (SRD Movement, Tactical) the same walker covers only 4 cells
(1+2+1+2=6 squares).

Threading the running diagonal count:
- _best_move_step / _best_flee_step: accept diagonals_used param
- _descend_path / _run_line_path / _flee_path: track and pass parity
- _charge_end: use _path_cost_5105() instead of len(path) for max check
- _path_cost_5105: new helper; computes total 5-10-5 cost of any path

Updated golden paths in test_aoo_limit_one_per_round (5-cell path now
takes an orthogonal detour to avoid expensive 2nd diagonal), test_run_*
 (speed-30 diagonal move stops at 4 cells), and staggered_withdraw
(single-speed diagonal withdraw stops at 4 cells).
This commit is contained in:
2026-09-16 13:15:49 +02:00
parent e5691bdb6f
commit 2cc2ba1907
4 changed files with 47 additions and 10 deletions
+3 -1
View File
@@ -358,7 +358,9 @@ def test_run_toward_target_outreaches_normal_move() -> None:
)
eng2.run()
move_hero = eng2._states[0]
assert move_hero.pos == (6, 6) # only 6 cells of the 15-cell path
# 5-10-5: 4 diagonal cells cost 1+2+1+2 = 6 = the speed budget; the old
# cell-count budget let a speed-30 walker cover 6 diagonal cells.
assert move_hero.pos == (4, 4) # only 4 diagonal cells of the 15-cell path
assert grid.distance(run_hero.pos, foe.pos) < grid.distance(move_hero.pos, foe2.pos)
+4 -1
View File
@@ -1071,7 +1071,10 @@ def test_aoo_limit_one_per_round() -> None:
"initiative: guard d20=9+0=9",
"initiative: dest d20=8+0=8",
"round 1 guard: AoO vs runner d20=15+5=20 AC 12 -> HIT 3 damage (20->17)",
"round 1 runner: move (0,0)->(0,1)->(1,2)->(2,3)->(3,3)",
# 5-10-5 parity: the second diagonal (1,2)->(2,3) now costs 2, so the
# greedy walk prefers the orthogonal (1,2)->(1,3) detour (both paths
# total 5 squares; the new one avoids the expensive second diagonal).
"round 1 runner: move (0,0)->(0,1)->(1,2)->(1,3)->(2,3)->(3,3)",
"round 1 guard: wait",
"round 1 dest: wait",
"battle over: draw after 1 rounds",
+3 -2
View File
@@ -220,8 +220,9 @@ class TestStaggeredActionEconomy:
line for line in engine._transcript if "withdraw" in line
]
assert len(withdraw_lines) == 1
# 6 steps from (10,10) — exactly the single-speed budget.
assert withdraw_lines[0].count("->") == 6
# 4 diagonal steps from (10,10) — the single-speed budget of 6 squares
# under 5-10-5 (1+2+1+2=6); the old cell-count budget let it walk 6.
assert withdraw_lines[0].count("->") == 4
assert h.pos != (10, 10)