Skip to content

Stop the AI paying real costs for abilities that do nothing - #11441

Closed
omraj21 wants to merge 2 commits into
Card-Forge:masterfrom
omraj21:ai-monarch-fix
Closed

Stop the AI paying real costs for abilities that do nothing#11441
omraj21 wants to merge 2 commits into
Card-Forge:masterfrom
omraj21:ai-monarch-fix

Conversation

@omraj21

@omraj21 omraj21 commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Two abilities were mapped to AlwaysPlayAi, whose entire logic is
return WillPlay with no look at the board. In both cases the engine can resolve
the ability to a no-op (or worse), while the activation cost sacrifices a permanent.

Monarch

GameAction.becomeMonarch returns immediately when the chosen player is already
the monarch, so activating one of these abilities while holding the crown does
nothing. Throne of the High City taps and sacrifices itself for {4}, King
Solomon's Frogs exiles itself, and Tchaka, Venerable King exiles itself from the
graveyard, so the AI was throwing a permanent away for no effect. With the old
mapping the added test shows Throne of the High City going from one copy on the
battlefield to zero.

BecomeMonarchAi declines only when every player the ability would crown is
already the monarch. Targeted versions are deliberately left alone: cards like
Jared Carthalion, True Heir and Garland, Royal Kidnapper hand the crown to an
opponent on purpose to turn on another ability.

Initiative is intentionally not changed - GameAction.takeInitiative documents
that "You can take the initiative even if you already have it" and still runs the
trigger, so always-play is correct there.

Rooms

UnlockDoor with Mode$ LockOrUnlock is wrong in two ways. When every door of
the targeted Room is already unlocked, UnlockDoorEffect's case 0 branch can
only call lockRoom, so the ability shuts off one of the AI's own Rooms. Keys to
the House pays {3}, taps and sacrifices itself for that ability.

When exactly one door is locked, the effect offers both doors and locks the one
that is still open. That choice went through SpellAbilityAi.chooseCardState,
whose base implementation prints a "default implementation is used" warning and
returns the first element, so which door the AI got was down to state ordering.

UnlockDoorAi declines when no reachable Room has a locked door, and picks a
locked door when asked to choose. Mode$ Unlock and Mode$ ThisDoor can only
ever unlock, so they keep the previous behaviour. Affects Keys to the House and
Marina Vendrell.

Each fix comes with a test that fails on the old mapping.

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


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

omraj21 and others added 2 commits July 29, 2026 01:46
BecomeMonarch was mapped to AlwaysPlayAi, which accepts every activation without
looking at the board. GameAction.becomeMonarch returns immediately when the
chosen player is already the monarch, so activating one of these abilities while
holding the crown does nothing.

That is not just a wasted activation. Throne of the High City taps and sacrifices
itself for 4, King Solomon's Frogs exiles itself, and Tchaka, Venerable King
exiles itself from the graveyard, so the AI was throwing a permanent away for no
effect. A test with the old mapping shows Throne of the High City going from one
copy on the battlefield to zero.

Add BecomeMonarchAi, which declines only when every player the ability would
crown is already the monarch. Targeted versions are deliberately left alone:
cards like Jared Carthalion, True Heir and Garland, Royal Kidnapper hand the
crown to an opponent on purpose to turn on another ability, so the previous
always-play behaviour is still right for them.

Initiative is intentionally not changed. GameAction.takeInitiative documents that
"You can take the initiative even if you already have it" and still runs the
trigger, so AlwaysPlayAi remains correct there.

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

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
UnlockDoor was mapped to AlwaysPlayAi, which accepts every activation without
looking at the board. For Mode$ LockOrUnlock that is wrong in two ways.

When every door of the targeted Room is already unlocked, UnlockDoorEffect's
case 0 branch can only call lockRoom, so the ability shuts off one of the AI's
own Rooms. Keys to the House pays {3}, taps and sacrifices itself for that
ability, so the AI was throwing away a permanent to make its own board worse.
With the old mapping the test here shows Keys to the House going from one copy
on the battlefield to zero.

When exactly one door is locked, the effect offers both doors and locks the one
that is still open. The choice went through SpellAbilityAi.chooseCardState,
whose base implementation prints a "default implementation is used" warning and
returns the first element, so which door the AI got was down to state ordering
rather than a decision.

Add UnlockDoorAi: decline when no reachable Room has a locked door, and pick a
locked door when asked to choose, so the ability always opens a Room instead of
closing one. Mode$ Unlock and Mode$ ThisDoor can only ever unlock, so they keep
the previous always-play behaviour.

Affects Keys to the House and Marina Vendrell.

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

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Hanmac

Hanmac commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Probably should do separate PRs for Monarch/Unlock

@omraj21

omraj21 commented Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

Splitting this into two PRs so each fix can be reviewed on its own, since they are unrelated beyond both being AlwaysPlayAi mappings:

Same commits, no content changes apart from correcting the test counts now that each branch is measured on its own.

@omraj21 omraj21 closed this Jul 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI General AI tag Game Mechanics

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants