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 4a945e05f0..ed298bbd01 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 @@ -160,7 +160,7 @@ public class ComputerPlayer6 extends ComputerPlayer implements } } - private void printOutState(Game game, UUID playerId) { + protected void printOutState(Game game, UUID playerId) { Player player = game.getPlayer(playerId); System.out.println("Turn::"+game.getTurnNum()); System.out.println("[" + game.getPlayer(playerId).getName() + "] " + game.getTurn().getStepType().name() +", life=" + player.getLife()); @@ -239,6 +239,7 @@ public class ComputerPlayer6 extends ComputerPlayer implements logger.info("simlating -- game value:" + game.getState().getValue() + " test value:" + test.gameValue); if (root.playerId.equals(playerId) && root.abilities != null && game.getState().getValue() == test.gameValue) { + /* // Try to fix horizon effect if (root.combat == null || root.combat.getAttackers().size() == 0) { FilterCreatureForAttack attackFilter = new FilterCreatureForAttack(); @@ -250,6 +251,7 @@ public class ComputerPlayer6 extends ComputerPlayer implements return false; } } + */ logger.info("simulating -- continuing previous action chain"); actions = new LinkedList(root.abilities); @@ -286,6 +288,11 @@ public class ComputerPlayer6 extends ComputerPlayer implements bestChild.setCombat(_combat); } } + // no need to check other actions + if (val == GameStateEvaluator2.LOSE_GAME_SCORE) { + logger.debug("lose - break"); + break; + } } else { if (val > alpha) { @@ -296,6 +303,11 @@ public class ComputerPlayer6 extends ComputerPlayer implements bestChild.setCombat(_combat); } } + // no need to check other actions + if (val == GameStateEvaluator2.WIN_GAME_SCORE) { + logger.debug("win - break"); + break; + } } } node.children.clear(); @@ -467,6 +479,12 @@ public class ComputerPlayer6 extends ComputerPlayer implements bestNode.setScore(val); node.setCombat(newNode.getCombat()); } + + // no need to check other actions + if (val == GameStateEvaluator2.LOSE_GAME_SCORE) { + logger.debug("lose - break"); + break; + } } else { if (val > alpha) { @@ -478,7 +496,7 @@ public class ComputerPlayer6 extends ComputerPlayer implements targets = node.getTargets(); if (node.getChoices().size() > 0) choices = node.getChoices(); - if (depth == 20) { + if (depth == Config2.maxDepth) { logger.info("saved"); node.children.clear(); node.children.add(bestNode); @@ -488,7 +506,7 @@ public class ComputerPlayer6 extends ComputerPlayer implements // no need to check other actions if (val == GameStateEvaluator2.WIN_GAME_SCORE) { - logger.info("win - break"); + logger.debug("win - break"); break; } } @@ -710,10 +728,12 @@ public class ComputerPlayer6 extends ComputerPlayer implements logger.debug("selectAttackers"); if (combat != null) { UUID opponentId = game.getCombat().getDefenders().iterator().next(); + String attackers = ""; for (UUID attackerId: combat.getAttackers()) { - logger.debug("declare attacker: " + game.getCard(attackerId).getName()); + attackers = "[" + game.getCard(attackerId).getName() + "]"; this.declareAttacker(attackerId, opponentId, game); } + logger.info("declare attackers: " + (attackers.isEmpty() ? "none" : attackers)); } } diff --git a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer7.java b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer7.java new file mode 100644 index 0000000000..49d6b409a2 --- /dev/null +++ b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer7.java @@ -0,0 +1,545 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.player.ai; + +import mage.Constants.AbilityType; +import mage.Constants.PhaseStep; +import mage.Constants.RangeOfInfluence; +import mage.Constants.Zone; +import mage.abilities.Ability; +import mage.filter.FilterAbility; +import mage.game.Game; +import mage.game.combat.Combat; +import mage.game.combat.CombatGroup; +import mage.game.events.GameEvent; +import mage.game.turn.*; +import mage.players.Player; +import org.apache.log4j.Logger; + +import java.util.LinkedList; +import java.util.UUID; + +/** + * + * @author ayratn + */ +public class ComputerPlayer7 extends ComputerPlayer6 implements Player { + + private static final transient Logger logger = Logger.getLogger(ComputerPlayer7.class); + + private static FilterAbility filterLand = new FilterAbility(); + private static FilterAbility filterNotLand = new FilterAbility(); + + static { + filterLand.getTypes().add(AbilityType.PLAY_LAND); + filterLand.setZone(Zone.HAND); + + filterNotLand.getTypes().add(AbilityType.PLAY_LAND); + filterNotLand.setZone(Zone.HAND); + filterNotLand.setNotFilter(true); + + } + + public ComputerPlayer7(String name, RangeOfInfluence range) { + super(name, range); + maxDepth = Config2.maxDepth; + maxNodes = Config2.maxNodes; + } + + public ComputerPlayer7(final ComputerPlayer7 player) { + super(player); + } + + @Override + public ComputerPlayer7 copy() { + return new ComputerPlayer7(this); + } + + @Override + public void priority(Game game) { + logState(game); + if (logger.isDebugEnabled()) + logger.debug("Game State: Turn-" + game.getTurnNum() + " Step-" + game.getTurn().getStepType() + " ActivePlayer-" + game.getPlayer(game.getActivePlayerId()).getName() + " PriorityPlayer-" + name); + game.firePriorityEvent(playerId); + switch (game.getTurn().getStepType()) { + case UPKEEP: + case DRAW: + pass(); + break; + case PRECOMBAT_MAIN: + if (game.getActivePlayerId().equals(playerId)) { + System.out.println("Computer7:"); + printOutState(game, playerId); + printOutState(game, game.getOpponents(playerId).iterator().next()); + if (actions.size() == 0) { + calculatePreCombatActions(game); + } + act(game); + } + else + pass(); + break; + case BEGIN_COMBAT: + pass(); + break; + case DECLARE_ATTACKERS: + if (!game.getActivePlayerId().equals(playerId)) { + printOutState(game, playerId); + printOutState(game, game.getOpponents(playerId).iterator().next()); + if (actions.size() == 0) { + calculatePreCombatActions(game); + } + act(game); + } + else + pass(); + break; + case DECLARE_BLOCKERS: + case COMBAT_DAMAGE: + case END_COMBAT: + pass(); + break; + case POSTCOMBAT_MAIN: + if (game.getActivePlayerId().equals(playerId)) { + printOutState(game, playerId); + printOutState(game, game.getOpponents(playerId).iterator().next()); + if (actions.size() == 0) { + calculatePostCombatActions(game); + } + act(game); + } + else + pass(); + break; + case END_TURN: + case CLEANUP: + pass(); + break; + } + } + + protected void calculatePreCombatActions(Game game) { + if (!getNextAction(game)) { + currentScore = GameStateEvaluator2.evaluate(playerId, game); + Game sim = createSimulation(game); + SimulationNode2.resetCount(); + root = new SimulationNode2(null, sim, maxDepth, playerId); + logger.debug("simulating pre combat actions -----------------------------------------------------------------------------------------"); + + addActionsTimed(new FilterAbility()); + if (root.children.size() > 0) { + root = root.children.get(0); + int bestScore = root.getScore(); + if (bestScore > currentScore) { + actions = new LinkedList(root.abilities); + combat = root.combat; + } + } + } + } + + protected void calculatePostCombatActions(Game game) { + if (!getNextAction(game)) { + currentScore = GameStateEvaluator2.evaluate(playerId, game); + Game sim = createSimulation(game); + SimulationNode2.resetCount(); + root = new SimulationNode2(null, sim, maxDepth, playerId); + logger.debug("simulating post combat actions ----------------------------------------------------------------------------------------"); + + addActionsTimed(new FilterAbility()); + if (root.children.size() > 0) { + root = root.children.get(0); + int bestScore = root.getScore(); + if (bestScore > currentScore) { + actions = new LinkedList(root.abilities); + combat = root.combat; + } + } + } + } + + @Override + protected int addActions(SimulationNode2 node, FilterAbility filter, int depth, int alpha, int beta) { + boolean stepFinished = false; + int val; + Game game = node.getGame(); + if (Thread.interrupted()) { + Thread.currentThread().interrupt(); + logger.debug("interrupted"); + return GameStateEvaluator2.evaluate(playerId, game); + } + if (depth <= 0 || SimulationNode2.nodeCount > maxNodes || game.isGameOver()) { + logger.debug("simulating -- reached end state"); + val = GameStateEvaluator2.evaluate(playerId, game); + } + else if (node.getChildren().size() > 0) { + logger.debug("simulating -- somthing added children:" + node.getChildren().size()); + val = minimaxAB(node, filter, depth-1, alpha, beta); + } + else { + if (logger.isDebugEnabled()) + logger.debug("simulating -- alpha: " + alpha + " beta: " + beta + " depth:" + depth + " step:" + game.getTurn().getStepType() + " for player:" + game.getPlayer(game.getPlayerList().get()).getName()); + if (allPassed(game)) { + if (!game.getStack().isEmpty()) { + resolve(node, depth, game); + } + else { + stepFinished = true; + } + } + + if (game.isGameOver()) { + val = GameStateEvaluator2.evaluate(playerId, game); + } + else if (stepFinished) { + logger.debug("step finished"); + int testScore = GameStateEvaluator2.evaluate(playerId, game); + if (game.getActivePlayerId().equals(playerId)) { + if (testScore < currentScore) { + // if score at end of step is worse than original score don't check further + logger.debug("simulating -- abandoning check, no immediate benefit"); + val = testScore; + } + else { + switch (game.getTurn().getStepType()) { + case PRECOMBAT_MAIN: + val = simulateCombat(game, node, depth-1, alpha, beta, false); + break; + case POSTCOMBAT_MAIN: + val = simulateCounterAttack(game, node, depth-1, alpha, beta); + break; + default: + val = GameStateEvaluator2.evaluate(playerId, game); + break; + } + } + } + else { + if (game.getTurn().getStepType() == PhaseStep.DECLARE_ATTACKERS) + val = simulateBlockers(game, node, playerId, depth-1, alpha, beta, true); + else + val = GameStateEvaluator2.evaluate(playerId, game); + } + } + else if (node.getChildren().size() > 0) { + logger.debug("simulating -- trigger added children:" + node.getChildren().size()); + val = minimaxAB(node, filter, depth, alpha, beta); + } + else { + val = simulatePriority(node, game, filter, depth, alpha, beta); + } + } + + if (logger.isDebugEnabled()) + logger.debug("returning -- score: " + val + " depth:" + depth + " step:" + game.getTurn().getStepType() + " for player:" + game.getPlayer(node.getPlayerId()).getName()); + return val; + + } + + protected int simulateCombat(Game game, SimulationNode2 node, int depth, int alpha, int beta, boolean counter) { + Integer val = null; + if (Thread.interrupted()) { + Thread.currentThread().interrupt(); + logger.debug("interrupted"); + return GameStateEvaluator2.evaluate(playerId, game); + } + if (game.getTurn().getStepType() != PhaseStep.DECLARE_BLOCKERS) { + game.getTurn().setPhase(new CombatPhase()); + if (game.getPhase().beginPhase(game, game.getActivePlayerId())) { + simulateStep(game, new BeginCombatStep()); + game.getPhase().setStep(new DeclareAttackersStep()); + if (!game.getStep().skipStep(game, game.getActivePlayerId())) { + game.fireEvent(new GameEvent(GameEvent.EventType.DECLARE_ATTACKERS_STEP_PRE, null, null, game.getActivePlayerId())); + if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARING_ATTACKERS, game.getActivePlayerId(), game.getActivePlayerId()))) { + val = simulateAttackers(game, node, game.getActivePlayerId(), depth, alpha, beta, counter); + } + } + else if (!counter) { + simulateToEnd(game); + val = simulatePostCombatMain(game, node, depth, alpha, beta); + } + } + } + else { + if (!game.getStep().skipStep(game, game.getActivePlayerId())) { + game.fireEvent(new GameEvent(GameEvent.EventType.DECLARE_BLOCKERS_STEP_PRE, null, null, game.getActivePlayerId())); + if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARING_BLOCKERS, game.getActivePlayerId(), game.getActivePlayerId()))) { + //only suitable for two player games - only simulates blocks for 1st defender + val = simulateBlockers(game, node, game.getCombat().getDefenders().iterator().next(), depth, alpha, beta, counter); + } + } + else if (!counter) { + finishCombat(game); + val = simulateCounterAttack(game, node, depth, alpha, beta); + } + } + if (val == null) + val = GameStateEvaluator2.evaluate(playerId, game); + if (logger.isDebugEnabled()) + logger.debug("returning -- combat score: " + val + " depth:" + depth + " for player:" + game.getPlayer(node.getPlayerId()).getName()); + return val; + } + + + protected int simulateAttackers(Game game, SimulationNode2 node, UUID attackerId, int depth, int alpha, int beta, boolean counter) { + if (Thread.interrupted()) { + Thread.currentThread().interrupt(); + logger.debug("interrupted"); + return GameStateEvaluator2.evaluate(playerId, game); + } + Integer val = null; + SimulationNode2 bestNode = null; + SimulatedPlayer2 attacker = (SimulatedPlayer2) game.getPlayer(attackerId); + + if (logger.isDebugEnabled()) + logger.debug(attacker.getName() + "'s possible attackers: " + attacker.getAvailableAttackers(game)); + for (Combat engagement: attacker.addAttackers(game)) { + if (alpha >= beta) { + logger.debug("simulating -- pruning attackers"); + break; + } + Game sim = game.copy(); + UUID defenderId = game.getOpponents(attackerId).iterator().next(); + for (CombatGroup group: engagement.getGroups()) { + for (UUID attackId: group.getAttackers()) { + sim.getPlayer(attackerId).declareAttacker(attackId, defenderId, sim); + } + } + sim.fireEvent(GameEvent.getEvent(GameEvent.EventType.DECLARED_ATTACKERS, attackerId, attackerId)); + SimulationNode2 newNode = new SimulationNode2(node, sim, depth, attackerId); + if (logger.isDebugEnabled()) + logger.debug("simulating attack for player:" + game.getPlayer(attackerId).getName()); + sim.checkStateAndTriggered(); + while (!sim.getStack().isEmpty()) { + sim.getStack().resolve(sim); + logger.debug("resolving triggered abilities"); + sim.applyEffects(); + } + sim.fireEvent(GameEvent.getEvent(GameEvent.EventType.DECLARE_ATTACKERS_STEP_POST, sim.getActivePlayerId(), sim.getActivePlayerId())); + Combat simCombat = sim.getCombat().copy(); + sim.getPhase().setStep(new DeclareBlockersStep()); + val = simulateCombat(sim, newNode, depth-1, alpha, beta, counter); + if (!attackerId.equals(playerId)) { + if (val < beta) { + beta = val; + bestNode = newNode; + bestNode.setScore(val); + node.setCombat(simCombat); + } + } + else { + if (val > alpha) { + alpha = val; + bestNode = newNode; + bestNode.setScore(val); + node.setCombat(simCombat); + } + } + } + if (val == null) + val = GameStateEvaluator2.evaluate(playerId, game); + if (bestNode != null) { + node.children.clear(); + node.children.add(bestNode); + node.setScore(bestNode.getScore()); + } + if (logger.isDebugEnabled()) + logger.debug("returning -- combat attacker score: " + val + " depth:" + depth + " for player:" + game.getPlayer(node.getPlayerId()).getName()); + return val; + } + + protected int simulateBlockers(Game game, SimulationNode2 node, UUID defenderId, int depth, int alpha, int beta, boolean counter) { + if (Thread.interrupted()) { + Thread.currentThread().interrupt(); + logger.debug("interrupted"); + return GameStateEvaluator2.evaluate(playerId, game); + } + Integer val = null; + SimulationNode2 bestNode = null; + //check if defender is being attacked + if (game.getCombat().isAttacked(defenderId, game)) { + SimulatedPlayer2 defender = (SimulatedPlayer2) game.getPlayer(defenderId); + if (logger.isDebugEnabled()) + logger.debug(defender.getName() + "'s possible blockers: " + defender.getAvailableBlockers(game)); + for (Combat engagement: defender.addBlockers(game)) { + if (alpha >= beta) { + logger.debug("simulating -- pruning blockers"); + break; + } + Game sim = game.copy(); + for (CombatGroup group: engagement.getGroups()) { + if (group.getAttackers().size() > 0) { + UUID attackerId = group.getAttackers().get(0); + for (UUID blockerId: group.getBlockers()) { + sim.getPlayer(defenderId).declareBlocker(blockerId, attackerId, sim); + } + } + } + sim.fireEvent(GameEvent.getEvent(GameEvent.EventType.DECLARED_BLOCKERS, defenderId, defenderId)); + SimulationNode2 newNode = new SimulationNode2(node, sim, depth, defenderId); + if (logger.isDebugEnabled()) + logger.debug("simulating block for player:" + game.getPlayer(defenderId).getName()); + sim.checkStateAndTriggered(); + while (!sim.getStack().isEmpty()) { + sim.getStack().resolve(sim); + logger.debug("resolving triggered abilities"); + sim.applyEffects(); + } + sim.fireEvent(GameEvent.getEvent(GameEvent.EventType.DECLARE_BLOCKERS_STEP_POST, sim.getActivePlayerId(), sim.getActivePlayerId())); + Combat simCombat = sim.getCombat().copy(); + finishCombat(sim); + if (sim.isGameOver()) { + val = GameStateEvaluator2.evaluate(playerId, sim); + } + else if (!counter) { + val = simulatePostCombatMain(sim, newNode, depth-1, alpha, beta); + } + else + val = GameStateEvaluator2.evaluate(playerId, sim); + if (!defenderId.equals(playerId)) { + if (val < beta) { + beta = val; + bestNode = newNode; + bestNode.setScore(val); + node.setCombat(simCombat); + } + } + else { + if (val > alpha) { + alpha = val; + bestNode = newNode; + node.setCombat(simCombat); + } + } + } + } + if (val == null) + val = GameStateEvaluator2.evaluate(playerId, game); + if (bestNode != null) { + node.children.clear(); + node.children.add(bestNode); + node.setScore(bestNode.getScore()); + } + if (logger.isDebugEnabled()) + logger.debug("returning -- combat blocker score: " + val + " depth:" + depth + " for player:" + game.getPlayer(node.getPlayerId()).getName()); + return val; + } + + protected int simulateCounterAttack(Game game, SimulationNode2 node, int depth, int alpha, int beta) { + if (Thread.interrupted()) { + Thread.currentThread().interrupt(); + logger.debug("interrupted"); + return GameStateEvaluator2.evaluate(playerId, game); + } + Integer val = null; + if (!game.isGameOver()) { + simulateToEnd(game); + game.getState().setActivePlayerId(game.getState().getPlayerList(game.getActivePlayerId()).getNext()); + logger.debug("simulating -- counter attack for player " + game.getPlayer(game.getActivePlayerId()).getName()); + game.getTurn().setPhase(new BeginningPhase()); + if (game.getPhase().beginPhase(game, game.getActivePlayerId())) { + simulateStep(game, new UntapStep()); + simulateStep(game, new UpkeepStep()); + simulateStep(game, new DrawStep()); + game.getPhase().endPhase(game, game.getActivePlayerId()); + } + val = simulateCombat(game, node, depth-1, alpha, beta, true); + if (logger.isDebugEnabled()) + logger.debug("returning -- counter attack score: " + val + " depth:" + depth + " for player:" + game.getPlayer(node.getPlayerId()).getName()); + } + if (val == null) + val = GameStateEvaluator2.evaluate(playerId, game); + return val; + } + + protected void simulateStep(Game game, Step step) { + if (Thread.interrupted()) { + Thread.currentThread().interrupt(); + logger.debug("interrupted"); + return; + } + if (!game.isGameOver()) { + game.getPhase().setStep(step); + if (!step.skipStep(game, game.getActivePlayerId())) { + step.beginStep(game, game.getActivePlayerId()); + game.checkStateAndTriggered(); + while (!game.getStack().isEmpty()) { + game.getStack().resolve(game); + game.applyEffects(); + } + step.endStep(game, game.getActivePlayerId()); + } + } + } + + protected void finishCombat(Game game) { + if (Thread.interrupted()) { + Thread.currentThread().interrupt(); + logger.debug("interrupted"); + return; + } + simulateStep(game, new CombatDamageStep(true)); + simulateStep(game, new CombatDamageStep(false)); + simulateStep(game, new EndOfCombatStep()); + } + + protected int simulatePostCombatMain(Game game, SimulationNode2 node, int depth, int alpha, int beta) { + if (Thread.interrupted()) { + Thread.currentThread().interrupt(); + logger.debug("interrupted"); + return GameStateEvaluator2.evaluate(playerId, game); + } + logger.debug("simulating -- post combat main"); + game.getTurn().setPhase(new PostCombatMainPhase()); + if (game.getPhase().beginPhase(game, game.getActivePlayerId())) { + game.getPhase().setStep(new PostCombatMainStep()); + game.getStep().beginStep(game, playerId); + game.getPlayers().resetPassed(); + return addActions(node, new FilterAbility(), depth, alpha, beta); + } + return simulateCounterAttack(game, node, depth, alpha, beta); + } + + protected void simulateToEnd(Game game) { + if (Thread.interrupted()) { + Thread.currentThread().interrupt(); + logger.debug("interrupted"); + return; + } + if (!game.isGameOver()) { + game.getTurn().getPhase().endPhase(game, game.getActivePlayerId()); + game.getTurn().setPhase(new EndPhase()); + if (game.getTurn().getPhase().beginPhase(game, game.getActivePlayerId())) { + simulateStep(game, new EndStep()); + simulateStep(game, new CleanupStep()); + } + } + } + +} diff --git a/Mage.Tests/config/config.xml b/Mage.Tests/config/config.xml index 7af45388f8..ea8d028cd5 100644 --- a/Mage.Tests/config/config.xml +++ b/Mage.Tests/config/config.xml @@ -4,7 +4,7 @@ - + diff --git a/Mage.Tests/plugins/mage-player-ai-ma.jar b/Mage.Tests/plugins/mage-player-ai-ma.jar index 61b204cfa6..849a57c7a3 100644 Binary files a/Mage.Tests/plugins/mage-player-ai-ma.jar and b/Mage.Tests/plugins/mage-player-ai-ma.jar differ diff --git a/Mage.Tests/plugins/mage-player-aiminimax.jar b/Mage.Tests/plugins/mage-player-aiminimax.jar index 59a6eda05f..d7c8a7a2e1 100644 Binary files a/Mage.Tests/plugins/mage-player-aiminimax.jar and b/Mage.Tests/plugins/mage-player-aiminimax.jar differ diff --git a/Mage.Tests/scenario1.txt b/Mage.Tests/scenario1.txt index e32560107c..1404aeac6c 100644 --- a/Mage.Tests/scenario1.txt +++ b/Mage.Tests/scenario1.txt @@ -1,7 +1,9 @@ +# Test playing Lightning Bolts (there was a bug with targeting players) + ### ComputerA ### # Battlefield battlefield:ComputerA:Mountain:1 -#battlefield:ComputerA:Goblin Guide:1 + # Hand hand:ComputerA:Mountain:2 hand:ComputerA:Lightning Bolt:5 diff --git a/Mage.Tests/scenario2.txt b/Mage.Tests/scenario2.txt index 7776ff7002..f4dab9f5f6 100644 --- a/Mage.Tests/scenario2.txt +++ b/Mage.Tests/scenario2.txt @@ -1,22 +1,36 @@ +# Reproduces game where AI didn't attack though could win + ### ComputerA ### # Battlefield -battlefield:ComputerA:Island:2 -battlefield:ComputerA:Mountain:2 +battlefield:ComputerA:Mountain:3 +battlefield:ComputerA:Lavaclaw Reaches:2 +battlefield:ComputerA:Dragonskull Summit:1 + # Hand -hand:ComputerA:Mountain:0 -hand:ComputerA:Hellspark Elemental:5 +hand:ComputerA:Searing Blaze:1 +hand:ComputerA:Lavaclaw Reaches:1 + # Library # from down to top library:ComputerA:clear:0 -library:ComputerA:Hellspark Elemental:10 +library:ComputerA:Mountain:10 + +# Life +player:ComputerA:life:3 ### ComputerB ### # Battlefield -battlefield:ComputerB:Plains:1 +battlefield:ComputerB:Mountain:4:{tapped} +battlefield:ComputerB:Dragonskull Summit:1:{tapped} +battlefield:ComputerB:Arid Mesa:1:{tapped} +battlefield:ComputerB:Lavaclaw Reaches:1:{tapped} # Hand -hand:ComputerB:Plains:2 +hand:ComputerB:Ball Lightning:2 # Library # from down to top library:ComputerB:clear:0 -library:ComputerB:Plains:10 +library:ComputerB:Lightning Bolt:10 +library:ComputerB:Mountain:1 +# Life +player:ComputerB:life:1 diff --git a/Mage.Tests/scenario3.txt b/Mage.Tests/scenario3.txt index 6b62aaa618..0ce553c17e 100644 --- a/Mage.Tests/scenario3.txt +++ b/Mage.Tests/scenario3.txt @@ -1,3 +1,5 @@ +# Test attacking with Hellspark Elemental + ### ComputerA ### # Battlefield battlefield:ComputerA:Island:2 diff --git a/Mage.Tests/scenario4.txt b/Mage.Tests/scenario4.txt index c0832cd0c6..b883a437b8 100644 --- a/Mage.Tests/scenario4.txt +++ b/Mage.Tests/scenario4.txt @@ -1,3 +1,5 @@ +# Test playing Blightning + ### ComputerA ### # Battlefield battlefield:ComputerA:Swamp:1 diff --git a/Mage.Tests/scenario8.txt b/Mage.Tests/scenario8.txt new file mode 100644 index 0000000000..27f64dc50b --- /dev/null +++ b/Mage.Tests/scenario8.txt @@ -0,0 +1,36 @@ +# This scenario makes AI frozen + +### ComputerA ### +# Battlefield +battlefield:ComputerA:Mountain:3 +battlefield:ComputerA:Lavaclaw Reaches:2 +battlefield:ComputerA:Dragonskull Summit:1 + +# Hand +hand:ComputerA:Searing Blaze:1 +hand:ComputerA:Lavaclaw Reaches:1 + +# Library +# from down to top +library:ComputerA:clear:0 +library:ComputerA:Mountain:10 + +# Life +player:ComputerA:life:3 + +### ComputerB ### +# Battlefield +battlefield:ComputerB:Mountain:4 +battlefield:ComputerB:Dragonskull Summit:1 +battlefield:ComputerB:Arid Mesa:1 +battlefield:ComputerB:Lavaclaw Reaches:1 +# Hand +hand:ComputerB:Ball Lightning:2 +# Library +# from down to top +library:ComputerB:clear:0 +library:ComputerB:Lightning Bolt:10 +library:ComputerB:Mountain:1 + +# Life +player:ComputerB:life:1 diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/PlayGameTest.java b/Mage.Tests/src/test/java/org/mage/test/serverside/PlayGameTest.java index 7cc3a5d15c..69bfda3b01 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/PlayGameTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/PlayGameTest.java @@ -7,6 +7,9 @@ import mage.cards.decks.Deck; import mage.game.Game; import mage.game.GameException; import mage.game.TwoPlayerDuel; +import mage.game.permanent.Permanent; +import mage.game.permanent.PermanentCard; +import mage.game.permanent.PermanentImpl; import mage.players.Player; import mage.server.game.PlayerFactory; import mage.sets.Sets; @@ -39,6 +42,7 @@ public class PlayGameTest extends MageTestBase { public void playOneGame() throws GameException, FileNotFoundException, IllegalArgumentException { Game game = new TwoPlayerDuel(Constants.MultiplayerAttackOption.LEFT, Constants.RangeOfInfluence.ALL); + //Player computerA = createPlayer("ComputerA", "Computer - minimax hybrid"); Player computerA = createPlayer("ComputerA", "Computer - mad"); Deck deck = Deck.load(Sets.loadDeck("RB Aggro.dck")); @@ -48,7 +52,8 @@ public class PlayGameTest extends MageTestBase { game.addPlayer(computerA, deck); game.loadCards(deck.getCards(), computerA.getId()); - Player computerB = createPlayer("ComputerB", "Computer - minimax hybrid"); + //Player computerB = createPlayer("ComputerB", "Computer - minimax hybrid"); + Player computerB = createPlayer("ComputerB", "Computer - mad"); Deck deck2 = Deck.load(Sets.loadDeck("RB Aggro.dck")); if (deck2.getCards().size() < 40) { throw new IllegalArgumentException("Couldn't load deck, deck size=" + deck2.getCards().size()); @@ -56,15 +61,17 @@ public class PlayGameTest extends MageTestBase { game.addPlayer(computerB, deck2); game.loadCards(deck2.getCards(), computerB.getId()); - /*parseScenario("scenario7.txt"); + parseScenario("scenario2.txt"); game.cheat(computerA.getId(), commandsA); game.cheat(computerA.getId(), libraryCardsA, handCardsA, battlefieldCardsA, graveyardCardsA); game.cheat(computerB.getId(), commandsB); game.cheat(computerB.getId(), libraryCardsB, handCardsB, battlefieldCardsB, graveyardCardsB); - */ + + //boolean testMode = false; + boolean testMode = true; long t1 = System.nanoTime(); - game.start(computerA.getId(), false); + game.start(computerA.getId(), testMode); long t2 = System.nanoTime(); logger.info("Winner: " + game.getWinner()); @@ -90,7 +97,7 @@ public class PlayGameTest extends MageTestBase { try { while (scanner.hasNextLine()) { String line = scanner.nextLine().trim(); - if (line.startsWith("#")) continue; + if (line == null || line.isEmpty() || line.startsWith("#")) continue; Matcher m = pattern.matcher(line); if (m.matches()) { @@ -128,6 +135,7 @@ public class PlayGameTest extends MageTestBase { String cardName = m.group(3); Integer amount = Integer.parseInt(m.group(4)); + boolean tapped = m.group(5) != null && m.group(5).equals(":{tapped}"); if (cardName.equals("clear")) { if (nickname.equals("ComputerA")) { @@ -139,7 +147,13 @@ public class PlayGameTest extends MageTestBase { for (int i = 0; i < amount; i++) { Card card = Sets.findCard(cardName, true); if (card != null) { - cards.add(card); + if (gameZone.equals(Constants.Zone.BATTLEFIELD)) { + PermanentCard p = new PermanentCard(card, null); + p.setTapped(tapped); + cards.add(p); + } else { + cards.add(card); + } } else { logger.fatal("Couldn't find a card: " + cardName); logger.fatal("line: " + line); diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestBase.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestBase.java index 2d4d6f4235..03518fd762 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestBase.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestBase.java @@ -29,7 +29,7 @@ public class MageTestBase { private final static String pluginFolder = "plugins"; - protected Pattern pattern = Pattern.compile("([a-zA-Z]*):([\\w]*):([a-zA-Z ,\\-.!'\\d]*):([\\d]*)"); + protected Pattern pattern = Pattern.compile("([a-zA-Z]*):([\\w]*):([a-zA-Z ,\\-.!'\\d]*):([\\d]*)(:\\{tapped\\})?"); @BeforeClass public static void init() { diff --git a/Mage.Tests/src/test/resources/log4j.properties b/Mage.Tests/src/test/resources/log4j.properties index 735c93a42f..4e87c90cf2 100644 --- a/Mage.Tests/src/test/resources/log4j.properties +++ b/Mage.Tests/src/test/resources/log4j.properties @@ -9,7 +9,7 @@ log4j.appender.console.Threshold=INFO #file log log4j.appender.file=org.apache.log4j.FileAppender -log4j.appender.file.file=MagicWars.log +log4j.appender.file.file=Mage.log log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=%-5p [%d{yyyy-MM-dd HH:mm [ss:SSS]}] %C{1}[%t]: %m%n log4j.appender.file.Threshold=INFO diff --git a/Mage/src/mage/game/GameImpl.java b/Mage/src/mage/game/GameImpl.java index 2decd78ef6..1e841bbc0f 100644 --- a/Mage/src/mage/game/GameImpl.java +++ b/Mage/src/mage/game/GameImpl.java @@ -1002,7 +1002,6 @@ public abstract class GameImpl> implements Game, Serializa setZone(card.getId(), Zone.GRAVEYARD); player.getGraveyard().add(card); } - List permanents = new ArrayList(); for (Card card : battlefield) { card.setOwnerId(ownerId); PermanentCard permanent = new PermanentCard(card, ownerId);