mirror of
https://github.com/magefree/mage.git
synced 2025-12-21 11:02:00 -08:00
AI, tests: added stability tests to make sure AI simulations can process errors and freezes (part of #13638, #13766);
This commit is contained in:
parent
85c04bca59
commit
c3a0c731d6
12 changed files with 298 additions and 20 deletions
|
|
@ -0,0 +1,237 @@
|
|||
package org.mage.test.AI.basic;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.LimitedTimesPerTurnActivatedAbility;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.GainLifeEffect;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.player.ai.ComputerPlayer;
|
||||
import mage.player.ai.ComputerPlayer7;
|
||||
import mage.util.ThreadUtils;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps;
|
||||
|
||||
/**
|
||||
* Tests for AI simulation stability (AI must process simulations with freezes or errors)
|
||||
*
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class SimulationStabilityAITest extends CardTestPlayerBaseWithAIHelps {
|
||||
|
||||
@Before
|
||||
public void prepare() {
|
||||
// comment it to enable AI code debug
|
||||
Assert.assertFalse("AI stability tests must be run under release config", ComputerPlayer.COMPUTER_DISABLE_TIMEOUT_IN_GAME_SIMULATIONS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_GameFreeze_OnlyGoodAbilities() {
|
||||
removeAllCardsFromLibrary(playerA);
|
||||
|
||||
// possible combinations: from 1 to 3 abilities - all fine
|
||||
addFreezeAbility("good 1", false);
|
||||
addFreezeAbility("good 2", false);
|
||||
addFreezeAbility("good 3", false);
|
||||
|
||||
// AI must activate all +3 life
|
||||
aiPlayStep(1, PhaseStep.PRECOMBAT_MAIN, playerA);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertLife(playerA, 20 + 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_GameFreeze_OnlyFreezeAbilities() {
|
||||
removeAllCardsFromLibrary(playerA);
|
||||
|
||||
// possible combinations: from 1 to 3 bad abilities - all bad
|
||||
addFreezeAbility("freeze 1", true);
|
||||
addFreezeAbility("freeze 2", true);
|
||||
addFreezeAbility("freeze 3", true);
|
||||
|
||||
// AI can't finish any simulation and do not choose to activate in real game
|
||||
aiPlayStep(1, PhaseStep.PRECOMBAT_MAIN, playerA);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertLife(playerA, 20);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
// TODO: AI actions simulation do not support multithreading, so whole next action search
|
||||
// will fail on any problem (enable after new simulation implement)
|
||||
public void test_GameFreeze_GoodAndFreezeAbilities() {
|
||||
removeAllCardsFromLibrary(playerA);
|
||||
|
||||
// possible combinations: some good chains, some bad chains
|
||||
addFreezeAbility("good 1", false);
|
||||
addFreezeAbility("good 2", false);
|
||||
addFreezeAbility("good 3", false);
|
||||
addFreezeAbility("freeze 1", true);
|
||||
|
||||
// AI must see and filter bad combinations with freeze ability in the chain
|
||||
// so only 1 + 2 + 3 will give best score and will be chosen for real game
|
||||
aiPlayStep(1, PhaseStep.PRECOMBAT_MAIN, playerA);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertLife(playerA, 20 + 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_GameError_OnlyGoodAbilities() {
|
||||
removeAllCardsFromLibrary(playerA);
|
||||
|
||||
// possible combinations: from 1 to 3 abilities - all fine
|
||||
addErrorAbility("good 1", false);
|
||||
addErrorAbility("good 2", false);
|
||||
addErrorAbility("good 3", false);
|
||||
|
||||
// AI must activate all +3 life
|
||||
aiPlayStep(1, PhaseStep.PRECOMBAT_MAIN, playerA);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertLife(playerA, 20 + 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_GameError_OnlyErrorAbilities() {
|
||||
removeAllCardsFromLibrary(playerA);
|
||||
|
||||
// it's ok to have error logs in output
|
||||
|
||||
// possible combinations: from 1 to 3 bad abilities - all bad
|
||||
addErrorAbility("error 1", true);
|
||||
addErrorAbility("error 2", true);
|
||||
addErrorAbility("error 3", true);
|
||||
|
||||
// AI can't finish any simulation and do not choose to activate in real game
|
||||
aiPlayStep(1, PhaseStep.PRECOMBAT_MAIN, playerA);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertLife(playerA, 20);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
// TODO: AI actions simulation do not support multithreading, so whole next action search
|
||||
// will fail on any problem (enable after new simulation implement)
|
||||
public void test_GameError_GoodAndFreezeAbilities() {
|
||||
removeAllCardsFromLibrary(playerA);
|
||||
|
||||
// it's ok to have error logs in output
|
||||
|
||||
// possible combinations: some good chains, some bad chains
|
||||
addErrorAbility("good 1", false);
|
||||
addErrorAbility("good 2", false);
|
||||
addErrorAbility("good 3", false);
|
||||
addErrorAbility("error 1", true);
|
||||
|
||||
// AI must see and filter bad combinations with error ability in the chain
|
||||
// so only 1 + 2 + 3 will give best score and will be chosen for real game
|
||||
aiPlayStep(1, PhaseStep.PRECOMBAT_MAIN, playerA);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertLife(playerA, 20 + 3);
|
||||
}
|
||||
|
||||
private void addFreezeAbility(String name, boolean isFreeze) {
|
||||
// change max think timeout to lower value, so test can work faster
|
||||
ComputerPlayer7 aiPlayer = (ComputerPlayer7) playerA.getRealPlayer();
|
||||
aiPlayer.setMaxThinkTimeSecs(1);
|
||||
|
||||
Effect effect;
|
||||
if (isFreeze) {
|
||||
effect = new GameFreezeEffect();
|
||||
} else {
|
||||
effect = new GainLifeEffect(1);
|
||||
}
|
||||
effect.setText(name);
|
||||
addCustomCardWithAbility(name, playerA, new LimitedTimesPerTurnActivatedAbility(effect, new ManaCostsImpl<>("{G}")));
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Forest", 1);
|
||||
}
|
||||
|
||||
private void addErrorAbility(String name, boolean isError) {
|
||||
// change error processing, so test can continue simulations after catch error - like a real game
|
||||
playerA.setFastFailInTestMode(false);
|
||||
|
||||
Effect effect;
|
||||
if (isError) {
|
||||
effect = new GameErrorEffect();
|
||||
} else {
|
||||
effect = new GainLifeEffect(1);
|
||||
}
|
||||
effect.setText(name);
|
||||
addCustomCardWithAbility(name, playerA, new LimitedTimesPerTurnActivatedAbility(effect, new ManaCostsImpl<>("{G}")));
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Forest", 1);
|
||||
}
|
||||
}
|
||||
|
||||
class GameFreezeEffect extends OneShotEffect {
|
||||
|
||||
GameFreezeEffect() {
|
||||
super(Outcome.Benefit);
|
||||
}
|
||||
|
||||
private GameFreezeEffect(final GameFreezeEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
public GameFreezeEffect copy() {
|
||||
return new GameFreezeEffect(this);
|
||||
}
|
||||
|
||||
public boolean apply(Game game, Ability source) {
|
||||
// freeze simulation, AI must close sim thread by timeout
|
||||
System.out.println("apply freeze effect on " + game); // for debug only, show logs from any sim thread
|
||||
while (true) {
|
||||
ThreadUtils.sleep(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class GameErrorEffect extends OneShotEffect {
|
||||
|
||||
GameErrorEffect() {
|
||||
super(Outcome.Benefit);
|
||||
}
|
||||
|
||||
private GameErrorEffect(final GameErrorEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
public GameErrorEffect copy() {
|
||||
return new GameErrorEffect(this);
|
||||
}
|
||||
|
||||
public boolean apply(Game game, Ability source) {
|
||||
// error simulation, AI must close error thread, do not use rollback
|
||||
System.out.println("apply error effect on " + game); // for debug only, show logs from any sim thread
|
||||
throw new IllegalStateException("Test error");
|
||||
}
|
||||
}
|
||||
|
|
@ -775,7 +775,12 @@ public class TestPlayer implements Player {
|
|||
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
|
||||
// remove AI's pass, so runtime/check commands can be executed in same priority
|
||||
// aiPlayStep can cause double priority call, but it's better to have workable checkXXX commands
|
||||
// (AI will do nothing on second priority call anyway)
|
||||
if (!actions.isEmpty()) {
|
||||
computerPlayer.resetPassed();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -3438,7 +3443,7 @@ public class TestPlayer implements Player {
|
|||
@Override
|
||||
public boolean isComputer() {
|
||||
// all players in unit tests are computers, so it allows testing different logic (Human vs AI)
|
||||
if (isTestsMode()) {
|
||||
if (isTestMode()) {
|
||||
// AIRealGameSimulation = true - full plyable AI
|
||||
// AIRealGameSimulation = false - choose assisted AI (Human)
|
||||
return AIRealGameSimulation;
|
||||
|
|
@ -3874,8 +3879,8 @@ public class TestPlayer implements Player {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isTestsMode() {
|
||||
return computerPlayer.isTestsMode();
|
||||
public boolean isTestMode() {
|
||||
return computerPlayer.isTestMode();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -3883,6 +3888,16 @@ public class TestPlayer implements Player {
|
|||
computerPlayer.setTestMode(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFastFailInTestMode() {
|
||||
return computerPlayer.isFastFailInTestMode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFastFailInTestMode(boolean value) {
|
||||
computerPlayer.setFastFailInTestMode(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTopCardRevealed() {
|
||||
return computerPlayer.isTopCardRevealed();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue