Fix AI implementation of Toxic Deluge and Flowstone Slide - #11337
Open
liamiak wants to merge 5 commits into
Open
Fix AI implementation of Toxic Deluge and Flowstone Slide#11337liamiak wants to merge 5 commits into
liamiak wants to merge 5 commits into
Conversation
getCurseCreatures and both filters in PumpAllAi carried the same predicate: shrink to zero toughness, or enough damage to kill and not indestructible. Collapse them onto one protected helper on PumpAiBase. No behaviour change. getCurseCreatures keeps its extra Targeting$ Dies clause at the call site, since that one is specific to targeted curses. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
removeBlocker already lets a caller ask what an attack looks like without one of the opponent's creatures. Add the counterpart for our own, so code weighing an effect that kills creatures on both sides can plan the attack that follows. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
predictNextCombatsRemainingLife already takes the blockers we will have lost; it had no way to say the same about the opponents' attackers. Anything weighing a board wipe needs both, or it measures the crack-back against the very creatures the wipe removes. Existing callers keep their signatures and pass nothing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Both carried AI:RemoveDeck:All because the AI could not cast them at all. Their X is announced as a cost and nothing ever chose it: PumpAll has an AI class, so the generic X handling in AiController is skipped, and Toxic Deluge's X lives in a PayLife<X> cost that path would not have seen anyway. With X unset both pump amounts read 0, so the sweep evaluated as -0/-0 and was always declined. Toxic Deluge was also missing IsCurse$ True, which meant PumpAllAi never reached its sweeper branch and instead asked whether to pump the AI's own creatures. PumpAllAi now picks X for a -X/-X sweep when the ability announces its own X, scoring candidates by the creatures each value destroys. Candidates are the toughnesses actually on the battlefield rather than every point up to the maximum, so the AI does not walk a 40 point life total one step at a time. Whether it survives the sweep is left to predictNextCombatsRemainingLife, which already models the blocks it will make, now told which attackers the sweep removes. Whether it wins outright is left to the attack planner, which already knows about fog, evasion and the rest - the two commits before this one gave both what they needed. Life spent is priced against how much of the total is going. A +X/-X hands survivors power until end of turn, so it counts against whoever still has a combat coming: on an opponent's turn theirs can swing back, on the AI's own turn theirs has expired before they untap. A mass pump lands on every opponent at once, so the evaluation weighs all of them instead of just the strongest. Two player games are unaffected. Triggers and sub-abilities are deliberately excluded, since X was already paid when the spell was cast - The Meathook Massacre would otherwise be refused. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
One test per card being unflagged - Toxic Deluge for the -X/-X and PayLife path, Flowstone Slide for the +X/-X one, where survivors get power out of it. The trigger case passes either way by design: it guards The Meathook Massacre against a future change re-announcing an X that was already paid on casting. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
liamiak
force-pushed
the
fix-ai-toxic-deluge-flowstone-slide
branch
from
August 3, 2026 01:31
5f28622 to
eb7874d
Compare
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.
Both carried
AI:RemoveDeck:Allbecause the AI could not cast them at all. Their X is announced as a cost and nothing ever chose it —PumpAllhas an AI class, soAiController's generic X handling is skipped, and Toxic Deluge's X lives in aPayLife<X>cost that path would not have seen anyway. With X unset both pump amounts read 0, so every sweep evaluated as -0/-0 and was declined. Toxic Deluge was also missingIsCurse$ True, soPumpAllAinever reached its sweeper branch and instead asked whether to pump the AI's own creatures.PumpAllAinow picks X for a -X/-X sweep when the ability announces its own X, scoring by what each value destroys. Candidates are the toughnesses actually on the battlefield rather than every point up to the maximum, so the AI does not walk a 40 point life total one step at a time.Rather than reimplementing combat reasoning, the two questions it needs are handed to the code that already answers them. Whether the swing simply wins goes to the attack planner, which knows about fog, evasion and
cantWin. Whether the AI lives through the counterattack goes topredictNextCombatsRemainingLife, which models the blocks it will make — and is only asked when the sweep is not lethal and a cheap pessimistic bound says the survivors could get there, so it is paid for exactly where it changes the answer.Each needed one small addition, in its own commit: a counterpart to
removeBlockerfor our own creatures, and a way to tell combat prediction which attackers a wipe removes.Best read commit by commit — a shared predicate, the two enabling additions, the fix, then tests.