forked from External/mage
* Some fixes/chnages to AI player. Needs probably some more testing.
This commit is contained in:
parent
c53c09a59b
commit
fa0a40b0d6
8 changed files with 209 additions and 150 deletions
|
|
@ -27,16 +27,43 @@
|
|||
*/
|
||||
package mage.player.ai;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Scanner;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.FutureTask;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.ActivatedAbility;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.common.PassAbility;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.SearchEffect;
|
||||
import mage.abilities.keyword.*;
|
||||
import mage.abilities.keyword.DeathtouchAbility;
|
||||
import mage.abilities.keyword.DoubleStrikeAbility;
|
||||
import mage.abilities.keyword.ExaltedAbility;
|
||||
import mage.abilities.keyword.FirstStrikeAbility;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.abilities.keyword.IndestructibleAbility;
|
||||
import mage.abilities.keyword.ReachAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.Cards;
|
||||
import mage.choices.Choice;
|
||||
import mage.constants.AbilityType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.RangeOfInfluence;
|
||||
|
|
@ -47,7 +74,25 @@ import mage.game.events.GameEvent;
|
|||
import mage.game.permanent.Permanent;
|
||||
import mage.game.stack.StackAbility;
|
||||
import mage.game.stack.StackObject;
|
||||
import mage.game.turn.*;
|
||||
import mage.game.turn.BeginCombatStep;
|
||||
import mage.game.turn.BeginningPhase;
|
||||
import mage.game.turn.CleanupStep;
|
||||
import mage.game.turn.CombatDamageStep;
|
||||
import mage.game.turn.CombatPhase;
|
||||
import mage.game.turn.DeclareAttackersStep;
|
||||
import mage.game.turn.DeclareBlockersStep;
|
||||
import mage.game.turn.DrawStep;
|
||||
import mage.game.turn.EndOfCombatStep;
|
||||
import mage.game.turn.EndPhase;
|
||||
import mage.game.turn.EndStep;
|
||||
import mage.game.turn.FirstCombatDamageStep;
|
||||
import mage.game.turn.Phase;
|
||||
import mage.game.turn.PostCombatMainPhase;
|
||||
import mage.game.turn.PostCombatMainStep;
|
||||
import mage.game.turn.PreCombatMainPhase;
|
||||
import mage.game.turn.PreCombatMainStep;
|
||||
import mage.game.turn.UntapStep;
|
||||
import mage.game.turn.UpkeepStep;
|
||||
import mage.player.ai.ma.optimizers.TreeOptimizer;
|
||||
import mage.player.ai.ma.optimizers.impl.DiscardCardOptimizer;
|
||||
import mage.player.ai.ma.optimizers.impl.EquipOptimizer;
|
||||
|
|
@ -60,11 +105,6 @@ import mage.target.Target;
|
|||
import mage.target.TargetCard;
|
||||
import mage.target.Targets;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import mage.constants.AbilityType;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author nantuko
|
||||
|
|
@ -89,6 +129,7 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
|
|||
protected int lastLoggedTurn = 0;
|
||||
Random random = new Random();
|
||||
protected static final String BLANKS = "...............................................";
|
||||
|
||||
static {
|
||||
optimizers.add(new LevelUpOptimizer());
|
||||
optimizers.add(new EquipOptimizer());
|
||||
|
|
@ -316,7 +357,6 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
|
|||
* attack with any of them // let's try once again to avoid
|
||||
* possible horizon effect return false; } }
|
||||
*/
|
||||
|
||||
logger.info("simulating -- continuing previous action chain");
|
||||
actions = new LinkedList<>(root.abilities);
|
||||
combat = root.combat;
|
||||
|
|
@ -523,9 +563,10 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
|
|||
List<Ability> allActions = currentPlayer.simulatePriority(game);
|
||||
optimize(game, allActions);
|
||||
if (logger.isInfoEnabled() && allActions.size() > 0 && depth == maxDepth) {
|
||||
logger.info("Sim Prio [" + depth + "] player " + currentPlayer.getName() + " adding " + allActions.size() + " actions:" + allActions);
|
||||
logger.info("ADDED ACTIONS (" + allActions.size() + ") " + " " + allActions);
|
||||
}
|
||||
int counter = 0;
|
||||
int bestValSubNodes = Integer.MIN_VALUE;
|
||||
for (Ability action : allActions) {
|
||||
counter++;
|
||||
if (ALLOW_INTERRUPT && Thread.interrupted()) {
|
||||
|
|
@ -553,15 +594,14 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
|
|||
sim.checkStateAndTriggered();
|
||||
int val;
|
||||
if (action instanceof PassAbility) {
|
||||
// Stop to simulate deeper if PassAbility
|
||||
// Stop to simulate deeper if PassAbility
|
||||
val = GameStateEvaluator2.evaluate(this.getId(), sim);
|
||||
// logger.info("evaluate = " + val );
|
||||
} else {
|
||||
val = addActions(newNode, depth - 1, alpha, beta);
|
||||
// logger.info("addAction = " + val );
|
||||
}
|
||||
logger.debug("Sim Prio " + BLANKS.substring(0, 2 + (maxDepth-depth) * 3)+ "["+depth+"]#"+counter+" <" + val +"> - ("+action.toString()+") "+newNode.hashCode()+" parent node "+node.hashCode());
|
||||
if (logger.isInfoEnabled() && depth == maxDepth) {
|
||||
logger.debug("Sim Prio " + BLANKS.substring(0, 2 + (maxDepth - depth) * 3) + "[" + depth + "]#" + counter + " <" + val + "> - (" + action.toString() + ") ");
|
||||
if (logger.isInfoEnabled() && depth >= maxDepth) {
|
||||
StringBuilder sb = new StringBuilder("Sim Prio [").append(depth).append("] #").append(counter)
|
||||
.append(" <").append(val).append("> (").append(action)
|
||||
.append(action.isModal() ? " Mode = " + action.getModes().getMode().toString() : "")
|
||||
|
|
@ -578,6 +618,9 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
|
|||
}
|
||||
|
||||
if (currentPlayer.getId().equals(playerId)) {
|
||||
if (val > bestValSubNodes) {
|
||||
bestValSubNodes = val;
|
||||
}
|
||||
if (depth == maxDepth && action instanceof PassAbility) {
|
||||
val = val - PASSIVITY_PENALTY; // passivity penalty
|
||||
}
|
||||
|
|
@ -594,7 +637,7 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
|
|||
* choices = node.getChoices();
|
||||
*/
|
||||
if (depth == maxDepth) {
|
||||
logger.info(new StringBuilder("Sim Prio [").append(depth).append("] -- Saved best node yet <").append(bestNode.getScore()).append("> ").append(bestNode.getAbilities().toString()).toString());
|
||||
logger.info("Sim Prio [" + depth + "] -- Saved best node yet <" + bestNode.getScore() + "> " + bestNode.getAbilities().toString());
|
||||
node.children.clear();
|
||||
node.children.add(bestNode);
|
||||
node.setScore(bestNode.getScore());
|
||||
|
|
@ -652,7 +695,7 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
|
|||
}
|
||||
*/
|
||||
//logger.info("returning priority alpha: " + alpha);
|
||||
return alpha;
|
||||
return bestValSubNodes;
|
||||
} else {
|
||||
// if (beta == Integer.MAX_VALUE) {
|
||||
// int val = GameStateEvaluator2.evaluate(playerId, game);
|
||||
|
|
@ -861,7 +904,6 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
|
|||
CombatUtil.sortByPower(attackers, false);
|
||||
|
||||
//this is where my code goes
|
||||
|
||||
CombatInfo combatInfo = CombatUtil.blockWithGoodTrade2(game, attackers, possibleBlockers);
|
||||
Player player = game.getPlayer(this.playerId);
|
||||
|
||||
|
|
@ -995,7 +1037,6 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
|
|||
}
|
||||
|
||||
//CombatUtil.handleExalted();
|
||||
|
||||
//TODO: refactor -- extract to method
|
||||
//List<Permanent> counterAttackList = new ArrayList<Permanent>();
|
||||
//int counterAttackDamage = 0;
|
||||
|
|
@ -1107,10 +1148,8 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
|
|||
* (totalFirstStrikeBlockPower < attacker.getToughness().getValue())
|
||||
* ) { finalAttackers.add(attacker); } }
|
||||
*/
|
||||
|
||||
// The AI will now attack more sanely. Simple, but good enough for now.
|
||||
// The sim minmax does not work at the moment.
|
||||
|
||||
// The sim minmax does not work at the moment.
|
||||
boolean safeToAttack;
|
||||
CombatEvaluator eval = new CombatEvaluator();
|
||||
|
||||
|
|
@ -1418,7 +1457,7 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
|
|||
if (powerLeftToKill <= 0) {
|
||||
return blockers.iterator().next().getId();
|
||||
}
|
||||
for (Permanent blocker: blockers) {
|
||||
for (Permanent blocker : blockers) {
|
||||
if (attackerDeathtouch || powerLeftToKill >= blocker.getToughness().getValue()) {
|
||||
if (!blocker.getAbilities().containsKey(IndestructibleAbility.getInstance().getId())) {
|
||||
return blocker.getId();
|
||||
|
|
@ -1435,7 +1474,7 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
|
|||
|
||||
private List<Permanent> getAlreadyBlockingPermanents(List<UUID> blockerOrder, Game game) {
|
||||
List<Permanent> blockerAlreadySet = new ArrayList<>();
|
||||
for (UUID uuid :blockerOrder) {
|
||||
for (UUID uuid : blockerOrder) {
|
||||
Permanent permanent = game.getPermanent(uuid);
|
||||
if (permanent != null) {
|
||||
blockerAlreadySet.add(permanent);
|
||||
|
|
@ -1455,5 +1494,4 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
|
|||
return toughnessAlreadyNeeded;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
|
||||
package mage.player.ai;
|
||||
|
||||
import java.util.UUID;
|
||||
|
|
@ -60,21 +59,21 @@ public class GameStateEvaluator2 {
|
|||
StringBuilder sbPlayer = new StringBuilder();
|
||||
StringBuilder sbOpponent = new StringBuilder();
|
||||
// add values of player
|
||||
for (Permanent permanent: game.getBattlefield().getAllActivePermanents(playerId)) {
|
||||
for (Permanent permanent : game.getBattlefield().getAllActivePermanents(playerId)) {
|
||||
int onePermScore = evaluatePermanent(permanent, game);
|
||||
playerScore += onePermScore;
|
||||
if (logger.isDebugEnabled()) {
|
||||
sbPlayer.append(permanent.getName()).append("[").append(onePermScore).append("] ");
|
||||
}
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
sbPlayer.insert(0, playerScore + " - ");
|
||||
sbPlayer.insert(0, "Player..: ");
|
||||
logger.debug(sbPlayer);
|
||||
}
|
||||
|
||||
// add values of opponent
|
||||
for (Permanent permanent: game.getBattlefield().getAllActivePermanents(opponent.getId())) {
|
||||
for (Permanent permanent : game.getBattlefield().getAllActivePermanents(opponent.getId())) {
|
||||
int onePermScore = evaluatePermanent(permanent, game);
|
||||
opponentScore += onePermScore;
|
||||
if (logger.isDebugEnabled()) {
|
||||
|
|
@ -84,7 +83,7 @@ public class GameStateEvaluator2 {
|
|||
if (logger.isDebugEnabled()) {
|
||||
sbOpponent.insert(0, opponentScore + " - ");
|
||||
sbOpponent.insert(0, "Opponent: ");
|
||||
|
||||
|
||||
logger.debug(sbOpponent);
|
||||
}
|
||||
permanentScore = playerScore - opponentScore;
|
||||
|
|
@ -97,28 +96,28 @@ public class GameStateEvaluator2 {
|
|||
handScore *= 5;
|
||||
|
||||
int score = lifeScore + permanentScore + handScore;
|
||||
logger.debug(score + " total Score (life:" + lifeScore + " permanents:" + permanentScore + " hand:" + handScore +")");
|
||||
logger.debug(score + " total Score (life:" + lifeScore + " permanents:" + permanentScore + " hand:" + handScore + ")");
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
public static int evaluatePermanent(Permanent permanent, Game game) {
|
||||
/*int value = permanent.isTapped()?4:5;
|
||||
if (permanent.getCardType().contains(CardType.CREATURE)) {
|
||||
value += evaluateCreature(permanent, game) * CREATURE_FACTOR;
|
||||
}
|
||||
value += permanent.getAbilities().getManaAbilities(Zone.BATTLEFIELD).size();
|
||||
for (ActivatedAbility ability: permanent.getAbilities().getActivatedAbilities(Zone.BATTLEFIELD)) {
|
||||
if (!(ability instanceof ManaAbility) && ability.canActivate(ability.getControllerId(), game))
|
||||
value += ability.getEffects().size();
|
||||
}
|
||||
value += permanent.getAbilities().getStaticAbilities(Zone.BATTLEFIELD).size();
|
||||
value += permanent.getAbilities().getTriggeredAbilities(Zone.BATTLEFIELD).size();
|
||||
value += permanent.getManaCost().convertedManaCost();
|
||||
*/
|
||||
if (permanent.getCardType().contains(CardType.CREATURE)) {
|
||||
value += evaluateCreature(permanent, game) * CREATURE_FACTOR;
|
||||
}
|
||||
value += permanent.getAbilities().getManaAbilities(Zone.BATTLEFIELD).size();
|
||||
for (ActivatedAbility ability: permanent.getAbilities().getActivatedAbilities(Zone.BATTLEFIELD)) {
|
||||
if (!(ability instanceof ManaAbility) && ability.canActivate(ability.getControllerId(), game))
|
||||
value += ability.getEffects().size();
|
||||
}
|
||||
value += permanent.getAbilities().getStaticAbilities(Zone.BATTLEFIELD).size();
|
||||
value += permanent.getAbilities().getTriggeredAbilities(Zone.BATTLEFIELD).size();
|
||||
value += permanent.getManaCost().convertedManaCost();
|
||||
*/
|
||||
|
||||
int value = ArtificialScoringSystem.getFixedPermanentScore(game, permanent)
|
||||
+ ArtificialScoringSystem.getVariablePermanentScore(game, permanent);
|
||||
+ ArtificialScoringSystem.getVariablePermanentScore(game, permanent);
|
||||
|
||||
//TODO: add a difficulty to calculation to ManaCost - sort permanents by difficulty for casting when evaluating game states
|
||||
return value;
|
||||
|
|
@ -126,21 +125,20 @@ public class GameStateEvaluator2 {
|
|||
|
||||
public static int evaluateCreature(Permanent creature, Game game) {
|
||||
int value = ArtificialScoringSystem.getFixedPermanentScore(game, creature)
|
||||
+ ArtificialScoringSystem.getVariablePermanentScore(game, creature);
|
||||
+ ArtificialScoringSystem.getVariablePermanentScore(game, creature);
|
||||
|
||||
/*int value = 0;
|
||||
value += creature.getPower().getValue();
|
||||
value += creature.getToughness().getValue();
|
||||
// if (creature.canAttack(game))
|
||||
// value += creature.getPower().getValue();
|
||||
// if (!creature.isTapped())
|
||||
// value += 2;
|
||||
value += creature.getAbilities().getEvasionAbilities().size();
|
||||
value += creature.getAbilities().getProtectionAbilities().size();
|
||||
value += creature.getAbilities().containsKey(FirstStrikeAbility.getInstance().getId())?1:0;
|
||||
value += creature.getAbilities().containsKey(DoubleStrikeAbility.getInstance().getId())?2:0;
|
||||
value += creature.getAbilities().containsKey(TrampleAbility.getInstance().getId())?1:0;*/
|
||||
|
||||
value += creature.getPower().getValue();
|
||||
value += creature.getToughness().getValue();
|
||||
// if (creature.canAttack(game))
|
||||
// value += creature.getPower().getValue();
|
||||
// if (!creature.isTapped())
|
||||
// value += 2;
|
||||
value += creature.getAbilities().getEvasionAbilities().size();
|
||||
value += creature.getAbilities().getProtectionAbilities().size();
|
||||
value += creature.getAbilities().containsKey(FirstStrikeAbility.getInstance().getId())?1:0;
|
||||
value += creature.getAbilities().containsKey(DoubleStrikeAbility.getInstance().getId())?2:0;
|
||||
value += creature.getAbilities().containsKey(TrampleAbility.getInstance().getId())?1:0;*/
|
||||
return value;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@
|
|||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
|
||||
package mage.player.ai;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
|
@ -37,6 +36,7 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.TriggeredAbility;
|
||||
import mage.abilities.common.PassAbility;
|
||||
|
|
@ -108,11 +108,16 @@ public class SimulatedPlayer2 extends ComputerPlayer {
|
|||
|
||||
if (logger.isTraceEnabled()) {
|
||||
for (Ability a : allActions) {
|
||||
logger.trace("ability==" + a);
|
||||
logger.info("ability==" + a);
|
||||
if (a.getTargets().size() > 0) {
|
||||
Player player = game.getPlayer(a.getFirstTarget());
|
||||
if (player != null) {
|
||||
logger.trace(" target="+player.getName());
|
||||
MageObject mageObject = game.getObject(a.getFirstTarget());
|
||||
if (mageObject != null) {
|
||||
logger.info(" target=" + mageObject.getName());
|
||||
} else {
|
||||
Player player = game.getPlayer(a.getFirstTarget());
|
||||
if (player != null) {
|
||||
logger.info(" target=" + player.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -124,21 +129,20 @@ public class SimulatedPlayer2 extends ComputerPlayer {
|
|||
protected void simulateOptions(Game game) {
|
||||
List<Ability> playables = game.getPlayer(playerId).getPlayable(game, isSimulatedPlayer);
|
||||
playables = filterAbilities(game, playables, suggested);
|
||||
for (Ability ability: playables) {
|
||||
for (Ability ability : playables) {
|
||||
if (ability.getAbilityType().equals(AbilityType.MANA)) {
|
||||
continue;
|
||||
}
|
||||
List<Ability> options = game.getPlayer(playerId).getPlayableOptions(ability, game);
|
||||
options = filterOptions(game, options, ability, suggested);
|
||||
options = filterOptions(game, options, ability, suggested);
|
||||
options = optimizeOptions(game, options, ability);
|
||||
if (options.isEmpty()) {
|
||||
allActions.add(ability);
|
||||
allActions.add(ability);
|
||||
// simulateAction(game, previousActions, ability);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// ExecutorService simulationExecutor = Executors.newFixedThreadPool(4);
|
||||
for (Ability option: options) {
|
||||
allActions.add(option);
|
||||
for (Ability option : options) {
|
||||
allActions.add(option);
|
||||
// SimulationWorker worker = new SimulationWorker(game, this, previousActions, option);
|
||||
// simulationExecutor.submit(worker);
|
||||
}
|
||||
|
|
@ -157,7 +161,7 @@ public class SimulatedPlayer2 extends ComputerPlayer {
|
|||
if (card != null && numAvailable > 0) {
|
||||
// check if variable mana costs is included and get the multiplier
|
||||
VariableManaCost variableManaCost = null;
|
||||
for (ManaCost cost: ability.getManaCostsToPay()) {
|
||||
for (ManaCost cost : ability.getManaCostsToPay()) {
|
||||
if (cost instanceof VariableManaCost && !cost.isPaid()) {
|
||||
variableManaCost = (VariableManaCost) cost;
|
||||
break; // only one VariableManCost per spell (or is it possible to have more?)
|
||||
|
|
@ -171,7 +175,7 @@ public class SimulatedPlayer2 extends ComputerPlayer {
|
|||
int xAmount = mana / multiplier;
|
||||
Ability newAbility = ability.copy();
|
||||
VariableManaCost varCost = null;
|
||||
for (ManaCost cost: newAbility.getManaCostsToPay()) {
|
||||
for (ManaCost cost : newAbility.getManaCostsToPay()) {
|
||||
if (cost instanceof VariableManaCost && !cost.isPaid()) {
|
||||
varCost = (VariableManaCost) cost;
|
||||
break; // only one VariableManCost per spell (or is it possible to have more?)
|
||||
|
|
@ -207,7 +211,6 @@ public class SimulatedPlayer2 extends ComputerPlayer {
|
|||
// allActions.add(new SimulatedAction(sim, actions));
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* if suggested abilities exist, return only those from playables
|
||||
*
|
||||
|
|
@ -253,7 +256,7 @@ public class SimulatedPlayer2 extends ComputerPlayer {
|
|||
Card card = game.getCard(ability.getSourceId());
|
||||
for (String s : suggested) {
|
||||
String[] groups = s.split(";");
|
||||
logger.trace("s="+s+";groups="+groups.length);
|
||||
logger.trace("s=" + s + ";groups=" + groups.length);
|
||||
if (groups.length == 2) {
|
||||
if (groups[0].equals(card.getName()) && groups[1].startsWith("name=")) {
|
||||
// extract target and compare to suggested
|
||||
|
|
@ -330,7 +333,7 @@ public class SimulatedPlayer2 extends ComputerPlayer {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
|
|
@ -352,15 +355,14 @@ public class SimulatedPlayer2 extends ComputerPlayer {
|
|||
for (int j = 0; j < attackersList.size(); j++) {
|
||||
if (binary.charAt(j) == '1') {
|
||||
setStoredBookmark(sim.bookmarkState()); // makes it possible to UNDO a declared attacker with costs from e.g. Propaganda
|
||||
if(!sim.getCombat().declareAttacker(attackersList.get(j).getId(), defenderId, playerId, sim)) {
|
||||
if (!sim.getCombat().declareAttacker(attackersList.get(j).getId(), defenderId, playerId, sim)) {
|
||||
sim.undo(playerId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (engagements.put(sim.getCombat().getValue().hashCode(), sim.getCombat()) != null) {
|
||||
logger.debug("simulating -- found redundant attack combination");
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
logger.debug("simulating -- attack:" + sim.getCombat().getGroups().size());
|
||||
}
|
||||
}
|
||||
|
|
@ -424,15 +426,14 @@ public class SimulatedPlayer2 extends ComputerPlayer {
|
|||
ability.activate(game, false);
|
||||
game.applyEffects();
|
||||
game.getPlayers().resetPassed();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
SimulationNode2 parent = (SimulationNode2) game.getCustomData();
|
||||
int depth = parent.getDepth() - 1;
|
||||
if (depth == 0) {
|
||||
return true;
|
||||
}
|
||||
logger.debug("simulating -- triggered ability - adding children:" + options.size());
|
||||
for (Ability option: options) {
|
||||
for (Ability option : options) {
|
||||
addAbilityNode(parent, option, depth, game);
|
||||
}
|
||||
}
|
||||
|
|
@ -446,12 +447,12 @@ public class SimulatedPlayer2 extends ComputerPlayer {
|
|||
sim.applyEffects();
|
||||
SimulationNode2 newNode = new SimulationNode2(parent, sim, depth, playerId);
|
||||
logger.debug("simulating -- node #:" + SimulationNode2.getCount() + " triggered ability option");
|
||||
for (Target target: ability.getTargets()) {
|
||||
for (UUID targetId: target.getTargets()) {
|
||||
for (Target target : ability.getTargets()) {
|
||||
for (UUID targetId : target.getTargets()) {
|
||||
newNode.getTargets().add(targetId);
|
||||
}
|
||||
}
|
||||
for (Choice choice: ability.getChoices()) {
|
||||
for (Choice choice : ability.getChoices()) {
|
||||
newNode.getChoices().add(choice.getChoice());
|
||||
}
|
||||
parent.children.add(newNode);
|
||||
|
|
@ -463,5 +464,4 @@ public class SimulatedPlayer2 extends ComputerPlayer {
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@
|
|||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
|
||||
package mage.player.ai;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
|
@ -49,10 +48,10 @@ public class SimulationNode2 implements Serializable {
|
|||
protected int score;
|
||||
protected List<Ability> abilities;
|
||||
protected int depth;
|
||||
protected List<SimulationNode2> children = new ArrayList<SimulationNode2>();
|
||||
protected List<SimulationNode2> children = new ArrayList<>();
|
||||
protected SimulationNode2 parent;
|
||||
protected List<UUID> targets = new ArrayList<UUID>();
|
||||
protected List<String> choices = new ArrayList<String>();
|
||||
protected List<UUID> targets = new ArrayList<>();
|
||||
protected List<String> choices = new ArrayList<>();
|
||||
protected UUID playerId;
|
||||
protected Combat combat;
|
||||
|
||||
|
|
@ -72,7 +71,7 @@ public class SimulationNode2 implements Serializable {
|
|||
|
||||
public SimulationNode2(SimulationNode2 parent, Game game, Ability ability, int depth, UUID playerId) {
|
||||
this(parent, game, depth, playerId);
|
||||
this.abilities = new ArrayList<Ability>();
|
||||
this.abilities = new ArrayList<>();
|
||||
abilities.add(ability);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,24 +1,23 @@
|
|||
package mage.player.ai.ma;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.keyword.HasteAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.counters.CounterType;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.constants.Outcome;
|
||||
import mage.abilities.effects.Effect;
|
||||
|
||||
/**
|
||||
* @author ubeefx, nantuko
|
||||
*/
|
||||
public class ArtificialScoringSystem {
|
||||
|
||||
public static final int WIN_GAME_SCORE=100000000;
|
||||
public static final int LOSE_GAME_SCORE=-WIN_GAME_SCORE;
|
||||
public static final int WIN_GAME_SCORE = 100000000;
|
||||
public static final int LOSE_GAME_SCORE = -WIN_GAME_SCORE;
|
||||
|
||||
private static final int LIFE_SCORES[] = {0, 1000, 2000, 3000, 4000, 4500, 5000, 5500, 6000, 6500, 7000, 7400, 7800, 8200, 8600, 9000, 9200, 9400, 9600, 9800, 10000};
|
||||
private static final int MAX_LIFE = LIFE_SCORES.length - 1;
|
||||
|
|
@ -33,9 +32,9 @@ public class ArtificialScoringSystem {
|
|||
//TODO: check this for "any color" lands
|
||||
//TODO: check this for dual and filter lands
|
||||
/*for (Mana mana : card.getMana()) {
|
||||
score += 50;
|
||||
}*/
|
||||
score += card.getMana().size()*50;
|
||||
score += 50;
|
||||
}*/
|
||||
score += card.getMana().size() * 50;
|
||||
return score;
|
||||
}
|
||||
|
||||
|
|
@ -43,19 +42,19 @@ public class ArtificialScoringSystem {
|
|||
if (card.getCardType().contains(CardType.CREATURE)) {
|
||||
return score + (card.getPower().getValue() + card.getToughness().getValue()) * 10;
|
||||
} else {
|
||||
return score + (/*card.getRemoval()*50*/ +card.getRarity().getRating() * 30);
|
||||
return score + (/*card.getRemoval()*50*/+card.getRarity().getRating() * 30);
|
||||
}
|
||||
}
|
||||
|
||||
public static int getFixedPermanentScore(final Game game, final Permanent permanent) {
|
||||
//TODO: cache it inside Card
|
||||
int score = getCardDefinitionScore(game, permanent);
|
||||
score += PERMANENT_SCORE;
|
||||
if (permanent.getCardType().contains(CardType.CREATURE)) {
|
||||
// TODO: implement in the mage core
|
||||
//score + =cardDefinition.getActivations().size()*50;
|
||||
//score += cardDefinition.getManaActivations().size()*80;
|
||||
} else {
|
||||
score += PERMANENT_SCORE;
|
||||
if (permanent.getSubtype().contains("Equipment")) {
|
||||
score += 100;
|
||||
}
|
||||
|
|
@ -101,7 +100,7 @@ public class ArtificialScoringSystem {
|
|||
}
|
||||
}
|
||||
}
|
||||
score += equipments*50 + enchantments*100;
|
||||
score += equipments * 50 + enchantments * 100;
|
||||
|
||||
if (!permanent.canAttack(game)) {
|
||||
score -= 100;
|
||||
|
|
@ -116,9 +115,9 @@ public class ArtificialScoringSystem {
|
|||
|
||||
private static boolean canTap(Permanent permanent) {
|
||||
return !permanent.isTapped()
|
||||
&&(!permanent.hasSummoningSickness()
|
||||
||!permanent.getCardType().contains(CardType.CREATURE)
|
||||
||permanent.getAbilities().contains(HasteAbility.getInstance()));
|
||||
&& (!permanent.hasSummoningSickness()
|
||||
|| !permanent.getCardType().contains(CardType.CREATURE)
|
||||
|| permanent.getAbilities().contains(HasteAbility.getInstance()));
|
||||
}
|
||||
|
||||
private static int getPositive(int value) {
|
||||
|
|
@ -152,23 +151,23 @@ public class ArtificialScoringSystem {
|
|||
public static int getAttackerScore(final Permanent attacker) {
|
||||
//TODO: implement this
|
||||
/*int score = attacker.getPower().getValue() * 5 + attacker.lethalDamage * 2 - attacker.candidateBlockers.length;
|
||||
for (final MagicCombatCreature blocker : attacker.candidateBlockers) {
|
||||
for (final MagicCombatCreature blocker : attacker.candidateBlockers) {
|
||||
|
||||
score -= blocker.power;
|
||||
}
|
||||
// Dedicated attacker.
|
||||
if (attacker.hasAbility(MagicAbility.AttacksEachTurnIfAble) || attacker.hasAbility(MagicAbility.CannotBlock)) {
|
||||
score += 10;
|
||||
}
|
||||
// Abilities for attacking.
|
||||
if (attacker.hasAbility(MagicAbility.Trample) || attacker.hasAbility(MagicAbility.Vigilance)) {
|
||||
score += 8;
|
||||
}
|
||||
// Dangerous to block.
|
||||
if (!attacker.normalDamage || attacker.hasAbility(MagicAbility.FirstStrike) || attacker.hasAbility(MagicAbility.Indestructible)) {
|
||||
score += 7;
|
||||
}
|
||||
*/
|
||||
score -= blocker.power;
|
||||
}
|
||||
// Dedicated attacker.
|
||||
if (attacker.hasAbility(MagicAbility.AttacksEachTurnIfAble) || attacker.hasAbility(MagicAbility.CannotBlock)) {
|
||||
score += 10;
|
||||
}
|
||||
// Abilities for attacking.
|
||||
if (attacker.hasAbility(MagicAbility.Trample) || attacker.hasAbility(MagicAbility.Vigilance)) {
|
||||
score += 8;
|
||||
}
|
||||
// Dangerous to block.
|
||||
if (!attacker.normalDamage || attacker.hasAbility(MagicAbility.FirstStrike) || attacker.hasAbility(MagicAbility.Indestructible)) {
|
||||
score += 7;
|
||||
}
|
||||
*/
|
||||
int score = 0;
|
||||
return score;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,6 +82,7 @@ import mage.cards.repository.ExpansionInfo;
|
|||
import mage.cards.repository.ExpansionRepository;
|
||||
import mage.choices.Choice;
|
||||
import mage.choices.ChoiceColor;
|
||||
import mage.constants.AsThoughEffectType;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.ColoredManaSymbol;
|
||||
import mage.constants.Outcome;
|
||||
|
|
@ -1116,21 +1117,23 @@ public class ComputerPlayer extends PlayerImpl implements Player {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean playMana(ManaCost unpaid, String promptText, Game game) {
|
||||
public boolean playMana(Ability ability, ManaCost unpaid, String promptText, Game game) {
|
||||
payManaMode = true;
|
||||
currentUnpaidMana = unpaid;
|
||||
boolean result = playManaHandling(unpaid, game);
|
||||
boolean result = playManaHandling(ability, unpaid, game);
|
||||
currentUnpaidMana = null;
|
||||
payManaMode = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
protected boolean playManaHandling(ManaCost unpaid, Game game) {
|
||||
protected boolean playManaHandling(Ability ability, ManaCost unpaid, Game game) {
|
||||
// log.info("paying for " + unpaid.getText());
|
||||
boolean spendAnyMana = game.getContinuousEffects().asThough(ability.getSourceId(), AsThoughEffectType.SPEND_ANY_MANA, ability, ability.getControllerId(), game);
|
||||
ManaCost cost;
|
||||
List<Permanent> producers;
|
||||
if (unpaid instanceof ManaCosts) {
|
||||
cost = ((ManaCosts<ManaCost>) unpaid).get(0);
|
||||
ManaCosts<ManaCost> manaCosts = (ManaCosts<ManaCost>) unpaid;
|
||||
cost = manaCosts.get(manaCosts.size() - 1);
|
||||
producers = getSortedProducers((ManaCosts) unpaid, game);
|
||||
} else {
|
||||
cost = unpaid;
|
||||
|
|
@ -1138,12 +1141,37 @@ public class ComputerPlayer extends PlayerImpl implements Player {
|
|||
producers.addAll(this.getAvailableManaProducersWithCost(game));
|
||||
}
|
||||
for (Permanent perm : producers) {
|
||||
// pay all colored costs first
|
||||
for (ManaAbility ability : perm.getAbilities().getAvailableManaAbilities(Zone.BATTLEFIELD, game)) {
|
||||
if (cost instanceof ColoredManaCost) {
|
||||
for (Mana netMana : ability.getNetMana(game)) {
|
||||
// use color producing mana abilities with costs first that produce all color manas that are needed to pay
|
||||
// otherwise the computer may not be able to pay the cost for that source
|
||||
ManaAbility:
|
||||
for (ManaAbility manaAbility : perm.getAbilities().getAvailableManaAbilities(Zone.BATTLEFIELD, game)) {
|
||||
int colored = 0;
|
||||
for (Mana mana : manaAbility.getNetMana(game)) {
|
||||
if (!unpaid.getMana().includesMana(mana)) {
|
||||
continue ManaAbility;
|
||||
}
|
||||
colored += mana.countColored();
|
||||
}
|
||||
if (colored > 1 && (cost instanceof ColoredManaCost)) {
|
||||
|
||||
for (Mana netMana : manaAbility.getNetMana(game)) {
|
||||
if (cost.testPay(netMana)) {
|
||||
if (activateAbility(ability, game)) {
|
||||
if (activateAbility(manaAbility, game)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Permanent perm : producers) {
|
||||
// pay all colored costs first
|
||||
for (ManaAbility manaAbility : perm.getAbilities().getAvailableManaAbilities(Zone.BATTLEFIELD, game)) {
|
||||
if (cost instanceof ColoredManaCost) {
|
||||
for (Mana netMana : manaAbility.getNetMana(game)) {
|
||||
if (cost.testPay(netMana) || spendAnyMana) {
|
||||
if (activateAbility(manaAbility, game)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1151,11 +1179,11 @@ public class ComputerPlayer extends PlayerImpl implements Player {
|
|||
}
|
||||
}
|
||||
// then pay hybrid
|
||||
for (ManaAbility ability : perm.getAbilities().getAvailableManaAbilities(Zone.BATTLEFIELD, game)) {
|
||||
for (ManaAbility manaAbility : perm.getAbilities().getAvailableManaAbilities(Zone.BATTLEFIELD, game)) {
|
||||
if (cost instanceof HybridManaCost) {
|
||||
for (Mana netMana : ability.getNetMana(game)) {
|
||||
if (cost.testPay(netMana)) {
|
||||
if (activateAbility(ability, game)) {
|
||||
for (Mana netMana : manaAbility.getNetMana(game)) {
|
||||
if (cost.testPay(netMana) || spendAnyMana) {
|
||||
if (activateAbility(manaAbility, game)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1163,11 +1191,11 @@ public class ComputerPlayer extends PlayerImpl implements Player {
|
|||
}
|
||||
}
|
||||
// then pay mono hybrid
|
||||
for (ManaAbility ability : perm.getAbilities().getAvailableManaAbilities(Zone.BATTLEFIELD, game)) {
|
||||
for (ManaAbility manaAbility : perm.getAbilities().getAvailableManaAbilities(Zone.BATTLEFIELD, game)) {
|
||||
if (cost instanceof MonoHybridManaCost) {
|
||||
for (Mana netMana : ability.getNetMana(game)) {
|
||||
if (cost.testPay(netMana)) {
|
||||
if (activateAbility(ability, game)) {
|
||||
for (Mana netMana : manaAbility.getNetMana(game)) {
|
||||
if (cost.testPay(netMana) || spendAnyMana) {
|
||||
if (activateAbility(manaAbility, game)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1175,11 +1203,11 @@ public class ComputerPlayer extends PlayerImpl implements Player {
|
|||
}
|
||||
}
|
||||
// finally pay generic
|
||||
for (ManaAbility ability : perm.getAbilities().getAvailableManaAbilities(Zone.BATTLEFIELD, game)) {
|
||||
for (ManaAbility manaAbility : perm.getAbilities().getAvailableManaAbilities(Zone.BATTLEFIELD, game)) {
|
||||
if (cost instanceof GenericManaCost) {
|
||||
for (Mana netMana : ability.getNetMana(game)) {
|
||||
if (cost.testPay(netMana)) {
|
||||
if (activateAbility(ability, game)) {
|
||||
for (Mana netMana : manaAbility.getNetMana(game)) {
|
||||
if (cost.testPay(netMana) || spendAnyMana) {
|
||||
if (activateAbility(manaAbility, game)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1189,7 +1217,7 @@ public class ComputerPlayer extends PlayerImpl implements Player {
|
|||
}
|
||||
// pay phyrexian life costs
|
||||
if (cost instanceof PhyrexianManaCost) {
|
||||
if (cost.pay(null, game, null, playerId, false)) {
|
||||
if (cost.pay(null, game, null, playerId, false) || spendAnyMana) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -342,14 +342,12 @@ class JaceArchitectOfThoughtEffect3 extends OneShotEffect {
|
|||
while (jaceExileZone.count(filter, game) > 0 && controller.choose(Outcome.PlayForFree, jaceExileZone, target, game)) {
|
||||
Card card = game.getCard(target.getFirstTarget());
|
||||
if (card != null) {
|
||||
|
||||
if (controller.cast(card.getSpellAbility(), game, true)) {
|
||||
game.getExile().removeCard(card, game);
|
||||
}
|
||||
}
|
||||
target.clearChosen();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,11 +41,10 @@ import org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl;
|
|||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
|
||||
public abstract class CardTestPlayerBaseAI extends CardTestPlayerAPIImpl {
|
||||
|
||||
int skill = 9;
|
||||
|
||||
|
||||
int skill = 6;
|
||||
|
||||
@Override
|
||||
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
|
||||
Game game = new TwoPlayerDuel(MultiplayerAttackOption.LEFT, RangeOfInfluence.ONE, 0, 20);
|
||||
|
|
@ -53,8 +52,8 @@ public abstract class CardTestPlayerBaseAI extends CardTestPlayerAPIImpl {
|
|||
playerA = createPlayer(game, playerA, "PlayerA");
|
||||
playerB = createPlayer(game, playerB, "PlayerB");
|
||||
return game;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TestPlayer createPlayer(String name) {
|
||||
if (name.equals("PlayerA")) {
|
||||
|
|
@ -65,7 +64,7 @@ public abstract class CardTestPlayerBaseAI extends CardTestPlayerAPIImpl {
|
|||
return super.createPlayer(name);
|
||||
}
|
||||
|
||||
public void setAISkill(int skill) {
|
||||
public void setAISkill(int skill) {
|
||||
this.skill = skill;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue