Skip to content

Let the AI use Goblin Welder - #11499

Open
liamiak wants to merge 2 commits into
Card-Forge:masterfrom
liamiak:ai-goblin-welder
Open

Let the AI use Goblin Welder#11499
liamiak wants to merge 2 commits into
Card-Forge:masterfrom
liamiak:ai-goblin-welder

Conversation

@liamiak

@liamiak liamiak commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Goblin Welder was AI:RemoveDeck:All, which is not only a deckbuilding flag:
AiController.chooseSpellAbilityToPlay strips every ability of a flagged card from the candidate
list, so on master the AI never activates it and no PumpAi code runs.

Lifting the flag alone is worse than leaving it. It targets both the artifact sacrificed and the
graveyard card replacing it; with no AILogic the generic pump targeting takes the most expensive
artifact on the board regardless of owner. With only the flag removed:

Board (Welder plus) Flag lifted, no AILogic
ours: Chromatic Star, Sol Ring; Wurmcoil in the yard sacrificed the Sol Ring
ours: Sol Ring; only an Ornithopter in the yard still welded — Sol Ring → Ornithopter
theirs: Wurmcoil, Blightsteel in their yard sacrificed their Wurmcoil, handing them the Colossus and two tokens

AILogic$ Weld picks the pair as one trade, delegating each choice to what already exists:

  • ours, going awaygetCardPreference(… "SacCost" …), else getWorstAI. That keeps Sol Ring
    over Chromatic Star, which carries SVar:SacMe:1.
  • theirs, going awaygetBestRemovalTargetAI; welding against an opponent is removal.
  • coming backpumpMandatoryTarget: best from our graveyard, cheapest from theirs. The
    sub-ability needs the tag too — pumpTgtAI resets targets before dispatching, and the generic path
    would take their best.
  • which trade — the largest mana-value delta, positive being the whole go/no-go test.
    evaluatePermanentList values a non-creature permanent at CMC + 1; ZoneExchangeAi gates its
    swap the same way. A point off an opponent counts as a point to us, as GameStateEvaluator does
    for board presence.
  • timing — the shared AtOppEOT rule.

Only this card carries the AILogic, so nothing else through PumpAi changes.
AI:RemoveDeck:Random goes too, since AI_CAN_PLAY needs both flags clear;
DeckNeeds:Type$Artifact replaces it.

Reasoned, not measured: the opponent's end step over acting earlier. Known limit: one candidate pair
per player, not every combination.

Six tests, second commit, droppable alone: two fail without the fix, two caught earlier versions of
this logic, two guard the "no trade" branch. Full desktop suite: 363 tests, 0 failures, 6 skipped.

Written with Claude Opus 5 (also recorded in the commit co-authors).

liamiak1 and others added 2 commits August 24, 2026 08:25
The card was AI:RemoveDeck:All, which is not only a deckbuilding flag:
AiController.chooseSpellAbilityToPlay strips every ability of a flagged card from the
candidate list, so the AI never activated it and no PumpAi code ran.

Lifting the flag on its own is worse than leaving it. The ability targets both the
artifact that gets sacrificed and the graveyard card that replaces it, and with no
AILogic the generic pump targeting takes the most expensive artifact on the board
regardless of who owns it. With only the flag removed it sacrificed a Sol Ring to get
a Wurmcoil Engine back, welded a Sol Ring away for an Ornithopter when that was all
our graveyard held, and - given an opponent holding the biggest artifact - sacrificed
theirs to hand them a Blightsteel Colossus and two Wurm tokens.

AILogic$ Weld picks the pair as one trade. Ours: something already marked expendable
through the existing SacCost preference, otherwise the worst artifact we control,
swapped for the best card in our graveyard. An opponent's: their best board artifact
for the cheapest card in their graveyard, which turns the gift above into removal.
The pair taken is the one with the largest mana value delta, and a positive delta is
the whole go/no-go test - evaluatePermanentList values a non-creature permanent at
CMC + 1, and ZoneExchangeAi gates its cross-zone swap on the same comparison.

The sub-ability carries the tag too, because pumpTgtAI resets targets before it
dispatches and the generic path would then take the opponent's best graveyard card.

Timing goes through the shared AtOppEOT rule.

AI:RemoveDeck:Random goes as well, since DeckGeneratorBase.AI_CAN_PLAY needs both
flags clear and leaving it would keep the Welder out of every AI-generated deck.
DeckNeeds:Type$Artifact replaces it.

Only this card carries the new AILogic, so nothing else that goes through PumpAi
changes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Plays until the AI welds of its own accord at the opponent's end step and checks what
it chose. Two cases fail without the fix: our own worst artifact swapped up into the
best one in our graveyard, and an opponent's artifact traded down for the junk in
theirs. Two more caught earlier versions of this logic - an opponent graveyard holding
both a bomb and junk, which needs the tag on the sub-ability, and a board offering our
own upgrade alongside a larger swing against an opponent, which needs the search to
weigh every pair rather than stop at the first profitable one. The last two guard the
branch that declines when no trade gains anything.

Drop this commit if you would rather not carry the test.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@liamiak

liamiak commented Aug 24, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto master and reworked. Two things changed the shape of this:

  • AI:RemoveDeck:All turns out to gate the ability at runtime, not only at deckbuilding — AiController.chooseSpellAbilityToPlay strips a flagged card's abilities from the candidate list, so no PumpAi code was ever reached. That makes removing the flag on its own a regression rather than a no-op, and the description now shows what the generic targeting does with the flag lifted and no AILogic.
  • The pair taken is now the one with the largest mana value delta rather than the first profitable one found, so a bigger swing against an opponent is no longer passed over for a smaller upgrade of our own.

AI:RemoveDeck:Random goes as well, since AI_CAN_PLAY needs both flags clear; DeckNeeds:Type$Artifact replaces it. Six tests, two of which fail without the fix.

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