* Some fixes/chnages to AI player. Needs probably some more testing.

This commit is contained in:
LevelX2 2015-07-12 19:51:04 +02:00
parent c53c09a59b
commit fa0a40b0d6
8 changed files with 209 additions and 150 deletions

View file

@ -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;
}
}

View file

@ -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;
}

View file

@ -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;
}
}

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}