/*
* 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 org.mage.test.player;
import java.io.Serializable;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import mage.MageException;
import mage.MageObject;
import mage.MageObjectReference;
import mage.abilities.Abilities;
import mage.abilities.Ability;
import mage.abilities.ActivatedAbility;
import mage.abilities.Mode;
import mage.abilities.Modes;
import mage.abilities.SpellAbility;
import mage.abilities.TriggeredAbility;
import mage.abilities.costs.AlternativeSourceCosts;
import mage.abilities.costs.Cost;
import mage.abilities.costs.Costs;
import mage.abilities.costs.VariableCost;
import mage.abilities.costs.mana.ManaCost;
import mage.abilities.costs.mana.ManaCosts;
import mage.abilities.mana.ActivatedManaAbilityImpl;
import mage.abilities.mana.ManaOptions;
import mage.cards.Card;
import mage.cards.Cards;
import mage.cards.decks.Deck;
import mage.choices.Choice;
import mage.constants.AbilityType;
import mage.constants.ManaType;
import mage.constants.Outcome;
import mage.constants.PhaseStep;
import mage.constants.RangeOfInfluence;
import mage.constants.SpellAbilityType;
import mage.constants.Zone;
import mage.counters.Counter;
import mage.counters.Counters;
import mage.filter.Filter;
import mage.filter.FilterPermanent;
import mage.filter.common.*;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.NamePredicate;
import mage.filter.predicate.permanent.AttackingPredicate;
import mage.filter.predicate.permanent.SummoningSicknessPredicate;
import mage.game.Game;
import mage.game.Graveyard;
import mage.game.Table;
import mage.game.combat.CombatGroup;
import mage.game.draft.Draft;
import mage.game.match.Match;
import mage.game.match.MatchPlayer;
import mage.game.permanent.Permanent;
import mage.game.stack.StackObject;
import mage.game.tournament.Tournament;
import mage.player.ai.ComputerPlayer;
import mage.players.Library;
import mage.players.ManaPool;
import mage.players.Player;
import mage.players.net.UserData;
import mage.target.Target;
import mage.target.TargetAmount;
import mage.target.TargetCard;
import mage.target.TargetPermanent;
import mage.target.TargetPlayer;
import mage.target.TargetSource;
import mage.target.TargetSpell;
import mage.target.common.TargetCardInHand;
import mage.target.common.TargetCardInLibrary;
import mage.target.common.TargetCardInOpponentsGraveyard;
import mage.target.common.TargetCardInYourGraveyard;
import mage.target.common.TargetCreatureOrPlayer;
import mage.target.common.TargetCreaturePermanentAmount;
import mage.target.common.TargetPermanentOrPlayer;
import org.junit.Ignore;
/**
* @author BetaSteward_at_googlemail.com
* @author Simown
*/
@Ignore
public class TestPlayer implements Player {
private int maxCallsWithoutAction = 100;
private int foundNoAction = 0;
private boolean AIPlayer;
private final List<PlayerAction> actions = new ArrayList<>();
private final List<String> choices = new ArrayList<>();
private final List<String> targets = new ArrayList<>();
private final List<String> modesSet = new ArrayList<>();
private final ComputerPlayer computerPlayer;
private String[] groupsForTargetHandling = null;
// Tracks the initial turns (turn 0s) both players are given at the start of the game.
// Before actual turns start. Needed for checking attacker/blocker legality in the tests
private static int initialTurns = 0;
public TestPlayer(ComputerPlayer computerPlayer) {
this.computerPlayer = computerPlayer;
AIPlayer = false;
}
public TestPlayer(final TestPlayer testPlayer) {
this.AIPlayer = testPlayer.AIPlayer;
this.foundNoAction = testPlayer.foundNoAction;
this.actions.addAll(testPlayer.actions);
this.choices.addAll(testPlayer.choices);
this.targets.addAll(testPlayer.targets);
this.modesSet.addAll(testPlayer.modesSet);
this.computerPlayer = testPlayer.computerPlayer.copy();
if (testPlayer.groupsForTargetHandling != null) {
this.groupsForTargetHandling = testPlayer.groupsForTargetHandling.clone();
}
}
public void addChoice(String choice) {
choices.add(choice);
}
public void addModeChoice(String mode) {
modesSet.add(mode);
}
public void addTarget(String target) {
targets.add(target);
}
public ManaOptions getAvailableManaTest(Game game) {
return computerPlayer.getManaAvailable(game);
}
public void addAction(int turnNum, PhaseStep step, String action) {
actions.add(new PlayerAction(turnNum, step, action));
}
public List<PlayerAction> getActions() {
return actions;
}
/**
* @param maxCallsWithoutAction max number of priority passes a player may
* have for this test (default = 100)
*/
public void setMaxCallsWithoutAction(int maxCallsWithoutAction) {
this.maxCallsWithoutAction = maxCallsWithoutAction;
}
public void setInitialTurns(int turns) {
initialTurns = turns;
}
private Permanent findPermanent(FilterPermanent filter, String name, UUID controllerID, Game game) {
return findPermanent(filter, name, controllerID, game, true);
}
/**
* Finds a permanent based on a general filter an their name and possible index.
*
* An index is permitted after the permanent's name to denote their index on the battlefield
* Either use name="<permanent>" which will get the first permanent with that name on the battlefield
* that meets the filter criteria or name="<permanent>:<index>" to get the named permanent with that index on
* the battlefield.
*
* Permanents are zero indexed in the order they entered the battlefield for each controller:
*
* findPermanent(new AttackingCreatureFilter(), "Human", <controllerID>, <game>)
* Will find the first "Human" creature that entered the battlefield under this controller and is attacking.
*
* findPermanent(new FilterControllerPermanent(), "Fabled Hero:3", <controllerID>, <game>)
* Will find the 4th permanent named "Fabled Hero" that entered the battlefield under this controller
*
* An exception will be thrown if no permanents match the criteria or the index is larger than the number
* of permanents found with that name.
*
* failOnNotFound boolean controls if this function returns null for a permanent not found on the battlefield. Currently
* used only as a workaround for attackers in selectAttackers() being able to attack multiple times each combat. See issue #3038
*/
private Permanent findPermanent(FilterPermanent filter, String name, UUID controllerID, Game game, boolean failOnNotFound) {
String filteredName = name;
Pattern indexedName = Pattern.compile("^([\\w| ]+):(\\d+)$"); // Ends with <:number>
Matcher indexedMatcher = indexedName.matcher(filteredName);
int index = 0;
if (indexedMatcher.matches()) {
filteredName = indexedMatcher.group(1);
index = Integer.valueOf(indexedMatcher.group(2));
}
filter.add(new NamePredicate(filteredName));
List<Permanent> allPermanents = game.getBattlefield().getAllActivePermanents(filter, controllerID, game);
if (allPermanents.isEmpty()) {
if (failOnNotFound)
throw new AssertionError("No permanents found called " + filteredName + " that match the filter criteria \"" + filter.getMessage() + "\"");
return null;
} else if (allPermanents.size() - 1 < index) {
if (failOnNotFound)
throw new AssertionError("Cannot find " + filteredName + ":" + index + " that match the filter criteria \"" + filter.getMessage() + "\"" + ".\nOnly " + allPermanents.size() + " called " + filteredName + " found for this controller(zero indexed).");
return null;
}
return allPermanents.get(index);
}
private boolean checkExecuteCondition(String[] groups, Game game) {
if (groups[2].startsWith("spellOnStack=")) {
String spellOnStack = groups[2].substring(13);
for (StackObject stackObject : game.getStack()) {
if (stackObject.getStackAbility().toString().contains(spellOnStack)) {
return true;
}
}
return false;
} else if (groups[2].startsWith("spellCopyOnStack=")) {
String spellOnStack = groups[2].substring(17);
for (StackObject stackObject : game.getStack()) {
if (stackObject.getStackAbility().toString().contains(spellOnStack)) {
if (stackObject.isCopy()) {
return true;
}
}
}
return false;
} else if (groups[2].startsWith("!spellOnStack=")) {
String spellNotOnStack = groups[2].substring(14);
for (StackObject stackObject : game.getStack()) {
if (stackObject.getStackAbility().toString().contains(spellNotOnStack)) {
return false;
}
}
return true;
} else if (groups[2].startsWith("spellOnTopOfStack=")) {
String spellOnTopOFStack = groups[2].substring(18);
if (!game.getStack().isEmpty()) {
StackObject stackObject = game.getStack().getFirst();
if (stackObject != null && stackObject.getStackAbility().toString().contains(spellOnTopOFStack)) {
return true;
}
}
return false;
} else if (groups[2].startsWith("manaInPool=")) {
String manaInPool = groups[2].substring(11);
int amountOfMana = Integer.parseInt(manaInPool);
return computerPlayer.getManaPool().getMana().count() >= amountOfMana;
}
return true;
}
@Override
public boolean addTargets(Ability ability, Game game) {
if (groupsForTargetHandling == null) {
return true;
}
boolean result = true;
for (int i = 1; i < groupsForTargetHandling.length; i++) {
String group = groupsForTargetHandling[i];
if (group.startsWith("spell") || group.startsWith("!spell") || group.startsWith("target=null") || group.startsWith("manaInPool=")) {
break;
}
if (ability instanceof SpellAbility && ((SpellAbility) ability).getSpellAbilityType() == SpellAbilityType.SPLIT_FUSED) {
if (group.contains("FuseLeft-")) {
result = handleTargetString(group.substring(group.indexOf("FuseLeft-") + 9), ability, game);
} else if (group.startsWith("FuseRight-")) {
result = handleTargetString(group.substring(group.indexOf("FuseRight-") + 10), ability, game);
} else {
result = false;
}
} else {
result = handleTargetString(group, ability, game);
}
}
return result;
}
private boolean handleTargetString(String target, Ability ability, Game game) {
boolean result = false;
if (target.startsWith("targetPlayer=")) {
result = handlePlayerTarget(target.substring(target.indexOf("targetPlayer=") + 13), ability, game);
} else if (target.startsWith("target=")) {
result = handleNonPlayerTargetTarget(target.substring(target.indexOf("target=") + 7), ability, game);
}
return result;
}
private boolean handlePlayerTarget(String target, Ability ability, Game game) {
boolean result = true;
int targetsSet = 0;
for (Player player : game.getPlayers().values()) {
if (player.getName().equals(target)) {
if (ability.getTargets().isEmpty()) {
throw new UnsupportedOperationException("Ability has no targets, but there is a player target set - " + ability.toString());
}
ability.getTargets().get(0).addTarget(player.getId(), ability, game);
targetsSet++;
break;
}
}
if (targetsSet < 1) {
result = false;
}
return result;
}
private boolean handleNonPlayerTargetTarget(String target, Ability ability, Game game) {
boolean result = true;
if (target == null) {
return true; // needed if spell has no target but waits until spell is on the stack
}
String[] targetList = target.split("\\^");
int index = 0;
int targetsSet = 0;
for (String targetName : targetList) {
Mode selectedMode;
if (targetName.startsWith("mode=")) {
int modeNr = Integer.parseInt(targetName.substring(5, 6));
if (modeNr == 0 || modeNr > (ability.getModes().isEachModeMoreThanOnce() ? ability.getModes().getSelectedModes().size() : ability.getModes().size())) {
throw new UnsupportedOperationException("Given mode number (" + modeNr + ") not available for " + ability.toString());
}
UUID modeId = ability.getModes().getModeId(modeNr);
selectedMode = ability.getModes().get(modeId);
if (modeId != ability.getModes().getMode().getId()) {
ability.getModes().setActiveMode(modeId);
index = 0; // reset target index if mode changes
}
targetName = targetName.substring(6);
} else {
selectedMode = ability.getModes().getMode();
}
if (selectedMode == null) {
throw new UnsupportedOperationException("Mode not available for " + ability.toString());
}
if (selectedMode.getTargets().isEmpty()) {
throw new AssertionError("Ability has no targets. " + ability.toString());
}
if (index >= selectedMode.getTargets().size()) {
break; // this can happen if targets should be set but can't be used because of hexproof e.g.
}
Target currentTarget = selectedMode.getTargets().get(index);
if (targetName.startsWith("targetPlayer=")) {
target = targetName.substring(targetName.indexOf("targetPlayer=") + 13);
for (Player player : game.getPlayers().values()) {
if (player.getName().equals(target)) {
currentTarget.addTarget(player.getId(), ability, game);
index++;
targetsSet++;
break;
}
}
} else {
boolean originOnly = false;
boolean copyOnly = false;
if (targetName.endsWith("]")) {
if (targetName.endsWith("[no copy]")) {
originOnly = true;
targetName = targetName.substring(0, targetName.length() - 9);
}
if (targetName.endsWith("[only copy]")) {
copyOnly = true;
targetName = targetName.substring(0, targetName.length() - 11);
}
}
for (UUID id : currentTarget.possibleTargets(ability.getSourceId(), ability.getControllerId(), game)) {
if (!currentTarget.getTargets().contains(id)) {
MageObject object = game.getObject(id);
if (object != null
&& ((object.isCopy() && !originOnly) || (!object.isCopy() && !copyOnly))
&& ((!targetName.isEmpty() && object.getName().startsWith(targetName)) || (targetName.isEmpty() && object.getName().isEmpty()))) {
if (currentTarget.getNumberOfTargets() == 1) {
currentTarget.clearChosen();
}
if (currentTarget instanceof TargetCreaturePermanentAmount) {
// supports only to set the complete amount to one target
TargetCreaturePermanentAmount targetAmount = (TargetCreaturePermanentAmount) currentTarget;
targetAmount.setAmount(ability, game);
int amount = targetAmount.getAmountRemaining();
targetAmount.addTarget(id, amount, ability, game);
targetsSet++;
} else {
currentTarget.addTarget(id, ability, game);
targetsSet++;
}
if (currentTarget.getTargets().size() == currentTarget.getMaxNumberOfTargets()) {
index++;
}
break;
}
}
}
}
}
if (targetsSet != targetList.length) {
result = false;
}
return result;
}
@Override
public int getActionCount() {
return actions.size();
}
@Override
public TestPlayer copy() {
return new TestPlayer(this);
}
@Override
public boolean priority(Game game) {
int numberOfActions = actions.size();
List<PlayerAction> tempActions = new ArrayList<>();
tempActions.addAll(actions);
for (PlayerAction action : tempActions) {
if (action.getTurnNum() == game.getTurnNum() && action.getStep() == game.getStep().getType()) {
if (action.getAction().startsWith("activate:")) {
String command = action.getAction();
command = command.substring(command.indexOf("activate:") + 9);
groupsForTargetHandling = null;
String[] groups = command.split("\\$");
if (groups.length > 2 && !checkExecuteCondition(groups, game)) {
break;
}
for (Ability ability : computerPlayer.getPlayable(game, true)) {
if (ability.toString().startsWith(groups[0])) {
int bookmark = game.bookmarkState();
Ability newAbility = ability.copy();
if (groups.length > 1 && !groups[1].equals("target=NO_TARGET")) {
groupsForTargetHandling = groups;
}
if (computerPlayer.activateAbility((ActivatedAbility) newAbility, game)) {
actions.remove(action);
groupsForTargetHandling = null;
return true;
} else {
game.restoreState(bookmark, ability.getRule());
}
}
}
} else if (action.getAction().startsWith("manaActivate:")) {
String command = action.getAction();
command = command.substring(command.indexOf("manaActivate:") + 13);
String[] groups = command.split("\\$");
List<MageObject> manaObjects = computerPlayer.getAvailableManaProducers(game);
for (MageObject mageObject : manaObjects) {
if (mageObject instanceof Permanent) {
for (Ability manaAbility : ((Permanent) mageObject).getAbilities(game).getAvailableActivatedManaAbilities(Zone.BATTLEFIELD, game)) {
if (manaAbility.toString().startsWith(groups[0])) {
Ability newManaAbility = manaAbility.copy();
computerPlayer.activateAbility((ActivatedAbility) newManaAbility, game);
actions.remove(action);
return true;
}
}
} else if (mageObject instanceof Card) {
for (Ability manaAbility : ((Card) mageObject).getAbilities(game).getAvailableActivatedManaAbilities(game.getState().getZone(mageObject.getId()), game)) {
if (manaAbility.toString().startsWith(groups[0])) {
Ability newManaAbility = manaAbility.copy();
computerPlayer.activateAbility((ActivatedAbility) newManaAbility, game);
actions.remove(action);
return true;
}
}
} else {
for (Ability manaAbility : mageObject.getAbilities().getAvailableActivatedManaAbilities(game.getState().getZone(mageObject.getId()), game)) {
if (manaAbility.toString().startsWith(groups[0])) {
Ability newManaAbility = manaAbility.copy();
computerPlayer.activateAbility((ActivatedAbility) newManaAbility, game);
actions.remove(action);
return true;
}
}
}
}
List<Permanent> manaPermsWithCost = computerPlayer.getAvailableManaProducersWithCost(game);
for (Permanent perm : manaPermsWithCost) {
for (ActivatedManaAbilityImpl manaAbility : perm.getAbilities().getAvailableActivatedManaAbilities(Zone.BATTLEFIELD, game)) {
if (manaAbility.toString().startsWith(groups[0]) && manaAbility.canActivate(computerPlayer.getId(), game)) {
Ability newManaAbility = manaAbility.copy();
computerPlayer.activateAbility((ActivatedAbility) newManaAbility, game);
actions.remove(action);
return true;
}
}
}
} else if (action.getAction().startsWith("addCounters:")) {
String command = action.getAction();
command = command.substring(command.indexOf("addCounters:") + 12);
String[] groups = command.split("\\$");
for (Permanent permanent : game.getBattlefield().getAllActivePermanents()) {
if (permanent.getName().equals(groups[0])) {
Counter counter = new Counter(groups[1], Integer.parseInt(groups[2]));
permanent.addCounters(counter, null, game);
break;
}
}
} else if (action.getAction().startsWith("playerAction:")) {
String command = action.getAction();
command = command.substring(command.indexOf("playerAction:") + 13);
groupsForTargetHandling = null;
String[] groups = command.split("\\$");
if (groups.length > 0) {
if (groups[0].equals("Rollback")) {
if (groups.length > 1 && groups[1].startsWith("turns=")) {
int turns = Integer.parseInt(groups[1].substring(6));
game.rollbackTurns(turns);
actions.remove(action);
return true;
}
}
}
}
}
}
if (AIPlayer) {
computerPlayer.priority(game);
} else {
computerPlayer.pass(game);
}
// check to prevent endless loops
if (numberOfActions == actions.size()) {
foundNoAction++;
if (foundNoAction > maxCallsWithoutAction) {
throw new AssertionError("More priority calls to " + getName() + " and doing no action than allowed (" + maxCallsWithoutAction + ')');
}
} else {
foundNoAction = 0;
}
return false;
}
/*
* Iterates through each player on the current turn and asserts if they can attack or block legally this turn
*/
private void checkLegalMovesThisTurn(Game game) {
// Each player is given priority before actual turns start for e.g. leylines and pre-game initialisation
if(initialTurns < game.getPlayers().size()) {
initialTurns++;
return;
}
// Check actions for next turn are going to be valid
int turnNum = game.getTurnNum();
// Loop through all game players and check if they are allowed to attack/block this turn
for (UUID playerID : game.getPlayers().keySet()) {
Player player = game.getPlayer(playerID);
// Has to be a TestPlayer to get a list of actions
if (player instanceof TestPlayer) {
// Check each player trying to attack or block on this turn
for (PlayerAction playerAction : ((TestPlayer) player).getActions()) {
String action = playerAction.getAction();
boolean currentPlayersTurn = playerID.equals(getId());
String playerName = player.getName();
int actionTurnNum = playerAction.getTurnNum();
// If the action is performed on this turn...
if (turnNum == actionTurnNum) {
// Attacking and it's not their turn is illegal
if (action.startsWith("attack:") && !currentPlayersTurn) {
throw new UnsupportedOperationException(playerName + " can't attack on turn " + turnNum + " as it is not their turn");
}
// Blocking and it is their turn is illegal
if (action.startsWith("block:") && currentPlayersTurn) {
throw new UnsupportedOperationException(playerName + " can't block on turn " + turnNum + " as it is their turn");
}
}
}
}
}
}
@Override
public void selectAttackers(Game game, UUID attackingPlayerId) {
// Loop through players and validate can attack/block this turn
UUID defenderId = null;
for (PlayerAction action : actions) {
if (action.getTurnNum() == game.getTurnNum() && action.getAction().startsWith("attack:")) {
String command = action.getAction();
command = command.substring(command.indexOf("attack:") + 7);
String[] groups = command.split("\\$");
for (int i = 1; i < groups.length; i++) {
String group = groups[i];
if (group.startsWith("planeswalker=")) {
String planeswalkerName = group.substring(group.indexOf("planeswalker=") + 13);
for (Permanent permanent : game.getBattlefield().getAllActivePermanents(new FilterPlaneswalkerPermanent(), game)) {
if (permanent.getName().equals(planeswalkerName)) {
defenderId = permanent.getId();
}
}
}
if (group.startsWith("defendingPlayer=")) {
String defendingPlayerName = group.substring(group.indexOf("defendingPlayer=") + 16);
for (Player defendingPlayer : game.getPlayers().values()) {
if (defendingPlayer.getName().equals(defendingPlayerName)) {
defenderId = defendingPlayer.getId();
break;
}
}
}
}
if (defenderId == null) {
for (UUID uuid : game.getCombat().getDefenders()) {
Player defender = game.getPlayer(uuid);
if (defender != null) {
defenderId = uuid;
}
}
}
// First check to see if this controller actually owns the creature
FilterControlledPermanent firstFilter = new FilterControlledPermanent();
findPermanent(firstFilter, groups[0], computerPlayer.getId(), game);
// Second check to filter creature for combat - less strict to workaround issue in #3038
FilterCreatureForCombat secondFilter = new FilterCreatureForCombat();
secondFilter.add(Predicates.not(new AttackingPredicate()));
secondFilter.add(Predicates.not(new SummoningSicknessPredicate()));
// TODO: Cannot enforce legal attackers multiple times per combat. See issue #3038
Permanent attacker = findPermanent(secondFilter, groups[0], computerPlayer.getId(), game, false);
if (attacker != null && attacker.canAttack(defenderId, game)) {
computerPlayer.declareAttacker(attacker.getId(), defenderId, game, false);
}
}
}
}
@Override
public void selectBlockers(Game game, UUID defendingPlayerId) {
UUID opponentId = game.getOpponents(computerPlayer.getId()).iterator().next();
// Map of Blocker reference -> list of creatures blocked
Map<MageObjectReference, List<MageObjectReference>> blockedCreaturesByCreature = new HashMap<>();
for (PlayerAction action : actions) {
if (action.getTurnNum() == game.getTurnNum() && action.getAction().startsWith("block:")) {
String command = action.getAction();
command = command.substring(command.indexOf("block:") + 6);
String[] groups = command.split("\\$");
String blockerName = groups[0];
String attackerName = groups[1];
Permanent attacker = findPermanent(new FilterAttackingCreature(), attackerName, opponentId, game);
Permanent blocker = findPermanent(new FilterControlledPermanent(), blockerName, computerPlayer.getId(), game);
if (canBlockAnother(game, blocker, attacker, blockedCreaturesByCreature)) {
computerPlayer.declareBlocker(defendingPlayerId, blocker.getId(), attacker.getId(), game);
} else {
throw new UnsupportedOperationException(blockerName + " cannot block " + attackerName + " it is already blocking the maximum amount of creatures.");
}
}
}
checkMultipleBlockers(game, blockedCreaturesByCreature);
}
// Checks if a creature can block at least one more creature
private boolean canBlockAnother(Game game, Permanent blocker, Permanent attacker, Map<MageObjectReference, List<MageObjectReference>> blockedCreaturesByCreature) {
MageObjectReference blockerRef = new MageObjectReference(blocker, game);
// See if we already reference this blocker
for (MageObjectReference r : blockedCreaturesByCreature.keySet()) {
if (r.equals(blockerRef)) {
// Use the existing reference if we do
blockerRef = r;
}
}
List<MageObjectReference> blocked = blockedCreaturesByCreature.getOrDefault(blockerRef, new ArrayList<>());
int numBlocked = blocked.size();
// Can't block any more creatures
if (++numBlocked > blocker.getMaxBlocks()) {
return false;
}
// Add the attacker reference to the list of creatures this creature is blocking
blocked.add(new MageObjectReference(attacker, game));
blockedCreaturesByCreature.put(blockerRef, blocked);
return true;
}
// Check for Menace type abilities - if creatures can be blocked by >X or <Y only
private void checkMultipleBlockers(Game game, Map<MageObjectReference, List<MageObjectReference>> blockedCreaturesByCreature) {
// Stores the total number of blockers for each attacker
Map<MageObjectReference, Integer> blockersForAttacker = new HashMap<>();
// Calculate the number of blockers each attacker has
for (List<MageObjectReference> attackers : blockedCreaturesByCreature.values()) {
for (MageObjectReference mr : attackers) {
Integer blockers = blockersForAttacker.getOrDefault(mr, 0);
blockersForAttacker.put(mr, blockers + 1);
}
}
// Check each attacker is blocked by an allowed amount of creatures
for (Map.Entry<MageObjectReference, Integer> entry : blockersForAttacker.entrySet()) {
Permanent attacker = entry.getKey().getPermanent(game);
Integer blockers = entry.getValue();
// If getMaxBlockedBy() == 0 it means any number of creatures can block this creature
if (attacker.getMaxBlockedBy() != 0 && blockers > attacker.getMaxBlockedBy()) {
throw new UnsupportedOperationException(attacker.getName() + " is blocked by " + blockers + " creature(s). It can only be blocked by " + attacker.getMaxBlockedBy() + " or less.");
} else if (blockers < attacker.getMinBlockedBy()) {
throw new UnsupportedOperationException(attacker.getName() + " is blocked by " + blockers + " creature(s). It has to be blocked by " + attacker.getMinBlockedBy() + " or more.");
}
}
// No errors raised - all the blockers pass the test!
}
@Override
public Mode chooseMode(Modes modes, Ability source, Game game) {
if (!modesSet.isEmpty() && modes.getMaxModes() > modes.getSelectedModes().size()) {
// set mode to null to select less than maximum modes if multiple modes are allowed
if (modesSet.get(0) == null) {
modesSet.remove(0);
return null;
}
int selectedMode = Integer.parseInt(modesSet.get(0));
int i = 1;
for (Mode mode : modes.getAvailableModes(source, game)) {
if (i == selectedMode) {
modesSet.remove(0);
return mode;
}
i++;
}
}
if (modes.getMinModes() <= modes.getSelectedModes().size()) {
return null;
}
return computerPlayer.chooseMode(modes, source, game); //To change body of generated methods, choose Tools | Templates.
}
@Override
public boolean choose(Outcome outcome, Choice choice, Game game) {
if (!choices.isEmpty()) {
for (String choose2 : choices) {
for (String choose1 : choice.getChoices()) {
if (choose1.equals(choose2)) {
choice.setChoice(choose2);
choices.remove(choose2);
return true;
}
}
}
}
return computerPlayer.choose(outcome, choice, game);
}
@Override
public int chooseReplacementEffect(Map<String, String> rEffects, Game game) {
if (!choices.isEmpty()) {
for (String choice : choices) {
for (int index = 0; index < rEffects.size(); index++) {
if (choice.equals(rEffects.get(Integer.toString(index)))) {
choices.remove(choice);
return index;
}
}
}
}
return computerPlayer.chooseReplacementEffect(rEffects, game);
}
@Override
public boolean choose(Outcome outcome, Target target, UUID sourceId, Game game, Map<String, Serializable> options) {
if (!choices.isEmpty()) {
Ability source = null;
StackObject stackObject = game.getStack().getStackObject(sourceId);
if (stackObject != null) {
source = stackObject.getStackAbility();
}
if ((target instanceof TargetPermanent) || (target instanceof TargetPermanentOrPlayer)) { // player target not implemted yet
FilterPermanent filterPermanent;
if (target instanceof TargetPermanentOrPlayer) {
filterPermanent = ((TargetPermanentOrPlayer) target).getFilterPermanent();
} else {
filterPermanent = ((TargetPermanent) target).getFilter();
}
for (String choose2 : choices) {
String[] targetList = choose2.split("\\^");
boolean targetFound = false;
for (String targetName : targetList) {
boolean originOnly = false;
boolean copyOnly = false;
if (targetName.endsWith("]")) {
if (targetName.endsWith("[no copy]")) {
originOnly = true;
targetName = targetName.substring(0, targetName.length() - 9);
}
if (targetName.endsWith("[only copy]")) {
copyOnly = true;
targetName = targetName.substring(0, targetName.length() - 11);
}
}
for (Permanent permanent : game.getBattlefield().getActivePermanents(filterPermanent, getId(), sourceId, game)) {
if (target.getTargets().contains(permanent.getId())) {
continue;
}
if (permanent.getName().equals(targetName)) {
if (target.isNotTarget() || ((TargetPermanent) target).canTarget(computerPlayer.getId(), permanent.getId(), source, game)) {
if ((permanent.isCopy() && !originOnly) || (!permanent.isCopy() && !copyOnly)) {
target.add(permanent.getId(), game);
targetFound = true;
break;
}
}
} else if ((permanent.getName() + '-' + permanent.getExpansionSetCode()).equals(targetName)) {
if (target.isNotTarget() || ((TargetPermanent) target).canTarget(computerPlayer.getId(), permanent.getId(), source, game)) {
if ((permanent.isCopy() && !originOnly) || (!permanent.isCopy() && !copyOnly)) {
target.add(permanent.getId(), game);
targetFound = true;
break;
}
}
}
}
}
if (targetFound) {
choices.remove(choose2);
return true;
}
}
}
if (target instanceof TargetPlayer) {
for (Player player : game.getPlayers().values()) {
for (String choose2 : choices) {
if (player.getName().equals(choose2)) {
if (((TargetPlayer) target).canTarget(computerPlayer.getId(), player.getId(), null, game) && !target.getTargets().contains(player.getId())) {
target.add(player.getId(), game);
choices.remove(choose2);
return true;
}
}
}
}
}
if (target instanceof TargetCard) {
TargetCard targetCard = ((TargetCard) target);
Set<UUID> possibleTargets = targetCard.possibleTargets(sourceId, target.getTargetController() == null ? getId() : target.getTargetController(), game);
for (String choose2 : choices) {
String[] targetList = choose2.split("\\^");
boolean targetFound = false;
Choice:
for (String targetName : targetList) {
for (UUID targetId : possibleTargets) {
MageObject targetObject = game.getObject(targetId);
if (targetObject != null) {
if (targetObject.getName().equals(targetName)) {
if (targetCard.canTarget(targetObject.getId(), game)) {
if (targetCard.getTargets() != null && !targetCard.getTargets().contains(targetObject.getId())) {
targetCard.add(targetObject.getId(), game);
targetFound = true;
if (target.getTargets().size() >= target.getMaxNumberOfTargets()) {
break Choice;
}
}
}
}
}
}
}
if (targetFound) {
if (targetCard.isChosen()) {
choices.remove(choose2);
return true;
} else {
target.clearChosen();
}
}
}
}
if (target instanceof TargetSource) {
Set<UUID> possibleTargets;
TargetSource t = ((TargetSource) target);
possibleTargets = t.possibleTargets(sourceId, computerPlayer.getId(), game);
for (String choose2 : choices) {
String[] targetList = choose2.split("\\^");
boolean targetFound = false;
for (String targetName : targetList) {
for (UUID targetId : possibleTargets) {
MageObject targetObject = game.getObject(targetId);
if (targetObject != null) {
if (targetObject.getName().equals(targetName)) {
List<UUID> alreadyTargetted = target.getTargets();
if (t.canTarget(targetObject.getId(), game)) {
if (alreadyTargetted != null && !alreadyTargetted.contains(targetObject.getId())) {
target.add(targetObject.getId(), game);
choices.remove(choose2);
targetFound = true;
}
}
}
}
if (targetFound) {
choices.remove(choose2);
return true;
}
}
}
}
}
}
return computerPlayer.choose(outcome, target, sourceId, game, options);
}
@Override
public boolean chooseTarget(Outcome outcome, Target target, Ability source, Game game) {
if (!targets.isEmpty()) {
UUID abilityControllerId = computerPlayer.getId();
if (target.getTargetController() != null && target.getAbilityController() != null) {
abilityControllerId = target.getAbilityController();
}
if (target instanceof TargetPlayer || target instanceof TargetCreatureOrPlayer) {
for (String targetDefinition : targets) {
if (targetDefinition.startsWith("targetPlayer=")) {
String playerName = targetDefinition.substring(targetDefinition.indexOf("targetPlayer=") + 13);
for (Player player : game.getPlayers().values()) {
if (player.getName().equals(playerName)
&& target.canTarget(computerPlayer.getId(), player.getId(), source, game)) {
target.add(player.getId(), game);
targets.remove(targetDefinition);
return true;
}
}
}
}
}
if ((target instanceof TargetPermanent) || (target instanceof TargetPermanentOrPlayer) || (target instanceof TargetCreatureOrPlayer)) {
for (String targetDefinition : targets) {
String[] targetList = targetDefinition.split("\\^");
boolean targetFound = false;
for (String targetName : targetList) {
boolean originOnly = false;
boolean copyOnly = false;
if (targetName.endsWith("]")) {
if (targetName.endsWith("[no copy]")) {
originOnly = true;
targetName = targetName.substring(0, targetName.length() - 9);
}
if (targetName.endsWith("[only copy]")) {
copyOnly = true;
targetName = targetName.substring(0, targetName.length() - 11);
}
}
Filter filter = target.getFilter();
if (filter instanceof FilterCreatureOrPlayer) {
filter = ((FilterCreatureOrPlayer) filter).getCreatureFilter();
}
for (Permanent permanent : game.getBattlefield().getAllActivePermanents((FilterPermanent) filter, game)) {
if (permanent.getName().equals(targetName) || (permanent.getName() + '-' + permanent.getExpansionSetCode()).equals(targetName)) {
if (target.canTarget(abilityControllerId, permanent.getId(), source, game) && !target.getTargets().contains(permanent.getId())) {
if ((permanent.isCopy() && !originOnly) || (!permanent.isCopy() && !copyOnly)) {
target.add(permanent.getId(), game);
targetFound = true;
break;
}
}
}
}
}
if (targetFound) {
targets.remove(targetDefinition);
return true;
}
}
}
if (target instanceof TargetCardInHand) {
for (String targetDefinition : targets) {
String[] targetList = targetDefinition.split("\\^");
boolean targetFound = false;
for (String targetName : targetList) {
for (Card card : computerPlayer.getHand().getCards(((TargetCardInHand) target).getFilter(), game)) {
if (card.getName().equals(targetName) || (card.getName() + '-' + card.getExpansionSetCode()).equals(targetName)) {
if (((TargetCardInHand) target).canTarget(abilityControllerId, card.getId(), source, game) && !target.getTargets().contains(card.getId())) {
target.add(card.getId(), game);
targetFound = true;
break;
}
}
}
}
if (targetFound) {
targets.remove(targetDefinition);
return true;
}
}
}
if (target instanceof TargetCardInYourGraveyard) {
for (String targetDefinition : targets) {
String[] targetList = targetDefinition.split("\\^");
boolean targetFound = false;
for (String targetName : targetList) {
for (Card card : computerPlayer.getGraveyard().getCards(((TargetCardInYourGraveyard) target).getFilter(), game)) {
if (card.getName().equals(targetName) || (card.getName() + '-' + card.getExpansionSetCode()).equals(targetName)) {
if (((TargetCardInYourGraveyard) target).canTarget(abilityControllerId, card.getId(), source, game) && !target.getTargets().contains(card.getId())) {
target.add(card.getId(), game);
targetFound = true;
break;
}
}
}
}
if (targetFound) {
targets.remove(targetDefinition);
return true;
}
}
}
if (target instanceof TargetCardInOpponentsGraveyard) {
for (String targetDefinition : targets) {
String[] targetList = targetDefinition.split("\\^");
boolean targetFound = false;
for (String targetName : targetList) {
IterateOpponentsGraveyards:
for (UUID opponentId : game.getState().getPlayersInRange(getId(), game)) {
if (computerPlayer.hasOpponent(opponentId, game)) {
Player opponent = game.getPlayer(opponentId);
for (Card card : opponent.getGraveyard().getCards(((TargetCardInOpponentsGraveyard) target).getFilter(), game)) {
if (card.getName().equals(targetName) || (card.getName() + '-' + card.getExpansionSetCode()).equals(targetName)) {
if (((TargetCardInOpponentsGraveyard) target).canTarget(abilityControllerId, card.getId(), source, game) && !target.getTargets().contains(card.getId())) {
target.add(card.getId(), game);
targetFound = true;
break IterateOpponentsGraveyards;
}
}
}
}
}
}
if (targetFound) {
targets.remove(targetDefinition);
return true;
}
}
}
if (target instanceof TargetSpell) {
for (String targetDefinition : targets) {
String[] targetList = targetDefinition.split("\\^");
boolean targetFound = false;
for (String targetName : targetList) {
for (StackObject stackObject : game.getStack()) {
if (stackObject.getName().equals(targetName)) {
target.add(stackObject.getId(), game);
targetFound = true;
break;
}
}
}
if (targetFound) {
targets.remove(targetDefinition);
return true;
}
}
}
}
return computerPlayer.chooseTarget(outcome, target, source, game);
}
@Override
public boolean chooseTarget(Outcome outcome, Cards cards, TargetCard target, Ability source, Game game) {
if (!targets.isEmpty()) {
for (String targetDefinition : targets) {
String[] targetList = targetDefinition.split("\\^");
boolean targetFound = false;
for (String targetName : targetList) {
for (Card card : cards.getCards(game)) {
if (card.getName().equals(targetName) && !target.getTargets().contains(card.getId())) {
target.add(card.getId(), game);
targetFound = true;
break;
}
}
}
if (targetFound) {
targets.remove(targetDefinition);
return true;
}
}
}
return computerPlayer.chooseTarget(outcome, cards, target, source, game);
}
@Override
public TriggeredAbility chooseTriggeredAbility(List<TriggeredAbility> abilities, Game game) {
if (!choices.isEmpty()) {
for (TriggeredAbility ability : abilities) {
if (ability.toString().startsWith(choices.get(0))) {
choices.remove(0);
return ability;
}
}
}
return computerPlayer.chooseTriggeredAbility(abilities, game);
}
@Override
public boolean chooseUse(Outcome outcome, String message, Ability source, Game game) {
return this.chooseUse(outcome, message, null, null, null, source, game);
}
@Override
public boolean chooseUse(Outcome outcome, String message, String secondMessage, String trueText, String falseText, Ability source, Game game) {
if (message.equals("Scry 1?")) {
return false;
}
if (!choices.isEmpty()) {
if (choices.get(0).equals("No")) {
choices.remove(0);
return false;
}
if (choices.get(0).equals("Yes")) {
choices.remove(0);
return true;
}
}
return computerPlayer.chooseUse(outcome, message, secondMessage, trueText, falseText, source, game);
}
@Override
public int announceXMana(int min, int max, String message, Game game, Ability ability) {
if (!choices.isEmpty()) {
for (String choice : choices) {
if (choice.startsWith("X=")) {
int xValue = Integer.parseInt(choice.substring(2));
choices.remove(choice);
return xValue;
}
}
}
return computerPlayer.announceXMana(min, max, message, game, ability);
}
@Override
public int announceXCost(int min, int max, String message, Game game, Ability ability, VariableCost variablCost) {
if (!choices.isEmpty()) {
if (choices.get(0).startsWith("X=")) {
int xValue = Integer.parseInt(choices.get(0).substring(2));
choices.remove(0);
return xValue;
}
}
return computerPlayer.announceXCost(min, max, message, game, ability, null);
}
@Override
public int getAmount(int min, int max, String message, Game game) {
if (!choices.isEmpty()) {
if (choices.get(0).startsWith("X=")) {
int xValue = Integer.parseInt(choices.get(0).substring(2));
choices.remove(0);
return xValue;
}
}
return computerPlayer.getAmount(min, max, message, game);
}
@Override
public void addAbility(Ability ability) {
computerPlayer.addAbility(ability);
}
@Override
public boolean activateAbility(ActivatedAbility ability, Game game) {
return computerPlayer.activateAbility(ability, game);
}
@Override
public void abort() {
computerPlayer.abort();
}
@Override
public void abortReset() {
computerPlayer.abortReset();
}
@Override
public void won(Game game) {
computerPlayer.won(game);
}
@Override
public void restore(Player player) {
this.modesSet.clear();
this.modesSet.addAll(((TestPlayer) player).modesSet);
this.actions.clear();
this.actions.addAll(((TestPlayer) player).actions);
this.choices.clear();
this.choices.addAll(((TestPlayer) player).choices);
this.targets.clear();
this.targets.addAll(((TestPlayer) player).targets);
computerPlayer.restore(player);
}
@Override
public void useDeck(Deck deck, Game game) {
computerPlayer.useDeck(deck, game);
}
@Override
public void init(Game game) {
initialTurns = 0;
computerPlayer.init(game);
}
@Override
public void init(Game game, boolean testMode) {
initialTurns = 0;
computerPlayer.init(game, testMode);
}
@Override
public void reset() {
computerPlayer.reset();
}
@Override
public Counters getCounters() {
return computerPlayer.getCounters();
}
@Override
public void otherPlayerLeftGame(Game game) {
computerPlayer.otherPlayerLeftGame(game);
}
@Override
public void beginTurn(Game game) {
checkLegalMovesThisTurn(game);
computerPlayer.beginTurn(game);
}
@Override
public RangeOfInfluence getRange() {
return computerPlayer.getRange();
}
@Override
public Set<UUID> getInRange() {
return computerPlayer.getInRange();
}
@Override
public Set<UUID> getPlayersUnderYourControl() {
return computerPlayer.getPlayersUnderYourControl();
}
@Override
public void controlPlayersTurn(Game game, UUID playerId) {
computerPlayer.controlPlayersTurn(game, playerId);
}
@Override
public void setTurnControlledBy(UUID playerId) {
computerPlayer.setTurnControlledBy(playerId);
}
@Override
public UUID getTurnControlledBy() {
return computerPlayer.getTurnControlledBy();
}
@Override
public void resetOtherTurnsControlled() {
computerPlayer.resetOtherTurnsControlled();
}
@Override
public boolean isGameUnderControl() {
return computerPlayer.isGameUnderControl();
}
@Override
public void setGameUnderYourControl(boolean value) {
computerPlayer.setGameUnderYourControl(value);
}
@Override
public void endOfTurn(Game game) {
computerPlayer.endOfTurn(game);
}
@Override
public boolean canBeTargetedBy(MageObject source, UUID sourceControllerId, Game game) {
return computerPlayer.canBeTargetedBy(source, sourceControllerId, game);
}
@Override
public boolean hasProtectionFrom(MageObject source, Game game) {
return computerPlayer.hasProtectionFrom(source, game);
}
@Override
public int drawCards(int num, Game game) {
return computerPlayer.drawCards(num, game);
}
@Override
public int drawCards(int num, Game game, ArrayList<UUID> appliedEffects) {
return computerPlayer.drawCards(num, game, appliedEffects);
}
@Override
public void discardToMax(Game game) {
computerPlayer.discardToMax(game);
}
@Override
public boolean putInHand(Card card, Game game) {
return computerPlayer.putInHand(card, game);
}
@Override
public boolean removeFromHand(Card card, Game game) {
return computerPlayer.removeFromHand(card, game);
}
@Override
public boolean removeFromLibrary(Card card, Game game) {
return computerPlayer.removeFromLibrary(card, game);
}
@Override
public void discard(int amount, Ability source, Game game) {
computerPlayer.discard(amount, source, game);
}
@Override
public Card discardOne(boolean random, Ability source, Game game) {
return computerPlayer.discardOne(random, source, game);
}
@Override
public Cards discard(int amount, boolean random, Ability source, Game game) {
return computerPlayer.discard(amount, random, source, game);
}
@Override
public boolean discard(Card card, Ability source, Game game) {
return computerPlayer.discard(card, source, game);
}
@Override
public List<UUID> getAttachments() {
return computerPlayer.getAttachments();
}
@Override
public boolean addAttachment(UUID permanentId, Game game) {
return computerPlayer.addAttachment(permanentId, game);
}
@Override
public boolean removeAttachment(Permanent attachment, Game game) {
return computerPlayer.removeAttachment(attachment, game);
}
@Override
public boolean removeFromBattlefield(Permanent permanent, Game game) {
return computerPlayer.removeFromBattlefield(permanent, game);
}
@Override
public boolean putInGraveyard(Card card, Game game) {
return computerPlayer.putInGraveyard(card, game);
}
@Override
public boolean removeFromGraveyard(Card card, Game game) {
return computerPlayer.removeFromGraveyard(card, game);
}
@Override
public boolean putCardsOnBottomOfLibrary(Cards cards, Game game, Ability source, boolean anyOrder) {
return computerPlayer.putCardsOnBottomOfLibrary(cards, game, source, anyOrder);
}
@Override
public boolean putCardsOnTopOfLibrary(Cards cards, Game game, Ability source, boolean anyOrder) {
return computerPlayer.putCardsOnTopOfLibrary(cards, game, source, anyOrder);
}
@Override
public void setCastSourceIdWithAlternateMana(UUID sourceId, ManaCosts manaCosts, Costs costs) {
computerPlayer.setCastSourceIdWithAlternateMana(sourceId, manaCosts, costs);
}
@Override
public UUID getCastSourceIdWithAlternateMana() {
return computerPlayer.getCastSourceIdWithAlternateMana();
}
@Override
public ManaCosts getCastSourceIdManaCosts() {
return computerPlayer.getCastSourceIdManaCosts();
}
@Override
public Costs<Cost> getCastSourceIdCosts() {
return computerPlayer.getCastSourceIdCosts();
}
@Override
public boolean isInPayManaMode() {
return computerPlayer.isInPayManaMode();
}
@Override
public boolean cast(SpellAbility ability, Game game, boolean noMana) {
return computerPlayer.cast(ability, game, noMana);
}
@Override
public boolean playCard(Card card, Game game, boolean noMana, boolean ignoreTiming) {
return computerPlayer.playCard(card, game, noMana, ignoreTiming);
}
@Override
public boolean playLand(Card card, Game game, boolean ignoreTiming) {
return computerPlayer.playLand(card, game, ignoreTiming);
}
@Override
public boolean triggerAbility(TriggeredAbility source, Game game) {
return computerPlayer.triggerAbility(source, game);
}
@Override
public LinkedHashMap<UUID, ActivatedAbility> getUseableActivatedAbilities(MageObject object, Zone zone, Game game) {
return computerPlayer.getUseableActivatedAbilities(object, zone, game);
}
@Override
public int getLandsPlayed() {
return computerPlayer.getLandsPlayed();
}
@Override
public boolean canPlayLand() {
return computerPlayer.canPlayLand();
}
@Override
public void shuffleLibrary(Ability source, Game game) {
computerPlayer.shuffleLibrary(source, game);
}
@Override
public void revealCards(String name, Cards cards, Game game) {
computerPlayer.revealCards(name, cards, game);
}
@Override
public void revealCards(String name, Cards cards, Game game, boolean postToLog) {
computerPlayer.revealCards(name, cards, game, postToLog);
}
@Override
public void lookAtCards(String name, Cards cards, Game game) {
computerPlayer.lookAtCards(name, cards, game);
}
@Override
public void lookAtCards(String name, Card card, Game game) {
computerPlayer.lookAtCards(name, card, game);
}
@Override
public void phasing(Game game) {
computerPlayer.phasing(game);
}
@Override
public void untap(Game game) {
computerPlayer.untap(game);
}
@Override
public UUID getId() {
return computerPlayer.getId();
}
@Override
public Cards getHand() {
return computerPlayer.getHand();
}
@Override
public Graveyard getGraveyard() {
return computerPlayer.getGraveyard();
}
@Override
public ManaPool getManaPool() {
return computerPlayer.getManaPool();
}
@Override
public String getName() {
return computerPlayer.getName();
}
@Override
public String getLogName() {
return computerPlayer.getLogName();
}
@Override
public boolean isHuman() {
return computerPlayer.isHuman();
}
@Override
public Library getLibrary() {
return computerPlayer.getLibrary();
}
@Override
public Cards getSideboard() {
return computerPlayer.getSideboard();
}
@Override
public int getLife() {
return computerPlayer.getLife();
}
@Override
public void initLife(int life) {
computerPlayer.initLife(life);
}
@Override
public void setLife(int life, Game game) {
computerPlayer.setLife(life, game);
}
@Override
public void setLifeTotalCanChange(boolean lifeTotalCanChange) {
computerPlayer.setLifeTotalCanChange(lifeTotalCanChange);
}
@Override
public boolean isLifeTotalCanChange() {
return computerPlayer.isLifeTotalCanChange();
}
@Override
public List<AlternativeSourceCosts> getAlternativeSourceCosts() {
return computerPlayer.getAlternativeSourceCosts();
}
@Override
public boolean isCanLoseLife() {
return computerPlayer.isCanLoseLife();
}
@Override
public void setCanLoseLife(boolean canLoseLife) {
computerPlayer.setCanLoseLife(canLoseLife);
}
@Override
public int loseLife(int amount, Game game, boolean atCombat) {
return computerPlayer.loseLife(amount, game, atCombat);
}
@Override
public boolean isCanGainLife() {
return computerPlayer.isCanGainLife();
}
@Override
public void setCanGainLife(boolean canGainLife) {
computerPlayer.setCanGainLife(canGainLife);
}
@Override
public int gainLife(int amount, Game game) {
return computerPlayer.gainLife(amount, game);
}
@Override
public int damage(int damage, UUID sourceId, Game game, boolean combatDamage, boolean preventable) {
return computerPlayer.damage(damage, sourceId, game, combatDamage, preventable);
}
@Override
public int damage(int damage, UUID sourceId, Game game, boolean combatDamage, boolean preventable, ArrayList<UUID> appliedEffects) {
return computerPlayer.damage(damage, sourceId, game, combatDamage, preventable, appliedEffects);
}
@Override
public boolean addCounters(Counter counter, Game game) {
return computerPlayer.addCounters(counter, game);
}
@Override
public void removeCounters(String name, int amount, Ability source, Game game) {
computerPlayer.removeCounters(name, amount, source, game);
}
@Override
public Abilities<Ability> getAbilities() {
return computerPlayer.getAbilities();
}
@Override
public int getLandsPerTurn() {
return computerPlayer.getLandsPerTurn();
}
@Override
public void setLandsPerTurn(int landsPerTurn) {
computerPlayer.setLandsPerTurn(landsPerTurn);
}
@Override
public int getLoyaltyUsePerTurn() {
return computerPlayer.getLoyaltyUsePerTurn();
}
@Override
public void setLoyaltyUsePerTurn(int loyaltyUsePerTurn) {
computerPlayer.setLoyaltyUsePerTurn(loyaltyUsePerTurn);
}
@Override
public int getMaxHandSize() {
return computerPlayer.getMaxHandSize();
}
@Override
public void setMaxHandSize(int maxHandSize) {
computerPlayer.setMaxHandSize(maxHandSize);
}
@Override
public void setMaxAttackedBy(int maxAttackedBy) {
computerPlayer.setMaxAttackedBy(maxAttackedBy);
}
@Override
public int getMaxAttackedBy() {
return computerPlayer.getMaxAttackedBy();
}
@Override
public void setResponseString(String responseString) {
computerPlayer.setResponseString(responseString);
}
@Override
public void setResponseManaType(UUID manaTypePlayerId, ManaType responseManaType) {
computerPlayer.setResponseManaType(manaTypePlayerId, responseManaType);
}
@Override
public void setResponseUUID(UUID responseUUID) {
computerPlayer.setResponseUUID(responseUUID);
}
@Override
public void setResponseBoolean(Boolean responseBoolean) {
computerPlayer.setResponseBoolean(responseBoolean);
}
@Override
public void setResponseInteger(Integer responseInteger) {
computerPlayer.setResponseInteger(responseInteger);
}
@Override
public boolean isPassed() {
return computerPlayer.isPassed();
}
@Override
public void pass(Game game) {
computerPlayer.pass(game);
}
@Override
public boolean isEmptyDraw() {
return computerPlayer.isEmptyDraw();
}
@Override
public void resetPassed() {
computerPlayer.resetPassed();
}
@Override
public void resetPlayerPassedActions() {
computerPlayer.resetPlayerPassedActions();
}
@Override
public void quit(Game game) {
computerPlayer.quit(game);
}
@Override
public void timerTimeout(Game game) {
computerPlayer.timerTimeout(game);
}
@Override
public void idleTimeout(Game game) {
computerPlayer.idleTimeout(game);
}
@Override
public void concede(Game game) {
computerPlayer.concede(game);
}
@Override
public void sendPlayerAction(mage.constants.PlayerAction playerAction, Game game, Object data) {
computerPlayer.sendPlayerAction(playerAction, game, data);
}
@Override
public void leave() {
computerPlayer.leave();
}
@Override
public boolean hasLeft() {
return computerPlayer.hasLeft();
}
@Override
public void lost(Game game) {
computerPlayer.lost(game);
}
@Override
public boolean hasDrew() {
return computerPlayer.hasDrew();
}
@Override
public void drew(Game game) {
computerPlayer.drew(game);
}
@Override
public void lostForced(Game game) {
computerPlayer.lostForced(game);
}
@Override
public boolean canLose(Game game) {
return computerPlayer.canLose(game);
}
@Override
public boolean hasLost() {
return computerPlayer.hasLost();
}
@Override
public boolean isInGame() {
return computerPlayer.isInGame();
}
@Override
public boolean canRespond() {
return computerPlayer.canRespond();
}
@Override
public boolean hasWon() {
return computerPlayer.hasWon();
}
@Override
public void declareAttacker(UUID attackerId, UUID defenderId, Game game, boolean allowUndo) {
computerPlayer.declareAttacker(attackerId, defenderId, game, allowUndo);
}
@Override
public void declareBlocker(UUID defenderId, UUID blockerId, UUID attackerId, Game game) {
computerPlayer.declareBlocker(defenderId, blockerId, attackerId, game);
}
@Override
public boolean searchLibrary(TargetCardInLibrary target, Game game) {
return computerPlayer.searchLibrary(target, game);
}
@Override
public boolean searchLibrary(TargetCardInLibrary target, Game game, UUID targetPlayerId) {
return computerPlayer.searchLibrary(target, game, targetPlayerId);
}
@Override
public boolean flipCoin(Game game) {
return computerPlayer.flipCoin(game);
}
@Override
public boolean flipCoin(Game game, ArrayList<UUID> appliedEffects) {
return computerPlayer.flipCoin(game, appliedEffects);
}
@Override
public List<Permanent> getAvailableAttackers(Game game) {
return computerPlayer.getAvailableAttackers(game);
}
@Override
public List<Permanent> getAvailableAttackers(UUID defenderId, Game game) {
return computerPlayer.getAvailableAttackers(defenderId, game);
}
@Override
public List<Permanent> getAvailableBlockers(Game game) {
return computerPlayer.getAvailableBlockers(game);
}
@Override
public ManaOptions getManaAvailable(Game game) {
return computerPlayer.getManaAvailable(game);
}
public List<Permanent> getAvailableManaProducersWithCost(Game game) {
return computerPlayer.getAvailableManaProducersWithCost(game);
}
@Override
public List<Ability> getPlayable(Game game, boolean hidden) {
return computerPlayer.getPlayable(game, hidden);
}
@Override
public Set<UUID> getPlayableInHand(Game game) {
return computerPlayer.getPlayableInHand(game);
}
@Override
public List<Ability> getPlayableOptions(Ability ability, Game game) {
return computerPlayer.getPlayableOptions(ability, game);
}
@Override
public boolean isTestMode() {
return computerPlayer.isTestMode();
}
@Override
public void setTestMode(boolean value) {
computerPlayer.setTestMode(value);
}
@Override
public boolean isTopCardRevealed() {
return computerPlayer.isTopCardRevealed();
}
@Override
public void setTopCardRevealed(boolean topCardRevealed) {
computerPlayer.setTopCardRevealed(topCardRevealed);
}
@Override
public UserData getUserData() {
return computerPlayer.getUserData();
}
@Override
public void setUserData(UserData userData) {
computerPlayer.setUserData(userData);
}
@Override
public void addAction(String action) {
computerPlayer.addAction(action);
}
@Override
public void setAllowBadMoves(boolean allowBadMoves) {
computerPlayer.setAllowBadMoves(allowBadMoves);
}
@Override
public boolean canPayLifeCost() {
return computerPlayer.canPayLifeCost();
}
@Override
public void setCanPayLifeCost(boolean canPayLifeCost) {
computerPlayer.setCanPayLifeCost(canPayLifeCost);
}
@Override
public boolean canPaySacrificeCost(Permanent permanent, UUID sourceId, UUID controllerId, Game game) {
return computerPlayer.canPaySacrificeCost(permanent, sourceId, controllerId, game);
}
@Override
public FilterPermanent getSacrificeCostFilter() {
return computerPlayer.getSacrificeCostFilter();
}
@Override
public void setCanPaySacrificeCostFilter(FilterPermanent permanent) {
computerPlayer.setCanPaySacrificeCostFilter(permanent);
}
@Override
public boolean canLoseByZeroOrLessLife() {
return computerPlayer.canLoseByZeroOrLessLife();
}
@Override
public void setLoseByZeroOrLessLife(boolean loseByZeroOrLessLife) {
computerPlayer.setLoseByZeroOrLessLife(loseByZeroOrLessLife);
}
@Override
public boolean canPlayCardsFromGraveyard() {
return computerPlayer.canPlayCardsFromGraveyard();
}
@Override
public void setPlayCardsFromGraveyard(boolean playCardsFromGraveyard) {
computerPlayer.setPlayCardsFromGraveyard(playCardsFromGraveyard);
}
@Override
public boolean autoLoseGame() {
return computerPlayer.autoLoseGame();
}
@Override
public void becomesActivePlayer() {
computerPlayer.becomesActivePlayer();
}
@Override
public int getTurns() {
return computerPlayer.getTurns();
}
@Override
public int getStoredBookmark() {
return computerPlayer.getStoredBookmark();
}
@Override
public void setStoredBookmark(int storedBookmark) {
computerPlayer.setStoredBookmark(storedBookmark);
}
@Override
public synchronized void resetStoredBookmark(Game game) {
computerPlayer.resetStoredBookmark(game);
}
@Override
public boolean lookAtFaceDownCard(Card card, Game game) {
return computerPlayer.lookAtFaceDownCard(card, game);
}
@Override
public void setPriorityTimeLeft(int timeLeft) {
computerPlayer.setPriorityTimeLeft(timeLeft);
}
@Override
public int getPriorityTimeLeft() {
return computerPlayer.getPriorityTimeLeft();
}
@Override
public boolean hasQuit() {
return computerPlayer.hasQuit();
}
@Override
public boolean hasTimerTimeout() {
return computerPlayer.hasTimerTimeout();
}
@Override
public boolean hasIdleTimeout() {
return computerPlayer.hasIdleTimeout();
}
@Override
public void setReachedNextTurnAfterLeaving(boolean reachedNextTurnAfterLeaving) {
computerPlayer.setReachedNextTurnAfterLeaving(reachedNextTurnAfterLeaving);
}
@Override
public boolean hasReachedNextTurnAfterLeaving() {
return computerPlayer.hasReachedNextTurnAfterLeaving();
}
@Override
public boolean canJoinTable(Table table) {
return computerPlayer.canJoinTable(table);
}
@Override
public void addCommanderId(UUID commanderId) {
computerPlayer.addCommanderId(commanderId);
}
@Override
public Set<UUID> getCommandersIds() {
return computerPlayer.getCommandersIds();
}
@Override
public boolean moveCardToHandWithInfo(Card card, UUID sourceId, Game game) {
return computerPlayer.moveCardToHandWithInfo(card, sourceId, game);
}
@Override
public boolean moveCardToHandWithInfo(Card card, UUID sourceId, Game game, boolean withName) {
return computerPlayer.moveCardToHandWithInfo(card, sourceId, game, withName);
}
@Override
public boolean moveCardsToExile(Card card, Ability source, Game game, boolean withName, UUID exileId, String exileZoneName) {
return computerPlayer.moveCardsToExile(card, source, game, withName, exileId, exileZoneName);
}
@Override
public boolean moveCardsToExile(Set<Card> cards, Ability source, Game game, boolean withName, UUID exileId, String exileZoneName) {
return computerPlayer.moveCardsToExile(cards, source, game, withName, exileId, exileZoneName);
}
@Override
public Set<Card> moveCardsToGraveyardWithInfo(Set<Card> allCards, Ability source, Game game, Zone fromZone) {
return computerPlayer.moveCardsToGraveyardWithInfo(allCards, source, game, fromZone);
}
@Override
public boolean moveCardToGraveyardWithInfo(Card card, UUID sourceId, Game game, Zone fromZone) {
return computerPlayer.moveCardToGraveyardWithInfo(card, sourceId, game, fromZone);
}
@Override
public boolean moveCardToLibraryWithInfo(Card card, UUID sourceId, Game game, Zone fromZone, boolean toTop, boolean withName) {
return computerPlayer.moveCardToLibraryWithInfo(card, sourceId, game, fromZone, toTop, withName);
}
@Override
public boolean moveCardToExileWithInfo(Card card, UUID exileId, String exileName, UUID sourceId, Game game, Zone fromZone, boolean withName) {
return computerPlayer.moveCardToExileWithInfo(card, exileId, exileName, sourceId, game, fromZone, withName);
}
@Override
public boolean hasOpponent(UUID playerToCheckId, Game game) {
return computerPlayer.hasOpponent(playerToCheckId, game);
}
@Override
public boolean getPassedAllTurns() {
return computerPlayer.getPassedAllTurns();
}
@Override
public boolean getPassedUntilNextMain() {
return computerPlayer.getPassedUntilNextMain();
}
@Override
public boolean getPassedUntilEndOfTurn() {
return computerPlayer.getPassedUntilEndOfTurn();
}
@Override
public boolean getPassedTurn() {
return computerPlayer.getPassedTurn();
}
@Override
public boolean getPassedUntilStackResolved() {
return computerPlayer.getPassedUntilStackResolved();
}
@Override
public boolean getPassedUntilEndStepBeforeMyTurn() {
return computerPlayer.getPassedUntilEndStepBeforeMyTurn();
}
@Override
public void revokePermissionToSeeHandCards() {
computerPlayer.revokePermissionToSeeHandCards();
}
@Override
public void addPermissionToShowHandCards(UUID watcherUserId) {
computerPlayer.addPermissionToShowHandCards(watcherUserId);
}
@Override
public boolean isRequestToShowHandCardsAllowed() {
return computerPlayer.isRequestToShowHandCardsAllowed();
}
@Override
public boolean hasUserPermissionToSeeHand(UUID userId) {
return computerPlayer.hasUserPermissionToSeeHand(userId);
}
@Override
public Set<UUID> getUsersAllowedToSeeHandCards() {
return computerPlayer.getUsersAllowedToSeeHandCards();
}
@Override
public void setMatchPlayer(MatchPlayer matchPlayer) {
computerPlayer.setMatchPlayer(matchPlayer);
}
@Override
public MatchPlayer getMatchPlayer() {
return computerPlayer.getMatchPlayer();
}
@Override
public AbilityType getJustActivatedType() {
return computerPlayer.getJustActivatedType();
}
@Override
public void setJustActivatedType(AbilityType justActivatedType) {
computerPlayer.setJustActivatedType(justActivatedType);
}
@Override
public void cleanUpOnMatchEnd() {
computerPlayer.cleanUpOnMatchEnd();
}
@Override
public SpellAbility chooseSpellAbilityForCast(SpellAbility ability, Game game, boolean noMana) {
return computerPlayer.chooseSpellAbilityForCast(ability, game, noMana);
}
@Override
public void skip() {
computerPlayer.skip();
}
@Override
public boolean choose(Outcome outcome, Target target, UUID sourceId, Game game) {
// needed to call here the TestPlayer because it's overwitten
return choose(outcome, target, sourceId, game, null);
}
@Override
public boolean choose(Outcome outcome, Cards cards, TargetCard target, Game game) {
if (!choices.isEmpty()) {
for (String choose2 : choices) {
// TODO: More targetting to fix
String[] targetList = choose2.split("\\^");
boolean targetFound = false;
for (String targetName : targetList) {
for (Card card : cards.getCards(game)) {
if (target.getTargets().contains(card.getId())) {
continue;
}
if (card.getName().equals(targetName)) {
if (target.isNotTarget() || target.canTarget(card.getId(), game)) {
target.add(card.getId(), game);
targetFound = true;
break;
}
}
}
}
if (targetFound) {
choices.remove(choose2);
return true;
}
}
}
return computerPlayer.choose(outcome, cards, target, game);
}
@Override
public boolean chooseTargetAmount(Outcome outcome, TargetAmount target, Ability source, Game game) {
return computerPlayer.chooseTargetAmount(outcome, target, source, game);
}
@Override
public boolean chooseMulligan(Game game) {
return computerPlayer.chooseMulligan(game);
}
@Override
public boolean choosePile(Outcome outcome, String message, List<? extends Card> pile1, List<? extends Card> pile2, Game game) {
return computerPlayer.choosePile(outcome, message, pile1, pile2, game);
}
@Override
public boolean playMana(Ability ability, ManaCost unpaid, String promptText, Game game) {
groupsForTargetHandling = null;
return computerPlayer.playMana(ability, unpaid, promptText, game);
}
@Override
public UUID chooseAttackerOrder(List<Permanent> attacker, Game game) {
return computerPlayer.chooseAttackerOrder(attacker, game);
}
@Override
public UUID chooseBlockerOrder(List<Permanent> blockers, CombatGroup combatGroup, List<UUID> blockerOrder, Game game) {
return computerPlayer.chooseBlockerOrder(blockers, combatGroup, blockerOrder, game);
}
@Override
public void assignDamage(int damage, List<UUID> targets, String singleTargetName, UUID sourceId, Game game) {
computerPlayer.assignDamage(damage, targets, singleTargetName, sourceId, game);
}
@Override
public void sideboard(Match match, Deck deck) {
computerPlayer.sideboard(match, deck);
}
@Override
public void construct(Tournament tournament, Deck deck) {
computerPlayer.construct(tournament, deck);
}
@Override
public void pickCard(List<Card> cards, Deck deck, Draft draft) {
computerPlayer.pickCard(cards, deck, draft);
}
@Override
public boolean scry(int value, Ability source, Game game) {
// Don't scry at the start of the game.
if (game.getTurnNum() == 1 && game.getStep() == null) {
return false;
}
return computerPlayer.scry(value, source, game);
}
@Override
public boolean moveCards(Card card, Zone toZone, Ability source, Game game) {
return computerPlayer.moveCards(card, toZone, source, game);
}
@Override
public boolean moveCards(Card card, Zone toZone, Ability source, Game game, boolean tapped, boolean faceDown, boolean byOwner, ArrayList<UUID> appliedEffects) {
return computerPlayer.moveCards(card, toZone, source, game, tapped, faceDown, byOwner, appliedEffects);
}
@Override
public boolean moveCards(Cards cards, Zone toZone, Ability source, Game game) {
return computerPlayer.moveCards(cards, toZone, source, game);
}
@Override
public boolean moveCards(Set<Card> cards, Zone toZone, Ability source, Game game) {
return computerPlayer.moveCards(cards, toZone, source, game);
}
@Override
public boolean moveCards(Set<Card> cards, Zone toZone, Ability source, Game game, boolean tapped, boolean faceDown, boolean byOwner, ArrayList<UUID> appliedEffects) {
return computerPlayer.moveCards(cards, toZone, source, game, tapped, faceDown, byOwner, appliedEffects);
}
public void setAIPlayer(boolean AIPlayer) {
this.AIPlayer = AIPlayer;
}
public boolean isAIPlayer() {
return AIPlayer;
}
public String getHistory() {
return computerPlayer.getHistory();
}
}