Add no-retreat-path Forced Withdrawal evaluation scenario - #501
Merged
Conversation
Variant of PrincessEval_ForcedWithdrawal with the withdrawal edge set to NONE on both sides, so crippled units are trapped with no path to a retreat edge. Companion to the megamek Forced Withdrawal loiter fix: measures that trapped crippled units keep fighting and back away instead of passively loitering with their fire suppressed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
IllianiBird
added a commit
to MegaMek/megamek
that referenced
this pull request
Aug 2, 2026
…8630) ## Summary QA reported units under Forced Withdrawal hanging in the engagement zone: not shooting, not retreating, often not moving at all. Three compounding defects in Princess caused it. Validated with ~500 headless Princess-vs-Princess games (100-game before/after arms on two scenarios) using the #8629 harness. ## Bug Fixed - **Return-fire deadlock**: permission to return fire was granted from the start-of-turn crippled set, so a unit crippled and attacked in the same turn needed a *later* attack to unlock - which bot opponents never deliver, because their honor rules stop them shooting crippled units. Permanently disarmed. A new end-of-turn pass grants permission from the freshly refreshed set; the dishonor check keeps its deliberate start-of-turn view (an attacker is only dishonored for shooting a unit that was already visibly crippled). - **No-path loiter**: with no route to the retreat edge (blocked, surrounded, or withdraw edge None), the unit was classified `NoPathToDestination`: zero retreat pull, fire still suppressed. Now a trapped withdrawer may fire and make physical attacks, and its path ranking rewards opening the range to the closest enemy. - **Combat pulls on withdrawers**: aggression, the melee closing incentive, and herding no longer apply to withdrawing units, so self-preservation actually decides their path. - **Predicate drift**: `wantsToFallBack`, `mustFleeBoard`, and `UnitBehavior` now agree with `refreshCrippledUnits` on `isCrippled(true)`, so crew-crippled Meks withdraw consistently. ## Files Changed - `megamek/src/megamek/client/bot/princess/Princess.java` - return-fire pass, trapped-unit fire permission, predicate unification - `megamek/src/megamek/client/bot/princess/UnitBehavior.java` - isCrippled(true) - `megamek/src/megamek/client/bot/princess/BasicPathRanker.java` - no-path retreat pull, withdrawal damping in rankPath - `megamek/unittests/.../PrincessTest.java` - new return-fire test, updated stubs - `megamek/unittests/.../BasicPathRankerTest.java` - new self-preservation test ## Testing - Unit tests: 2 new, both fail with the fix reverted; 2 updated for the unified arity. All 216 `megamek.client.bot.princess` tests pass. checkstyle and javadoc clean. - Headless benchmark, 100-game mirror-match arms (scenarios: mm-data #500 and MegaMek/mm-data#501), Princess decision logs mined from complete (incl. rotated) files: - **Trapped case** (no retreat path): passive holds-fire 49 -> 0, "no path - fight on" 0 -> 49, return-fire 0 -> 37. The passive loiter is eliminated. Crippled units still loitering at game end 0.62 -> 0.41 per game and game length 11.8 -> 10.9 rounds (both beyond 2 standard errors). - **Normal case** (edge reachable): return-fire permission grants 0 -> 4-11 per run, return-fire decisions 3 -> up to 31 (two independent after-runs; honor-cascade dynamics make these counts high-variance between runs). Movement metrics unchanged - movement-to-edge already worked when a path exists. ## Rules clarification | Behavior | Status | | --- | --- | | A crippled unit that returns fire loses honor protection (enemy bots mark its player dishonored and resume shooting its cripples) | **Working as designed.** Bot-vs-bot, fled dropped 0.70 -> ~0.56 per game with destroyed up ~0.2: some cripples now trade escape for self-defense. Against human opponents - the QA report - there is no honor protection to lose, so returning fire is strictly better. | ## Out of scope - Restricting a withdrawing unit's return fire to units that actually attacked it (would soften the bot-vs-bot dishonor cascade above). Deliberately not included - minimal in-scope fix first; happy to file it as a follow-up if wanted. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a variant of the Forced Withdrawal benchmark scenario (from #500) with the withdrawal edge set to NONE on both sides, so crippled units are trapped with no path to a retreat edge. Companion to the megamek Forced Withdrawal loiter fix, which it was built to measure.
Changes Made
PrincessEval_ForcedWithdrawal.mms, butwithdrawto: NONEfor both bot factions. Pre-fix, trapped crippled units passively loitered with their fire suppressed; the scenario measures that they now keep fighting and back away instead.Files Changed
data/scenarios/Evaluation/PrincessEval_ForcedWithdrawal_NoPath.mms- NEWTesting
🤖 Generated with Claude Code