Flashback, mandatory cost, and 3 card implementations#246
Conversation
ActionMapper now scans Graveyard and Exile zones for castable spells (flashback, escape, etc.) via chooseCastAbility/getAlternativeCosts. Uses the alternative cost's ManaCost for mana requirements instead of the base card cost. AutoTap skipped for zone casts since the alt cost may differ from cardData.manaCost. MatchFlowHarness.castSpellByName extended to search Hand → GY → Exile. Forge's Flashback keyword handles resolve-to-exile automatically via AlternativeCost.Flashback replacement effect — no leyline change needed. Tests: - Pure: ActionMapper offers Cast for Think Twice in GY with 3 Islands - Integration: cast resolves, draws card, Think Twice exiled from GY Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… end-to-end
Add PromptSemantic.SelectNDiscard so discard-as-cost prompts are classified
as SelectN (not Targeting). The engine fires a choose_cards prompt via
WebCostDecision.visit(CostDiscard); the new semantic routes it through
SelectNReq with context=Discard, listType=Static, optionContext=Payment.
Changes:
- InteractivePromptBridge: add SelectNDiscard to PromptSemantic enum
- WebCostDecision: pass semantic param to selectCards helper; tag typed
discard path with SelectNDiscard
- PromptClassifier: add SelectN.Reason.Discard; classify SelectNDiscard
before the candidateRefs catch-all
- TargetingHandler: handle ClassifiedPrompt.SelectN in handlePostCastPrompt
(sends SelectNReq to client during cost payment)
- RequestBuilder: set Discard context/listType/optionContext for discard
SelectNReq (vs Resolution for legend rule)
- Puzzle + test: Mardu Outrider (75493, {1}{B}{B} + discard) end-to-end
cast → discard prompt → resolve → 5/5 on battlefield
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Puzzle now tests hand cast → GY → flashback cast → exile in one turn.
Library top = Coral Merfolk (drawn by hand cast), verifying both draws
land in hand. 6 Islands for full mana coverage ({1}{U} + {2}{U}).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Use promptId=1024 (DISCARD_COST) for mandatory discard SelectNReq instead of generic 1243 — client now shows correct "discard" label - Flashback puzzle: add valid Goal type (Play the Specified Permanent) - Mardu puzzle: add second hand card so discard prompt is exercised (single card was auto-resolved without sending SelectNReq) Playtested both puzzles in Arena — flashback and mandatory cost work end-to-end. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…r, Immersturm Predator Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…r-draw
Integration test covering the full mechanic chain: cast Novice Inspector,
ETB creates Clue artifact token via investigate, activate Clue ({2}, sac)
to draw a card. Includes puzzle fixture and Clue token grpId registration.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…g from GY Add zone cast actions (GY/Exile/Command) to ActionMapper with abilityGrpId derived from the alternate cost keyword (flashback, escape, etc.). Fix PlayableActionQuery to not gate GY casts on mayPlay() — keyword-based alt costs may not register explicit grants but are still castable. - ActionMapper.addZoneCastActions: iterate GY/Exile/Command zones, offer Cast actions with abilityGrpId from keywordAbilityGrpIds map - PlayableActionQuery: remove mayPlay guard for zone casts (fixes smart phase skip incorrectly skipping Main when flashback is available) - MatchFlowHarness.castFromGraveyard: new helper for flashback/escape tests - Puzzle: electroduplicate-flashback.pzl (Electroduplicate in GY, 2 creatures, 4 Mountains for flashback cost) - Integration test: verifies abilityGrpId on offered action, targeting prompt, flashback exile, and copy token creation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…r chain Adds integration test and puzzle for Immersturm Predator's sacrifice-as-cost activated ability. Validates the full mechanic chain: activate ability -> sacrifice cost prompt (routed as SelectTargetsReq via candidateRefs) -> creature sacrificed -> Predator tapped -> tap trigger fires -> GY exile targeting -> +1/+1 counter added. No production code changes needed — sacrifice-as-cost prompts already route correctly through the existing PromptClassifier (candidateRefs -> Targeting) and SelectTargetsReq pipeline. The test uses direct prompt bridge submission for reliability. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
GY→Exile (e.g. Immersturm Predator trigger) was rendering the exiled card under the source creature instead of in the exile zone. Added fromBattlefield flag to CardExiled event; displayCardUnderCard now only fires for BF→Exile transitions (Fiend Hunter pattern). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- ImmersturmPredatorTest: verify exiled card in Exile zone (not under source) when tap trigger exercises GY→Exile path - NoviceInspectorTest: register Clue CardData with abilityIds so uniqueAbilities are populated in proto (ability grpId 152 = sac-draw) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Test Results 262 files + 149 262 suites +149 3m 10s ⏱️ +59s For more details on these failures, see this check. Results for commit 4f97f7b. ± Comparison against base commit 0d16c23. ♻️ This comment has been updated with latest results. |
CI Report — GateTests: 182/813 passed, 1 failed (630 skipped) Failed tests
Coverage: 10.8% (642/5923 lines)
Slow tests (>3s): 4
|
Code ReviewBranch: OverviewFive cards wired end-to-end with puzzle fixtures and integration tests: flashback (Think Twice, Electroduplicate), mandatory discard cost (Mardu Outrider), investigate/Clue tokens (Novice Inspector), and sacrifice-as-cost (Immersturm Predator). Also fixes What's Good
Issues
Minor
VerdictSolid feature PR with good test coverage and clean mechanics wiring. Two blockers: LOG.md (remove) and window-bounds binary (explain or remove). The 🤖 Generated with Claude Code |
…lByName to hand-only - Remove LOG.md (session scratch, not for public repo) - Remove tools/overlay/native/window-bounds (stray binary from worktree merge) - Revert castSpellByName to Hand-only search — GY fallback broke TargetingFlowTest (found spent spell in GY, submitted invalid cast) - FlashbackTest uses castFromGraveyard explicitly for GY casts - Remove @Suppress("UNUSED_PARAMETER") — give abilityRegistryLookup a default value instead - Replace brittle prompt message assertion in ImmersturmPredatorTest with structural check (candidateRefs.size > 0) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Detekt UnusedParameter — the function resolves abilityGrpId from cardData.keywordAbilityGrpIds directly, doesn't need the registry. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Existing test for displayCardUnderCard assumed any CardExiled with source triggers the annotation. Now requires fromBattlefield=true to match the production gate (BF→Exile only). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
Five cards working end-to-end with puzzles and integration tests:
ActionMapperextended to scan GY/Exile for castable spells, Forge handles resolve-to-exile automaticallyPromptClassifierroutes discard-cost prompts to client asSelectNReqwithpromptId=1024abilityGrpIdon GY cast actions + targeting during flashback castAlso fixed:
displayCardUnderCardannotation now only fires for BF→Exile (not GY→Exile).What's NOT in this PR (follow-up issues)
Test plan
🤖 Generated with Claude Code