mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-15 18:28:00 +00:00
Merge pull request #3865 from tool4ever/revertPH
Revert "Modify main game loop for use with basic AI unit tests (#3669)"
This commit is contained in:
@@ -519,8 +519,6 @@ public class AiController {
|
||||
landList = unreflectedLands;
|
||||
}
|
||||
|
||||
// TODO If there's nothing to do with the mana, then play a tapland
|
||||
|
||||
//try to skip lands that enter the battlefield tapped
|
||||
if (!nonLandsInHand.isEmpty()) {
|
||||
CardCollection nonTappedLands = new CardCollection();
|
||||
|
||||
@@ -45,7 +45,7 @@ public class SimulationController {
|
||||
currentStack = new ArrayList<>();
|
||||
}
|
||||
|
||||
public int getRecursionDepth() {
|
||||
private int getRecursionDepth() {
|
||||
return scoreStack.size() - 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -98,13 +98,6 @@ public class SpellAbilityPicker {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (printOutput && game.getPhaseHandler().getPhase() == PhaseType.MAIN1) {
|
||||
System.out.println("Current state for " + player + ":");
|
||||
for (Card c : player.getCardsIn(ZoneType.Battlefield)) {
|
||||
System.out.println(c);
|
||||
}
|
||||
}
|
||||
|
||||
Score origGameScore = new GameStateEvaluator().getScoreForGameState(game, player);
|
||||
List<SpellAbility> candidateSAs = getCandidateSpellsAndAbilities();
|
||||
if (controller != null) {
|
||||
@@ -194,7 +187,7 @@ public class SpellAbilityPicker {
|
||||
|
||||
SpellAbility bestSa = null;
|
||||
Score bestSaValue = origGameScore;
|
||||
print("Evaluating as " + player + "... (orig score = " + origGameScore + ")");
|
||||
print("Evaluating... (orig score = " + origGameScore + ")");
|
||||
for (int i = 0; i < candidateSAs.size(); i++) {
|
||||
Score value = evaluateSa(controller, phase, candidateSAs, i);
|
||||
if (value.value > bestSaValue.value) {
|
||||
|
||||
@@ -382,10 +382,6 @@ public class StaticData {
|
||||
return printSheets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a database of all non-variant cards
|
||||
* @return
|
||||
*/
|
||||
public CardDb getCommonCards() {
|
||||
return commonCards;
|
||||
}
|
||||
|
||||
@@ -69,15 +69,6 @@ public class Deck extends DeckBase implements Iterable<Entry<DeckSection, CardPo
|
||||
getOrCreate(DeckSection.Main);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new deck from a cardpool
|
||||
*/
|
||||
public Deck(final String name0, CardPool main) {
|
||||
super(name0);
|
||||
getOrCreate(DeckSection.Main).add(main.toFlatList());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Copy constructor.
|
||||
*
|
||||
|
||||
@@ -111,11 +111,6 @@ public class PhaseHandler implements java.io.Serializable {
|
||||
|
||||
private final transient Game game;
|
||||
|
||||
private int loopCount = 0;
|
||||
private static final int MAX_LOOP_COUNT = 999;
|
||||
private StopWatch sw = new StopWatch();
|
||||
|
||||
|
||||
public PhaseHandler(final Game game0) {
|
||||
game = game0;
|
||||
}
|
||||
@@ -250,7 +245,6 @@ public class PhaseHandler implements java.io.Serializable {
|
||||
return playerTurn.isSkippingCombat();
|
||||
|
||||
case COMBAT_DECLARE_BLOCKERS:
|
||||
// Rule 508.8
|
||||
if (inCombat() && combat.getAttackers().isEmpty()) {
|
||||
endCombat();
|
||||
}
|
||||
@@ -267,8 +261,6 @@ public class PhaseHandler implements java.io.Serializable {
|
||||
private final void onPhaseBegin() {
|
||||
boolean skipped = false;
|
||||
|
||||
// default to not giving priority to players.
|
||||
givePriorityToPlayer = false;
|
||||
game.getTriggerHandler().resetActiveTriggers();
|
||||
if (isSkippingPhase(phase)) {
|
||||
skipped = true;
|
||||
@@ -287,16 +279,12 @@ public class PhaseHandler implements java.io.Serializable {
|
||||
nUpkeepsThisGame++;
|
||||
game.getUpkeep().executeUntil(playerTurn);
|
||||
game.getUpkeep().executeAt();
|
||||
// Rule 503.1
|
||||
givePriorityToPlayer = true;
|
||||
break;
|
||||
|
||||
// Rule 504
|
||||
case DRAW:
|
||||
for (Player p : game.getPlayers()) {
|
||||
p.resetNumDrawnThisDrawStep();
|
||||
}
|
||||
// Rule 504.1
|
||||
playerTurn.drawCard();
|
||||
for (Player p : game.getPlayers()) {
|
||||
if (p.isOpponentOf(playerTurn) &&
|
||||
@@ -304,17 +292,13 @@ public class PhaseHandler implements java.io.Serializable {
|
||||
p.drawCard();
|
||||
}
|
||||
}
|
||||
// Rule 504.2
|
||||
givePriorityToPlayer = true;
|
||||
break;
|
||||
|
||||
case MAIN1:
|
||||
{
|
||||
// Rule 505.3
|
||||
if (playerTurn.isArchenemy()) {
|
||||
playerTurn.setSchemeInMotion();
|
||||
}
|
||||
// Rule 505.4
|
||||
GameEntityCounterTable table = new GameEntityCounterTable();
|
||||
// all Saga get Lore counter at the begin of pre combat
|
||||
for (Card c : playerTurn.getCardsIn(ZoneType.Battlefield)) {
|
||||
@@ -324,17 +308,12 @@ public class PhaseHandler implements java.io.Serializable {
|
||||
}
|
||||
table.replaceCounterEffect(game, null, false);
|
||||
}
|
||||
// Rule 505.6
|
||||
givePriorityToPlayer = true;
|
||||
break;
|
||||
|
||||
// Rule 507
|
||||
case COMBAT_BEGIN:
|
||||
nCombatsThisTurn++;
|
||||
combat = new Combat(playerTurn);
|
||||
//PhaseUtil.verifyCombat();
|
||||
// Rule 507.2
|
||||
givePriorityToPlayer = true;
|
||||
break;
|
||||
|
||||
case COMBAT_DECLARE_ATTACKERS:
|
||||
@@ -343,22 +322,14 @@ public class PhaseHandler implements java.io.Serializable {
|
||||
declareAttackersTurnBasedAction();
|
||||
game.getStack().unfreezeStack();
|
||||
|
||||
// Rule 508.2
|
||||
// For correctness, this should give priority,
|
||||
// but there are almost no cases where you'd actually want to have priority
|
||||
// if there are no attackers, so keep this for now or someday make a setting for it.
|
||||
givePriorityToPlayer = inCombat();
|
||||
break;
|
||||
|
||||
// Rule 509
|
||||
case COMBAT_DECLARE_BLOCKERS:
|
||||
combat.removeAbsentCombatants();
|
||||
game.getStack().freezeStack();
|
||||
// Rule 509
|
||||
declareBlockersTurnBasedAction();
|
||||
game.getStack().unfreezeStack();
|
||||
// Rule 509.4
|
||||
givePriorityToPlayer = true;
|
||||
break;
|
||||
|
||||
case COMBAT_FIRST_STRIKE_DAMAGE:
|
||||
@@ -372,8 +343,6 @@ public class PhaseHandler implements java.io.Serializable {
|
||||
} else {
|
||||
combat.dealAssignedDamage();
|
||||
}
|
||||
// Rule 510.3
|
||||
givePriorityToPlayer = true;
|
||||
break;
|
||||
|
||||
case COMBAT_DAMAGE:
|
||||
@@ -386,8 +355,6 @@ public class PhaseHandler implements java.io.Serializable {
|
||||
} else {
|
||||
combat.dealAssignedDamage();
|
||||
}
|
||||
// Rule 510.3
|
||||
givePriorityToPlayer = true;
|
||||
break;
|
||||
|
||||
case COMBAT_END:
|
||||
@@ -398,14 +365,10 @@ public class PhaseHandler implements java.io.Serializable {
|
||||
game.getEndOfCombat().executeAt();
|
||||
|
||||
//SDisplayUtil.showTab(EDocID.REPORT_STACK.getDoc());
|
||||
// Rule 511.1
|
||||
givePriorityToPlayer = true;
|
||||
break;
|
||||
|
||||
case MAIN2:
|
||||
//SDisplayUtil.showTab(EDocID.REPORT_STACK.getDoc());
|
||||
// Rule 505.6
|
||||
givePriorityToPlayer = true;
|
||||
break;
|
||||
|
||||
case END_OF_TURN:
|
||||
@@ -415,8 +378,6 @@ public class PhaseHandler implements java.io.Serializable {
|
||||
}
|
||||
|
||||
game.getEndOfTurn().executeAt();
|
||||
// Rule 513.1
|
||||
givePriorityToPlayer = true;
|
||||
break;
|
||||
|
||||
case CLEANUP:
|
||||
@@ -1041,7 +1002,12 @@ public class PhaseHandler implements java.io.Serializable {
|
||||
|
||||
private final static boolean DEBUG_PHASES = false;
|
||||
|
||||
public void setupFirstTurn(Player goesFirst, Runnable startGameHook) {
|
||||
public void startFirstTurn(Player goesFirst) {
|
||||
startFirstTurn(goesFirst, null);
|
||||
}
|
||||
public void startFirstTurn(Player goesFirst, Runnable startGameHook) {
|
||||
StopWatch sw = new StopWatch();
|
||||
|
||||
if (phase != null) {
|
||||
throw new IllegalStateException("Turns already started, call this only once per game");
|
||||
}
|
||||
@@ -1050,110 +1016,94 @@ public class PhaseHandler implements java.io.Serializable {
|
||||
advanceToNextPhase();
|
||||
onPhaseBegin();
|
||||
|
||||
// don't even offer priority, because it's untap of 1st turn now
|
||||
givePriorityToPlayer = false;
|
||||
|
||||
if (startGameHook != null) {
|
||||
startGameHook.run();
|
||||
givePriorityToPlayer = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void startFirstTurn(Player goesFirst) {
|
||||
startFirstTurn(goesFirst, null);
|
||||
}
|
||||
public void startFirstTurn(Player goesFirst, Runnable startGameHook) {
|
||||
|
||||
// do setup
|
||||
this.setupFirstTurn(goesFirst, startGameHook);
|
||||
|
||||
// MAIN GAME LOOP
|
||||
while (!game.isGameOver()) {
|
||||
this.mainLoopStep();
|
||||
}
|
||||
}
|
||||
|
||||
public void mainLoopStep() {
|
||||
boolean keep_looping = false;
|
||||
if (givePriorityToPlayer) {
|
||||
if (DEBUG_PHASES && !sw.isStarted()) {
|
||||
sw.start();
|
||||
}
|
||||
|
||||
game.fireEvent(new GameEventPlayerPriority(playerTurn, phase, getPriorityPlayer()));
|
||||
|
||||
if (checkStateBasedEffects()) {
|
||||
// state-based effects check could lead to game over
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Why does this return a list?
|
||||
List<SpellAbility> chosenSa = pPlayerPriority.getController().chooseSpellAbilityToPlay();
|
||||
|
||||
// this needs to come after chosenSa so it sees you conceding on own turn
|
||||
if (playerTurn.hasLost() && pPlayerPriority.equals(playerTurn) && pFirstPriority.equals(playerTurn)) {
|
||||
// If the active player has lost, and they have priority, set the next player to have priority
|
||||
System.out.println("Active player is no longer in the game...");
|
||||
pPlayerPriority = game.getNextPlayerAfter(getPriorityPlayer());
|
||||
pFirstPriority = pPlayerPriority;
|
||||
}
|
||||
|
||||
if (chosenSa != null) {
|
||||
if (givePriorityToPlayer) {
|
||||
if (DEBUG_PHASES) {
|
||||
System.out.print("... " + pPlayerPriority + " plays " + chosenSa);
|
||||
sw.start();
|
||||
}
|
||||
for (SpellAbility sa : chosenSa) {
|
||||
Card saHost = sa.getHostCard();
|
||||
final Zone originZone = saHost.getZone();
|
||||
|
||||
// Note: this involves going back to the controller (player or AI) for choices
|
||||
if (pPlayerPriority.getController().playChosenSpellAbility(sa)) {
|
||||
pFirstPriority = pPlayerPriority; // all opponents have to pass before stack is allowed to resolve
|
||||
game.fireEvent(new GameEventPlayerPriority(playerTurn, phase, getPriorityPlayer()));
|
||||
List<SpellAbility> chosenSa = null;
|
||||
|
||||
int loopCount = 0;
|
||||
do {
|
||||
if (checkStateBasedEffects()) {
|
||||
// state-based effects check could lead to game over
|
||||
return;
|
||||
}
|
||||
|
||||
saHost = game.getCardState(saHost);
|
||||
final Zone currentZone = saHost.getZone();
|
||||
chosenSa = pPlayerPriority.getController().chooseSpellAbilityToPlay();
|
||||
|
||||
// Need to check if Zone did change
|
||||
if (currentZone != null && originZone != null && !currentZone.equals(originZone) && (sa.isSpell() || sa instanceof LandAbility)) {
|
||||
// currently there can be only one Spell put on the Stack at once, or Land Abilities be played
|
||||
final CardZoneTable triggerList = new CardZoneTable();
|
||||
triggerList.put(originZone.getZoneType(), currentZone.getZoneType(), saHost);
|
||||
triggerList.triggerChangesZoneAll(game, sa);
|
||||
// this needs to come after chosenSa so it sees you conceding on own turn
|
||||
if (playerTurn.hasLost() && pPlayerPriority.equals(playerTurn) && pFirstPriority.equals(playerTurn)) {
|
||||
// If the active player has lost, and they have priority, set the next player to have priority
|
||||
System.out.println("Active player is no longer in the game...");
|
||||
pPlayerPriority = game.getNextPlayerAfter(getPriorityPlayer());
|
||||
pFirstPriority = pPlayerPriority;
|
||||
}
|
||||
}
|
||||
game.copyLastState();
|
||||
loopCount++;
|
||||
if (loopCount >= MAX_LOOP_COUNT && pPlayerPriority.getController().isAI()) {
|
||||
|
||||
if (chosenSa == null) {
|
||||
break; // that means 'I pass'
|
||||
}
|
||||
if (DEBUG_PHASES) {
|
||||
System.out.print("... " + pPlayerPriority + " plays " + chosenSa);
|
||||
}
|
||||
for (SpellAbility sa : chosenSa) {
|
||||
Card saHost = sa.getHostCard();
|
||||
final Zone originZone = saHost.getZone();
|
||||
|
||||
if (pPlayerPriority.getController().playChosenSpellAbility(sa)) {
|
||||
pFirstPriority = pPlayerPriority; // all opponents have to pass before stack is allowed to resolve
|
||||
}
|
||||
|
||||
saHost = game.getCardState(saHost);
|
||||
final Zone currentZone = saHost.getZone();
|
||||
|
||||
// Need to check if Zone did change
|
||||
if (currentZone != null && originZone != null && !currentZone.equals(originZone) && (sa.isSpell() || sa instanceof LandAbility)) {
|
||||
// currently there can be only one Spell put on the Stack at once, or Land Abilities be played
|
||||
final CardZoneTable triggerList = new CardZoneTable();
|
||||
triggerList.put(originZone.getZoneType(), currentZone.getZoneType(), saHost);
|
||||
triggerList.triggerChangesZoneAll(game, sa);
|
||||
}
|
||||
}
|
||||
game.copyLastState();
|
||||
loopCount++;
|
||||
} while (loopCount < 999 || !pPlayerPriority.getController().isAI());
|
||||
|
||||
if (loopCount >= 999 && pPlayerPriority.getController().isAI()) {
|
||||
System.out.print("AI looped too much with: " + chosenSa);
|
||||
}
|
||||
keep_looping = loopCount < MAX_LOOP_COUNT || !pPlayerPriority.getController().isAI();
|
||||
}
|
||||
|
||||
// reset the loop counter and stopwatch
|
||||
if (!keep_looping) {
|
||||
loopCount = 0;
|
||||
if (DEBUG_PHASES) {
|
||||
sw.stop();
|
||||
System.out.print("... passed in " + sw.getTime() / 1000f + " s\n");
|
||||
System.out.print("... passed in " + sw.getTime()/1000f + " s\n");
|
||||
System.out.println("\t\tStack: " + game.getStack());
|
||||
sw.reset();
|
||||
}
|
||||
}
|
||||
else if (DEBUG_PHASES) {
|
||||
System.out.print(" >> (no priority given to " + getPriorityPlayer() + ")\n");
|
||||
}
|
||||
|
||||
} else if (DEBUG_PHASES) {
|
||||
System.out.print(" >> (no priority given to " + getPriorityPlayer() + ")\n");
|
||||
}
|
||||
|
||||
// skip these to be able to rollback if an invalid spellability is chosen
|
||||
if (!keep_looping) {
|
||||
// actingPlayer is the player who may act
|
||||
// the firstAction is the player who gained Priority First in this segment
|
||||
// of Priority
|
||||
Player nextPlayer = game.getNextPlayerAfter(getPriorityPlayer());
|
||||
|
||||
if (game.isGameOver() || nextPlayer == null) {
|
||||
return;
|
||||
} // conceded?
|
||||
if (game.isGameOver() || nextPlayer == null) { return; } // conceded?
|
||||
|
||||
if (DEBUG_PHASES) {
|
||||
System.out.println(TextUtil.concatWithSpace(playerTurn.toString(), TextUtil.addSuffix(phase.toString(), ":"), pPlayerPriority.toString(), "is active, previous was", nextPlayer.toString()));
|
||||
System.out.println(TextUtil.concatWithSpace(playerTurn.toString(),TextUtil.addSuffix(phase.toString(),":"), pPlayerPriority.toString(),"is active, previous was", nextPlayer.toString()));
|
||||
}
|
||||
if (pFirstPriority == nextPlayer) {
|
||||
if (game.getStack().isEmpty()) {
|
||||
@@ -1164,10 +1114,12 @@ public class PhaseHandler implements java.io.Serializable {
|
||||
}
|
||||
|
||||
// end phase
|
||||
givePriorityToPlayer = true;
|
||||
onPhaseEnd();
|
||||
advanceToNextPhase();
|
||||
onPhaseBegin();
|
||||
} else if (!game.getStack().hasSimultaneousStackEntries()) {
|
||||
}
|
||||
else if (!game.getStack().hasSimultaneousStackEntries()) {
|
||||
game.getStack().resolveStack();
|
||||
}
|
||||
} else {
|
||||
@@ -1185,7 +1137,10 @@ public class PhaseHandler implements java.io.Serializable {
|
||||
|
||||
// update Priority for all players
|
||||
for (final Player p : game.getPlayers()) {
|
||||
p.setHasPriority(getPriorityPlayer() == p);
|
||||
if (getPriorityPlayer() == p)
|
||||
p.setHasPriority(true);
|
||||
else
|
||||
p.setHasPriority(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,7 +172,7 @@ public class SimulateMatch {
|
||||
System.out.println("\tq - Quiet flag. Output just the game result, not the entire game log.");
|
||||
}
|
||||
|
||||
public static Game simulateSingleGameOfMatch(final Match mc, int timeout) {
|
||||
public static void simulateSingleMatch(final Match mc, int iGame, boolean outputGamelog) {
|
||||
final StopWatch sw = new StopWatch();
|
||||
sw.start();
|
||||
|
||||
@@ -182,7 +182,7 @@ public class SimulateMatch {
|
||||
TimeLimitedCodeBlock.runWithTimeout(() -> {
|
||||
mc.startGame(g1);
|
||||
sw.stop();
|
||||
}, timeout, TimeUnit.SECONDS);
|
||||
}, 120, TimeUnit.SECONDS);
|
||||
} catch (TimeoutException e) {
|
||||
System.out.println("Stopping slow match as draw");
|
||||
} catch (Exception | StackOverflowError e) {
|
||||
@@ -196,14 +196,6 @@ public class SimulateMatch {
|
||||
}
|
||||
}
|
||||
|
||||
return g1;
|
||||
}
|
||||
|
||||
public static void simulateSingleMatch(final Match mc, int iGame, boolean outputGamelog) {
|
||||
final StopWatch sw = new StopWatch();
|
||||
sw.start();
|
||||
final Game g1 = simulateSingleGameOfMatch(mc, 120);
|
||||
|
||||
List<GameLogEntry> log;
|
||||
if (outputGamelog) {
|
||||
log = g1.getGameLog().getLogEntries(null);
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
package forge;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import forge.ai.AIOption;
|
||||
import forge.ai.LobbyPlayerAi;
|
||||
import forge.card.CardDb;
|
||||
import forge.deck.Deck;
|
||||
import forge.deck.DeckFormat;
|
||||
import forge.deck.generation.DeckGenerator5Color;
|
||||
import forge.game.GameRules;
|
||||
import forge.game.GameType;
|
||||
import forge.game.Match;
|
||||
import forge.game.player.RegisteredPlayer;
|
||||
import forge.gui.GuiBase;
|
||||
import forge.localinstance.properties.ForgePreferences;
|
||||
import forge.model.FModel;
|
||||
import org.testng.annotations.Ignore;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static forge.view.SimulateMatch.simulateSingleGameOfMatch;
|
||||
|
||||
public class GameFuzzingTest {
|
||||
@Ignore
|
||||
@Test
|
||||
public void PlayGameWithRandomDecks() {
|
||||
GuiBase.setInterface(new GuiDesktop());
|
||||
FModel.initialize(null, new Function<ForgePreferences, Void>() {
|
||||
@Override
|
||||
public Void apply(ForgePreferences preferences) {
|
||||
preferences.setPref(ForgePreferences.FPref.LOAD_CARD_SCRIPTS_LAZILY, false);
|
||||
preferences.setPref(ForgePreferences.FPref.UI_LANGUAGE, "en-US");
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// first deck
|
||||
CardDb cardDb = FModel.getMagicDb().getCommonCards();
|
||||
final DeckGenerator5Color gen = new DeckGenerator5Color(cardDb, DeckFormat.Constructed);
|
||||
final Deck first_deck = new Deck("first", gen.getDeck(60, false));
|
||||
final Deck second_deck = new Deck("second", gen.getDeck(60, false));
|
||||
|
||||
final RegisteredPlayer p1 = new RegisteredPlayer(first_deck);
|
||||
final RegisteredPlayer p2 = new RegisteredPlayer(second_deck);
|
||||
|
||||
Set<AIOption> options = new HashSet<>();
|
||||
// options.add(AIOption.USE_SIMULATION);
|
||||
p1.setPlayer(new LobbyPlayerAi("p1", options));
|
||||
p2.setPlayer(new LobbyPlayerAi("p2", options));
|
||||
GameRules rules = new GameRules(GameType.Constructed);
|
||||
// need game rules, players, and title
|
||||
Match m = new Match(rules, Arrays.asList(p1, p2), "test");
|
||||
|
||||
simulateSingleGameOfMatch(m, 120);
|
||||
}
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
package forge.ai;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import forge.GuiDesktop;
|
||||
import forge.StaticData;
|
||||
import forge.deck.Deck;
|
||||
import forge.game.Game;
|
||||
import forge.game.GameRules;
|
||||
import forge.game.GameStage;
|
||||
import forge.game.GameType;
|
||||
import forge.game.Match;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.RegisteredPlayer;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.gui.GuiBase;
|
||||
import forge.item.IPaperCard;
|
||||
import forge.localinstance.properties.ForgePreferences;
|
||||
import forge.localinstance.properties.ForgePreferences.FPref;
|
||||
import forge.model.FModel;
|
||||
|
||||
public class AITest {
|
||||
private static boolean initialized = false;
|
||||
|
||||
public Game resetGame() {
|
||||
// need to be done after FModel.initialize, or the Localizer isn't loaded yet
|
||||
List<RegisteredPlayer> players = Lists.newArrayList();
|
||||
Deck d1 = new Deck();
|
||||
players.add(new RegisteredPlayer(d1).setPlayer(new LobbyPlayerAi("p2", null)));
|
||||
players.add(new RegisteredPlayer(d1).setPlayer(new LobbyPlayerAi("p1", null)));
|
||||
GameRules rules = new GameRules(GameType.Constructed);
|
||||
Match match = new Match(rules, players, "Test");
|
||||
Game game = new Game(players, rules, match);
|
||||
Player p = game.getPlayers().get(1);
|
||||
game.setAge(GameStage.Play);
|
||||
game.getPhaseHandler().devModeSet(PhaseType.MAIN1, p);
|
||||
game.getPhaseHandler().onStackResolved();
|
||||
// game.getAction().checkStateEffects(true);
|
||||
|
||||
return game;
|
||||
}
|
||||
|
||||
protected Game initAndCreateGame() {
|
||||
if (!initialized) {
|
||||
GuiBase.setInterface(new GuiDesktop());
|
||||
FModel.initialize(null, new Function<ForgePreferences, Void>() {
|
||||
@Override
|
||||
public Void apply(ForgePreferences preferences) {
|
||||
preferences.setPref(FPref.LOAD_CARD_SCRIPTS_LAZILY, false);
|
||||
preferences.setPref(FPref.UI_LANGUAGE, "en-US");
|
||||
return null;
|
||||
}
|
||||
});
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
return resetGame();
|
||||
}
|
||||
|
||||
protected int countCardsWithName(Game game, String name) {
|
||||
int i = 0;
|
||||
for (Card c : game.getCardsIn(ZoneType.Battlefield)) {
|
||||
if (c.getName().equals(name)) {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
protected Card findCardWithName(Game game, String name) {
|
||||
for (Card c : game.getCardsIn(ZoneType.Battlefield)) {
|
||||
if (c.getName().equals(name)) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected String gameStateToString(Game game) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (ZoneType zone : ZoneType.values()) {
|
||||
CardCollectionView cards = game.getCardsIn(zone);
|
||||
if (!cards.isEmpty()) {
|
||||
sb.append("Zone ").append(zone.name()).append(":\n");
|
||||
for (Card c : game.getCardsIn(zone)) {
|
||||
sb.append(" ").append(c);
|
||||
if (c.isTapped()) {
|
||||
sb.append(" (T)");
|
||||
}
|
||||
sb.append("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
protected SpellAbility findSAWithPrefix(Card c, String prefix) {
|
||||
return findSAWithPrefix(c.getSpellAbilities(), prefix);
|
||||
}
|
||||
|
||||
protected SpellAbility findSAWithPrefix(Iterable<SpellAbility> abilities, String prefix) {
|
||||
for (SpellAbility sa : abilities) {
|
||||
if (sa.getDescription().startsWith(prefix)) {
|
||||
return sa;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected Card createCard(String name, Player p) {
|
||||
IPaperCard paperCard = FModel.getMagicDb().getCommonCards().getCard(name);
|
||||
if (paperCard == null) {
|
||||
StaticData.instance().attemptToLoadCard(name);
|
||||
paperCard = FModel.getMagicDb().getCommonCards().getCard(name);
|
||||
}
|
||||
return Card.fromPaperCard(paperCard, p);
|
||||
}
|
||||
|
||||
protected Card addCardToZone(String name, Player p, ZoneType zone) {
|
||||
Card c = createCard(name, p);
|
||||
// card need a new Timestamp otherwise Static Abilities might collide
|
||||
c.setTimestamp(p.getGame().getNextTimestamp());
|
||||
p.getZone(zone).add(c);
|
||||
return c;
|
||||
}
|
||||
|
||||
protected Card addCard(String name, Player p) {
|
||||
return addCardToZone(name, p, ZoneType.Battlefield);
|
||||
}
|
||||
|
||||
protected List<Card> addCards(String name, int count, Player p) {
|
||||
List<Card> cards = Lists.newArrayList();
|
||||
for (int i = 0; i < count; i++) {
|
||||
cards.add(addCard(name, p));
|
||||
}
|
||||
return cards;
|
||||
}
|
||||
|
||||
void playUntilStackClear(Game game) {
|
||||
do {
|
||||
game.getPhaseHandler().mainLoopStep();
|
||||
} while (!game.isGameOver() && !game.getStack().isEmpty());
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1,877 +0,0 @@
|
||||
package forge.ai;
|
||||
|
||||
import forge.ai.simulation.GameStateEvaluator;
|
||||
import forge.game.spellability.LandAbility;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import forge.item.PaperCard;
|
||||
import forge.model.FModel;
|
||||
import org.testng.AssertJUnit;
|
||||
import org.testng.annotations.Ignore;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import forge.game.Game;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.zone.ZoneType;
|
||||
|
||||
public class SpellAbilityPickerTest extends AITest {
|
||||
@Test
|
||||
public void testPickingLethalDamage() {
|
||||
Game game = initAndCreateGame();
|
||||
Player p = game.getPlayers().get(1);
|
||||
p.setTeam(0);
|
||||
|
||||
addCard("Mountain", p);
|
||||
addCardToZone("Shock", p, ZoneType.Hand);
|
||||
|
||||
Player opponent = game.getPlayers().get(0);
|
||||
opponent.setTeam(1);
|
||||
|
||||
addCard("Runeclaw Bear", opponent);
|
||||
opponent.setLife(2, null);
|
||||
|
||||
game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p);
|
||||
game.getAction().checkStateEffects(true);
|
||||
|
||||
List<SpellAbility> chosenSa = p.getController().chooseSpellAbilityToPlay();
|
||||
System.out.println(chosenSa);
|
||||
SpellAbility sa = chosenSa.get(0);
|
||||
AssertJUnit.assertNotNull(sa);
|
||||
AssertJUnit.assertNull(sa.getTargetCard());
|
||||
AssertJUnit.assertEquals(opponent, sa.getTargets().getFirstTargetedPlayer());
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
public void testPickingKillingCreature() {
|
||||
Game game = initAndCreateGame();
|
||||
Player p = game.getPlayers().get(1);
|
||||
|
||||
addCard("Mountain", p);
|
||||
addCardToZone("Shock", p, ZoneType.Hand);
|
||||
|
||||
Player opponent = game.getPlayers().get(0);
|
||||
Card bearCard = addCard("Runeclaw Bear", opponent);
|
||||
opponent.setLife(20, null);
|
||||
|
||||
game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p);
|
||||
game.getAction().checkStateEffects(true);
|
||||
|
||||
List<SpellAbility> chosenSa = p.getController().chooseSpellAbilityToPlay();
|
||||
System.out.println(chosenSa);
|
||||
System.out.println(gameStateToString(game));
|
||||
SpellAbility sa = chosenSa.get(0);
|
||||
AssertJUnit.assertNotNull(sa);
|
||||
AssertJUnit.assertEquals(bearCard, sa.getTargetCard());
|
||||
AssertJUnit.assertNull(sa.getTargets().getFirstTargetedPlayer());
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
public void testSequenceStartingWithPlayingLand() {
|
||||
Game game = initAndCreateGame();
|
||||
Player p = game.getPlayers().get(1);
|
||||
|
||||
Card mountain = addCardToZone("Mountain", p, ZoneType.Hand);
|
||||
addCardToZone("Shock", p, ZoneType.Hand);
|
||||
|
||||
Player opponent = game.getPlayers().get(0);
|
||||
addCard("Runeclaw Bear", opponent);
|
||||
opponent.setLife(20, null);
|
||||
|
||||
game.getPhaseHandler().devModeSet(PhaseType.MAIN1, p);
|
||||
game.getAction().checkStateEffects(true);
|
||||
|
||||
List<SpellAbility> chosenSa = p.getController().chooseSpellAbilityToPlay();
|
||||
System.out.println(chosenSa);
|
||||
System.out.println(gameStateToString(game));
|
||||
SpellAbility sa = chosenSa.get(0);
|
||||
AssertJUnit.assertTrue(sa instanceof LandAbility);
|
||||
AssertJUnit.assertEquals(mountain, sa.getHostCard());
|
||||
|
||||
playUntilStackClear(game);
|
||||
game.getPhaseHandler().mainLoopStep();
|
||||
System.out.println(gameStateToString(game));
|
||||
sa = p.getController().chooseSpellAbilityToPlay().get(0);
|
||||
|
||||
AssertJUnit.assertEquals("Shock deals 2 damage to any target.", sa.toString());
|
||||
// AssertJUnit.assertTrue(plan.getDecisions().get(1).targets.toString().contains("Runeclaw Bear"));
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
public void testPlayingLandAfterSpell() {
|
||||
Game game = initAndCreateGame();
|
||||
Player p = game.getPlayers().get(1);
|
||||
|
||||
addCards("Island", 2, p);
|
||||
addCards("Forest", 3, p);
|
||||
|
||||
Card tatyova = addCardToZone("Tatyova, Benthic Druid", p, ZoneType.Hand);
|
||||
addCardToZone("Forest", p, ZoneType.Hand);
|
||||
addCardToZone("Forest", p, ZoneType.Library);
|
||||
|
||||
game.getPhaseHandler().devModeSet(PhaseType.MAIN1, p);
|
||||
game.getAction().checkStateEffects(true);
|
||||
|
||||
SpellAbility sa = p.getController().chooseSpellAbilityToPlay().get(0);
|
||||
AssertJUnit.assertEquals(tatyova, sa.getHostCard());
|
||||
|
||||
// The plan should involve playing Tatyova first and then playing a land, to benefit from
|
||||
// the landfall trigger.
|
||||
playUntilStackClear(game);
|
||||
sa = p.getController().chooseSpellAbilityToPlay().get(0);
|
||||
AssertJUnit.assertEquals("Play land", sa.toString());
|
||||
// Plan plan = picker.getPlan();
|
||||
// AssertJUnit.assertEquals(2, plan.getDecisions().size());
|
||||
// AssertJUnit.assertEquals("Tatyova, Benthic Druid - Creature 3 / 3", plan.getDecisions().get(0).saRef.toString());
|
||||
// AssertJUnit.assertEquals("Play land", plan.getDecisions().get(1).saRef.toString());
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
public void testModeSelection() {
|
||||
Game game = initAndCreateGame();
|
||||
Player p = game.getPlayers().get(1);
|
||||
|
||||
addCard("Plains", p);
|
||||
addCard("Island", p);
|
||||
addCard("Swamp", p);
|
||||
Card spell = addCardToZone("Dromar's Charm", p, ZoneType.Hand);
|
||||
|
||||
Player opponent = game.getPlayers().get(0);
|
||||
addCard("Runeclaw Bear", opponent);
|
||||
|
||||
game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p);
|
||||
game.getAction().checkStateEffects(true);
|
||||
|
||||
// Expected: All creatures get -2/-2 to kill the bear.
|
||||
SpellAbility sa = p.getController().chooseSpellAbilityToPlay().get(0);
|
||||
// SpellAbilityPicker picker = new SpellAbilityPicker(game, p);
|
||||
// SpellAbility sa = picker.chooseSpellAbilityToPlay(null);
|
||||
AssertJUnit.assertEquals(spell.getSpellAbilities().get(0), sa);
|
||||
AssertJUnit.assertEquals("Dromar's Charm -> Target creature gets -2/-2 until end of turn.",
|
||||
sa.toString());
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
public void testModeSelection2() {
|
||||
Game game = initAndCreateGame();
|
||||
Player p = game.getPlayers().get(1);
|
||||
|
||||
addCard("Plains", p);
|
||||
addCard("Island", p);
|
||||
addCard("Swamp", p);
|
||||
Card spell = addCardToZone("Dromar's Charm", p, ZoneType.Hand);
|
||||
|
||||
game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p);
|
||||
game.getAction().checkStateEffects(true);
|
||||
|
||||
// Expected: Gain 5 life, since other modes aren't helpful.
|
||||
SpellAbility sa = p.getController().chooseSpellAbilityToPlay().get(0);
|
||||
// SpellAbilityPicker picker = new SpellAbilityPicker(game, p);
|
||||
// SpellAbility sa = picker.chooseSpellAbilityToPlay(null);
|
||||
AssertJUnit.assertEquals(spell.getSpellAbilities().get(0), sa);
|
||||
AssertJUnit.assertEquals("Dromar's Charm -> You gain 5 life.", sa.toString());
|
||||
}
|
||||
|
||||
/*
|
||||
@Test
|
||||
public void testMultipleModes() {
|
||||
Game game = initAndCreateGame();
|
||||
Player p = game.getPlayers().get(1);
|
||||
|
||||
addCards("Mountain", 4, p);
|
||||
Card spell = addCardToZone("Fiery Confluence", p, ZoneType.Hand);
|
||||
|
||||
Player opponent = game.getPlayers().get(0);
|
||||
addCard("Runeclaw Bear", opponent);
|
||||
opponent.setLife(20, null);
|
||||
|
||||
game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p);
|
||||
game.getAction().checkStateEffects(true);
|
||||
|
||||
// Expected: 2x 1 damage to each creature, 1x 2 damage to each opponent.
|
||||
SpellAbilityPicker picker = new SpellAbilityPicker(game, p);
|
||||
SpellAbility sa = picker.chooseSpellAbilityToPlay(null);
|
||||
AssertJUnit.assertEquals(spell.getSpellAbilities().get(0), sa);
|
||||
|
||||
String dmgCreaturesStr = "Fiery Confluence deals 1 damage to each creature.";
|
||||
String dmgOppStr = "Fiery Confluence deals 2 damage to each opponent.";
|
||||
String expected = "Fiery Confluence -> " + dmgCreaturesStr + " " + dmgCreaturesStr + " " + dmgOppStr;
|
||||
AssertJUnit.assertEquals(expected, picker.getPlan().getDecisions().get(0).modesStr);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleModes2() {
|
||||
Game game = initAndCreateGame();
|
||||
Player p = game.getPlayers().get(1);
|
||||
|
||||
addCards("Mountain", 4, p);
|
||||
Card spell = addCardToZone("Fiery Confluence", p, ZoneType.Hand);
|
||||
|
||||
Player opponent = game.getPlayers().get(0);
|
||||
addCard("Runeclaw Bear", opponent);
|
||||
opponent.setLife(6, null);
|
||||
|
||||
game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p);
|
||||
game.getAction().checkStateEffects(true);
|
||||
|
||||
// Expected: 3x 2 damage to each opponent.
|
||||
SpellAbilityPicker picker = new SpellAbilityPicker(game, p);
|
||||
SpellAbility sa = picker.chooseSpellAbilityToPlay(null);
|
||||
AssertJUnit.assertEquals(spell.getSpellAbilities().get(0), sa);
|
||||
|
||||
String dmgOppStr = "Fiery Confluence deals 2 damage to each opponent.";
|
||||
String expected = "Fiery Confluence -> " + dmgOppStr + " " + dmgOppStr + " " + dmgOppStr;
|
||||
AssertJUnit.assertEquals(expected, picker.getPlan().getDecisions().get(0).modesStr);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleTargets() {
|
||||
Game game = initAndCreateGame();
|
||||
Player p = game.getPlayers().get(1);
|
||||
|
||||
addCards("Mountain", 2, p);
|
||||
Card spell = addCardToZone("Arc Trail", p, ZoneType.Hand);
|
||||
|
||||
Player opponent = game.getPlayers().get(0);
|
||||
Card bear = addCard("Runeclaw Bear", opponent);
|
||||
Card men = addCard("Flying Men", opponent);
|
||||
opponent.setLife(20, null);
|
||||
|
||||
game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p);
|
||||
game.getAction().checkStateEffects(true);
|
||||
|
||||
SpellAbilityPicker picker = new SpellAbilityPicker(game, p);
|
||||
SpellAbility sa = picker.chooseSpellAbilityToPlay(null);
|
||||
AssertJUnit.assertEquals(spell.getSpellAbilities().get(0), sa);
|
||||
AssertJUnit.assertEquals(bear, sa.getTargetCard());
|
||||
AssertJUnit.assertEquals("2", sa.getParam("NumDmg"));
|
||||
SpellAbility subSa = sa.getSubAbility();
|
||||
AssertJUnit.assertEquals(men, subSa.getTargetCard());
|
||||
AssertJUnit.assertEquals("1", subSa.getParam("NumDmg"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLandSearchForCombo() {
|
||||
Game game = initAndCreateGame();
|
||||
Player p = game.getPlayers().get(1);
|
||||
|
||||
addCard("Forest", p);
|
||||
addCard("Thespian's Stage", p);
|
||||
Card darkDepths = addCard("Dark Depths", p);
|
||||
|
||||
Card cropRotation = addCardToZone("Crop Rotation", p, ZoneType.Hand);
|
||||
|
||||
addCardToZone("Forest", p, ZoneType.Library);
|
||||
addCardToZone("Urborg, Tomb of Yawgmoth", p, ZoneType.Library);
|
||||
addCardToZone("Swamp", p, ZoneType.Library);
|
||||
|
||||
darkDepths.setCounters(CounterEnumType.ICE, 10);
|
||||
|
||||
game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p);
|
||||
game.getAction().checkStateEffects(true);
|
||||
|
||||
AssertJUnit.assertEquals(10, darkDepths.getCounters(CounterEnumType.ICE));
|
||||
SpellAbilityPicker picker = new SpellAbilityPicker(game, p);
|
||||
SpellAbility sa = picker.chooseSpellAbilityToPlay(null);
|
||||
AssertJUnit.assertEquals(cropRotation.getSpellAbilities().get(0), sa);
|
||||
// Expected: Sac a Forest to get an Urborg.
|
||||
List<String> choices = picker.getPlan().getDecisions().get(0).choices;
|
||||
AssertJUnit.assertEquals(2, choices.size());
|
||||
AssertJUnit.assertEquals("Forest", choices.get(0));
|
||||
AssertJUnit.assertEquals("Urborg, Tomb of Yawgmoth", choices.get(1));
|
||||
// Next, expected to use Thespian's Stage to copy Dark Depths.
|
||||
Plan.Decision d2 = picker.getPlan().getDecisions().get(1);
|
||||
String expected = "{2}, {T}: Thespian's Stage becomes a copy of target land, except it has this ability.";
|
||||
AssertJUnit.assertEquals(expected, d2.saRef.toString());
|
||||
AssertJUnit.assertTrue(d2.targets.toString().contains("Dark Depths"));
|
||||
}
|
||||
*/
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
public void playTaplandIfNoPlays() {
|
||||
Game game = initAndCreateGame();
|
||||
Player p = game.getPlayers().get(1);
|
||||
|
||||
// start with a hand with a basic, a tapland, and a card that can't be cast
|
||||
addCard("Forest", p);
|
||||
addCardToZone("Forest", p, ZoneType.Hand);
|
||||
Card desired = addCardToZone("Simic Guildgate", p, ZoneType.Hand);
|
||||
addCardToZone("Centaur Courser", p, ZoneType.Hand);
|
||||
game.getPhaseHandler().devModeSet(PhaseType.MAIN1, p);
|
||||
game.getAction().checkStateEffects(true);
|
||||
|
||||
// ensure that the tapland is paid
|
||||
SpellAbility sa = p.getController().chooseSpellAbilityToPlay().get(0);
|
||||
AssertJUnit.assertEquals(desired, sa.getHostCard());
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
public void playBouncelandIfNoPlays() {
|
||||
Game game = initAndCreateGame();
|
||||
Player p = game.getPlayers().get(1);
|
||||
|
||||
// start with a hand with a basic, a bounceland, and a card that can't be cast
|
||||
addCard("Forest", p);
|
||||
addCardToZone("Forest", p, ZoneType.Hand);
|
||||
Card desired = addCardToZone("Simic Growth Chamber", p, ZoneType.Hand);
|
||||
addCardToZone("Centaur Courser", p, ZoneType.Hand);
|
||||
game.getPhaseHandler().devModeSet(PhaseType.MAIN1, p);
|
||||
game.getAction().checkStateEffects(true);
|
||||
|
||||
// ensure that the tapland is played
|
||||
SpellAbility sa = p.getController().chooseSpellAbilityToPlay().get(0);
|
||||
AssertJUnit.assertEquals(desired, sa.getHostCard());
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
public void playTronOverBasic() {
|
||||
Game game = initAndCreateGame();
|
||||
Player p = game.getPlayers().get(1);
|
||||
|
||||
// start with a hand with a basic, a Tron land, and a card that can't be cast
|
||||
addCard("Urza's Tower", p);
|
||||
addCard("Urza's Mine", p);
|
||||
addCardToZone("Forest", p, ZoneType.Hand);
|
||||
Card desired = addCardToZone("Urza's Power Plant", p, ZoneType.Hand);
|
||||
addCardToZone("Opt", p, ZoneType.Hand);
|
||||
game.getPhaseHandler().devModeSet(PhaseType.MAIN1, p);
|
||||
game.getAction().checkStateEffects(true);
|
||||
|
||||
// ensure that the tron land is played
|
||||
SpellAbility sa = p.getController().chooseSpellAbilityToPlay().get(0);
|
||||
AssertJUnit.assertEquals(desired, sa.getHostCard());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void playManalessLands() {
|
||||
Game game = initAndCreateGame();
|
||||
Player p = game.getPlayers().get(1);
|
||||
|
||||
// start with a hand with a land that can't produce mana.
|
||||
Card desired = addCardToZone("Maze of Ith", p, ZoneType.Hand);
|
||||
game.getPhaseHandler().devModeSet(PhaseType.MAIN1, p);
|
||||
game.getAction().checkStateEffects(true);
|
||||
|
||||
// ensure that the land is played
|
||||
SpellAbility sa = p.getController().chooseSpellAbilityToPlay().get(0);
|
||||
AssertJUnit.assertEquals(desired, sa.getHostCard());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void playBasicOverUtility() {
|
||||
Game game = initAndCreateGame();
|
||||
Player p = game.getPlayers().get(1);
|
||||
|
||||
// start with a hand with a colorless utility land and a basic
|
||||
addCardToZone("Rogue's Passage", p, ZoneType.Hand);
|
||||
Card desired = addCardToZone("Forest", p, ZoneType.Hand);
|
||||
|
||||
// make sure that there is a card in the library with G mana cost
|
||||
addCardToZone("Grizzly Bears", p, ZoneType.Library);
|
||||
|
||||
game.getPhaseHandler().devModeSet(PhaseType.MAIN1, p);
|
||||
game.getAction().checkStateEffects(true);
|
||||
|
||||
// ensure that the basic land is played
|
||||
SpellAbility sa = p.getController().chooseSpellAbilityToPlay().get(0);
|
||||
AssertJUnit.assertEquals(desired, sa.getHostCard());
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
public void targetUtilityLandOverRainbow() {
|
||||
Game game = initAndCreateGame();
|
||||
Player p = game.getPlayers().get(1);
|
||||
Player opponent = game.getPlayers().get(0);
|
||||
opponent.setLife(20, null);
|
||||
|
||||
// start with the opponent having a basic land, a dual, and a rainbow
|
||||
addCard("Forest", opponent);
|
||||
addCard("Breeding Pool", opponent);
|
||||
addCard("Mana Confluence", opponent);
|
||||
Card desired = addCard("Mutavault", opponent);
|
||||
addCard("Strip Mine", p);
|
||||
|
||||
// It doesn't want to use strip mine in main
|
||||
game.getPhaseHandler().devModeSet(PhaseType.COMBAT_DECLARE_BLOCKERS, p);
|
||||
game.getAction().checkStateEffects(true);
|
||||
|
||||
// ensure that the land is played
|
||||
SpellAbility sa = p.getController().chooseSpellAbilityToPlay().get(0);
|
||||
AssertJUnit.assertEquals(desired, sa.getTargetCard());
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
public void ensureAllLandsArePlayable() {
|
||||
initAndCreateGame();
|
||||
|
||||
System.out.println("Adding lands to hand");
|
||||
|
||||
// add every land to the player's hand
|
||||
List<Card> funky = new ArrayList<>();
|
||||
String previous = "";
|
||||
for (PaperCard c : FModel.getMagicDb().getCommonCards().getAllCards()) {
|
||||
// Only test one version of a card
|
||||
if (c.getName().equals(previous)) {
|
||||
continue;
|
||||
}
|
||||
previous = c.getName();
|
||||
|
||||
// skip nonland cards
|
||||
if (!c.getRules().getType().isLand()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// System.out.println(c.getName());
|
||||
|
||||
// Skip glacial chasm, it's really weird.
|
||||
if (c.getName().equals("Glacial Chasm")) {
|
||||
System.out.println("Skipping " + c.getName());
|
||||
continue;
|
||||
}
|
||||
|
||||
// reset the game
|
||||
Game game = resetGame();
|
||||
Player p = game.getPlayers().get(1);
|
||||
Player opponent = game.getPlayers().get(0);
|
||||
opponent.setLife(20, null);
|
||||
|
||||
// add one of each basic to the battlefield so that bouncelands and similar work
|
||||
addCard("Plains", p);
|
||||
addCard("Island", p);
|
||||
addCard("Swamp", p);
|
||||
addCard("Mountain", p);
|
||||
addCard("Forest", p);
|
||||
// Add basics to library to ensure fetches work
|
||||
addCardToZone("Plains", p, ZoneType.Library);
|
||||
addCardToZone("Island", p, ZoneType.Library);
|
||||
addCardToZone("Swamp", p, ZoneType.Library);
|
||||
addCardToZone("Mountain", p, ZoneType.Library);
|
||||
addCardToZone("Forest", p, ZoneType.Library);
|
||||
|
||||
game.getPhaseHandler().devModeSet(PhaseType.MAIN1, p);
|
||||
game.getAction().checkStateEffects(true);
|
||||
|
||||
// Add the target card to the hand and test it
|
||||
Card desired = addCardToZone(c.getName(), p, ZoneType.Hand);
|
||||
|
||||
List<SpellAbility> choices = p.getController().chooseSpellAbilityToPlay();
|
||||
if (choices == null) {
|
||||
funky.add(desired);
|
||||
continue;
|
||||
}
|
||||
SpellAbility sa = choices.get(0);
|
||||
if (sa == null || sa.getHostCard() != desired) {
|
||||
funky.add(desired);
|
||||
continue;
|
||||
}
|
||||
// AssertJUnit.assertEquals(desired, sa.getTargetCard());
|
||||
// GameStateEvaluator.Score s = new GameStateEvaluator().getScoreForGameState(game, p);
|
||||
// System.out.println("Starting score: " + s);
|
||||
// SpellAbilityPicker picker = new SpellAbilityPicker(game, p);
|
||||
// List<SpellAbility> candidateSAs = picker.getCandidateSpellsAndAbilities();
|
||||
// for (int i = 0; i < candidateSAs.size(); i++) {
|
||||
// SpellAbility sa = candidateSAs.get(i);
|
||||
// if (sa.isActivatedAbility()) {
|
||||
// continue;
|
||||
// }
|
||||
// GameStateEvaluator.Score value = picker.evaluateSa(new SimulationController(s), game.getPhaseHandler().getPhase(), candidateSAs, i);
|
||||
// System.out.println("sa: " + sa.getHostCard() + ", value: " + value);
|
||||
// if (!(value.value > s.value)) {
|
||||
// funky.add(sa.getHostCard());
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
// ensure that every land play has a higher evaluation than doing nothing
|
||||
System.out.println(funky);
|
||||
for (Card c : funky) {
|
||||
GameStateEvaluator gse = new GameStateEvaluator();
|
||||
Game game = resetGame();
|
||||
System.out.println(c.getName() + ": " + gse.evalCard(game, game.getStartingPlayer(), c));
|
||||
}
|
||||
AssertJUnit.assertEquals(0, funky.size());
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
@Test
|
||||
public void testPlayRememberedCardsLand() {
|
||||
Game game = initAndCreateGame();
|
||||
Player p = game.getPlayers().get(1);
|
||||
|
||||
addCards("Mountain", 2, p);
|
||||
Card abbot = addCardToZone("Abbot of Keral Keep", p, ZoneType.Hand);
|
||||
addCardToZone("Lightning Bolt", p, ZoneType.Hand);
|
||||
// Note: This assumes the top of library is revealed. If the AI is made
|
||||
// smarter to not assume that, then this test can be updated to have
|
||||
// something that reveals top of library active - e.g. Lens of Clarity.
|
||||
addCardToZone("Mountain", p, ZoneType.Library);
|
||||
|
||||
game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p);
|
||||
game.getAction().checkStateEffects(true);
|
||||
|
||||
// Expected plan:
|
||||
// 1. Play Abbot.
|
||||
// 2. Play land exiled by Abbot.
|
||||
// 3. Play Bolt targeting opponent.
|
||||
SpellAbilityPicker picker = new SpellAbilityPicker(game, p);
|
||||
SpellAbility sa = picker.chooseSpellAbilityToPlay(null);
|
||||
AssertJUnit.assertEquals(abbot.getSpellAbilities().get(0), sa);
|
||||
Plan plan = picker.getPlan();
|
||||
AssertJUnit.assertEquals(3, plan.getDecisions().size());
|
||||
AssertJUnit.assertEquals("Mountain (5) -> Play land by Abbot of Keral Keep (3)",
|
||||
plan.getDecisions().get(1).saRef.toString(true));
|
||||
AssertJUnit.assertEquals("Lightning Bolt deals 3 damage to any target.",
|
||||
plan.getDecisions().get(2).saRef.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPlayRememberedCardsSpell() {
|
||||
Game game = initAndCreateGame();
|
||||
Player p = game.getPlayers().get(1);
|
||||
|
||||
addCards("Mountain", 3, p);
|
||||
Card abbot = addCardToZone("Abbot of Keral Keep", p, ZoneType.Hand);
|
||||
// Note: This assumes the top of library is revealed. If the AI is made
|
||||
// smarter to not assume that, then this test can be updated to have
|
||||
// something that reveals top of library active - e.g. Lens of Clarity.
|
||||
addCardToZone("Lightning Bolt", p, ZoneType.Library);
|
||||
|
||||
game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p);
|
||||
game.getAction().checkStateEffects(true);
|
||||
|
||||
// Expected plan:
|
||||
// 1. Play Abbot.
|
||||
// 3. Play Bolt exiled by Abbot.
|
||||
SpellAbilityPicker picker = new SpellAbilityPicker(game, p);
|
||||
SpellAbility sa = picker.chooseSpellAbilityToPlay(null);
|
||||
AssertJUnit.assertEquals(abbot.getSpellAbilities().get(0), sa);
|
||||
Plan plan = picker.getPlan();
|
||||
AssertJUnit.assertEquals(2, plan.getDecisions().size());
|
||||
String saDesc = plan.getDecisions().get(1).saRef.toString();
|
||||
AssertJUnit.assertTrue(saDesc, saDesc.startsWith("Lightning Bolt deals 3 damage to any target."));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPlayingPumpSpellsAfterBlocks() {
|
||||
Game game = initAndCreateGame();
|
||||
Player p = game.getPlayers().get(1);
|
||||
Player opponent = game.getPlayers().get(0);
|
||||
opponent.setLife(2, null);
|
||||
|
||||
Card blocker = addCard("Fugitive Wizard", opponent);
|
||||
Card attacker1 = addCard("Dwarven Trader", p);
|
||||
attacker1.setSickness(false);
|
||||
Card attacker2 = addCard("Dwarven Trader", p);
|
||||
attacker2.setSickness(false);
|
||||
addCard("Mountain", p);
|
||||
addCardToZone("Brute Force", p, ZoneType.Hand);
|
||||
|
||||
game.getPhaseHandler().devModeSet(PhaseType.MAIN1, p);
|
||||
game.getAction().checkStateEffects(true);
|
||||
|
||||
SpellAbilityPicker picker = new SpellAbilityPicker(game, p);
|
||||
AssertJUnit.assertNull(picker.chooseSpellAbilityToPlay(null));
|
||||
|
||||
game.getPhaseHandler().devAdvanceToPhase(PhaseType.COMBAT_BEGIN);
|
||||
game.getAction().checkStateEffects(true);
|
||||
AssertJUnit.assertNull(picker.chooseSpellAbilityToPlay(null));
|
||||
|
||||
game.getPhaseHandler().devModeSet(PhaseType.COMBAT_DECLARE_ATTACKERS, p);
|
||||
Combat combat = new Combat(p);
|
||||
combat.addAttacker(attacker1, opponent);
|
||||
combat.addAttacker(attacker2, opponent);
|
||||
game.getPhaseHandler().setCombat(combat);
|
||||
game.getAction().checkStateEffects(true);
|
||||
AssertJUnit.assertNull(picker.chooseSpellAbilityToPlay(null));
|
||||
|
||||
game.getPhaseHandler().devModeSet(PhaseType.COMBAT_DECLARE_BLOCKERS, p, false);
|
||||
game.getAction().checkStateEffects(true);
|
||||
combat.addBlocker(attacker1, blocker);
|
||||
combat.getBandOfAttacker(attacker1).setBlocked(true);
|
||||
combat.getBandOfAttacker(attacker2).setBlocked(false);
|
||||
combat.orderBlockersForDamageAssignment();
|
||||
combat.orderAttackersForDamageAssignment();
|
||||
SpellAbility sa = picker.chooseSpellAbilityToPlay(null);
|
||||
AssertJUnit.assertNotNull(sa);
|
||||
AssertJUnit.assertEquals("Target creature gets +3/+3 until end of turn.", sa.toString());
|
||||
AssertJUnit.assertEquals(attacker2, sa.getTargetCard());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPlayingSorceryPumpSpellsBeforeBlocks() {
|
||||
Game game = initAndCreateGame();
|
||||
Player p = game.getPlayers().get(1);
|
||||
Player opponent = game.getPlayers().get(0);
|
||||
opponent.setLife(2, null);
|
||||
|
||||
addCard("Fugitive Wizard", opponent);
|
||||
Card attacker1 = addCard("Dwarven Trader", p);
|
||||
attacker1.setSickness(false);
|
||||
Card attacker2 = addCard("Kird Ape", p);
|
||||
attacker2.setSickness(false);
|
||||
addCard("Mountain", p);
|
||||
Card furor = addCardToZone("Furor of the Bitten", p, ZoneType.Hand);
|
||||
|
||||
game.getPhaseHandler().devModeSet(PhaseType.MAIN1, p);
|
||||
game.getAction().checkStateEffects(true);
|
||||
|
||||
SpellAbilityPicker picker = new SpellAbilityPicker(game, p);
|
||||
SpellAbility sa = picker.chooseSpellAbilityToPlay(null);
|
||||
AssertJUnit.assertNotNull(sa);
|
||||
AssertJUnit.assertEquals(furor.getSpellAbilities().get(0), sa);
|
||||
AssertJUnit.assertEquals(attacker1, sa.getTargetCard());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPlayingRemovalBeforeBlocks() {
|
||||
Game game = initAndCreateGame();
|
||||
Player p = game.getPlayers().get(1);
|
||||
Player opponent = game.getPlayers().get(0);
|
||||
opponent.setLife(2, null);
|
||||
|
||||
Card blocker = addCard("Fugitive Wizard", opponent);
|
||||
Card attacker1 = addCard("Dwarven Trader", p);
|
||||
attacker1.setSickness(false);
|
||||
addCards("Swamp", 2, p);
|
||||
addCardToZone("Doom Blade", p, ZoneType.Hand);
|
||||
|
||||
game.getPhaseHandler().devModeSet(PhaseType.MAIN1, p);
|
||||
game.getAction().checkStateEffects(true);
|
||||
|
||||
SpellAbilityPicker picker = new SpellAbilityPicker(game, p);
|
||||
SpellAbility sa = picker.chooseSpellAbilityToPlay(null);
|
||||
AssertJUnit.assertNotNull(sa);
|
||||
AssertJUnit.assertEquals("Destroy target nonblack creature.", sa.toString());
|
||||
AssertJUnit.assertEquals(blocker, sa.getTargetCard());
|
||||
}
|
||||
|
||||
// Run the test 100 times to ensure there's no flakiness.
|
||||
@Test(invocationCount = 100)
|
||||
public void testChoicesResultingFromRandomEffects() {
|
||||
// Sometimes, the effect of a spell can be random, and as a result of that, new choices
|
||||
// could be selected during simulation. This test verifies that this doesn't cause problems.
|
||||
//
|
||||
// Note: The current implementation works around the issue by setting a consistent random
|
||||
// seed during choice evaluation, although in the future, it may make sense to handle it
|
||||
// some other way.
|
||||
|
||||
Game game = initAndCreateGame();
|
||||
Player p = game.getPlayers().get(1);
|
||||
Player opponent = game.getPlayers().get(0);
|
||||
|
||||
addCardToZone("Chaos Warp", p, ZoneType.Hand);
|
||||
addCards("Mountain", 3, p);
|
||||
|
||||
addCard("Plains", opponent);
|
||||
addCard("Mountain", opponent);
|
||||
addCard("Forest", opponent);
|
||||
// Use a card that is worthwhile to target even if the shuffle ends up choosing it
|
||||
// again. In this case, life loss on ETB and leaving.
|
||||
Card expectedTarget = addCard("Raving Oni-Slave", opponent);
|
||||
|
||||
addCardToZone("Chaos Warp", opponent, ZoneType.Library);
|
||||
addCardToZone("Island", opponent, ZoneType.Library);
|
||||
addCardToZone("Swamp", opponent, ZoneType.Library);
|
||||
// The presence of Pilgrim's Eye in the library is important for this test, as this
|
||||
// will result in sub-choices (which land to pick) if this card ends up being the top
|
||||
// of the library during simulation.
|
||||
addCardToZone("Pilgrim's Eye", opponent, ZoneType.Library);
|
||||
|
||||
game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p);
|
||||
game.getAction().checkStateEffects(true);
|
||||
|
||||
SpellAbilityPicker picker = new SpellAbilityPicker(game, p);
|
||||
SpellAbility sa = picker.chooseSpellAbilityToPlay(null);
|
||||
AssertJUnit.assertNotNull(sa);
|
||||
AssertJUnit.assertEquals("Chaos Warp", sa.getHostCard().getName());
|
||||
AssertJUnit.assertEquals(expectedTarget, sa.getTargetCard());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoSimulationsWhenNoTargets() {
|
||||
Game game = initAndCreateGame();
|
||||
Player p = game.getPlayers().get(1);
|
||||
|
||||
addCards("Forest", 2, p);
|
||||
addCardToZone("Counterspell", p, ZoneType.Hand);
|
||||
addCardToZone("Unsummon", p, ZoneType.Hand);
|
||||
|
||||
game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p);
|
||||
game.getAction().checkStateEffects(true);
|
||||
|
||||
SpellAbilityPicker picker = new SpellAbilityPicker(game, p);
|
||||
SpellAbility sa = picker.chooseSpellAbilityToPlay(null);
|
||||
AssertJUnit.assertNull(sa);
|
||||
AssertJUnit.assertEquals(0, picker.getNumSimulations());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSpellCantTargetSelf() {
|
||||
Game game = initAndCreateGame();
|
||||
Player p = game.getPlayers().get(1);
|
||||
Player opponent = game.getPlayers().get(0);
|
||||
|
||||
addCardToZone("Unsubstantiate", p, ZoneType.Hand);
|
||||
addCard("Forest", p);
|
||||
addCard("Island", p);
|
||||
Card expectedTarget = addCard("Flying Men", opponent);
|
||||
|
||||
game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p);
|
||||
game.getAction().checkStateEffects(true);
|
||||
|
||||
SpellAbilityPicker picker = new SpellAbilityPicker(game, p);
|
||||
SpellAbility sa = picker.chooseSpellAbilityToPlay(null);
|
||||
AssertJUnit.assertNotNull(sa);
|
||||
AssertJUnit.assertEquals(expectedTarget, sa.getTargetCard());
|
||||
// Only a single simulation expected (no target self).
|
||||
AssertJUnit.assertEquals(1, picker.getNumSimulations());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testModalSpellCantTargetSelf() {
|
||||
Game game = initAndCreateGame();
|
||||
Player p = game.getPlayers().get(1);
|
||||
Player opponent = game.getPlayers().get(0);
|
||||
|
||||
addCardToZone("Decisive Denial", p, ZoneType.Hand);
|
||||
addCard("Forest", p);
|
||||
addCard("Island", p);
|
||||
addCard("Runeclaw Bear", p);
|
||||
addCard("Flying Men", opponent);
|
||||
|
||||
game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p);
|
||||
game.getAction().checkStateEffects(true);
|
||||
|
||||
SpellAbilityPicker picker = new SpellAbilityPicker(game, p);
|
||||
SpellAbility sa = picker.chooseSpellAbilityToPlay(null);
|
||||
AssertJUnit.assertNotNull(sa);
|
||||
// Expected: Runeclaw Bear fights Flying Men
|
||||
// Only a single simulation expected (no target self).
|
||||
AssertJUnit.assertEquals(1, picker.getNumSimulations());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testModalSpellNoTargetsForModeWithSubAbility() {
|
||||
Game game = initAndCreateGame();
|
||||
Player p = game.getPlayers().get(1);
|
||||
|
||||
addCardToZone("Temur Charm", p, ZoneType.Hand);
|
||||
addCard("Forest", p);
|
||||
addCard("Island", p);
|
||||
addCard("Mountain", p);
|
||||
|
||||
game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p);
|
||||
game.getAction().checkStateEffects(true);
|
||||
|
||||
SpellAbilityPicker picker = new SpellAbilityPicker(game, p);
|
||||
picker.chooseSpellAbilityToPlay(null);
|
||||
// Only mode "Creatures with power 3 or less can't block this turn" should be simulated.
|
||||
AssertJUnit.assertEquals(1, picker.getNumSimulations());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testModalSpellNoTargetsForAnyModes() {
|
||||
Game game = initAndCreateGame();
|
||||
Player p = game.getPlayers().get(1);
|
||||
|
||||
addCardToZone("Drown in the Loch", p, ZoneType.Hand);
|
||||
addCard("Swamp", p);
|
||||
addCard("Island", p);
|
||||
|
||||
game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p);
|
||||
game.getAction().checkStateEffects(true);
|
||||
|
||||
SpellAbilityPicker picker = new SpellAbilityPicker(game, p);
|
||||
picker.chooseSpellAbilityToPlay(null);
|
||||
// TODO: Ideally, this would be 0 simulations, but we currently only determine there are no
|
||||
// valid modes in SpellAbilityChoicesIterator, which runs already when we're simulating.
|
||||
// Still, this test case exercises the code path and ensures we don't crash in this case.
|
||||
AssertJUnit.assertEquals(1, picker.getNumSimulations());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void threeDistinctTargetSpell() {
|
||||
Game game = initAndCreateGame();
|
||||
Player p = game.getPlayers().get(1);
|
||||
Player opponent = game.getPlayers().get(0);
|
||||
|
||||
addCardToZone("Incremental Growth", p, ZoneType.Hand);
|
||||
addCards("Forest", 5, p);
|
||||
addCard("Forest Bear", p);
|
||||
addCard("Flying Men", opponent);
|
||||
addCard("Runeclaw Bear", p);
|
||||
addCard("Water Elemental", opponent);
|
||||
addCard("Grizzly Bears", p);
|
||||
|
||||
game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p);
|
||||
game.getAction().checkStateEffects(true);
|
||||
SpellAbilityPicker picker = new SpellAbilityPicker(game, p);
|
||||
SpellAbility sa = picker.chooseSpellAbilityToPlay(null);
|
||||
AssertJUnit.assertNotNull(sa);
|
||||
MultiTargetSelector.Targets targets = picker.getPlan().getSelectedDecision().targets;
|
||||
AssertJUnit.assertEquals(3, targets.size());
|
||||
AssertJUnit.assertTrue(targets.toString().contains("Forest Bear"));
|
||||
AssertJUnit.assertTrue(targets.toString().contains("Runeclaw Bear"));
|
||||
AssertJUnit.assertTrue(targets.toString().contains("Grizzly Bear"));
|
||||
// Expected 5*4*3=60 iterations (5 choices for first target, 4 for next, 3 for last.)
|
||||
AssertJUnit.assertEquals(60, picker.getNumSimulations());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void threeDistinctTargetSpellCantBeCast() {
|
||||
Game game = initAndCreateGame();
|
||||
Player p = game.getPlayers().get(1);
|
||||
Player opponent = game.getPlayers().get(0);
|
||||
|
||||
addCardToZone("Incremental Growth", p, ZoneType.Hand);
|
||||
addCards("Forest", 5, p);
|
||||
addCard("Forest Bear", p);
|
||||
addCard("Flying Men", opponent);
|
||||
|
||||
game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p);
|
||||
game.getAction().checkStateEffects(true);
|
||||
SpellAbilityPicker picker = new SpellAbilityPicker(game, p);
|
||||
SpellAbility sa = picker.chooseSpellAbilityToPlay(null);
|
||||
AssertJUnit.assertNull(sa);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void correctTargetChoicesWithTwoTargetSpell() {
|
||||
Game game = initAndCreateGame();
|
||||
Player p = game.getPlayers().get(1);
|
||||
Player opponent = game.getPlayers().get(0);
|
||||
|
||||
addCardToZone("Rites of Reaping", p, ZoneType.Hand);
|
||||
addCard("Swamp", p);
|
||||
addCards("Forest", 5, p);
|
||||
addCard("Flying Men", opponent);
|
||||
addCard("Forest Bear", p);
|
||||
addCard("Water Elemental", opponent);
|
||||
|
||||
game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p);
|
||||
game.getAction().checkStateEffects(true);
|
||||
SpellAbilityPicker picker = new SpellAbilityPicker(game, p);
|
||||
SpellAbility sa = picker.chooseSpellAbilityToPlay(null);
|
||||
AssertJUnit.assertNotNull(sa);
|
||||
MultiTargetSelector.Targets targets = picker.getPlan().getSelectedDecision().targets;
|
||||
AssertJUnit.assertEquals(2, targets.size());
|
||||
AssertJUnit.assertTrue(targets.toString().contains("Forest Bear"));
|
||||
AssertJUnit.assertTrue(targets.toString().contains("Flying Men"));
|
||||
}
|
||||
*/
|
||||
}
|
||||
@@ -5,10 +5,12 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import forge.GuiDesktop;
|
||||
import forge.StaticData;
|
||||
import forge.ai.AIOption;
|
||||
import forge.ai.AITest;
|
||||
import forge.ai.LobbyPlayerAi;
|
||||
import forge.ai.simulation.GameStateEvaluator.Score;
|
||||
import forge.deck.Deck;
|
||||
@@ -22,11 +24,17 @@ import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardFactory;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.RegisteredPlayer;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.gui.GuiBase;
|
||||
import forge.item.IPaperCard;
|
||||
import forge.item.PaperToken;
|
||||
import forge.localinstance.properties.ForgePreferences;
|
||||
import forge.localinstance.properties.ForgePreferences.FPref;
|
||||
import forge.model.FModel;
|
||||
|
||||
public class SimulationTest extends AITest {
|
||||
public class SimulationTest {
|
||||
private static boolean initialized = false;
|
||||
|
||||
public Game resetGame() {
|
||||
// need to be done after FModel.initialize, or the Localizer isn't loaded yet
|
||||
@@ -44,6 +52,23 @@ public class SimulationTest extends AITest {
|
||||
return game;
|
||||
}
|
||||
|
||||
protected Game initAndCreateGame() {
|
||||
if (!initialized) {
|
||||
GuiBase.setInterface(new GuiDesktop());
|
||||
FModel.initialize(null, new Function<ForgePreferences, Void>() {
|
||||
@Override
|
||||
public Void apply(ForgePreferences preferences) {
|
||||
preferences.setPref(FPref.LOAD_CARD_SCRIPTS_LAZILY, false);
|
||||
preferences.setPref(FPref.UI_LANGUAGE, "en-US");
|
||||
return null;
|
||||
}
|
||||
});
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
return resetGame();
|
||||
}
|
||||
|
||||
protected GameSimulator createSimulator(Game game, Player p) {
|
||||
return new GameSimulator(new SimulationController(new Score(0)) {
|
||||
@Override
|
||||
@@ -53,6 +78,25 @@ public class SimulationTest extends AITest {
|
||||
}, game, p, null);
|
||||
}
|
||||
|
||||
protected int countCardsWithName(Game game, String name) {
|
||||
int i = 0;
|
||||
for (Card c : game.getCardsIn(ZoneType.Battlefield)) {
|
||||
if (c.getName().equals(name)) {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
protected Card findCardWithName(Game game, String name) {
|
||||
for (Card c : game.getCardsIn(ZoneType.Battlefield)) {
|
||||
if (c.getName().equals(name)) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected String gameStateToString(Game game) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (ZoneType zone : ZoneType.values()) {
|
||||
@@ -67,6 +111,48 @@ public class SimulationTest extends AITest {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
protected SpellAbility findSAWithPrefix(Card c, String prefix) {
|
||||
return findSAWithPrefix(c.getSpellAbilities(), prefix);
|
||||
}
|
||||
|
||||
protected SpellAbility findSAWithPrefix(Iterable<SpellAbility> abilities, String prefix) {
|
||||
for (SpellAbility sa : abilities) {
|
||||
if (sa.getDescription().startsWith(prefix)) {
|
||||
return sa;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected Card createCard(String name, Player p) {
|
||||
IPaperCard paperCard = FModel.getMagicDb().getCommonCards().getCard(name);
|
||||
if (paperCard == null) {
|
||||
StaticData.instance().attemptToLoadCard(name);
|
||||
paperCard = FModel.getMagicDb().getCommonCards().getCard(name);
|
||||
}
|
||||
return Card.fromPaperCard(paperCard, p);
|
||||
}
|
||||
|
||||
protected Card addCardToZone(String name, Player p, ZoneType zone) {
|
||||
Card c = createCard(name, p);
|
||||
// card need a new Timestamp otherwise Static Abilities might collide
|
||||
c.setTimestamp(p.getGame().getNextTimestamp());
|
||||
p.getZone(zone).add(c);
|
||||
return c;
|
||||
}
|
||||
|
||||
protected Card addCard(String name, Player p) {
|
||||
return addCardToZone(name, p, ZoneType.Battlefield);
|
||||
}
|
||||
|
||||
protected List<Card> addCards(String name, int count, Player p) {
|
||||
List<Card> cards = Lists.newArrayList();
|
||||
for (int i = 0; i < count; i++) {
|
||||
cards.add(addCard(name, p));
|
||||
}
|
||||
return cards;
|
||||
}
|
||||
|
||||
protected Card createToken(String name, Player p) {
|
||||
PaperToken token = FModel.getMagicDb().getAllTokens().getToken(name);
|
||||
if (token == null) {
|
||||
|
||||
Reference in New Issue
Block a user