AI and test framework improved:

* Now AI can see and use special mana payments like convoke, delve, improvise pays;
* Now devs can test special mana payments (disable auto-payment and use choices for mana pool and special pays);
* Fixed broken TargetDiscard in tests;
* Fixed broken same named targets in tests;
This commit is contained in:
Oleg Agafonov 2020-06-19 13:16:26 +04:00
parent c2e7b02e13
commit 10cf884923
2 changed files with 88 additions and 19 deletions

View file

@ -1533,10 +1533,34 @@ public class ComputerPlayer extends PlayerImpl implements Player {
}
}
}
// pay phyrexian life costs
if (cost instanceof PhyrexianManaCost) {
return cost.pay(null, game, null, playerId, false, null) || permittingObject != null;
}
// pay special mana like convoke cost (tap for pay)
// GUI: user see "special" button while pay spell's cost
// TODO: AI can't prioritize special mana types to pay, e.g. it will use first available
SpecialAction specialAction = game.getState().getSpecialActions().getControlledBy(this.getId(), true)
.values().stream().findFirst().orElse(null);
ManaOptions specialMana = specialAction == null ? null : specialAction.getManaOptions(ability, game, unpaid);
if (specialMana != null) {
for (Mana netMana : specialMana) {
if (cost.testPay(netMana) || permittingObject != null) {
if (netMana instanceof ConditionalMana && !((ConditionalMana) netMana).apply(ability, game, getId(), cost)) {
continue;
}
specialAction.setUnpaidMana(unpaid);
if (activateAbility(specialAction, game)) {
return true;
}
// only one time try to pay
break;
}
}
}
return false;
}

View file

@ -1795,7 +1795,7 @@ public class TestPlayer implements Player {
assertAliasSupportInChoices(true);
if (!choices.isEmpty()) {
List<String> usedChoices = new ArrayList<>();
List<Integer> usedChoices = new ArrayList<>();
List<UUID> usedTargets = new ArrayList<>();
Ability source = null;
@ -1883,7 +1883,8 @@ public class TestPlayer implements Player {
boolean targetCompleted = false;
CheckAllChoices:
for (String choiceRecord : choices) {
for (int choiceIndex = 0; choiceIndex < choices.size(); choiceIndex++) {
String choiceRecord = choices.get(choiceIndex);
if (targetCompleted) {
break CheckAllChoices;
}
@ -1923,14 +1924,19 @@ public class TestPlayer implements Player {
}
if (targetFound) {
usedChoices.add(choiceRecord);
usedChoices.add(choiceIndex);
}
}
// apply only on ALL targets or revert
if (usedChoices.size() > 0) {
if (target.isChosen()) {
choices.removeAll(usedChoices);
// remove all used choices
for (int i = choices.size(); i >= 0; i--) {
if (usedChoices.contains(i)) {
choices.remove(i);
}
}
return true;
} else {
Assert.fail("Not full targets list.");
@ -2091,7 +2097,7 @@ public class TestPlayer implements Player {
if ((permanent.isCopy() && !originOnly) || (!permanent.isCopy() && !copyOnly)) {
target.addTarget(permanent.getId(), source, game);
targetFound = true;
break; // return to for (String targetName
break; // return to next targetName
}
}
}
@ -2105,18 +2111,19 @@ public class TestPlayer implements Player {
}
// card in hand
if (target.getOriginalTarget() instanceof TargetCardInHand) {
if (target.getOriginalTarget() instanceof TargetCardInHand
|| target.getOriginalTarget() instanceof TargetDiscard) {
for (String targetDefinition : targets) {
checkTargetDefinitionMarksSupport(target, targetDefinition, "^");
String[] targetList = targetDefinition.split("\\^");
boolean targetFound = false;
for (String targetName : targetList) {
for (Card card : computerPlayer.getHand().getCards(((TargetCardInHand) target.getOriginalTarget()).getFilter(), game)) {
for (Card card : computerPlayer.getHand().getCards(((TargetCard) target.getOriginalTarget()).getFilter(), game)) {
if (hasObjectTargetNameOrAlias(card, targetName) || (card.getName() + '-' + card.getExpansionSetCode()).equals(targetName)) { // TODO: remove set code search?
if (target.canTarget(abilityControllerId, card.getId(), source, game) && !target.getTargets().contains(card.getId())) {
target.addTarget(card.getId(), source, game);
targetFound = true;
break; // return to for (String targetName
break; // return to next targetName
}
}
}
@ -2141,7 +2148,7 @@ public class TestPlayer implements Player {
if (target.canTarget(abilityControllerId, card.getId(), source, game) && !target.getTargets().contains(card.getId())) {
target.addTarget(card.getId(), source, game);
targetFound = true;
break; // return to for (String targetName
break; // return to next targetName
}
}
}
@ -2166,7 +2173,7 @@ public class TestPlayer implements Player {
if (targetFull.canTarget(abilityControllerId, card.getId(), source, game) && !targetFull.getTargets().contains(card.getId())) {
targetFull.add(card.getId(), game);
targetFound = true;
break; // return to for (String targetName
break; // return to next targetName
}
}
}
@ -2216,7 +2223,7 @@ public class TestPlayer implements Player {
if (target.canTarget(abilityControllerId, card.getId(), source, game) && !target.getTargets().contains(card.getId())) {
target.addTarget(card.getId(), source, game);
targetFound = true;
break IterateGraveyards; // return to for (String targetName
break IterateGraveyards; // return to next targetName
}
}
}
@ -2243,7 +2250,7 @@ public class TestPlayer implements Player {
if (target.canTarget(abilityControllerId, stackObject.getId(), source, game) && !target.getTargets().contains(stackObject.getId())) {
target.addTarget(stackObject.getId(), source, game);
targetFound = true;
break; // return to for (String targetName
break; // return to next targetName
}
}
}
@ -3620,38 +3627,76 @@ public class TestPlayer implements Player {
groupsForTargetHandling = null;
if (!computerPlayer.getManaPool().isAutoPayment()) {
// manual pay by mana clicks/commands
if (!choices.isEmpty()) {
String needColor = choices.get(0);
switch (needColor) {
// manual pay by mana clicks/commands
String choice = choices.get(0);
boolean choiceUsed = false;
boolean choiceRemoved = false;
switch (choice) {
case "White":
Assert.assertTrue("pool must have white mana", computerPlayer.getManaPool().getWhite() > 0);
computerPlayer.getManaPool().unlockManaType(ManaType.WHITE);
choiceUsed = true;
break;
case "Blue":
Assert.assertTrue("pool must have blue mana", computerPlayer.getManaPool().getBlue() > 0);
computerPlayer.getManaPool().unlockManaType(ManaType.BLUE);
choiceUsed = true;
break;
case "Black":
Assert.assertTrue("pool must have black mana", computerPlayer.getManaPool().getBlack() > 0);
computerPlayer.getManaPool().unlockManaType(ManaType.BLACK);
choiceUsed = true;
break;
case "Red":
Assert.assertTrue("pool must have red mana", computerPlayer.getManaPool().getRed() > 0);
computerPlayer.getManaPool().unlockManaType(ManaType.RED);
choiceUsed = true;
break;
case "Green":
Assert.assertTrue("pool must have green mana", computerPlayer.getManaPool().getGreen() > 0);
computerPlayer.getManaPool().unlockManaType(ManaType.GREEN);
choiceUsed = true;
break;
case "Colorless":
Assert.assertTrue("pool must have colorless mana", computerPlayer.getManaPool().getColorless() > 0);
computerPlayer.getManaPool().unlockManaType(ManaType.COLORLESS);
choiceUsed = true;
break;
default:
Assert.fail("Unknown choice command for mana unlock: " + needColor);
// go to special block
//Assert.fail("Unknown choice command for mana unlock: " + needColor);
break;
}
// manual pay by special actions like convoke
if (!choiceUsed) {
Map<UUID, SpecialAction> specialActions = game.getState().getSpecialActions().getControlledBy(this.getId(), true);
for (SpecialAction specialAction : specialActions.values()) {
if (specialAction.getRule(true).startsWith(choice)) {
if (specialAction.canActivate(this.getId(), game).canActivate()) {
choices.remove(0);
return true;
choiceRemoved = true;
specialAction.setUnpaidMana(unpaid);
if (activateAbility(specialAction, game)) {
choiceUsed = true;
}
Assert.fail(this.getName() + " disabled mana auto-payment, but no choices found for color unlock in pool for unpaid cost: " + unpaid.getText());
}
}
}
}
if (choiceUsed) {
if (!choiceRemoved) {
choices.remove(0);
}
return true;
} else {
Assert.fail("Can't use choice in play mana: " + choice);
}
}
Assert.fail(this.getName() + " disabled mana auto-payment, but no choices found for color unlock in pool or special action for unpaid cost: " + unpaid.getText());
}
return computerPlayer.playMana(ability, unpaid, promptText, game);