I've been trying to fix the fact that the AI is able to pay 0 for Marath's ability, even though it shouldn't be allowed.
First, to simplify things, I've made this more simplified version of Marath's ability that still has a problem:
- Code: Select all
A:AB$ Token | Cost$ X SubCounter<X/P1P1> | Announce$ X | XCantBe0$ True | TokenAmount$ 1 | TokenName$ Elemental | TokenOwner$ You | TokenPower$ X | TokenToughness$ X | TokenTypes$ Creature,Elemental | TokenColors$ Green | TokenImage$ g x x elemental | References$ X | Defined$ You | SpellDescription$ Put an X/X green Elemental creature token onto the battlefield. X can't be 0.
SVar:X:Count$xPaid
The above still works correctly for the human player, but AI does not play the ability correctly. Although, the problem is slightly different than before. It now plays it for
X=3 for example, but fails to remove the actual counters from Marath.
I've narrowed the above down to this difference between Human and AI logic.
AI logic (CostPayment.java):
- Code: Select all
for (final CostPart part : this.cost.getCostParts()) {
PaymentDecision decision = part.accept(decisionMaker);
// ...
decisions.put(part, decision);
}
for (final CostPart part : this.cost.getCostParts()) {
if (!part.payAsDecided(decisionMaker.getPlayer(), decisions.get(part), this.ability)) {
return false;
}
// ...
}
Whereas, the human logic only has a single pass algorithm. Note the ability has two CostParts - one to pay
X mana and a second to remove
X counters.
The 1-pass algorithm gets the right result - because first it pays the mana - and then when it gets to removing the counters, it ends up getting the value from getXManaCostPaid() which has been set when the mana was paid. However, the two pass algorithm doesn't work. When it first creates the Decisions, it creates a Decision for paying the
X mana but does not pay it (thus does not set XManaCostPaid) - so when it comes to evaluating how many counters to remove, it ends up treating it as 0.
Given, the above, it's easy to see why it doesn't work. But I'm not sure what's the right fix. For example, we can make the AI use a one-pass algorithm like the human, but probably there's a reason it's 2-pass? Or perhaps the card .txt can be adjusted somehow? For example, does
X need to be set to Count$xPaid? But if I don't have that line in the txt, it seems the AI can cast it for 0. Or perhaps the right fix is to treat "Count$xPaid" as some special value? i.e. either don't resolve it to a number in the Decision object (i.e. delay its resolution) or calculate it based on existing Decisions?
Another thing to note is that it's not clear to me that there's anything in AI/engine code that looks like "XCantBe0$ True". It seems that maybe only HumanPlaySpellAbility.java looks at that. However, there seems to exist another syntax - which is to put XCantBe0 as an entry in the Cost line. Anyone know what's the difference between these two?