From fa0a40b0d6b22906079b53436e01752b785aa2d3 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 12 Jul 2015 19:51:04 +0200 Subject: [PATCH] * Some fixes/chnages to AI player. Needs probably some more testing. --- .../src/mage/player/ai/ComputerPlayer6.java | 84 ++++++++++++++----- .../mage/player/ai/GameStateEvaluator2.java | 62 +++++++------- .../src/mage/player/ai/SimulatedPlayer2.java | 56 ++++++------- .../src/mage/player/ai/SimulationNode2.java | 9 +- .../player/ai/ma/ArtificialScoringSystem.java | 61 +++++++------- .../java/mage/player/ai/ComputerPlayer.java | 72 +++++++++++----- .../JaceArchitectOfThought.java | 2 - .../serverside/base/CardTestPlayerBaseAI.java | 13 ++- 8 files changed, 209 insertions(+), 150 deletions(-) diff --git a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer6.java b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer6.java index da899f41f40..66fc062f4d7 100644 --- a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer6.java +++ b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer6.java @@ -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 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 counterAttackList = new ArrayList(); //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 getAlreadyBlockingPermanents(List blockerOrder, Game game) { List 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; } - } diff --git a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/GameStateEvaluator2.java b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/GameStateEvaluator2.java index 52b99359a6e..0e2c9f409c1 100644 --- a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/GameStateEvaluator2.java +++ b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/GameStateEvaluator2.java @@ -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; } diff --git a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java index 40e947230c6..42a7e062dd7 100644 --- a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java +++ b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java @@ -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 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 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; } - } diff --git a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulationNode2.java b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulationNode2.java index b5e1fef4400..bbbf1f05c2f 100644 --- a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulationNode2.java +++ b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulationNode2.java @@ -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 abilities; protected int depth; - protected List children = new ArrayList(); + protected List children = new ArrayList<>(); protected SimulationNode2 parent; - protected List targets = new ArrayList(); - protected List choices = new ArrayList(); + protected List targets = new ArrayList<>(); + protected List 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(); + this.abilities = new ArrayList<>(); abilities.add(ability); } diff --git a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ma/ArtificialScoringSystem.java b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ma/ArtificialScoringSystem.java index 4c008d09ff8..abaa31aaf60 100644 --- a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ma/ArtificialScoringSystem.java +++ b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ma/ArtificialScoringSystem.java @@ -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; } diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java index 57c42140b9b..28264fff1a5 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java @@ -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 producers; if (unpaid instanceof ManaCosts) { - cost = ((ManaCosts) unpaid).get(0); + ManaCosts manaCosts = (ManaCosts) 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; } } diff --git a/Mage.Sets/src/mage/sets/returntoravnica/JaceArchitectOfThought.java b/Mage.Sets/src/mage/sets/returntoravnica/JaceArchitectOfThought.java index fa1d8f6e552..58be732e789 100644 --- a/Mage.Sets/src/mage/sets/returntoravnica/JaceArchitectOfThought.java +++ b/Mage.Sets/src/mage/sets/returntoravnica/JaceArchitectOfThought.java @@ -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; } } diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestPlayerBaseAI.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestPlayerBaseAI.java index 2f50da22db0..910f3de6ec4 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestPlayerBaseAI.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestPlayerBaseAI.java @@ -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; } }