mirror of
https://github.com/correl/mage.git
synced 2024-11-28 19:19:55 +00:00
Test framework: added AI support in basic card tests:
* added new command to play one best action on priority by AI (aiPlayPriority); * added new command to play all best actions on step by AI (aiPlayStep); * now you can setup battlefield by standard commands and run AI simulations for single priority/step; * CardTestPlayerBase - old default class for card tests, it uses simple AI for choosing, but do not play/simulate it; * CardTestPlayerBaseAI - old default class for AI game tests, auto-playable playerA, you can control only playerB; * CardTestPlayerBaseWithAIHelps - new improved class for card tests, it uses controllable and real AI with game simulations;
This commit is contained in:
parent
6cbf94bad6
commit
c767d19534
4 changed files with 166 additions and 2 deletions
|
@ -0,0 +1,77 @@
|
||||||
|
package org.mage.test.AI.basic;
|
||||||
|
|
||||||
|
import mage.constants.CardType;
|
||||||
|
import mage.constants.PhaseStep;
|
||||||
|
import mage.constants.Zone;
|
||||||
|
import mage.game.permanent.Permanent;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Ignore;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author JayDi85
|
||||||
|
*/
|
||||||
|
public class TestFrameworkCanPlayAITest extends CardTestPlayerBaseWithAIHelps {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_AI_PlayOnePriorityAction() {
|
||||||
|
addCard(Zone.HAND, playerA, "Lightning Bolt", 3);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
|
||||||
|
//
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Balduvian Bears", 5);
|
||||||
|
|
||||||
|
// AI must play one time
|
||||||
|
aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerA);
|
||||||
|
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
|
assertGraveyardCount(playerA, "Lightning Bolt", 1);
|
||||||
|
assertPermanentCount(playerB, "Balduvian Bears", 5 - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_AI_PlayManyActionsInOneStep() {
|
||||||
|
addCard(Zone.HAND, playerA, "Lightning Bolt", 3);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
|
||||||
|
//
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Balduvian Bears", 5);
|
||||||
|
|
||||||
|
// AI must play all step actions
|
||||||
|
aiPlayStep(1, PhaseStep.PRECOMBAT_MAIN, playerA);
|
||||||
|
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
|
assertGraveyardCount(playerA, "Lightning Bolt", 3);
|
||||||
|
assertPermanentCount(playerB, "Balduvian Bears", 5 - 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Ignore // AI can't play blade cause score system give priority for boost instead restriction effects like goad
|
||||||
|
public void test_AI_GoadedByBloodthirstyBlade_Normal() {
|
||||||
|
// Equipped creature gets +2/+0 and is goaded
|
||||||
|
// {1}: Attach Bloodthirsty Blade to target creature an opponent controls. Activate this ability only any time you could cast a sorcery.
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Bloodthirsty Blade", 1);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Forest", 1);
|
||||||
|
//
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Balduvian Bears", 1);
|
||||||
|
|
||||||
|
// AI must play
|
||||||
|
aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerA);
|
||||||
|
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
|
Assert.assertEquals(1, currentGame.getBattlefield().getAllActivePermanents(CardType.CREATURE).size());
|
||||||
|
Permanent permanent = currentGame.getBattlefield().getAllActivePermanents(CardType.CREATURE).get(0);
|
||||||
|
Assert.assertEquals(1, permanent.getAttachments().size());
|
||||||
|
}
|
||||||
|
}
|
|
@ -84,6 +84,7 @@ public class TestPlayer implements Player {
|
||||||
private int foundNoAction = 0;
|
private int foundNoAction = 0;
|
||||||
private boolean AIPlayer;
|
private boolean AIPlayer;
|
||||||
private final List<PlayerAction> actions = new ArrayList<>();
|
private final List<PlayerAction> actions = new ArrayList<>();
|
||||||
|
private final Map<PlayerAction, PhaseStep> actionsToRemovesLater = new HashMap<>(); // remove actions later, on next step (e.g. for AI commands)
|
||||||
private final List<String> choices = new ArrayList<>(); // choices stack for choice
|
private final List<String> choices = new ArrayList<>(); // choices stack for choice
|
||||||
private final List<String> targets = new ArrayList<>(); // targets stack for choose (it's uses on empty direct target by cast command)
|
private final List<String> targets = new ArrayList<>(); // targets stack for choose (it's uses on empty direct target by cast command)
|
||||||
private final Map<String, UUID> aliases = new HashMap<>(); // aliases for game objects/players (use it for cards with same name to save and use)
|
private final Map<String, UUID> aliases = new HashMap<>(); // aliases for game objects/players (use it for cards with same name to save and use)
|
||||||
|
@ -499,6 +500,18 @@ public class TestPlayer implements Player {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean priority(Game game) {
|
public boolean priority(Game game) {
|
||||||
|
// later remove actions (ai commands related)
|
||||||
|
if (actionsToRemovesLater.size() > 0) {
|
||||||
|
List<PlayerAction> removed = new ArrayList<>();
|
||||||
|
actionsToRemovesLater.forEach((action, step) -> {
|
||||||
|
if (game.getStep().getType() != step) {
|
||||||
|
actions.remove(action);
|
||||||
|
removed.add(action);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
removed.forEach(actionsToRemovesLater::remove);
|
||||||
|
}
|
||||||
|
|
||||||
int numberOfActions = actions.size();
|
int numberOfActions = actions.size();
|
||||||
List<PlayerAction> tempActions = new ArrayList<>();
|
List<PlayerAction> tempActions = new ArrayList<>();
|
||||||
tempActions.addAll(actions);
|
tempActions.addAll(actions);
|
||||||
|
@ -622,6 +635,25 @@ public class TestPlayer implements Player {
|
||||||
actions.remove(action);
|
actions.remove(action);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (action.getAction().startsWith(AI_PREFIX)) {
|
||||||
|
String command = action.getAction();
|
||||||
|
command = command.substring(command.indexOf(AI_PREFIX) + AI_PREFIX.length());
|
||||||
|
|
||||||
|
// play priority
|
||||||
|
if (command.equals(AI_COMMAND_PLAY_PRIORITY)) {
|
||||||
|
computerPlayer.priority(game);
|
||||||
|
actions.remove(action);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// play step
|
||||||
|
if (command.equals(AI_COMMAND_PLAY_STEP)) {
|
||||||
|
actionsToRemovesLater.put(action, game.getStep().getType());
|
||||||
|
computerPlayer.priority(game);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.fail("Unknow ai command: " + command);
|
||||||
} else if (action.getAction().startsWith(CHECK_PREFIX)) {
|
} else if (action.getAction().startsWith(CHECK_PREFIX)) {
|
||||||
String command = action.getAction();
|
String command = action.getAction();
|
||||||
command = command.substring(command.indexOf(CHECK_PREFIX) + CHECK_PREFIX.length());
|
command = command.substring(command.indexOf(CHECK_PREFIX) + CHECK_PREFIX.length());
|
||||||
|
@ -752,9 +784,9 @@ public class TestPlayer implements Player {
|
||||||
if (!wasProccessed) {
|
if (!wasProccessed) {
|
||||||
Assert.fail("Unknow check command or params: " + command);
|
Assert.fail("Unknow check command or params: " + command);
|
||||||
}
|
}
|
||||||
} else if (action.getAction().startsWith("show:")) {
|
} else if (action.getAction().startsWith(SHOW_PREFIX)) {
|
||||||
String command = action.getAction();
|
String command = action.getAction();
|
||||||
command = command.substring(command.indexOf("show:") + "show:".length());
|
command = command.substring(command.indexOf(SHOW_PREFIX) + SHOW_PREFIX.length());
|
||||||
|
|
||||||
String[] params = command.split(CHECK_PARAM_DELIMETER);
|
String[] params = command.split(CHECK_PARAM_DELIMETER);
|
||||||
boolean wasProccessed = false;
|
boolean wasProccessed = false;
|
||||||
|
@ -3640,4 +3672,8 @@ public class TestPlayer implements Player {
|
||||||
this.chooseStrictModeFailed("choice", game, getInfo(card) + " - can't select ability to cast.\n" + "Card's abilities:\n" + allInfo);
|
this.chooseStrictModeFailed("choice", game, getInfo(card) + " - can't select ability to cast.\n" + "Card's abilities:\n" + allInfo);
|
||||||
return computerPlayer.chooseAbilityForCast(card, game, noMana);
|
return computerPlayer.chooseAbilityForCast(card, game, noMana);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ComputerPlayer getComputerPlayer() {
|
||||||
|
return computerPlayer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
package org.mage.test.serverside.base;
|
||||||
|
|
||||||
|
import mage.constants.RangeOfInfluence;
|
||||||
|
import org.mage.test.player.TestComputerPlayer7;
|
||||||
|
import org.mage.test.player.TestPlayer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class but with latest computer player to test single AI commands (it's different from full AI simulation from CardTestPlayerBaseAI):
|
||||||
|
* 1. AI don't play normal priorities (you must use ai*** commands to play it);
|
||||||
|
* 2. AI will choose in non strict mode (it's simulated ComputerPlayer7, not simple ComputerPlayer from basic tests)
|
||||||
|
*
|
||||||
|
* @author JayDi85
|
||||||
|
*/
|
||||||
|
public abstract class CardTestPlayerBaseWithAIHelps extends CardTestPlayerBase {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected TestPlayer createPlayer(String name, RangeOfInfluence rangeOfInfluence) {
|
||||||
|
TestPlayer testPlayer = new TestPlayer(new TestComputerPlayer7(name, RangeOfInfluence.ONE, 6));
|
||||||
|
testPlayer.setAIPlayer(false); // AI can't play it by itself, use AI commands
|
||||||
|
return testPlayer;
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,6 +22,7 @@ import mage.game.GameOptions;
|
||||||
import mage.game.command.CommandObject;
|
import mage.game.command.CommandObject;
|
||||||
import mage.game.permanent.Permanent;
|
import mage.game.permanent.Permanent;
|
||||||
import mage.game.permanent.PermanentCard;
|
import mage.game.permanent.PermanentCard;
|
||||||
|
import mage.player.ai.ComputerPlayer7;
|
||||||
import mage.players.ManaPool;
|
import mage.players.ManaPool;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
import mage.util.CardUtil;
|
import mage.util.CardUtil;
|
||||||
|
@ -53,6 +54,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
|
||||||
public static final String ALIAS_PREFIX = "@"; // don't change -- it uses in user's tests
|
public static final String ALIAS_PREFIX = "@"; // don't change -- it uses in user's tests
|
||||||
public static final String CHECK_PARAM_DELIMETER = "#";
|
public static final String CHECK_PARAM_DELIMETER = "#";
|
||||||
public static final String CHECK_PREFIX = "check:"; // prefix for all check commands
|
public static final String CHECK_PREFIX = "check:"; // prefix for all check commands
|
||||||
|
public static final String SHOW_PREFIX = "show:"; // prefix for all show commands
|
||||||
|
public static final String AI_PREFIX = "ai:"; // prefix for all ai commands
|
||||||
|
|
||||||
static {
|
static {
|
||||||
// aliases can be used in check commands, so all prefixes and delimeters must be unique
|
// aliases can be used in check commands, so all prefixes and delimeters must be unique
|
||||||
|
@ -67,6 +70,10 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
|
||||||
public static final String ACTIVATE_PLAY = "activate:Play ";
|
public static final String ACTIVATE_PLAY = "activate:Play ";
|
||||||
public static final String ACTIVATE_CAST = "activate:Cast ";
|
public static final String ACTIVATE_CAST = "activate:Cast ";
|
||||||
|
|
||||||
|
// commands for AI
|
||||||
|
public static final String AI_COMMAND_PLAY_PRIORITY = "play priority";
|
||||||
|
public static final String AI_COMMAND_PLAY_STEP = "play step";
|
||||||
|
|
||||||
static {
|
static {
|
||||||
// cards can be played/casted by activate ability command too
|
// cards can be played/casted by activate ability command too
|
||||||
Assert.assertTrue("musts contains activate ability part", ACTIVATE_PLAY.startsWith(ACTIVATE_ABILITY));
|
Assert.assertTrue("musts contains activate ability part", ACTIVATE_PLAY.startsWith(ACTIVATE_ABILITY));
|
||||||
|
@ -1391,6 +1398,28 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
|
||||||
player.addAction(turnNum, step, ACTIVATE_CAST + cardName + "$targetPlayer=" + target.getName() + "$manaInPool=" + manaInPool);
|
player.addAction(turnNum, step, ACTIVATE_CAST + cardName + "$targetPlayer=" + target.getName() + "$manaInPool=" + manaInPool);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AI play one PRIORITY with multi game simulations (calcs and play ONE best action, can be called with stack)
|
||||||
|
*/
|
||||||
|
public void aiPlayPriority(int turnNum, PhaseStep step, TestPlayer player) {
|
||||||
|
assertAiPlayAndGameCompatible(player);
|
||||||
|
player.addAction(turnNum, step, AI_PREFIX + AI_COMMAND_PLAY_PRIORITY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AI play STEP to the end with multi game simulations (calcs and play best actions until step ends, can be called in the middle of the step)
|
||||||
|
*/
|
||||||
|
public void aiPlayStep(int turnNum, PhaseStep step, TestPlayer player) {
|
||||||
|
assertAiPlayAndGameCompatible(player);
|
||||||
|
player.addAction(turnNum, step, AI_PREFIX + AI_COMMAND_PLAY_STEP);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertAiPlayAndGameCompatible(TestPlayer player) {
|
||||||
|
if (player.isAIPlayer() || !(player.getComputerPlayer() instanceof ComputerPlayer7)) {
|
||||||
|
Assert.fail("AI commands supported by CardTestPlayerBaseWithAIHelps only");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void waitStackResolved(int turnNum, PhaseStep step, TestPlayer player) {
|
public void waitStackResolved(int turnNum, PhaseStep step, TestPlayer player) {
|
||||||
player.addAction(turnNum, step, "waitStackResolved");
|
player.addAction(turnNum, step, "waitStackResolved");
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue