mirror of
https://github.com/magefree/mage.git
synced 2025-12-25 04:52:07 -08:00
test framework: improved aiXXX commands support:
- added more options for priority control (play single priority, play multiple priorities until stack resolved); - added more options for step control (play single step, play multiple steps); - improved compatibility with AI and real time commands (now check commands can be called inside AI controlled steps); - added tests for assign non-blocked damage;
This commit is contained in:
parent
a06935da81
commit
a9bdf2eb18
12 changed files with 342 additions and 88 deletions
|
|
@ -48,8 +48,8 @@ public class CopyAITest extends CardTestPlayerBaseWithAIHelps {
|
|||
//
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Balduvian Bears", 1); // 2/2
|
||||
|
||||
// clone (AI must choose most valueable permanent - own)
|
||||
aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerA);
|
||||
// clone (AI must choose most valuable permanent - own)
|
||||
aiPlayStep(1, PhaseStep.PRECOMBAT_MAIN, playerA);
|
||||
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
setStrictChooseMode(true);
|
||||
|
|
@ -70,8 +70,8 @@ public class CopyAITest extends CardTestPlayerBaseWithAIHelps {
|
|||
addCard(Zone.BATTLEFIELD, playerB, "Balduvian Bears", 1); // 2/2
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Spectral Bears", 1); // 3/3
|
||||
|
||||
// clone (AI must choose most valueable permanent - opponent)
|
||||
aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerA);
|
||||
// clone (AI must choose most valuable permanent - opponent)
|
||||
aiPlayStep(1, PhaseStep.PRECOMBAT_MAIN, playerA);
|
||||
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
setStrictChooseMode(true);
|
||||
|
|
|
|||
|
|
@ -15,14 +15,36 @@ import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps;
|
|||
public class TestFrameworkCanPlayAITest extends CardTestPlayerBaseWithAIHelps {
|
||||
|
||||
@Test
|
||||
public void test_AI_PlayOnePriorityAction() {
|
||||
public void test_AI_PlayOnePriority_FromSingle() {
|
||||
addCard(Zone.HAND, playerA, "Lightning Bolt", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
|
||||
//
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Balduvian Bears", 3);
|
||||
|
||||
// AI must play one time
|
||||
aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerA);
|
||||
// make sure runtime commands can be called with same priority after stack resolve
|
||||
checkGraveyardCount("after resolve", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", 1);
|
||||
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
setStrictChooseMode(true);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerA, "Lightning Bolt", 1);
|
||||
assertPermanentCount(playerB, "Balduvian Bears", 3 - 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_AI_PlayOnePriority_FromMultiple() {
|
||||
// must choose only 1 spell
|
||||
|
||||
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);
|
||||
aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerA, false); // stop after first spell
|
||||
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
setStrictChooseMode(true);
|
||||
|
|
@ -33,7 +55,45 @@ public class TestFrameworkCanPlayAITest extends CardTestPlayerBaseWithAIHelps {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void test_AI_PlayManyActionsInOneStep() {
|
||||
public void test_AI_PlayOnePriority_WithChoicesOnCast() {
|
||||
// 1/1
|
||||
// {2}{W}, Discard a card: Aven Trooper gets +1/+2 until end of turn.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Aven Trooper", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains", 3);
|
||||
addCard(Zone.HAND, playerA, "Mountain", 3);
|
||||
|
||||
// AI must play one time and make all choices on cast
|
||||
aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerA);
|
||||
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
setStrictChooseMode(true);
|
||||
execute();
|
||||
|
||||
assertPowerToughness(playerA, "Aven Trooper", 1 + 1, 1 + 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_AI_PlayOnePriority_WithChoicesOnResolve() {
|
||||
// {2}{B}, {T}: Target player discards a card. Activate only as a sorcery.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Cat Burglar", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
|
||||
//
|
||||
addCard(Zone.HAND, playerB, "Mountain", 5);
|
||||
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{B}, {T}: Target player", playerB);
|
||||
|
||||
// AI must be able to use choices
|
||||
aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerB);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerB, "Mountain", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_AI_PlayManyPrioritiesInOneStep() {
|
||||
addCard(Zone.HAND, playerA, "Lightning Bolt", 3);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
|
||||
//
|
||||
|
|
@ -50,6 +110,86 @@ public class TestFrameworkCanPlayAITest extends CardTestPlayerBaseWithAIHelps {
|
|||
assertPermanentCount(playerB, "Balduvian Bears", 5 - 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_AI_CleanStepCommands() {
|
||||
// some step commands don't have priorities, so it must be clean another way, e.g. on start of the turn
|
||||
aiPlayStep(1, PhaseStep.PRECOMBAT_MAIN, PhaseStep.END_TURN, playerA);
|
||||
|
||||
setStopAt(3, PhaseStep.END_TURN);
|
||||
setStrictChooseMode(true);
|
||||
execute(); // on failed clean code it will raise error about not used command
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_AI_PlayStep_CallRuntimeCommandsInsideAIControl() {
|
||||
// make sure runtime check commands can be called under AI control
|
||||
|
||||
runCode("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> {
|
||||
Assert.assertFalse("must be non AI before", player.isComputer());
|
||||
});
|
||||
|
||||
aiPlayStep(1, PhaseStep.PRECOMBAT_MAIN, PhaseStep.END_TURN, playerA);
|
||||
|
||||
checkLife("on same start", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 20);
|
||||
runCode("on inner step", 1, PhaseStep.BEGIN_COMBAT, playerA, (info, player, game) -> {
|
||||
Assert.assertTrue("must be AI", player.isComputer());
|
||||
});
|
||||
//
|
||||
checkLife("on inner step", 1, PhaseStep.DECLARE_ATTACKERS, playerA, 20);
|
||||
runCode("on inner step", 1, PhaseStep.BEGIN_COMBAT, playerA, (info, player, game) -> {
|
||||
Assert.assertTrue("must be AI", player.isComputer());
|
||||
});
|
||||
//
|
||||
checkLife("on same end", 1, PhaseStep.END_TURN, playerA, 20);
|
||||
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
setStrictChooseMode(true);
|
||||
execute();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_AI_PlayPriority_CallRuntimeCommandsInsideAIControl() {
|
||||
// make sure runtime check commands can be called under AI control
|
||||
|
||||
runCode("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> {
|
||||
Assert.assertFalse("must be non AI before", player.isComputer());
|
||||
});
|
||||
|
||||
aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerA);
|
||||
|
||||
checkLife("on same start", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 20);
|
||||
runCode("on same start", 1, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> {
|
||||
Assert.assertFalse("must be non AI after", player.isComputer());
|
||||
});
|
||||
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
setStrictChooseMode(true);
|
||||
execute();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_AI_PlayManyPrioritiesInManySteps() {
|
||||
addCard(Zone.HAND, playerA, "Lightning Bolt", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
|
||||
//
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Balduvian Bears", 1);
|
||||
//
|
||||
addCard(Zone.HAND, playerA, "Mountain", 1);
|
||||
|
||||
// AI must play multiple actions in multiple steps:
|
||||
// - cast bolt
|
||||
// - play land on second main
|
||||
aiPlayStep(1, PhaseStep.DECLARE_ATTACKERS, PhaseStep.POSTCOMBAT_MAIN, playerA);
|
||||
|
||||
setStopAt(3, PhaseStep.END_TURN); // make sure no land plays on turn 1
|
||||
setStrictChooseMode(true);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerA, "Lightning Bolt", 1);
|
||||
assertPermanentCount(playerB, "Balduvian Bears", 0);
|
||||
assertPermanentCount(playerA, "Mountain", 2 + 1); // must play land on second main
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_AI_Attack() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,71 @@
|
|||
package org.mage.test.cards.damage;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class AssignDamageTest extends CardTestPlayerBaseWithAIHelps {
|
||||
|
||||
@Test
|
||||
public void test_ThornElemental_Manual_DamageToBlock() {
|
||||
// 7/7
|
||||
// You may have Thorn Elemental assign its combat damage as though it weren't blocked.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Thorn Elemental", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Grizzly Bears");
|
||||
|
||||
// attack and kill bear due block
|
||||
attack(1, playerA, "Thorn Elemental");
|
||||
block(1, playerB, "Grizzly Bears", "Thorn Elemental");
|
||||
setChoice(playerA, false); // use blocked damage
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertLife(playerB, 20);
|
||||
assertGraveyardCount(playerB, "Grizzly Bears", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_ThornElemental_Manual_DamageToPlayer() {
|
||||
// 7/7
|
||||
// You may have Thorn Elemental assign its combat damage as though it weren't blocked.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Thorn Elemental", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Grizzly Bears");
|
||||
|
||||
// attack and ignore bear
|
||||
attack(1, playerA, "Thorn Elemental");
|
||||
block(1, playerB, "Grizzly Bears", "Thorn Elemental");
|
||||
setChoice(playerA, true); // use ignored damage
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertLife(playerB, 20 - 7);
|
||||
assertDamageReceived(playerB, "Grizzly Bears", 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_ThornElemental_AI() {
|
||||
// 7/7
|
||||
// You may have Thorn Elemental assign its combat damage as though it weren't blocked.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Thorn Elemental", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Grizzly Bears");
|
||||
|
||||
// AI must attack and decide to ignore block damage (e.g. damage a player)
|
||||
aiPlayStep(1, PhaseStep.DECLARE_ATTACKERS, playerA);
|
||||
block(1, playerB, "Grizzly Bears", "Thorn Elemental");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertLife(playerB, 20 - 7);
|
||||
assertDamageReceived(playerB, "Grizzly Bears", 0);
|
||||
}
|
||||
}
|
||||
|
|
@ -489,6 +489,7 @@ public class RollDiceTest extends CardTestPlayerBaseWithAIHelps {
|
|||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Box of Free-Range Goblins");
|
||||
setDieRollResult(playerA, 3); // normal roll
|
||||
setDieRollResult(playerA, 6); // additional roll
|
||||
|
||||
// AI must choose max value due good outcome
|
||||
aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerA);
|
||||
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ public class RadiateTest extends CardTestPlayerBaseWithAIHelps {
|
|||
addCard(Zone.BATTLEFIELD, playerB, "Kitesail Corsair", 2);
|
||||
|
||||
// cast bolt and copy spell for each another target
|
||||
// must call commands manually cause it's a bad scenario and AI don't cast it itself
|
||||
// must call commands manually because it's a bad scenario and AI don't cast it itself
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB);
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Radiate", "Lightning Bolt", "Lightning Bolt");
|
||||
aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerA); // but AI can choose targets
|
||||
|
|
|
|||
|
|
@ -49,12 +49,14 @@ public class PlayerAction {
|
|||
|
||||
/**
|
||||
* Calls after action removed from commands queue later (for multi steps
|
||||
* action, e.g.AI related)
|
||||
*
|
||||
* @param game
|
||||
* @param player
|
||||
* action, e.g. AI related)
|
||||
*/
|
||||
public void onActionRemovedLater(Game game, TestPlayer player) {
|
||||
//
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "T" + this.turnNum + "." + this.step.getStepShortText() + ": " + this.action;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,6 @@ import mage.util.MultiAmountMessage;
|
|||
import mage.util.RandomUtil;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.junit.Assert;
|
||||
import static org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl.*;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
|
|
@ -62,6 +61,8 @@ import java.util.regex.Matcher;
|
|||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl.*;
|
||||
|
||||
/**
|
||||
* Basic implementation of testable player
|
||||
*
|
||||
|
|
@ -90,15 +91,16 @@ public class TestPlayer implements Player {
|
|||
|
||||
// warning, test player do not restore own data by game rollback
|
||||
|
||||
// full playable AI, TODO: can be deleted?
|
||||
private boolean AIPlayer;
|
||||
// full playable AI
|
||||
private boolean AIPlayer; // TODO: better rename
|
||||
// AI simulates a real game, e.g. ignores strict mode and play command/priority, see aiXXX commands
|
||||
// true - unit tests uses real AI logic (e.g. AI hints and AI workarounds in cards)
|
||||
// false - unit tests uses Human logic and dialogs
|
||||
// false - unit tests uses Human logic and dialogs (in non-strict mode AI replace miss target/choice commands)
|
||||
private boolean AIRealGameSimulation = false;
|
||||
private PhaseStep AIRealGameControlUntil = null; // enable temporary AI control until some point in time
|
||||
|
||||
private final List<PlayerAction> actions = new ArrayList<>();
|
||||
private final Map<PlayerAction, PhaseStep> actionsToRemoveLater = new HashMap<>(); // remove actions later, on next step (e.g. for AI commands)
|
||||
//private final Map<PlayerAction, PhaseStep> actionsToRemoveLater = new HashMap<>(); // remove actions after some step (used for AI commands)
|
||||
private final Map<Integer, HashMap<UUID, ArrayList<PlayerAction>>> rollbackActions = new HashMap<>(); // actions to add after a executed rollback
|
||||
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)
|
||||
|
|
@ -140,6 +142,7 @@ public class TestPlayer implements Player {
|
|||
public TestPlayer(final TestPlayer testPlayer) {
|
||||
this.AIPlayer = testPlayer.AIPlayer;
|
||||
this.AIRealGameSimulation = testPlayer.AIRealGameSimulation;
|
||||
this.AIRealGameControlUntil = testPlayer.AIRealGameControlUntil;
|
||||
this.foundNoAction = testPlayer.foundNoAction;
|
||||
this.actions.addAll(testPlayer.actions);
|
||||
this.choices.addAll(testPlayer.choices);
|
||||
|
|
@ -573,17 +576,23 @@ public class TestPlayer implements Player {
|
|||
|
||||
@Override
|
||||
public boolean priority(Game game) {
|
||||
// later remove actions (ai commands related)
|
||||
if (actionsToRemoveLater.size() > 0) {
|
||||
List<PlayerAction> removed = new ArrayList<>();
|
||||
actionsToRemoveLater.forEach((action, step) -> {
|
||||
if (game.getTurnStepType() != step) {
|
||||
action.onActionRemovedLater(game, this);
|
||||
actions.remove(action);
|
||||
removed.add(action);
|
||||
}
|
||||
});
|
||||
removed.forEach(actionsToRemoveLater::remove);
|
||||
boolean oldControl = AIRealGameSimulation;
|
||||
if (AIPlayer) {
|
||||
// full AI control
|
||||
changeAIControl(game, true);
|
||||
} else {
|
||||
// temporary AI control
|
||||
// after enabled on priority it must work until end of the priority
|
||||
// e.g. AI can be called multiple times in complex choices
|
||||
if (game.getTurnStepType().equals(PhaseStep.UPKEEP)) {
|
||||
// reset
|
||||
AIRealGameControlUntil = null;
|
||||
changeAIControl(game, false);
|
||||
} else {
|
||||
// setup
|
||||
boolean enable = AIRealGameControlUntil != null && game.getTurnStepType().getIndex() <= AIRealGameControlUntil.getIndex();
|
||||
changeAIControl(game, enable);
|
||||
}
|
||||
}
|
||||
|
||||
// fake test ability for triggers and events
|
||||
|
|
@ -746,19 +755,31 @@ public class TestPlayer implements Player {
|
|||
String command = action.getAction();
|
||||
command = command.substring(command.indexOf(AI_PREFIX) + AI_PREFIX.length());
|
||||
|
||||
// play priority
|
||||
if (command.equals(AI_COMMAND_PLAY_PRIORITY)) {
|
||||
AIRealGameSimulation = true; // disable on action's remove
|
||||
// play single priority, two modes support:
|
||||
// - really single priority
|
||||
// - multiple priorities until empty stack
|
||||
if (command.startsWith(AI_COMMAND_PLAY_PRIORITY)) {
|
||||
boolean needEmptyStack = Boolean.parseBoolean(command.split(AI_PARAM_DELIMETER)[1]);
|
||||
changeAIControl(game, true);
|
||||
computerPlayer.priority(game);
|
||||
actions.remove(action);
|
||||
if (!needEmptyStack || game.getStack().isEmpty()) {
|
||||
changeAIControl(game, false);
|
||||
actions.remove(action);
|
||||
computerPlayer.resetPassed(); // remove AI's pass, so runtime/check commands can be executed in same priority
|
||||
}
|
||||
// control will be disabled on next priority, not here
|
||||
// (require to process triggers and other non-direct actions and choices)
|
||||
return true;
|
||||
}
|
||||
|
||||
// play step
|
||||
if (command.equals(AI_COMMAND_PLAY_STEP)) {
|
||||
AIRealGameSimulation = true; // disable on action's remove
|
||||
actionsToRemoveLater.put(action, game.getTurnStepType());
|
||||
// play multiple priorities on one or multiple steps
|
||||
if (command.startsWith(AI_COMMAND_PLAY_STEP)) {
|
||||
PhaseStep endStep = PhaseStep.fromString(command.split(AI_PARAM_DELIMETER)[1]);
|
||||
changeAIControl(game, true); // enable AI
|
||||
AIRealGameControlUntil = endStep; // disable on end step
|
||||
computerPlayer.priority(game);
|
||||
actions.remove(action);
|
||||
computerPlayer.resetPassed(); // remove AI's pass, so runtime/check commands can be executed in same priority
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -1087,6 +1108,7 @@ public class TestPlayer implements Player {
|
|||
} // turn/step
|
||||
}
|
||||
|
||||
// normal priority (by AI or pass)
|
||||
tryToPlayPriority(game);
|
||||
|
||||
// check to prevent endless loops
|
||||
|
|
@ -1103,6 +1125,16 @@ public class TestPlayer implements Player {
|
|||
return false;
|
||||
}
|
||||
|
||||
private void changeAIControl(Game game, boolean enable) {
|
||||
if (AIRealGameSimulation != enable) {
|
||||
LOGGER.info("AI control for " + getName()
|
||||
+ " " + (enable ? "ENABLED" : "DISABLED")
|
||||
//+ " on T" + game.getTurnNum() + "." + game.getTurnStepType().getStepShortText());
|
||||
+ " on " + game);
|
||||
}
|
||||
AIRealGameSimulation = enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds actions to the player actions after an executed rollback Actions
|
||||
* have to be added after the rollback because otherwise the actions are
|
||||
|
|
@ -1130,7 +1162,7 @@ public class TestPlayer implements Player {
|
|||
}
|
||||
|
||||
private void tryToPlayPriority(Game game) {
|
||||
if (AIPlayer) {
|
||||
if (AIPlayer || AIRealGameSimulation) {
|
||||
computerPlayer.priority(game);
|
||||
} else {
|
||||
computerPlayer.pass(game);
|
||||
|
|
@ -1788,16 +1820,6 @@ public class TestPlayer implements Player {
|
|||
boolean madeAttackByAction = false;
|
||||
for (Iterator<org.mage.test.player.PlayerAction> it = actions.iterator(); it.hasNext(); ) {
|
||||
PlayerAction action = it.next();
|
||||
|
||||
// aiXXX commands
|
||||
if (action.getTurnNum() == game.getTurnNum() && action.getAction().equals(AI_PREFIX + AI_COMMAND_PLAY_STEP)) {
|
||||
mustAttackByAction = true;
|
||||
madeAttackByAction = true;
|
||||
this.computerPlayer.selectAttackers(game, attackingPlayerId);
|
||||
// play step action will be removed on step end
|
||||
continue;
|
||||
}
|
||||
|
||||
if (action.getTurnNum() == game.getTurnNum() && action.getAction().startsWith("attack:")) {
|
||||
mustAttackByAction = true;
|
||||
String command = action.getAction();
|
||||
|
|
@ -1861,7 +1883,7 @@ public class TestPlayer implements Player {
|
|||
}
|
||||
|
||||
// AI FULL play if no actions available
|
||||
if (!mustAttackByAction && this.AIPlayer) {
|
||||
if (!mustAttackByAction && (this.AIPlayer || this.AIRealGameSimulation)) {
|
||||
this.computerPlayer.selectAttackers(game, attackingPlayerId);
|
||||
}
|
||||
}
|
||||
|
|
@ -1879,15 +1901,6 @@ public class TestPlayer implements Player {
|
|||
|
||||
boolean mustBlockByAction = false;
|
||||
for (PlayerAction action : tempActions) {
|
||||
|
||||
// aiXXX commands
|
||||
if (action.getTurnNum() == game.getTurnNum() && action.getAction().equals(AI_PREFIX + AI_COMMAND_PLAY_STEP)) {
|
||||
mustBlockByAction = true;
|
||||
this.computerPlayer.selectBlockers(source, game, defendingPlayerId);
|
||||
// play step action will be removed on step end
|
||||
continue;
|
||||
}
|
||||
|
||||
if (action.getTurnNum() == game.getTurnNum() && action.getAction().startsWith("block:")) {
|
||||
mustBlockByAction = true;
|
||||
String command = action.getAction();
|
||||
|
|
@ -1916,7 +1929,7 @@ public class TestPlayer implements Player {
|
|||
checkMultipleBlockers(game, blockedCreaturesList);
|
||||
|
||||
// AI FULL play if no actions available
|
||||
if (!mustBlockByAction && this.AIPlayer) {
|
||||
if (!mustBlockByAction && (this.AIPlayer || this.AIRealGameSimulation)) {
|
||||
this.computerPlayer.selectBlockers(source, game, defendingPlayerId);
|
||||
}
|
||||
}
|
||||
|
|
@ -4606,10 +4619,6 @@ public class TestPlayer implements Player {
|
|||
return computerPlayer;
|
||||
}
|
||||
|
||||
public void setAIRealGameSimulation(boolean AIRealGameSimulation) {
|
||||
this.AIRealGameSimulation = AIRealGameSimulation;
|
||||
}
|
||||
|
||||
public Map<Integer, HashMap<UUID, ArrayList<org.mage.test.player.PlayerAction>>> getRollbackActions() {
|
||||
return rollbackActions;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,8 +59,7 @@ public abstract class CardTestPlayerBaseAI extends CardTestPlayerAPIImpl {
|
|||
protected TestPlayer createPlayer(String name, RangeOfInfluence rangeOfInfluence) {
|
||||
if (getFullSimulatedPlayers().contains(name)) {
|
||||
TestPlayer testPlayer = new TestPlayer(new TestComputerPlayer7(name, RangeOfInfluence.ONE, getSkillLevel()));
|
||||
testPlayer.setAIPlayer(true);
|
||||
testPlayer.setAIRealGameSimulation(true); // enable full AI support (game simulations) for all turns by default
|
||||
testPlayer.setAIPlayer(true); // enable full AI support (game simulations) for all turns by default
|
||||
return testPlayer;
|
||||
}
|
||||
return super.createPlayer(name, rangeOfInfluence);
|
||||
|
|
|
|||
|
|
@ -27,14 +27,11 @@ import mage.player.ai.ComputerPlayerMCTS;
|
|||
import mage.players.ManaPool;
|
||||
import mage.players.Player;
|
||||
import mage.server.game.GameSessionPlayer;
|
||||
import mage.util.CardUtil;
|
||||
import mage.util.ThreadUtils;
|
||||
import mage.utils.SystemUtil;
|
||||
import mage.util.CardUtil;
|
||||
import mage.view.GameView;
|
||||
import org.junit.Assert;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.mage.test.player.PlayerAction;
|
||||
import org.mage.test.player.TestPlayer;
|
||||
|
|
@ -48,6 +45,8 @@ import java.util.regex.Matcher;
|
|||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* API for test initialization and asserting the test results.
|
||||
*
|
||||
|
|
@ -61,10 +60,11 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
|
|||
private static final boolean SHOW_EXECUTE_TIME_PER_TEST = false;
|
||||
|
||||
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 CHECK_PARAM_DELIMETER = "#";
|
||||
public static final String SHOW_PREFIX = "show:"; // prefix for all show commands
|
||||
public static final String AI_PREFIX = "ai:"; // prefix for all ai commands
|
||||
public static final String AI_PARAM_DELIMETER = "#";
|
||||
public static final String RUN_PREFIX = "run:"; // prefix for all run commands
|
||||
|
||||
// prefix for activate commands
|
||||
|
|
@ -1769,36 +1769,59 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
|
|||
* AI play one PRIORITY with multi game simulations like real game
|
||||
* (calcs and play ONE best action, can be called with stack)
|
||||
* All choices must be made by AI (e.g.strict mode possible)
|
||||
*
|
||||
* @param turnNum
|
||||
* @param step
|
||||
* @param player
|
||||
* <p>
|
||||
* Warning, by default it will take control until stack resolved
|
||||
*/
|
||||
public void aiPlayPriority(int turnNum, PhaseStep step, TestPlayer player) {
|
||||
aiPlayPriority(turnNum, step, player, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* AI play one PRIORITY
|
||||
*
|
||||
* @param untilStackResolved use false to stop AI after first cast
|
||||
*/
|
||||
public void aiPlayPriority(int turnNum, PhaseStep step, TestPlayer player, boolean untilStackResolved) {
|
||||
assertAiPlayAndGameCompatible(player);
|
||||
addPlayerAction(player, createAIPlayerAction(turnNum, step, AI_COMMAND_PLAY_PRIORITY));
|
||||
addPlayerAction(player, createAIPlayerAction(turnNum, step, AI_COMMAND_PLAY_PRIORITY + AI_PARAM_DELIMETER + untilStackResolved));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) All
|
||||
* choices must be made by AI (e.g. strict mode possible)
|
||||
* actions until step ends, can be called in the middle of the step)
|
||||
* All choices must be made by AI (e.g. strict mode possible)
|
||||
* <p>
|
||||
* Can be used for AI's declare of attackers/blockers
|
||||
*/
|
||||
public void aiPlayStep(int turnNum, PhaseStep step, TestPlayer player) {
|
||||
aiPlayStep(turnNum, step, step, player);
|
||||
}
|
||||
|
||||
public void aiPlayStep(int turnNum, PhaseStep startStep, PhaseStep endStep, TestPlayer player) {
|
||||
assertAiPlayAndGameCompatible(player);
|
||||
addPlayerAction(player, createAIPlayerAction(turnNum, step, AI_COMMAND_PLAY_STEP));
|
||||
|
||||
// direct steps support removed to simplify AI enabling code
|
||||
// (no needs in code duplicating in selectAttackers and selectBlockers methods anymore)
|
||||
if (startStep == PhaseStep.DECLARE_ATTACKERS
|
||||
|| startStep == PhaseStep.DECLARE_BLOCKERS) {
|
||||
PhaseStep newStartStep = PhaseStep.BEGIN_COMBAT;
|
||||
PhaseStep newEndStep = endStep == startStep ? PhaseStep.END_COMBAT : endStep;
|
||||
aiPlayStep(turnNum, newStartStep, newEndStep, player);
|
||||
return;
|
||||
}
|
||||
|
||||
if (startStep == PhaseStep.UPKEEP
|
||||
|| startStep == PhaseStep.CLEANUP
|
||||
|| startStep == PhaseStep.FIRST_COMBAT_DAMAGE
|
||||
|| startStep == PhaseStep.COMBAT_DAMAGE) {
|
||||
Assert.fail("AI can't be started from step without priority");
|
||||
}
|
||||
|
||||
addPlayerAction(player, createAIPlayerAction(turnNum, startStep, AI_COMMAND_PLAY_STEP + AI_PARAM_DELIMETER + endStep.toString()));
|
||||
}
|
||||
|
||||
public PlayerAction createAIPlayerAction(int turnNum, PhaseStep step, String aiCommand) {
|
||||
// AI commands must disable and enable real game simulation and strict mode
|
||||
return new PlayerAction("", turnNum, step, AI_PREFIX + aiCommand) {
|
||||
@Override
|
||||
public void onActionRemovedLater(Game game, TestPlayer player) {
|
||||
player.setAIRealGameSimulation(false);
|
||||
}
|
||||
};
|
||||
return new PlayerAction("", turnNum, step, AI_PREFIX + aiCommand);
|
||||
}
|
||||
|
||||
private void assertAiPlayAndGameCompatible(TestPlayer player) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue