mirror of
https://github.com/magefree/mage.git
synced 2026-01-17 17:06:45 -08:00
Test framework improves (Monte Carlo AI):
* Added support to test Monte Carlo AI (CardTestPlayerBaseWithMonteCarloAIHelps - any aiXXX commands); * Added Quick Start button to test Monte Carlo AI games (MCTS);
This commit is contained in:
parent
a7ac35a82d
commit
79c5c7a6a5
6 changed files with 224 additions and 47 deletions
|
|
@ -0,0 +1,42 @@
|
|||
package org.mage.test.cards.requirement;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBaseWithMonteCarloAIHelps;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class BecomeBlockTriggersMonteCarloAITest extends CardTestPlayerBaseWithMonteCarloAIHelps {
|
||||
|
||||
// continue from BecomeBlockTriggersTest
|
||||
@Test
|
||||
public void test_AI_CantBlockAgain() {
|
||||
// Monte Carlo bug: Triggered ability triggered twice (should be once), see https://github.com/magefree/mage/issues/6367
|
||||
|
||||
removeAllCardsFromHand(playerA);
|
||||
removeAllCardsFromHand(playerB);
|
||||
|
||||
// All creatures able to block Nessian Boar do so.
|
||||
// Whenever Nessian Boar becomes blocked by a creature, that creature’s controller draws a card.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Nessian Boar", 1);
|
||||
//
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Balduvian Bears", 1);
|
||||
|
||||
// auto-block by requirement effect
|
||||
attack(1, playerA, "Nessian Boar");
|
||||
// AI can't block same creature twice
|
||||
aiPlayStep(1, PhaseStep.DECLARE_BLOCKERS, playerB);
|
||||
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
setStrictChooseMode(true);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertGraveyardCount(playerA, 0);
|
||||
assertGraveyardCount(playerB, 1);
|
||||
assertHandCount(playerA, 0);
|
||||
assertHandCount(playerB, 1);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
package org.mage.test.player;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.abilities.ActivatedAbility;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.RangeOfInfluence;
|
||||
import mage.game.Game;
|
||||
import mage.player.ai.ComputerPlayerMCTS;
|
||||
import mage.target.Target;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
|
||||
// mock class to override AI logic in tests
|
||||
public class TestComputerPlayerMonteCarlo extends ComputerPlayerMCTS {
|
||||
|
||||
private TestPlayer testPlayerLink;
|
||||
|
||||
public TestComputerPlayerMonteCarlo(String name, RangeOfInfluence range, int skill) {
|
||||
super(name, range, skill);
|
||||
}
|
||||
|
||||
public void setTestPlayerLink(TestPlayer testPlayerLink) {
|
||||
this.testPlayerLink = testPlayerLink;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpellAbility chooseSpellAbilityForCast(SpellAbility ability, Game game, boolean noMana) {
|
||||
// copy-paste for TestComputerXXX
|
||||
|
||||
// workaround to cast fused cards in tests by it's NAMES (Wear, Tear, Wear // Tear)
|
||||
// reason: TestPlayer uses outer computerPlayer to cast, not TestPlayer
|
||||
switch (ability.getSpellAbilityType()) {
|
||||
case SPLIT:
|
||||
case SPLIT_FUSED:
|
||||
case SPLIT_AFTERMATH:
|
||||
if (!this.testPlayerLink.getChoices().isEmpty()) {
|
||||
MageObject object = game.getObject(ability.getSourceId());
|
||||
if (object != null) {
|
||||
LinkedHashMap<UUID, ActivatedAbility> useableAbilities = getSpellAbilities(playerId, object, game.getState().getZone(object.getId()), game);
|
||||
|
||||
// left, right or fused cast
|
||||
for (String choose : this.testPlayerLink.getChoices()) {
|
||||
for (ActivatedAbility activatedAbility : useableAbilities.values()) {
|
||||
if (activatedAbility instanceof SpellAbility) {
|
||||
if (((SpellAbility) activatedAbility).getCardName().equals(choose)) {
|
||||
return (SpellAbility) activatedAbility;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// default implementation by AI
|
||||
return super.chooseSpellAbilityForCast(ability, game, noMana);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean choose(Outcome outcome, Target target, UUID sourceId, Game game) {
|
||||
// copy-paste for TestComputerXXX
|
||||
|
||||
// workaround for discard spells
|
||||
// reason: TestPlayer uses outer computerPlayer to discard but inner code uses choose
|
||||
return testPlayerLink.choose(outcome, target, sourceId, game);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package org.mage.test.serverside.base;
|
||||
|
||||
import mage.constants.RangeOfInfluence;
|
||||
import org.mage.test.player.TestComputerPlayerMonteCarlo;
|
||||
import org.mage.test.player.TestPlayer;
|
||||
|
||||
/**
|
||||
* Base class but with Monte Carlo 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 ComputerPlayerMCTS, not simple ComputerPlayer from basic tests)
|
||||
*
|
||||
* @author JayDi85
|
||||
*/
|
||||
public abstract class CardTestPlayerBaseWithMonteCarloAIHelps extends CardTestPlayerBase {
|
||||
|
||||
@Override
|
||||
protected TestPlayer createPlayer(String name, RangeOfInfluence rangeOfInfluence) {
|
||||
TestPlayer testPlayer = new TestPlayer(new TestComputerPlayerMonteCarlo(name, RangeOfInfluence.ONE, 6));
|
||||
testPlayer.setAIPlayer(false); // AI can't play it by itself, use AI commands
|
||||
return testPlayer;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue