Fix AI cloning creatures that shrink under its own control - #11675
Fix AI cloning creatures that shrink under its own control#11675liamiak wants to merge 3 commits into
Conversation
|
The idea is nice, but over-engineered in typical AI fashion :P See my related implementation here: #11690 |
CloneAi ranks candidates with evaluateCreature, which reads live power and toughness. For a characteristic-defining creature those are computed under whoever controls it now, so an opponent's Nightmare scores as the 8/8 it is for them rather than the 0/0 it would be for us. With Cryptoplasm out and the opponent holding a Nightmare and eight Swamps, the AI copied it, became a 0/0 and died on the spot. The value cannot be worked out ahead of the fact. Power and toughness come from the continuous effects layer, which only runs for cards on the battlefield: an LKI copy with its controller reassigned still reports the opponent's numbers. So a candidate carrying a CharacteristicDefining static that an opponent controls is now left alone unless the AI can actually price it. Everything else is taken on the evaluation as before, which is most of the time and costs nothing. The check is deliberately broader than the cards that are really controller-dependent, because a false positive passes over one candidate while a false negative only leaves the previous behaviour in place. Where the profile already pays for simulation, it can do better than pass over them: the candidates are walked in evaluation order and the first one a one-play simulation agrees is an improvement is taken, so an opponent's Nightmare is copied when we have Swamps of our own too. No simulation is run outside those profiles - every other entry to the simulator is behind usesHybridSimulation or usesFullSimulation and this keeps to that. That path needed an engine fix. GameSimulator.findSaInSimGame matches on description against the host card's spell abilities, and a trigger's ability is in neither, so it returned the "could not be simulated" sentinel - which OnePlaySafetyChecker treats as approval. Trigger ids are reassigned by the copy, but the order is rebuilt from the same script, so it now falls back to matching on position and confirms the API. Six cards reach this with nothing keeping them out of AI decks: Artisan of Forms, Cephalid Facetaker, Cryptoplasm, Crystalline Resonance, Hulkling and Protean Thaumaturge. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Plays until Cryptoplasm's upkeep trigger fires of its own accord. Two of the default profile cases fail without the fix: copying an opponent's Nightmare and dying as a 0/0, and the same board with a Serra Angel alongside, where it should reach the honest second best instead. A third has no controller-dependent creature in reach and covers the ordinary path. The two simulation cases cover the other half: with Swamps of our own the Nightmare is worth copying and is taken, without them it is refused. The second of those fails if the GameSimulator trigger lookup is dropped, since the simulation then reports that it could not run and OnePlaySafetyChecker reads that as approval. Drop this commit if you would rather not carry the tests. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Card-Forge#11690 answers this far more cheaply than this branch did: take an LKI copy, put it under our control, recheck statics on that one card, and see whether the toughness-setting CDA leaves it alive. No simulation, and it works on every AI profile. The version here ranked candidates and then paid for up to three one-play simulations, gated on usesHybridSimulation() || usesFullSimulation(). On the default profile that gate is false, so it never priced a CDA creature at all - it just skipped past every one of them. Avoidance, not an answer. Lift the check out of CopyPermanentAi into ComputerUtilCard.filterOutFatalCopies and call it from both, as suggested on the PR. CopyPermanentAi keeps its own SetToughness guard, since that is about the copy effect overriding toughness rather than the candidate. The GameSimulator trigger-matching hunk and its test go with the simulation approach - nothing calls that path now. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
70d9c31 to
e4faae4
Compare
|
Done — thanks, your version is better than mine on two counts, not just smaller. Lifted the check out of The old version also had a hole I had not spotted: it was gated on What is left on top of master is one behaviour: don't clone into a body that dies under our control. Their 8/8 Nightmare while we hold Islands is a 0/0 for us, and
Three tests, all through |
CloneAiranks candidates withevaluateCreature, which reads live power and toughness. For a characteristic-defining creature those are computed under whoever controls it now, so an opponent's Nightmare scores as the 8/8 it is for them rather than the 0/0 it would be for us.With Cryptoplasm out and the opponent holding a Nightmare and eight Swamps, the AI copies it, becomes a 0/0 and dies on the spot. Six cards reach this with nothing keeping them out of AI decks: Artisan of Forms, Cephalid Facetaker, Cryptoplasm, Crystalline Resonance, Hulkling and Protean Thaumaturge. The one flagged card on the path, Mindlink Mech, only ever sees creatures that crewed it.
The check
#11690 already answers this for token copies: take an LKI copy, put it under our control, recheck statics on that card alone, and see whether a toughness-setting CDA leaves it alive. That is now
ComputerUtilCard.filterOutFatalCopies, called from bothCopyPermanentAiandCloneAi.An earlier version of this PR claimed the value could not be worked out ahead of the fact and priced candidates with a one-play simulation instead. That was wrong on both halves — the LKI route works, and the simulation was gated behind
usesHybridSimulation/usesFullSimulation, so on the default profile it skipped every CDA candidate rather than pricing one.Known gap: cards choosing through
Choices$rather than targeting — Vesuvan Doppelganger — resolve viaCloneEffectandchooseSingleEntityForEffect, a path this does not touch.Three tests, all driving
AiControlleron the default profile; two fail without the fix. Full desktop suite: 360 tests, 0 failures.Written with Claude Opus 5 (also recorded in the commit co-authors).