mirror of
https://github.com/correl/mage.git
synced 2024-11-25 03:00:11 +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 boolean AIPlayer;
|
||||
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> 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)
|
||||
|
@ -499,6 +500,18 @@ public class TestPlayer implements Player {
|
|||
|
||||
@Override
|
||||
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();
|
||||
List<PlayerAction> tempActions = new ArrayList<>();
|
||||
tempActions.addAll(actions);
|
||||
|
@ -622,6 +635,25 @@ public class TestPlayer implements Player {
|
|||
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)) {
|
||||
String command = action.getAction();
|
||||
command = command.substring(command.indexOf(CHECK_PREFIX) + CHECK_PREFIX.length());
|
||||
|
@ -752,9 +784,9 @@ public class TestPlayer implements Player {
|
|||
if (!wasProccessed) {
|
||||
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();
|
||||
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);
|
||||
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);
|
||||
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.permanent.Permanent;
|
||||
import mage.game.permanent.PermanentCard;
|
||||
import mage.player.ai.ComputerPlayer7;
|
||||
import mage.players.ManaPool;
|
||||
import mage.players.Player;
|
||||
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 CHECK_PARAM_DELIMETER = "#";
|
||||
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 {
|
||||
// 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_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 {
|
||||
// cards can be played/casted by activate ability command too
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
player.addAction(turnNum, step, "waitStackResolved");
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue