Skip to content

Fix AI implementation of Toxic Deluge and Flowstone Slide - #11337

Open
liamiak wants to merge 5 commits into
Card-Forge:masterfrom
liamiak:fix-ai-toxic-deluge-flowstone-slide
Open

Fix AI implementation of Toxic Deluge and Flowstone Slide#11337
liamiak wants to merge 5 commits into
Card-Forge:masterfrom
liamiak:fix-ai-toxic-deluge-flowstone-slide

Conversation

@liamiak

@liamiak liamiak commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

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 AiController's generic X handling 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 every sweep evaluated as -0/-0 and was declined. Toxic Deluge was also missing IsCurse$ True, so 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 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 to predictNextCombatsRemainingLife, 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 removeBlocker for 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.

@Hanmac Hanmac added the AI General AI tag label Jul 23, 2026
liamiak1 and others added 5 commits August 2, 2026 15:37
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
liamiak force-pushed the fix-ai-toxic-deluge-flowstone-slide branch from 5f28622 to eb7874d Compare August 3, 2026 01:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI General AI tag

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants