1
0
Fork 0
mirror of https://github.com/correl/mage.git synced 2025-04-10 09:11:04 -09:00

* Fixed that a spell that becomes a permanent didn't had the colors of the spell (e.g. ERsatz Gnomes).

This commit is contained in:
LevelX2 2016-10-22 10:38:10 +02:00
parent da4e0bc20e
commit 97b872d926
10 changed files with 188 additions and 139 deletions
Mage.Server.Plugins
Mage.Player.AI.MA/src/mage/player/ai
Mage.Player.Human/src/mage/player/human
Mage.Tests/src/test/java/org/mage/test/cards
abilities/keywords
continuous
Mage.Verify/src/test/java/mage/verify
Mage/src/main/java/mage/game

View file

@ -1,16 +1,16 @@
/* /*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without modification, are * Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met: * permitted provided that the following conditions are met:
* *
* 1. Redistributions of source code must retain the above copyright notice, this list of * 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer. * conditions and the following disclaimer.
* *
* 2. Redistributions in binary form must reproduce the above copyright notice, this list * 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 * of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution. * provided with the distribution.
* *
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED * 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 * 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 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
@ -20,12 +20,11 @@
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * 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 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
* The views and conclusions contained in the software and documentation are those of the * 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 * authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com. * or implied, of BetaSteward_at_googlemail.com.
*/ */
package mage.player.ai; package mage.player.ai;
import java.util.LinkedList; import java.util.LinkedList;
@ -53,9 +52,6 @@ import mage.game.turn.PostCombatMainStep;
import mage.game.turn.Step; import mage.game.turn.Step;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
/** /**
* *
* @author ayratn * @author ayratn
@ -91,10 +87,10 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
private boolean priorityPlay(Game game) { private boolean priorityPlay(Game game) {
if (lastLoggedTurn != game.getTurnNum()) { if (lastLoggedTurn != game.getTurnNum()) {
lastLoggedTurn = game.getTurnNum(); lastLoggedTurn = game.getTurnNum();
logger.info("======================= Turn: "+ game.getTurnNum() + " ["+ game.getPlayer(game.getActivePlayerId()).getName() +"] ========================================="); logger.info("======================= Turn: " + game.getTurnNum() + " [" + game.getPlayer(game.getActivePlayerId()).getName() + "] =========================================");
} }
logState(game); logState(game);
logger.debug("Priority -- Step: " + (game.getTurn().getStepType() + " ").substring(0,25) + " ActivePlayer-" + game.getPlayer(game.getActivePlayerId()).getName() + " PriorityPlayer-" + name); logger.debug("Priority -- Step: " + (game.getTurn().getStepType() + " ").substring(0, 25) + " ActivePlayer-" + game.getPlayer(game.getActivePlayerId()).getName() + " PriorityPlayer-" + name);
game.getState().setPriorityPlayerId(playerId); game.getState().setPriorityPlayerId(playerId);
game.firePriorityEvent(playerId); game.firePriorityEvent(playerId);
switch (game.getTurn().getStepType()) { switch (game.getTurn().getStepType()) {
@ -111,8 +107,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
} }
act(game); act(game);
return true; return true;
} } else {
else {
pass(game); pass(game);
} }
return false; return false;
@ -128,8 +123,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
} }
act(game); act(game);
return true; return true;
} } else {
else {
pass(game); pass(game);
} }
return false; return false;
@ -141,12 +135,12 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
return false; return false;
case POSTCOMBAT_MAIN: case POSTCOMBAT_MAIN:
// if (game.getActivePlayerId().equals(playerId)) { // if (game.getActivePlayerId().equals(playerId)) {
printOutState(game); printOutState(game);
if (actions.isEmpty()) { if (actions.isEmpty()) {
calculatePostCombatActions(game); calculatePostCombatActions(game);
} }
act(game); act(game);
return true; return true;
// } // }
// else { // else {
// pass(game); // pass(game);
@ -161,24 +155,24 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
return false; return false;
} }
protected void calculatePreCombatActions(Game game) { protected void calculatePreCombatActions(Game game) {
if (!getNextAction(game)) { if (!getNextAction(game)) {
currentScore = GameStateEvaluator2.evaluate(playerId, game); currentScore = GameStateEvaluator2.evaluate(playerId, game);
Game sim = createSimulation(game); Game sim = createSimulation(game);
SimulationNode2.resetCount(); SimulationNode2.resetCount();
root = new SimulationNode2(null, sim, maxDepth, playerId); root = new SimulationNode2(null, sim, maxDepth, playerId);
addActionsTimed(); addActionsTimed();
logger.trace("After add actions timed: root.children.size = " + root.children.size()); logger.trace("After add actions timed: root.children.size = " + root.children.size());
if (root.children.size() > 0) { if (root.children.size() > 0) {
root = root.children.get(0); root = root.children.get(0);
// int bestScore = root.getScore(); // int bestScore = root.getScore();
// if (bestScore > currentScore || allowBadMoves) { // if (bestScore > currentScore || allowBadMoves) {
// prevent repeating always the same action with no cost // prevent repeating always the same action with no cost
boolean doThis = true; boolean doThis = true;
if (root.abilities.size() == 1) { if (root.abilities.size() == 1) {
for (Ability ability:root.abilities) { for (Ability ability : root.abilities) {
if (ability.getManaCosts().convertedManaCost() == 0 && ability.getCosts().isEmpty()) { if (ability.getManaCosts().convertedManaCost() == 0 && ability.getCosts().isEmpty()) {
if (actionCache.contains(ability.getRule() + "_" + ability.getSourceId())) { if (actionCache.contains(ability.getRule() + "_" + ability.getSourceId())) {
doThis = false; // don't do it again doThis = false; // don't do it again
@ -217,7 +211,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
actions = new LinkedList<>(root.abilities); actions = new LinkedList<>(root.abilities);
combat = root.combat; combat = root.combat;
} else { } else {
logger.debug("[" + game.getPlayer(playerId).getName() + "] no better score current: " + currentScore + " bestScore: " + bestScore ); logger.debug("[" + game.getPlayer(playerId).getName() + "] no better score current: " + currentScore + " bestScore: " + bestScore);
} }
} else { } else {
logger.debug("[" + game.getPlayer(playerId).getName() + "][post] Action: skip"); logger.debug("[" + game.getPlayer(playerId).getName() + "][post] Action: skip");
@ -229,7 +223,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
protected int addActions(SimulationNode2 node, int depth, int alpha, int beta) { protected int addActions(SimulationNode2 node, int depth, int alpha, int beta) {
boolean stepFinished = false; boolean stepFinished = false;
int val; int val;
if (logger.isTraceEnabled() && node !=null && node.getAbilities() != null && !node.getAbilities().toString().equals("[Pass]")){ if (logger.isTraceEnabled() && node != null && node.getAbilities() != null && !node.getAbilities().toString().equals("[Pass]")) {
logger.trace("Add Action [" + depth + "] " + node.getAbilities().toString() + " a: " + alpha + " b: " + beta); logger.trace("Add Action [" + depth + "] " + node.getAbilities().toString() + " a: " + alpha + " b: " + beta);
} }
Game game = node.getGame(); Game game = node.getGame();
@ -243,42 +237,39 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
val = GameStateEvaluator2.evaluate(playerId, game); val = GameStateEvaluator2.evaluate(playerId, game);
if (logger.isTraceEnabled()) { if (logger.isTraceEnabled()) {
StringBuilder sb = new StringBuilder("Add Actions -- reached end state <").append(val).append(">"); StringBuilder sb = new StringBuilder("Add Actions -- reached end state <").append(val).append(">");
SimulationNode2 logNode = node; SimulationNode2 logNode = node;
do { do {
sb.append(new StringBuilder(" <- ["+logNode.getDepth()+"]" + (logNode.getAbilities() != null ? logNode.getAbilities().toString():"[empty]"))); sb.append(new StringBuilder(" <- [" + logNode.getDepth() + "]" + (logNode.getAbilities() != null ? logNode.getAbilities().toString() : "[empty]")));
logNode = logNode.getParent(); logNode = logNode.getParent();
} while((logNode.getParent() != null)); } while ((logNode.getParent() != null));
logger.trace(sb); logger.trace(sb);
} }
} else if (node.getChildren().size() > 0) { } else if (node.getChildren().size() > 0) {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
StringBuilder sb = new StringBuilder("Add Action [").append(depth) StringBuilder sb = new StringBuilder("Add Action [").append(depth)
.append("] -- something added children ") .append("] -- something added children ")
.append(node.getAbilities() != null ? node.getAbilities().toString():"null") .append(node.getAbilities() != null ? node.getAbilities().toString() : "null")
.append(" added children: ").append(node.getChildren().size()).append(" ("); .append(" added children: ").append(node.getChildren().size()).append(" (");
for (SimulationNode2 logNode: node.getChildren()) { for (SimulationNode2 logNode : node.getChildren()) {
sb.append(logNode.getAbilities() != null ? logNode.getAbilities().toString():"null").append(", "); sb.append(logNode.getAbilities() != null ? logNode.getAbilities().toString() : "null").append(", ");
} }
sb.append(")"); sb.append(")");
logger.debug(sb); logger.debug(sb);
} }
val = minimaxAB(node, depth-1, alpha, beta); val = minimaxAB(node, depth - 1, alpha, beta);
} } else {
else {
logger.trace("Add Action -- alpha: " + alpha + " beta: " + beta + " depth:" + depth + " step:" + game.getTurn().getStepType() + " for player:" + game.getPlayer(game.getPlayerList().get()).getName()); logger.trace("Add Action -- alpha: " + alpha + " beta: " + beta + " depth:" + depth + " step:" + game.getTurn().getStepType() + " for player:" + game.getPlayer(game.getPlayerList().get()).getName());
if (allPassed(game)) { if (allPassed(game)) {
if (!game.getStack().isEmpty()) { if (!game.getStack().isEmpty()) {
resolve(node, depth, game); resolve(node, depth, game);
} } else {
else {
stepFinished = true; stepFinished = true;
} }
} }
if (game.gameOver(null)) { if (game.gameOver(null)) {
val = GameStateEvaluator2.evaluate(playerId, game); val = GameStateEvaluator2.evaluate(playerId, game);
} } else if (stepFinished) {
else if (stepFinished) {
logger.debug("Step finished"); logger.debug("Step finished");
int testScore = GameStateEvaluator2.evaluate(playerId, game); int testScore = GameStateEvaluator2.evaluate(playerId, game);
if (game.getActivePlayerId().equals(playerId)) { if (game.getActivePlayerId().equals(playerId)) {
@ -286,8 +277,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
// if score at end of step is worse than original score don't check further // if score at end of step is worse than original score don't check further
//logger.debug("Add Action -- abandoning check, no immediate benefit"); //logger.debug("Add Action -- abandoning check, no immediate benefit");
val = testScore; val = testScore;
} } else {
else {
/*switch (game.getTurn().getStepType()) { /*switch (game.getTurn().getStepType()) {
case PRECOMBAT_MAIN: case PRECOMBAT_MAIN:
val = simulateCombat(game, node, depth-1, alpha, beta, false); val = simulateCombat(game, node, depth-1, alpha, beta, false);
@ -301,32 +291,29 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
}*/ }*/
val = GameStateEvaluator2.evaluate(playerId, game); val = GameStateEvaluator2.evaluate(playerId, game);
} }
} } else {
else {
val = GameStateEvaluator2.evaluate(playerId, game); val = GameStateEvaluator2.evaluate(playerId, game);
/*if (game.getTurn().getStepType() == PhaseStep.DECLARE_ATTACKERS) /*if (game.getTurn().getStepType() == PhaseStep.DECLARE_ATTACKERS)
val = simulateBlockers(game, node, playerId, depth-1, alpha, beta, true); val = simulateBlockers(game, node, playerId, depth-1, alpha, beta, true);
else else
val = GameStateEvaluator2.evaluate(playerId, game); val = GameStateEvaluator2.evaluate(playerId, game);
*/ */
} }
} } else if (node.getChildren().size() > 0) {
else if (node.getChildren().size() > 0) {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
StringBuilder sb = new StringBuilder("Add Action [").append(depth) StringBuilder sb = new StringBuilder("Add Action [").append(depth)
.append("] -- trigger ") .append("] -- trigger ")
.append(node.getAbilities() != null ? node.getAbilities().toString():"null") .append(node.getAbilities() != null ? node.getAbilities().toString() : "null")
.append(" added children: ").append(node.getChildren().size()).append(" ("); .append(" added children: ").append(node.getChildren().size()).append(" (");
for (SimulationNode2 logNode: node.getChildren()) { for (SimulationNode2 logNode : node.getChildren()) {
sb.append(logNode.getAbilities() != null ? logNode.getAbilities().toString():"null").append(", "); sb.append(logNode.getAbilities() != null ? logNode.getAbilities().toString() : "null").append(", ");
} }
sb.append(")"); sb.append(")");
logger.debug(sb); logger.debug(sb);
} }
val = minimaxAB(node, depth, alpha, beta); val = minimaxAB(node, depth, alpha, beta);
} } else {
else {
val = simulatePriority(node, game, depth, alpha, beta); val = simulatePriority(node, game, depth, alpha, beta);
} }
} }
@ -353,25 +340,20 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARING_ATTACKERS, game.getActivePlayerId(), 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); val = simulateAttackers(game, node, game.getActivePlayerId(), depth, alpha, beta, counter);
} }
} } else if (!counter) {
else if (!counter) {
simulateToEnd(game); simulateToEnd(game);
val = simulatePostCombatMain(game, node, depth, alpha, beta); val = simulatePostCombatMain(game, node, depth, alpha, beta);
} }
} }
} } else if (!game.getStep().skipStep(game, game.getActivePlayerId())) {
else { game.fireEvent(new GameEvent(GameEvent.EventType.DECLARE_BLOCKERS_STEP_PRE, null, null, game.getActivePlayerId()));
if (!game.getStep().skipStep(game, game.getActivePlayerId())) { if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARING_BLOCKERS, game.getActivePlayerId(), game.getActivePlayerId()))) {
game.fireEvent(new GameEvent(GameEvent.EventType.DECLARE_BLOCKERS_STEP_PRE, null, null, game.getActivePlayerId())); //only suitable for two player games - only simulates blocks for 1st defender
if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARING_BLOCKERS, game.getActivePlayerId(), game.getActivePlayerId()))) { val = simulateBlockers(game, node, game.getCombat().getDefenders().iterator().next(), depth, alpha, beta, counter);
//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);
} }
} else if (!counter) {
finishCombat(game);
///val = simulateCounterAttack(game, node, depth, alpha, beta);
} }
if (val == null) { if (val == null) {
val = GameStateEvaluator2.evaluate(playerId, game); val = GameStateEvaluator2.evaluate(playerId, game);
@ -382,7 +364,6 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
return val; return val;
} }
protected int simulateAttackers(Game game, SimulationNode2 node, UUID attackerId, int depth, int alpha, int beta, boolean counter) { protected int simulateAttackers(Game game, SimulationNode2 node, UUID attackerId, int depth, int alpha, int beta, boolean counter) {
if (ALLOW_INTERRUPT && Thread.interrupted()) { if (ALLOW_INTERRUPT && Thread.interrupted()) {
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
@ -396,7 +377,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug(attacker.getName() + "'s possible attackers: " + attacker.getAvailableAttackers(defenderId, game)); logger.debug(attacker.getName() + "'s possible attackers: " + attacker.getAvailableAttackers(defenderId, game));
} }
for (Combat engagement: attacker.addAttackers(game)) { for (Combat engagement : attacker.addAttackers(game)) {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("Sim Attackers: " + engagement.getAttackers() + ", blockers: " + engagement.getBlockers()); logger.debug("Sim Attackers: " + engagement.getAttackers() + ", blockers: " + engagement.getBlockers());
} }
@ -404,9 +385,9 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
logger.debug("Sim Attackers -- pruning attackers"); logger.debug("Sim Attackers -- pruning attackers");
break; break;
} }
Game sim = game.copy(); Game sim = game.copy();
for (CombatGroup group: engagement.getGroups()) { for (CombatGroup group : engagement.getGroups()) {
for (UUID attackId: group.getAttackers()) { for (UUID attackId : group.getAttackers()) {
sim.getPlayer(attackerId).declareAttacker(attackId, defenderId, sim, false); sim.getPlayer(attackerId).declareAttacker(attackId, defenderId, sim, false);
} }
} }
@ -424,7 +405,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
sim.fireEvent(GameEvent.getEvent(GameEvent.EventType.DECLARE_ATTACKERS_STEP_POST, sim.getActivePlayerId(), sim.getActivePlayerId())); sim.fireEvent(GameEvent.getEvent(GameEvent.EventType.DECLARE_ATTACKERS_STEP_POST, sim.getActivePlayerId(), sim.getActivePlayerId()));
Combat simCombat = sim.getCombat().copy(); Combat simCombat = sim.getCombat().copy();
sim.getPhase().setStep(new DeclareBlockersStep()); sim.getPhase().setStep(new DeclareBlockersStep());
val = simulateCombat(sim, newNode, depth-1, alpha, beta, counter); val = simulateCombat(sim, newNode, depth - 1, alpha, beta, counter);
if (!attackerId.equals(playerId)) { if (!attackerId.equals(playerId)) {
if (val < beta) { if (val < beta) {
beta = val; beta = val;
@ -434,15 +415,12 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
bestNode.setCombat(newNode.getChildren().get(0).getCombat()); bestNode.setCombat(newNode.getChildren().get(0).getCombat());
} }
} }
} } else if (val > alpha) {
else { alpha = val;
if (val > alpha) { bestNode = newNode;
alpha = val; bestNode.setScore(val);
bestNode = newNode; if (newNode.getChildren().size() > 0) {
bestNode.setScore(val); bestNode.setCombat(newNode.getChildren().get(0).getCombat());
if (newNode.getChildren().size() > 0) {
bestNode.setCombat(newNode.getChildren().get(0).getCombat());
}
} }
} }
} }
@ -475,16 +453,16 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
logger.debug(defender.getName() + "'s possible blockers: " + defender.getAvailableBlockers(game)); logger.debug(defender.getName() + "'s possible blockers: " + defender.getAvailableBlockers(game));
} }
List<Combat> combats = defender.addBlockers(game); List<Combat> combats = defender.addBlockers(game);
for (Combat engagement: combats) { for (Combat engagement : combats) {
if (alpha >= beta) { if (alpha >= beta) {
logger.debug("Sim blockers -- pruning blockers"); logger.debug("Sim blockers -- pruning blockers");
break; break;
} }
Game sim = game.copy(); Game sim = game.copy();
for (CombatGroup group: engagement.getGroups()) { for (CombatGroup group : engagement.getGroups()) {
if (group.getAttackers().size() > 0) { if (group.getAttackers().size() > 0) {
UUID attackerId = group.getAttackers().get(0); UUID attackerId = group.getAttackers().get(0);
for (UUID blockerId: group.getBlockers()) { for (UUID blockerId : group.getBlockers()) {
sim.getPlayer(defenderId).declareBlocker(defenderId, blockerId, attackerId, sim); sim.getPlayer(defenderId).declareBlocker(defenderId, blockerId, attackerId, sim);
} }
} }
@ -505,11 +483,9 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
finishCombat(sim); finishCombat(sim);
if (sim.gameOver(null)) { if (sim.gameOver(null)) {
val = GameStateEvaluator2.evaluate(playerId, sim); val = GameStateEvaluator2.evaluate(playerId, sim);
} } else if (!counter) {
else if (!counter) { val = simulatePostCombatMain(sim, newNode, depth - 1, alpha, beta);
val = simulatePostCombatMain(sim, newNode, depth-1, alpha, beta); } else {
}
else {
val = GameStateEvaluator2.evaluate(playerId, sim); val = GameStateEvaluator2.evaluate(playerId, sim);
} }
if (!defenderId.equals(playerId)) { if (!defenderId.equals(playerId)) {
@ -519,14 +495,11 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
bestNode.setScore(val); bestNode.setScore(val);
bestNode.setCombat(simCombat); bestNode.setCombat(simCombat);
} }
} } else if (val > alpha) {
else { alpha = val;
if (val > alpha) { bestNode = newNode;
alpha = val; bestNode.setScore(val);
bestNode = newNode; bestNode.setCombat(simCombat);
bestNode.setScore(val);
bestNode.setCombat(simCombat);
}
} }
} }
} }
@ -570,7 +543,6 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
val = GameStateEvaluator2.evaluate(playerId, game); val = GameStateEvaluator2.evaluate(playerId, game);
return val; return val;
}*/ }*/
protected void simulateStep(Game game, Step step) { protected void simulateStep(Game game, Step step) {
if (ALLOW_INTERRUPT && Thread.interrupted()) { if (ALLOW_INTERRUPT && Thread.interrupted()) {
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();

View file

@ -65,9 +65,14 @@ import mage.constants.ManaType;
import mage.constants.Outcome; import mage.constants.Outcome;
import mage.constants.PhaseStep; import mage.constants.PhaseStep;
import mage.constants.PlayerAction; import mage.constants.PlayerAction;
import static mage.constants.PlayerAction.HOLD_PRIORITY;
import static mage.constants.PlayerAction.REQUEST_AUTO_ANSWER_ID_NO;
import static mage.constants.PlayerAction.REQUEST_AUTO_ANSWER_RESET_ALL; import static mage.constants.PlayerAction.REQUEST_AUTO_ANSWER_RESET_ALL;
import static mage.constants.PlayerAction.TRIGGER_AUTO_ORDER_NAME_LAST;
import static mage.constants.PlayerAction.TRIGGER_AUTO_ORDER_RESET_ALL; import static mage.constants.PlayerAction.TRIGGER_AUTO_ORDER_RESET_ALL;
import mage.constants.RangeOfInfluence; import mage.constants.RangeOfInfluence;
import static mage.constants.SpellAbilityType.SPLIT;
import static mage.constants.SpellAbilityType.SPLIT_FUSED;
import mage.constants.Zone; import mage.constants.Zone;
import mage.filter.common.FilterAttackingCreature; import mage.filter.common.FilterAttackingCreature;
import mage.filter.common.FilterBlockingCreature; import mage.filter.common.FilterBlockingCreature;

View file

@ -67,7 +67,7 @@ public class BloodthirstTest extends CardTestPlayerBase {
public void testBloodlord() { public void testBloodlord() {
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 8); addCard(Zone.BATTLEFIELD, playerA, "Swamp", 8);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
// Bloodthirst 3 // Bloodthirst 3 (If an opponent was dealt damage this turn, this creature enters the battlefield with 3 +1/+1 counters)
// Whenever you cast a Vampire creature spell, it gains bloodthirst 3 // Whenever you cast a Vampire creature spell, it gains bloodthirst 3
addCard(Zone.HAND, playerA, "Bloodlord of Vaasgoth"); // {3}{B}{B} addCard(Zone.HAND, playerA, "Bloodlord of Vaasgoth"); // {3}{B}{B}
addCard(Zone.HAND, playerA, "Barony Vampire"); // 3/2 {2}{B} addCard(Zone.HAND, playerA, "Barony Vampire"); // 3/2 {2}{B}

View file

@ -90,10 +90,73 @@ public class ErsatzGnomesTest extends CardTestPlayerBase {
execute(); execute();
assertGraveyardCount(playerB, "Unsummon", 1); assertGraveyardCount(playerB, "Unsummon", 1);
assertHandCount(playerA, "Silvercoat Lion", 0);
assertPermanentCount(playerA, "Silvercoat Lion", 1); assertPermanentCount(playerA, "Silvercoat Lion", 1);
assertTapped("Ersatz Gnomes", true); assertTapped("Ersatz Gnomes", true);
Permanent lion = getPermanent("Silvercoat Lion", playerA); Permanent lion = getPermanent("Silvercoat Lion", playerA);
Assert.assertTrue("Silvercoat lion has to be white", lion.getColor(currentGame).isWhite()); Assert.assertTrue("Silvercoat lion has to be white", lion.getColor(currentGame).isWhite());
} }
@Test
public void testChangeColorOfBestowSpell() {
addCard(Zone.BATTLEFIELD, playerA, "Plains", 4);
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion");
// Bestow {3}{W}
// Lifelink
// Echanted creature gets +1/+1 and has lifelink.
addCard(Zone.HAND, playerA, "Hopeful Eidolon");// Creature {W}
// {T}: Target spell becomes colorless.
// {T}: Target permanent becomes colorless until end of turn.
addCard(Zone.BATTLEFIELD, playerA, "Ersatz Gnomes");
addCard(Zone.BATTLEFIELD, playerB, "Island", 1);
addCard(Zone.HAND, playerB, "Unsummon");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Hopeful Eidolon using bestow", "Silvercoat Lion");
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target spell", "Hopeful Eidolon");
setStopAt(1, PhaseStep.END_TURN);
execute();
assertHandCount(playerA, "Hopeful Eidolon", 0);
assertPermanentCount(playerA, "Hopeful Eidolon", 1);
assertPowerToughness(playerA, "Silvercoat Lion", 3, 3);
assertTapped("Ersatz Gnomes", true);
Permanent eidolon = getPermanent("Hopeful Eidolon", playerA);
Assert.assertTrue("Hopeful Eidolon Enchantment has to be colorless", eidolon.getColor(currentGame).isColorless());
}
@Test
public void testChangeColorOfBestowSpellUnsummon() {
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
// Bestow {3}{W}
// Lifelink
// Echanted creature gets +1/+1 and has lifelink.
addCard(Zone.HAND, playerA, "Hopeful Eidolon");// Creature {W}
// {T}: Target spell becomes colorless.
// {T}: Target permanent becomes colorless until end of turn.
addCard(Zone.BATTLEFIELD, playerA, "Ersatz Gnomes");
addCard(Zone.BATTLEFIELD, playerB, "Island", 1);
addCard(Zone.HAND, playerB, "Unsummon");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Hopeful Eidolon");
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target spell", "Hopeful Eidolon");
castSpell(1, PhaseStep.END_COMBAT, playerB, "Unsummon", "Hopeful Eidolon");
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Hopeful Eidolon");
setStopAt(1, PhaseStep.END_TURN);
execute();
assertGraveyardCount(playerB, "Unsummon", 1);
assertHandCount(playerA, "Hopeful Eidolon", 0);
assertPermanentCount(playerA, "Hopeful Eidolon", 1);
assertTapped("Ersatz Gnomes", true);
Permanent lion = getPermanent("Hopeful Eidolon", playerA);
Assert.assertTrue("Hopeful Eidolon has to be white", lion.getColor(currentGame).isWhite());
}
} }

View file

@ -1,5 +1,13 @@
package mage.verify; package mage.verify;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Paths;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import mage.ObjectColor; import mage.ObjectColor;
import mage.cards.Card; import mage.cards.Card;
import mage.cards.CardImpl; import mage.cards.CardImpl;
@ -12,15 +20,6 @@ import mage.constants.CardType;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Paths;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class VerifyCardDataTest { public class VerifyCardDataTest {
// right now this is very noisy, and not useful enough to make any assertions on // right now this is very noisy, and not useful enough to make any assertions on
@ -105,8 +104,7 @@ public class VerifyCardDataTest {
if (!(token.equals(card.getName()) if (!(token.equals(card.getName())
|| containsInTypesOrText(ref, token) || containsInTypesOrText(ref, token)
|| containsInTypesOrText(ref, token.toLowerCase()) || containsInTypesOrText(ref, token.toLowerCase())
|| (ref2 != null && (containsInTypesOrText(ref2, token) || containsInTypesOrText(ref2, token.toLowerCase()))) || (ref2 != null && (containsInTypesOrText(ref2, token) || containsInTypesOrText(ref2, token.toLowerCase()))))) {
)) {
System.out.println("unexpected token " + token + " in " + card); System.out.println("unexpected token " + token + " in " + card);
} }
} }
@ -143,12 +141,12 @@ public class VerifyCardDataTest {
if (expected == null) { if (expected == null) {
expected = Collections.emptyList(); expected = Collections.emptyList();
} }
if (expected.size() != color.getColorCount() || if (expected.size() != color.getColorCount()
(color.isBlack() && !expected.contains("Black")) || || (color.isBlack() && !expected.contains("Black"))
(color.isBlue() && !expected.contains("Blue")) || || (color.isBlue() && !expected.contains("Blue"))
(color.isGreen() && !expected.contains("Green")) || || (color.isGreen() && !expected.contains("Green"))
(color.isRed() && !expected.contains("Red")) || || (color.isRed() && !expected.contains("Red"))
(color.isWhite() && !expected.contains("White"))) { || (color.isWhite() && !expected.contains("White"))) {
fail(card, "colors", color + " != " + expected); fail(card, "colors", color + " != " + expected);
} }
} }

View file

@ -208,6 +208,8 @@ public class ZonesHandler {
// If we can't find the card we can't remove it. // If we can't find the card we can't remove it.
return false; return false;
} }
// If needed take attributes from the spell (e.g. color of spell was changed)
card = takeAttributesFromSpell(card, event, game);
boolean success = false; boolean success = false;
if (info.faceDown) { if (info.faceDown) {
card.setFaceDown(true, game); card.setFaceDown(true, game);
@ -282,4 +284,23 @@ public class ZonesHandler {
order.add(cards.getCards(game).iterator().next()); order.add(cards.getCards(game).iterator().next());
return order; return order;
} }
private static Card takeAttributesFromSpell(Card card, ZoneChangeEvent event, Game game) {
if (Zone.STACK.equals(event.getFromZone())) {
Spell spell = game.getStack().getSpell(event.getTargetId());
if (spell != null) {
boolean doCopy = false;
if (!card.getColor(game).equals(spell.getColor(game))) {
doCopy = true;
}
if (doCopy) {
// the card that is referenced to in the permanent is copied and the spell attributes are set to this copied card
card = card.copy();
card.getColor(game).setColor(spell.getColor(game));
}
}
}
return card;
}
} }

View file

@ -132,7 +132,7 @@ public class PermanentCard extends PermanentImpl {
transformable = card.isTransformable(); transformable = card.isTransformable();
if (transformable) { if (transformable) {
this.nightCard = card.isNightCard(); this.nightCard = card.isNightCard();
if (! this.nightCard) { if (!this.nightCard) {
this.secondSideCard = card.getSecondCardFace(); this.secondSideCard = card.getSecondCardFace();
this.secondSideCardClazz = this.secondSideCard.getClass(); this.secondSideCardClazz = this.secondSideCard.getClass();
} }

View file

@ -27,15 +27,9 @@
*/ */
package mage.game.permanent; package mage.game.permanent;
import java.util.ArrayList;
import java.util.UUID; import java.util.UUID;
import mage.cards.Card; import mage.cards.Card;
import mage.cards.CardsImpl;
import mage.cards.MeldCard;
import mage.constants.Zone;
import mage.game.Game; import mage.game.Game;
import mage.game.events.ZoneChangeEvent;
import mage.players.Player;
/** /**
* *

View file

@ -277,9 +277,6 @@ public class Spell extends StackObjImpl implements Card {
} }
} else { } else {
updateOptionalCosts(0); updateOptionalCosts(0);
if (!getColor(game).equals(card.getColor(game))) { // if spell color was changed, the created permanent needs to be of that color
game.getState().getCreateCardAttribute(card).getColor().setColor(getColor(game));
}
return controller.moveCards(card, Zone.BATTLEFIELD, ability, game, false, faceDown, false, null); return controller.moveCards(card, Zone.BATTLEFIELD, ability, game, false, faceDown, false, null);
} }
} }
@ -902,7 +899,7 @@ public class Spell extends StackObjImpl implements Card {
@Override @Override
public void checkForCountersToAdd(Permanent permanent, Game game) { public void checkForCountersToAdd(Permanent permanent, Game game) {
throw new UnsupportedOperationException("Not supported for Spell"); card.checkForCountersToAdd(permanent, game);
} }
@Override @Override

View file

@ -1,16 +1,16 @@
/* /*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without modification, are * Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met: * permitted provided that the following conditions are met:
* *
* 1. Redistributions of source code must retain the above copyright notice, this list of * 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer. * conditions and the following disclaimer.
* *
* 2. Redistributions in binary form must reproduce the above copyright notice, this list * 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 * of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution. * provided with the distribution.
* *
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED * 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 * 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 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
@ -20,20 +20,18 @@
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * 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 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
* The views and conclusions contained in the software and documentation are those of the * 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 * authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com. * or implied, of BetaSteward_at_googlemail.com.
*/ */
package mage.game.turn; package mage.game.turn;
import mage.constants.PhaseStep;
import mage.constants.TurnPhase;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.ListIterator; import java.util.ListIterator;
import java.util.UUID; import java.util.UUID;
import mage.constants.PhaseStep;
import mage.constants.TurnPhase;
/** /**
* *
@ -41,10 +39,11 @@ import java.util.UUID;
*/ */
public class TurnMods extends ArrayList<TurnMod> { public class TurnMods extends ArrayList<TurnMod> {
public TurnMods() {} public TurnMods() {
}
public TurnMods(final TurnMods mods) { public TurnMods(final TurnMods mods) {
for (TurnMod mod: mods) { for (TurnMod mod : mods) {
this.add(mod.copy()); this.add(mod.copy());
} }
} }
@ -95,7 +94,7 @@ public class TurnMods extends ArrayList<TurnMod> {
it.remove(); it.remove();
} }
} }
// now delete all other - control next turn effect is not cumulative // now delete all other effects that control current active player - control next turn of player effects are not cumulative
it = this.listIterator(this.size()); it = this.listIterator(this.size());
while (it.hasPrevious()) { while (it.hasPrevious()) {
TurnMod turnMod = it.previous(); TurnMod turnMod = it.previous();