mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-16 10:48:00 +00:00
[Simulated AI] Refactor code to create a Plan object.
This allows coming up with a multi-step planning and caching it, so it doesn't need to be re-computed at subsequent steps if nothing meaningful changed.
This commit is contained in:
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -156,6 +156,7 @@ forge-ai/src/main/java/forge/ai/ability/ZoneExchangeAi.java -text
|
||||
forge-ai/src/main/java/forge/ai/simulation/GameCopier.java -text
|
||||
forge-ai/src/main/java/forge/ai/simulation/GameSimulator.java -text
|
||||
forge-ai/src/main/java/forge/ai/simulation/GameStateEvaluator.java -text
|
||||
forge-ai/src/main/java/forge/ai/simulation/Plan.java -text
|
||||
forge-ai/src/main/java/forge/ai/simulation/PossibleTargetSelector.java -text
|
||||
forge-ai/src/main/java/forge/ai/simulation/SimulationController.java -text
|
||||
forge-ai/src/main/java/forge/ai/simulation/SpellAbilityPicker.java -text
|
||||
|
||||
@@ -192,7 +192,7 @@ public class GameSimulator {
|
||||
}
|
||||
controller.printState(score, origSa);
|
||||
if (controller.shouldRecurse() && !simGame.isGameOver()) {
|
||||
controller.push(sa);
|
||||
controller.push(sa, score);
|
||||
SpellAbilityPicker sim = new SpellAbilityPicker(simGame, aiPlayer);
|
||||
CardCollection cards = ComputerUtilAbility.getAvailableCards(simGame, aiPlayer);
|
||||
List<SpellAbility> all = ComputerUtilAbility.getSpellAbilities(cards, aiPlayer);
|
||||
|
||||
@@ -79,9 +79,9 @@ public class GameStateEvaluator {
|
||||
for (Card c : game.getCardsIn(ZoneType.Battlefield)) {
|
||||
int value = evalCard(game, aiPlayer, c, combat);
|
||||
int summonSickValue = value;
|
||||
// To make the AI hold-off on playing creatures in MAIN1 if they give no other benefits,
|
||||
// To make the AI hold-off on playing creatures before MAIN2 if they give no other benefits,
|
||||
// keep track of the score while treating summon sick creatures as having a value of 0.
|
||||
if (gamePhase == PhaseType.MAIN1 && c.isSick() && c.getController() == aiPlayer) {
|
||||
if (gamePhase.isBefore(PhaseType.MAIN2) && c.isSick() && c.getController() == aiPlayer) {
|
||||
summonSickValue = 0;
|
||||
}
|
||||
String str = c.getName();
|
||||
@@ -167,7 +167,6 @@ public class GameStateEvaluator {
|
||||
public static class Score {
|
||||
public final int value;
|
||||
public final int summonSickValue;
|
||||
public String choice;
|
||||
|
||||
public Score(int value) {
|
||||
this.value = value;
|
||||
|
||||
78
forge-ai/src/main/java/forge/ai/simulation/Plan.java
Normal file
78
forge-ai/src/main/java/forge/ai/simulation/Plan.java
Normal file
@@ -0,0 +1,78 @@
|
||||
package forge.ai.simulation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import forge.ai.simulation.GameStateEvaluator.Score;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
|
||||
public class Plan {
|
||||
private List<Decision> decisions;
|
||||
private int nextDecisionIndex;
|
||||
private Decision selectedDecision;
|
||||
|
||||
public Plan(ArrayList<Decision> decisions) {
|
||||
this.decisions = decisions;
|
||||
}
|
||||
|
||||
public List<Decision> getDecisions() {
|
||||
return decisions;
|
||||
}
|
||||
|
||||
public boolean hasNextDecision() {
|
||||
return nextDecisionIndex < decisions.size();
|
||||
}
|
||||
|
||||
public Decision selectNextDecision() {
|
||||
selectedDecision = decisions.get(nextDecisionIndex);
|
||||
nextDecisionIndex++;
|
||||
return selectedDecision;
|
||||
}
|
||||
|
||||
public Decision getSelectedDecision() {
|
||||
return selectedDecision;
|
||||
}
|
||||
|
||||
public int getNextDecisionIndex() {
|
||||
return nextDecisionIndex;
|
||||
}
|
||||
|
||||
public static class Decision {
|
||||
final Decision prevDecision;
|
||||
final Score initialScore;
|
||||
|
||||
final String sa;
|
||||
PossibleTargetSelector.Targets targets;
|
||||
String choice;
|
||||
|
||||
public Decision(Score initialScore, Decision prevDecision, SpellAbility sa) {
|
||||
this.initialScore = initialScore;
|
||||
this.prevDecision = prevDecision;
|
||||
this.sa = sa.toString();
|
||||
this.targets = null;
|
||||
this.choice = null;
|
||||
}
|
||||
|
||||
public Decision(Score initialScore, Decision prevDecision, PossibleTargetSelector.Targets targets) {
|
||||
this.initialScore = initialScore;
|
||||
this.prevDecision = prevDecision;
|
||||
this.sa = null;
|
||||
this.targets = targets;
|
||||
this.choice = null;
|
||||
}
|
||||
|
||||
public Decision(Score initialScore, Decision prevDecision, Card choice) {
|
||||
this.initialScore = initialScore;
|
||||
this.prevDecision = prevDecision;
|
||||
this.sa = null;
|
||||
this.targets = null;
|
||||
this.choice = choice.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[initScore=" + initialScore + " " + sa + " " + targets + " " + choice + "]";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,23 +16,43 @@ public class PossibleTargetSelector {
|
||||
private int targetIndex;
|
||||
private List<GameObject> validTargets;
|
||||
|
||||
public static class Targets {
|
||||
final int originalTargetCount;
|
||||
final int targetIndex;
|
||||
final String description;
|
||||
|
||||
private Targets(int originalTargetCount, int targetIndex, String description) {
|
||||
this.originalTargetCount = originalTargetCount;
|
||||
this.targetIndex = targetIndex;
|
||||
this.description = description;
|
||||
|
||||
if (targetIndex < 0 || targetIndex >= originalTargetCount) {
|
||||
throw new IllegalArgumentException("Invalid targetIndex=" + targetIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
|
||||
public PossibleTargetSelector(Game game, Player self, SpellAbility sa) {
|
||||
this.sa = sa;
|
||||
this.tgt = sa.getTargetRestrictions();
|
||||
this.targetIndex = 0;
|
||||
this.validTargets = new ArrayList<GameObject>();
|
||||
sa.resetTargets();
|
||||
sa.setActivatingPlayer(self);
|
||||
for (GameObject o : tgt.getAllCandidates(sa, true)) {
|
||||
validTargets.add(o);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean selectNextTargets() {
|
||||
if (targetIndex >= validTargets.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void selectTargetsByIndex(int index) {
|
||||
sa.resetTargets();
|
||||
int index = targetIndex;
|
||||
|
||||
// TODO: smarter about multiple targets, identical targets, etc...
|
||||
while (sa.getTargets().getNumTargeted() < tgt.getMaxTargets(sa.getHostCard(), sa) && index < validTargets.size()) {
|
||||
sa.getTargets().add(validTargets.get(index++));
|
||||
}
|
||||
@@ -53,8 +73,25 @@ public class PossibleTargetSelector {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: smarter about multiple targets, identical targets, etc...
|
||||
public Targets getLastSelectedTargets() {
|
||||
return new Targets(validTargets.size(), targetIndex - 1, sa.getTargets().getTargetedString());
|
||||
}
|
||||
|
||||
public boolean selectTargets(Targets targets) {
|
||||
if (targets.originalTargetCount != validTargets.size()) {
|
||||
return false;
|
||||
}
|
||||
selectTargetsByIndex(targets.targetIndex);
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean selectNextTargets() {
|
||||
if (targetIndex >= validTargets.size()) {
|
||||
return false;
|
||||
}
|
||||
selectTargetsByIndex(targetIndex);
|
||||
targetIndex++;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,28 +1,105 @@
|
||||
package forge.ai.simulation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import forge.ai.simulation.GameStateEvaluator.Score;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
|
||||
public class SimulationController {
|
||||
private static int MAX_DEPTH = 2;
|
||||
|
||||
private int recursionDepth;
|
||||
|
||||
private List<Plan.Decision> currentStack;
|
||||
private List<Score> scoreStack;
|
||||
private Plan.Decision bestSequence; // last action of sequence
|
||||
private Score bestScore;
|
||||
|
||||
public SimulationController() {
|
||||
public SimulationController(Score score) {
|
||||
bestScore = score;
|
||||
scoreStack = new ArrayList<Score>();
|
||||
scoreStack.add(score);
|
||||
currentStack = new ArrayList<Plan.Decision>();
|
||||
}
|
||||
|
||||
public boolean shouldRecurse() {
|
||||
return recursionDepth < MAX_DEPTH;
|
||||
}
|
||||
|
||||
public void push(SpellAbility sa) {
|
||||
private Plan.Decision getLastDecision() {
|
||||
if (currentStack.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return currentStack.get(currentStack.size() - 1);
|
||||
}
|
||||
|
||||
private Score getCurrentScore() {
|
||||
return scoreStack.get(scoreStack.size() - 1);
|
||||
}
|
||||
|
||||
public void evaluateSpellAbility(SpellAbility sa) {
|
||||
currentStack.add(new Plan.Decision(getCurrentScore(), getLastDecision(), sa));
|
||||
}
|
||||
|
||||
public void evaluateCardChoice(Card choice) {
|
||||
currentStack.add(new Plan.Decision(getCurrentScore(), getLastDecision(), choice));
|
||||
}
|
||||
|
||||
public void evaluateTargetChoices(PossibleTargetSelector.Targets targets) {
|
||||
currentStack.add(new Plan.Decision(getCurrentScore(), getLastDecision(), targets));
|
||||
}
|
||||
|
||||
public void doneEvaluating(Score score) {
|
||||
if (score.value > bestScore.value) {
|
||||
bestScore = score;
|
||||
bestSequence = currentStack.get(currentStack.size() - 1);
|
||||
}
|
||||
currentStack.remove(currentStack.size() - 1);
|
||||
}
|
||||
|
||||
public Score getBestScore() {
|
||||
return bestScore;
|
||||
}
|
||||
|
||||
public Plan getBestPlan() {
|
||||
ArrayList<Plan.Decision> sequence = new ArrayList<Plan.Decision>();
|
||||
Plan.Decision current = bestSequence;
|
||||
while (current != null) {
|
||||
sequence.add(current);
|
||||
current = current.prevDecision;
|
||||
}
|
||||
Collections.reverse(sequence);
|
||||
// Merge targets & choices into their parents.
|
||||
int writeIndex = 0;
|
||||
for (int i = 0; i < sequence.size(); i++) {
|
||||
Plan.Decision d = sequence.get(i);
|
||||
System.out.println("SeqInput: " + d);
|
||||
if (d.sa != null) {
|
||||
sequence.set(writeIndex, d);
|
||||
writeIndex++;
|
||||
} else if (d.targets != null) {
|
||||
sequence.get(writeIndex - 1).targets = d.targets;
|
||||
} else if (d.choice != null) {
|
||||
sequence.get(writeIndex - 1).choice = d.choice;
|
||||
}
|
||||
}
|
||||
sequence.subList(writeIndex, sequence.size()).clear();
|
||||
return new Plan(sequence);
|
||||
}
|
||||
|
||||
public void push(SpellAbility sa, Score score) {
|
||||
GameSimulator.debugPrint("Recursing DEPTH=" + recursionDepth);
|
||||
GameSimulator.debugPrint(" With: " + sa);
|
||||
recursionDepth++;
|
||||
scoreStack.add(score);
|
||||
}
|
||||
|
||||
public void pop(Score score, SpellAbility nextSa) {
|
||||
recursionDepth--;
|
||||
scoreStack.remove(scoreStack.size() - 1);
|
||||
GameSimulator.debugPrint("DEPTH"+recursionDepth+" best score " + score + " " + nextSa);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package forge.ai.simulation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
@@ -25,7 +24,9 @@ public class SpellAbilityPicker {
|
||||
private Score bestScore;
|
||||
private boolean printOutput;
|
||||
private Interceptor interceptor;
|
||||
|
||||
|
||||
private Plan plan;
|
||||
|
||||
public SpellAbilityPicker(Game game, Player player) {
|
||||
this.game = game;
|
||||
this.player = player;
|
||||
@@ -40,22 +41,20 @@ public class SpellAbilityPicker {
|
||||
System.out.println(str);
|
||||
}
|
||||
}
|
||||
|
||||
public SpellAbility chooseSpellAbilityToPlay(SimulationController controller, final List<SpellAbility> all, boolean skipCounter) {
|
||||
printOutput = false;
|
||||
if (controller == null) {
|
||||
controller = new SimulationController();
|
||||
printOutput = true;
|
||||
}
|
||||
|
||||
private void printPhaseInfo() {
|
||||
String phaseStr = game.getPhaseHandler().getPhase().toString();
|
||||
if (game.getPhaseHandler().getPlayerTurn() != player) {
|
||||
phaseStr = "opponent " + phaseStr;
|
||||
}
|
||||
print("---- choose ability (phase = " + phaseStr + ")");
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
List<SpellAbility> candidateSAs = new ArrayList<>();
|
||||
for (final SpellAbility sa : ComputerUtilAbility.getOriginalAndAltCostAbilities(all, player)) {
|
||||
}
|
||||
|
||||
private List<SpellAbility> getCandidateSpellsAndAbilities(List<SpellAbility> all) {
|
||||
List<SpellAbility> candidateSAs = ComputerUtilAbility.getOriginalAndAltCostAbilities(all, player);
|
||||
int writeIndex = 0;
|
||||
for (int i = 0; i < candidateSAs.size(); i++) {
|
||||
SpellAbility sa = candidateSAs.get(i);
|
||||
if (sa.isManaAbility()) {
|
||||
continue;
|
||||
}
|
||||
@@ -68,20 +67,59 @@ public class SpellAbilityPicker {
|
||||
|
||||
if (opinion != AiPlayDecision.WillPlay)
|
||||
continue;
|
||||
candidateSAs.add(sa);
|
||||
candidateSAs.set(writeIndex, sa);
|
||||
writeIndex++;
|
||||
}
|
||||
candidateSAs.subList(writeIndex, candidateSAs.size()).clear();
|
||||
return candidateSAs;
|
||||
}
|
||||
|
||||
public SpellAbility chooseSpellAbilityToPlay(SimulationController controller, List<SpellAbility> all, boolean skipCounter) {
|
||||
printOutput = (controller == null);
|
||||
|
||||
// FIXME: This is wasteful, we should re-use the same simulator...
|
||||
GameSimulator simulator = new GameSimulator(controller, game, player);
|
||||
Score origGameScore = simulator.getScoreForOrigGame();
|
||||
List<SpellAbility> candidateSAs = getCandidateSpellsAndAbilities(all);
|
||||
if (controller != null) {
|
||||
// This is a recursion during a higher-level simulation. Just return the head of the best
|
||||
// sequence directly, no need to create a Plan object.
|
||||
return chooseSpellAbilityToPlayImpl(controller, candidateSAs, origGameScore);
|
||||
}
|
||||
|
||||
if (candidateSAs.isEmpty()) {
|
||||
return null;
|
||||
printPhaseInfo();
|
||||
SpellAbility sa = getPlannedSpellAbility(origGameScore, candidateSAs);
|
||||
if (sa != null) {
|
||||
return sa;
|
||||
}
|
||||
createNewPlan(origGameScore, candidateSAs);
|
||||
return getPlannedSpellAbility(origGameScore, candidateSAs);
|
||||
}
|
||||
|
||||
private void createNewPlan(Score origGameScore, List<SpellAbility> candidateSAs) {
|
||||
plan = null;
|
||||
SimulationController controller = new SimulationController(origGameScore);
|
||||
SpellAbility sa = chooseSpellAbilityToPlayImpl(controller, candidateSAs, origGameScore);
|
||||
if (sa == null) {
|
||||
print("No good plan at this time");
|
||||
return;
|
||||
}
|
||||
|
||||
plan = controller.getBestPlan();
|
||||
print("New plan with score " + controller.getBestScore() + ":");
|
||||
int i = 0;
|
||||
for (Plan.Decision d : plan.getDecisions()) {
|
||||
print(++i + ". " + d);
|
||||
}
|
||||
}
|
||||
|
||||
private SpellAbility chooseSpellAbilityToPlayImpl(SimulationController controller, List<SpellAbility> candidateSAs, Score origGameScore) {
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
SpellAbility bestSa = null;
|
||||
GameSimulator simulator = new GameSimulator(controller, game, player);
|
||||
// FIXME: This is wasteful, we should re-use the same simulator...
|
||||
Score origGameScore = simulator.getScoreForOrigGame();
|
||||
Score bestSaValue = origGameScore;
|
||||
print("Evaluating... (orig score = " + origGameScore + ")");
|
||||
for (final SpellAbility sa : candidateSAs) {
|
||||
print(abilityToString(sa));;
|
||||
Score value = evaluateSa(controller, sa);
|
||||
if (value.value > bestSaValue.value) {
|
||||
bestSaValue = value;
|
||||
@@ -103,6 +141,41 @@ public class SpellAbilityPicker {
|
||||
this.bestScore = bestSaValue;
|
||||
return bestSa;
|
||||
}
|
||||
|
||||
private SpellAbility getPlannedSpellAbility(Score origGameScore, List<SpellAbility> availableSAs) {
|
||||
if (plan != null && plan.hasNextDecision()) {
|
||||
boolean badTargets = false;
|
||||
boolean saNotFound = false;
|
||||
Plan.Decision decision = plan.selectNextDecision();
|
||||
if (decision.initialScore.equals(origGameScore)) {
|
||||
// TODO: Other safeguards like list of SAs and maybe the index and such?
|
||||
for (final SpellAbility sa : availableSAs) {
|
||||
if (sa.toString().equals(decision.sa)) {
|
||||
if (decision.targets != null) {
|
||||
PossibleTargetSelector selector = new PossibleTargetSelector(game, player, sa);
|
||||
if (!selector.selectTargets(decision.targets)) {
|
||||
badTargets = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
print("Planned decision " + plan.getNextDecisionIndex() + ": " + abilityToString(sa) + " " + decision.choice);
|
||||
return sa;
|
||||
}
|
||||
}
|
||||
saNotFound = true;
|
||||
}
|
||||
print("Failed to continue planned action (" + decision.sa + "). Cause:");
|
||||
if (badTargets) {
|
||||
print(" Bad targets!");
|
||||
} else if (saNotFound) {
|
||||
print(" Couldn't find spell/ability!");
|
||||
} else {
|
||||
print(" Unexpected game score (" + decision.initialScore + " vs. expected " + origGameScore + ")!");
|
||||
}
|
||||
plan = null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Score getScoreForChosenAbility() {
|
||||
return bestScore;
|
||||
@@ -161,12 +234,11 @@ public class SpellAbilityPicker {
|
||||
return AiPlayDecision.WillPlay;
|
||||
}
|
||||
|
||||
private Score evaluateSa(SimulationController controller, SpellAbility sa) {
|
||||
GameSimulator.debugPrint("Evaluate SA: " + sa);
|
||||
private Score evaluateSa(final SimulationController controller, SpellAbility sa) {
|
||||
controller.evaluateSpellAbility(sa);
|
||||
|
||||
Score bestScore = new Score(Integer.MIN_VALUE);
|
||||
if (!sa.usesTargeting()) {
|
||||
// TODO: Refactor this into a general decision tree.
|
||||
Interceptor interceptor = new Interceptor() {
|
||||
private int numChoices = -1;
|
||||
private int nextChoice = 0;
|
||||
@@ -185,7 +257,9 @@ public class SpellAbilityPicker {
|
||||
}
|
||||
numChoices = uniqueCards.size();
|
||||
nextChoice++;
|
||||
GameSimulator.debugPrint("Trying out choice " + choice);
|
||||
if (choice != null) {
|
||||
controller.evaluateCardChoice(choice);
|
||||
}
|
||||
return choice;
|
||||
}
|
||||
|
||||
@@ -204,30 +278,33 @@ public class SpellAbilityPicker {
|
||||
GameSimulator simulator = new GameSimulator(controller, game, player);
|
||||
simulator.setInterceptor(interceptor);
|
||||
Score score = simulator.simulateSpellAbility(sa);
|
||||
if (interceptor.getLastChoice() != null) {
|
||||
controller.doneEvaluating(score);
|
||||
}
|
||||
if (score.value > bestScore.value) {
|
||||
bestScore = score;
|
||||
Card choice = interceptor.getLastChoice();
|
||||
if (choice != null) {
|
||||
bestScore.choice = choice.getName();
|
||||
}
|
||||
}
|
||||
} while (interceptor.hasMoreChoices());
|
||||
controller.doneEvaluating(bestScore);
|
||||
return bestScore;
|
||||
}
|
||||
|
||||
GameSimulator.debugPrint("Checking out targets");
|
||||
PossibleTargetSelector selector = new PossibleTargetSelector(game, player, sa);
|
||||
TargetChoices tgt = null;
|
||||
while (selector.selectNextTargets()) {
|
||||
GameSimulator.debugPrint("Trying targets: " + sa.getTargets().getTargetedString());
|
||||
controller.evaluateTargetChoices(selector.getLastSelectedTargets());
|
||||
GameSimulator simulator = new GameSimulator(controller, game, player);
|
||||
Score score = simulator.simulateSpellAbility(sa);
|
||||
controller.doneEvaluating(score);
|
||||
// TODO: Get rid of the below when no longer needed.
|
||||
if (score.value > bestScore.value) {
|
||||
bestScore = score;
|
||||
tgt = sa.getTargets();
|
||||
sa.resetTargets();
|
||||
}
|
||||
}
|
||||
controller.doneEvaluating(bestScore);
|
||||
|
||||
if (tgt != null) {
|
||||
sa.setTargets(tgt);
|
||||
}
|
||||
@@ -240,12 +317,15 @@ public class SpellAbilityPicker {
|
||||
return interceptor.chooseCard(fetchList);
|
||||
}
|
||||
// TODO: Make the below more robust?
|
||||
if (bestScore != null && bestScore.choice != null) {
|
||||
if (plan != null && plan.getSelectedDecision() != null) {
|
||||
String choice = plan.getSelectedDecision().choice;
|
||||
for (Card c : fetchList) {
|
||||
if (c.getName().equals(bestScore.choice)) {
|
||||
if (c.getName().equals(choice)) {
|
||||
print(" Planned choice: " + c);
|
||||
return c;
|
||||
}
|
||||
}
|
||||
print("Failed to use planned choice (" + choice + "). Not found!");
|
||||
}
|
||||
return ChangeZoneAi.chooseCardToHiddenOriginChangeZone(destination, origin, sa, fetchList, player2, decider);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import forge.GuiBase;
|
||||
import forge.GuiDesktop;
|
||||
import forge.ai.ComputerUtilAbility;
|
||||
import forge.ai.LobbyPlayerAi;
|
||||
import forge.ai.simulation.GameStateEvaluator.Score;
|
||||
import forge.card.CardStateName;
|
||||
import forge.deck.Deck;
|
||||
import forge.game.Game;
|
||||
@@ -49,11 +50,11 @@ public class GameSimulatorTest extends TestCase {
|
||||
}
|
||||
return game;
|
||||
}
|
||||
|
||||
|
||||
private GameSimulator createSimulator(Game game, Player p) {
|
||||
return new GameSimulator(new SimulationController(), game, p);
|
||||
return new GameSimulator(new SimulationController(new Score(0)), game, p);
|
||||
}
|
||||
|
||||
|
||||
private Card findCardWithName(Game game, String name) {
|
||||
for (Card c : game.getCardsIn(ZoneType.Battlefield)) {
|
||||
if (c.getName().equals(name)) {
|
||||
|
||||
Reference in New Issue
Block a user