Skip to content

Stop the AI seeking a card its library cannot supply - #11448

Open
omraj21 wants to merge 2 commits into
Card-Forge:masterfrom
omraj21:ai-seek-empty-pool
Open

Stop the AI seeking a card its library cannot supply#11448
omraj21 wants to merge 2 commits into
Card-Forge:masterfrom
omraj21:ai-seek-empty-pool

Conversation

@omraj21

@omraj21 omraj21 commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Seek is mapped to AlwaysPlayAi, whose entire logic is return WillPlay — it never looks at the board.

SeekEffect builds a pool from the library filtered by the seek type, and when that pool is empty it simply skips the type:

if (pool.isEmpty()) {
    if (notify.length() != 0) notify.append("\r\n");
    notify.append(Localizer.getInstance().getMessage("lblSeekFailed", seekType));
    continue; // can't find if nothing to seek
}

The cost has already been paid by that point. Runecarved Obelisk pays it with the card itself:

A:AB$ Seek | Cost$ T Sac<1/CARDNAME> | Type$ Card.cmcEQY | ...
SVar:Y:Count$ValidLibrary Card.YouOwn+cmcLEX$GreatestCardManaCost
SVar:X:Count$CardCounters.CHARGE

With no charge counters nothing in the library can match, so the AI sacrificed the artifact for nothing. The added test shows it going from one copy on the battlefield to zero under the old mapping.

The fix

SeekAi mirrors how SeekEffect builds its pool (including the DefinedCards variant) and declines when no seek type can find anything.

Cost-dependent types are deliberately left alone

Spawning Pod seeks Creature.cmcEQX where SVar:X:Sacrificed$CardManaCost/Plus.1 — the filter depends on which creature the cost sacrifices, so it cannot be resolved before the cost is paid. Guessing there would make the AI stop using the card entirely, which would be a worse bug than the one being fixed.

So any type whose X/Y/Z resolves to a Sacrificed$ SVar is treated as findable, preserving the old behaviour. I verified this by running Spawning Pod activations with and without the change and getting identical results.

Verification

  • doesNotSacrificeItselfSeekingAnEmptyPool — the regression; fails with Runecarved Obelisk should not have been sacrificed expected:<1> but was:<0> without the fix
  • seeksWhenTheLibraryCanAnswer — with 6 charge counters it still sacrifices and draws, so the guard does not over-decline

forge.ai.** test suite: 247 tests, 0 failures. Full mvn -U -B clean test across all 12 modules passes.


Written with the help of GitHub Copilot CLI; the commit carries a Co-authored-by trailer for it.

omraj21 and others added 2 commits July 29, 2026 09:30
Seek was mapped to AlwaysPlayAi, which accepts every activation without looking
at the board. SeekEffect builds a pool from the library filtered by the seek
type, and when that pool is empty it just skips the type:

    if (pool.isEmpty()) {
        ...
        continue; // can't find if nothing to seek
    }

The cost has already been paid by then. Runecarved Obelisk taps and sacrifices
itself to seek a card whose mana value is at most the number of charge counters
on it, so with no counters on it the AI sacrificed the artifact and got nothing.
A test with the old mapping shows it going from one copy on the battlefield to
zero.

Add SeekAi, which mirrors how SeekEffect builds the pool and declines when no
seek type can find anything.

Types whose filter depends on what the cost sacrifices are deliberately left
alone. Spawning Pod seeks Creature.cmcEQX where X is Sacrificed$CardManaCost
plus one, which cannot be resolved before the cost is paid, so guessing there
would stop the AI using the card at all. Those are treated as findable, which
keeps the previous behaviour for them - verified by comparing Spawning Pod
activations with and without this change.

forge.ai.** test suite: 247 tests, 0 failures.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Agetian
Agetian requested review from Agetian and Hanmac July 31, 2026 13:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants