Fixed some bugs and added a AI Test player class.

This commit is contained in:
LevelX2 2015-06-08 18:31:54 +02:00
parent 9da44a547d
commit 3b61a10237
25 changed files with 1732 additions and 388 deletions

View file

@ -0,0 +1,97 @@
1 [MMQ:316] Dust Bowl
1 [MBS:43] Go for the Throat
1 [ALL:42] Force of Will
1 [ZEN:220] Misty Rainforest
1 [10E:361] Treetop Village
1 [10E:362] Underground River
1 [ROE:115] Inquisition of Kozilek
1 [STH:137] Volrath's Stronghold
1 [PLC:85] Damnation
1 [FUT:173] Tolaria West
1 [ISD:78] Snapcaster Mage
1 [ZEN:67] Spell Pierce
1 [ZEN:229] Verdant Catacombs
1 [M11:70] Preordain
1 [C13:177] Baleful Strix
1 [ZEN:223] Scalding Tarn
1 [MBS:138] Sword of Feast and Famine
1 [NPH:57] Dismember
1 [TMP:58] Dismiss
1 [INV:57] Fact or Fiction
1 [DGM:93] Putrefy
1 [ONS:320] Lonely Sandbar
1 [GTC:240] Breeding Pool
1 [DTK:262] Forest
1 [ISD:105] Liliana of the Veil
1 [TMP:340] Wasteland
1 [JUD:46] Mental Note
1 [ULG:36] Miscalculation
2 [CSP:152] Snow-Covered Island
1 [RAV:63] Remand
1 [LRW:56] Cryptic Command
1 [3ED:283] Bayou
1 [5ED:191] Sylvan Library
1 [CMD:269] Command Tower
1 [EXO:35] Forbid
1 [DDO:36] AEtherize
1 [KTK:36] Dig Through Time
1 [RTR:141] Abrupt Decay
1 [SHM:280] Sunken Ruins
1 [DIS:33] Spell Snare
1 [ARB:92] Maelstrom Pulse
1 [KTK:81] Murderous Cut
1 [TSP:48] Ancestral Vision
1 [TMP:22] Diabolic Edict
1 [10E:319] Crucible of Worlds
1 [M12:63] Mana Leak
1 [FUT:52] Logic Knot
1 [CHK:268] Sensei's Divining Top
1 [THS:225] Temple of Deceit
1 [ODY:317] Cephalid Coliseum
1 [WWK:145] Tectonic Edge
1 [EVE:41] Raven's Crime
1 [C13:96] Toxic Deluge
1 [9ED:152] Phyrexian Arena
1 [KTK:248] Windswept Heath
1 [KTK:204] Sultai Charm
1 [KTK:249] Wooded Foothills
1 [M13:223] Drowned Catacomb
1 [RAV:172] Life from the Loam
1 [THS:107] Thoughtseize
1 [MMQ:61] Brainstorm
1 [M15:244] Llanowar Wastes
1 [ISD:55] Forbidden Alchemy
1 [APC:114] Pernicious Deed
1 [RTR:243] Overgrown Tomb
1 [7ED:76] Force Spike
1 [TMP:70] Intuition
1 [3ED:305] Underground Sea
1 [M15:248] Urborg, Tomb of Yawgmoth
1 [AVR:226] Cavern of Souls
1 [ISD:241] Hinterland Harbor
1 [DKA:52] Thought Scour
1 [3ED:303] Tropical Island
1 [3ED:13] Demonic Tutor
1 [ISD:249] Woodland Cemetery
1 [M12:73] Ponder
1 [DTK:65] Negate
1 [RAV:82] Darkblast
1 [WWK:134] Creeping Tar Pit
1 [GTC:249] Watery Grave
1 [WWK:132] Bojuka Bog
1 [KTK:239] Polluted Delta
1 [KTK:233] Flooded Strand
1 [KTK:59] Treasure Cruise
1 [TSP:202] Krosan Grip
1 [7ED:67] Counterspell
1 [MIR:80] Mystical Tutor
1 [MOR:55] Vendilion Clique
1 [TSP:69] Mystical Teachings
1 [KTK:230] Bloodstained Mire
1 [ZEN:219] Marsh Flats
1 [DTK:256] Swamp
1 [THS:90] Hero's Downfall
3 [DTK:253] Island
1 [WWK:31] Jace, the Mind Sculptor
1 [DTK:98] Duress
SB: 1 [FRF:87] Tasigur, the Golden Fang

View file

@ -84,6 +84,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
public ComputerPlayer7(final ComputerPlayer7 player) { public ComputerPlayer7(final ComputerPlayer7 player) {
super(player); super(player);
this.allowBadMoves = player.allowBadMoves;
} }
@Override @Override

View file

@ -1595,7 +1595,7 @@ public class ComputerPlayer extends PlayerImpl implements Player {
} }
@Override @Override
protected List<Permanent> getAvailableManaProducers(Game game) { public List<Permanent> getAvailableManaProducers(Game game) {
return super.getAvailableManaProducers(game); return super.getAvailableManaProducers(game);
} }

View file

@ -1038,6 +1038,12 @@ public class HumanPlayer extends PlayerImpl {
} }
} }
@Override
public boolean activateAbility(ActivatedAbility ability, Game game) {
getManaPool().setStock(); // needed for the "mana already in the pool has to be used manually" option
return super.activateAbility(ability, game);
}
protected void activateAbility(LinkedHashMap<UUID, ? extends ActivatedAbility> abilities, MageObject object, Game game) { protected void activateAbility(LinkedHashMap<UUID, ? extends ActivatedAbility> abilities, MageObject object, Game game) {
updateGameStatePriority("activateAbility", game); updateGameStatePriority("activateAbility", game);
if (abilities.size() == 1 && suppressAbilityPicker(abilities.values().iterator().next())) { if (abilities.size() == 1 && suppressAbilityPicker(abilities.values().iterator().next())) {

View file

@ -78,7 +78,7 @@ public class DevoutChaplain extends CardImpl {
this.power = new MageInt(2); this.power = new MageInt(2);
this.toughness = new MageInt(2); this.toughness = new MageInt(2);
// {tap}, Tap two untapped Humans you control: Exile target artifact or enchantment. // {T}, Tap two untapped Humans you control: Exile target artifact or enchantment.
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileTargetEffect(), new TapSourceCost()); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileTargetEffect(), new TapSourceCost());
ability.addCost(new TapTargetCost(new TargetControlledPermanent(2, 2, humanFilter, false))); ability.addCost(new TapTargetCost(new TargetControlledPermanent(2, 2, humanFilter, false)));
ability.addTarget(new TargetPermanent(filter)); ability.addTarget(new TargetPermanent(filter));

View file

@ -102,11 +102,11 @@ class SewerNemesisChoosePlayerEffect extends OneShotEffect {
Player player = game.getPlayer(source.getControllerId()); Player player = game.getPlayer(source.getControllerId());
Permanent permanent = game.getPermanent(source.getSourceId()); Permanent permanent = game.getPermanent(source.getSourceId());
if (player != null && permanent != null) { if (player != null && permanent != null) {
TargetPlayer target = new TargetPlayer(); TargetPlayer target = new TargetPlayer(1,1,true);
if (player.choose(this.outcome, target, source.getSourceId(), game)) { if (player.choose(this.outcome, target, source.getSourceId(), game)) {
Player chosenPlayer = game.getPlayer(target.getFirstTarget()); Player chosenPlayer = game.getPlayer(target.getFirstTarget());
if (chosenPlayer != null) { if (chosenPlayer != null) {
game.informPlayers(permanent.getName() + ": " + player.getLogName() + " has chosen " + chosenPlayer.getLogName()); game.informPlayers(permanent.getLogName() + ": " + player.getLogName() + " has chosen " + chosenPlayer.getLogName());
game.getState().setValue(permanent.getId() + "_player", target.getFirstTarget()); game.getState().setValue(permanent.getId() + "_player", target.getFirstTarget());
return true; return true;
} }

View file

@ -58,10 +58,16 @@ public class SheoldredWhisperingOne extends CardImpl {
this.power = new MageInt(6); this.power = new MageInt(6);
this.toughness = new MageInt(6); this.toughness = new MageInt(6);
// Swampwalk
this.addAbility(new SwampwalkAbility()); this.addAbility(new SwampwalkAbility());
// At the beginning of your upkeep, return target creature card from your graveyard to the battlefield.
Ability ability = new BeginningOfUpkeepTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect(false), TargetController.YOU, false); Ability ability = new BeginningOfUpkeepTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect(false), TargetController.YOU, false);
ability.addTarget(new TargetCardInYourGraveyard(new FilterCreatureCard("creature card from your graveyard"))); ability.addTarget(new TargetCardInYourGraveyard(new FilterCreatureCard("creature card from your graveyard")));
this.addAbility(ability); this.addAbility(ability);
// At the beginning of each opponent's upkeep, that player sacrifices a creature.
ability = new BeginningOfUpkeepTriggeredAbility(new SacrificeEffect(new FilterCreaturePermanent(), 1, "that player "), TargetController.OPPONENT, false); ability = new BeginningOfUpkeepTriggeredAbility(new SacrificeEffect(new FilterCreaturePermanent(), 1, "that player "), TargetController.OPPONENT, false);
this.addAbility(ability); this.addAbility(ability);
} }

View file

@ -45,6 +45,7 @@ public class DiabolicEdict extends CardImpl {
super(ownerId, 22, "Diabolic Edict", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{1}{B}"); super(ownerId, 22, "Diabolic Edict", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{1}{B}");
this.expansionSetCode = "TMP"; this.expansionSetCode = "TMP";
// Target player sacrifices a creature.
this.getSpellAbility().addEffect(new SacrificeEffect(new FilterCreaturePermanent(), 1, "Target player")); this.getSpellAbility().addEffect(new SacrificeEffect(new FilterCreaturePermanent(), 1, "Target player"));
this.getSpellAbility().addTarget(new TargetPlayer()); this.getSpellAbility().addTarget(new TargetPlayer());
} }

View file

@ -0,0 +1,54 @@
/*
* 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.AI.basic;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBaseAI;
/**
*
* @author LevelX2
*/
public class CastCreaturesTest extends CardTestPlayerBaseAI {
/**
* Tests that the creature is cast if enough mana is available
*/
@Test
public void testSimpleCast() {
addCard(Zone.HAND, playerA, "Silvercoat Lion");
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, "Silvercoat Lion", 1);
}
}

View file

@ -41,12 +41,16 @@ public class SewerNemesisTest extends CardTestPlayerBase {
/** /**
* Sewer Nemesis count's all cards in each player's graveyard to determine it's * / *, not just the cosen player's graveyard. * BUG: Sewer Nemesis count's all cards in each player's graveyard to determine it's * / *, not just the chosen player's graveyard.
* *
*/ */
@Test @Test
public void test1() { public void test1() {
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4); addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4);
// As Sewer Nemesis enters the battlefield, choose a player.
// Sewer Nemesis's power and toughness are each equal to the number of cards in the chosen player's graveyard.
// Whenever the chosen player casts a spell, that player puts the top card of his or her library into his or her graveyard.
addCard(Zone.HAND, playerA, "Sewer Nemesis"); addCard(Zone.HAND, playerA, "Sewer Nemesis");
addCard(Zone.GRAVEYARD, playerA, "Raging Goblin",4); addCard(Zone.GRAVEYARD, playerA, "Raging Goblin",4);

View file

@ -18,18 +18,25 @@ public class SigardaHostOfHeronsTest extends CardTestPlayerBase {
*/ */
@Test @Test
public void testCard() { public void testCard() {
// Spells and abilities your opponents control can't cause you to sacrifice permanents.
addCard(Zone.BATTLEFIELD, playerA, "Sigarda, Host of Herons"); addCard(Zone.BATTLEFIELD, playerA, "Sigarda, Host of Herons");
// {T}, Tap two untapped Humans you control: Exile target artifact or enchantment.
addCard(Zone.BATTLEFIELD, playerA, "Devout Chaplain"); addCard(Zone.BATTLEFIELD, playerA, "Devout Chaplain");
// {2}{B}, Sacrifice a creature: Target opponent reveals his or her hand. You choose a card from it. That player discards that card. Activate this ability only any time you could cast a sorcery.
addCard(Zone.BATTLEFIELD, playerA, "Corpse Traders"); addCard(Zone.BATTLEFIELD, playerA, "Corpse Traders");
// Target player sacrifices a creature.
addCard(Zone.HAND, playerA, "Diabolic Edict"); addCard(Zone.HAND, playerA, "Diabolic Edict");
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2);
addCard(Zone.HAND, playerB, "Diabolic Edict"); addCard(Zone.HAND, playerB, "Diabolic Edict");
addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2); addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2);
// At the beginning of your upkeep, return target creature card from your graveyard to the battlefield.
// At the beginning of each opponent's upkeep, that player sacrifices a creature.
addCard(Zone.BATTLEFIELD, playerB, "Sheoldred, Whispering One"); addCard(Zone.BATTLEFIELD, playerB, "Sheoldred, Whispering One");
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Diabolic Edict", playerA); castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Diabolic Edict", playerA); // sacrificing for player A prevented by Sigarda
castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Diabolic Edict", playerB); castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Diabolic Edict", playerB); // playerB has to sacrifice Sheldred
setStopAt(3, PhaseStep.END_TURN); setStopAt(3, PhaseStep.END_TURN);
execute(); execute();
@ -40,9 +47,13 @@ public class SigardaHostOfHeronsTest extends CardTestPlayerBase {
assertPermanentCount(playerA, "Sigarda, Host of Herons", 1); assertPermanentCount(playerA, "Sigarda, Host of Herons", 1);
assertPermanentCount(playerA, "Devout Chaplain", 1); assertPermanentCount(playerA, "Devout Chaplain", 1);
assertPermanentCount(playerA, "Corpse Traders", 1); assertPermanentCount(playerA, "Corpse Traders", 1);
assertGraveyardCount(playerA, "Diabolic Edict", 1);
assertGraveyardCount(playerA, 1); assertGraveyardCount(playerA, 1);
assertPermanentCount(playerB, "Sheoldred, Whispering One", 0); assertPermanentCount(playerB, "Sheoldred, Whispering One", 0);
assertHandCount(playerB, "Diabolic Edict", 0);
assertGraveyardCount(playerB, "Sheoldred, Whispering One", 1);
assertGraveyardCount(playerB, "Diabolic Edict", 1);
assertGraveyardCount(playerB, 2); assertGraveyardCount(playerB, 2);
} }

View file

@ -102,16 +102,18 @@ public class RandomPlayer extends ComputerPlayer {
List<Ability> playables = getPlayableAbilities(game); List<Ability> playables = getPlayableAbilities(game);
Ability ability; Ability ability;
while (true) { while (true) {
if (playables.size() == 1) if (playables.size() == 1) {
ability = playables.get(0); ability = playables.get(0);
else } else {
ability = playables.get(rnd.nextInt(playables.size())); ability = playables.get(rnd.nextInt(playables.size()));
}
List<Ability> options = getPlayableOptions(ability, game); List<Ability> options = getPlayableOptions(ability, game);
if (!options.isEmpty()) { if (!options.isEmpty()) {
if (options.size() == 1) if (options.size() == 1) {
ability = options.get(0); ability = options.get(0);
else } else {
ability = options.get(rnd.nextInt(options.size())); ability = options.get(rnd.nextInt(options.size()));
}
} }
if (ability.getManaCosts().getVariableCosts().size() > 0) { if (ability.getManaCosts().getVariableCosts().size() > 0) {
int amount = getAvailableManaProducers(game).size() - ability.getManaCosts().convertedManaCost(); int amount = getAvailableManaProducers(game).size() - ability.getManaCosts().convertedManaCost();
@ -154,10 +156,11 @@ public class RandomPlayer extends ComputerPlayer {
ability = source; ability = source;
} }
else { else {
if (options.size() == 1) if (options.size() == 1) {
ability = options.get(0); ability = options.get(0);
else } else {
ability = options.get(rnd.nextInt(options.size())); ability = options.get(rnd.nextInt(options.size()));
}
} }
if (ability.isUsesStack()) { if (ability.isUsesStack()) {
game.getStack().push(new StackAbility(ability, playerId)); game.getStack().push(new StackAbility(ability, playerId));
@ -203,15 +206,18 @@ public class RandomPlayer extends ComputerPlayer {
@Override @Override
public void selectBlockers(Game game, UUID defendingPlayerId) { public void selectBlockers(Game game, UUID defendingPlayerId) {
int numGroups = game.getCombat().getGroups().size(); int numGroups = game.getCombat().getGroups().size();
if (numGroups == 0) return; if (numGroups == 0) {
return;
}
List<Permanent> blockers = getAvailableBlockers(game); List<Permanent> blockers = getAvailableBlockers(game);
for (Permanent blocker: blockers) { for (Permanent blocker: blockers) {
int check = rnd.nextInt(numGroups + 1); int check = rnd.nextInt(numGroups + 1);
if (check < numGroups) { if (check < numGroups) {
CombatGroup group = game.getCombat().getGroups().get(check); CombatGroup group = game.getCombat().getGroups().get(check);
if (group.getAttackers().size() > 0) if (group.getAttackers().size() > 0) {
this.declareBlocker(this.getId(), blocker.getId(), group.getAttackers().get(0), game); this.declareBlocker(this.getId(), blocker.getId(), group.getAttackers().get(0), game);
}
} }
} }
actionCount++; actionCount++;
@ -243,8 +249,9 @@ public class RandomPlayer extends ComputerPlayer {
protected boolean chooseRandomTarget(Target target, Ability source, Game game) { protected boolean chooseRandomTarget(Target target, Ability source, Game game) {
Set<UUID> possibleTargets = target.possibleTargets(source==null?null:source.getSourceId(), playerId, game); Set<UUID> possibleTargets = target.possibleTargets(source==null?null:source.getSourceId(), playerId, game);
if (possibleTargets.isEmpty()) if (possibleTargets.isEmpty()) {
return false; return false;
}
if (!target.isRequired(source)) { if (!target.isRequired(source)) {
if (rnd.nextInt(possibleTargets.size() + 1) == 0) { if (rnd.nextInt(possibleTargets.size() + 1) == 0) {
return false; return false;
@ -300,8 +307,9 @@ public class RandomPlayer extends ComputerPlayer {
@Override @Override
public boolean chooseTarget(Outcome outcome, Cards cards, TargetCard target, Ability source, Game game) { public boolean chooseTarget(Outcome outcome, Cards cards, TargetCard target, Ability source, Game game) {
if (cards.isEmpty()) if (cards.isEmpty()) {
return !target.isRequired(source); return !target.isRequired(source);
}
Card card = cards.getRandom(game); Card card = cards.getRandom(game);
target.addTarget(card.getId(), source, game); target.addTarget(card.getId(), source, game);
return true; return true;
@ -373,8 +381,9 @@ public class RandomPlayer extends ComputerPlayer {
public Mode chooseMode(Modes modes, Ability source, Game game) { public Mode chooseMode(Modes modes, Ability source, Game game) {
Iterator<Mode> it = modes.values().iterator(); Iterator<Mode> it = modes.values().iterator();
Mode mode = it.next(); Mode mode = it.next();
if (modes.size() == 1) if (modes.size() == 1) {
return mode; return mode;
}
int modeNum = rnd.nextInt(modes.values().size()); int modeNum = rnd.nextInt(modes.values().size());
for (int i = 0; i < modeNum; i++) { for (int i = 0; i < modeNum; i++) {
mode = it.next(); mode = it.next();

File diff suppressed because it is too large Load diff

View file

@ -1,13 +1,14 @@
package org.mage.test.serverside.base; package org.mage.test.serverside.base;
import mage.constants.PhaseStep; import java.util.List;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.constants.PhaseStep;
import mage.constants.Zone; import mage.constants.Zone;
import mage.filter.Filter; import mage.filter.Filter;
import mage.players.Player; import mage.players.Player;
import org.mage.test.player.TestPlayer; import org.mage.test.player.TestPlayer;
import java.util.List;
/** /**
* Interface for all test initialization and assertion operations. * Interface for all test initialization and assertion operations.
@ -81,11 +82,14 @@ public interface CardTestAPI {
/** /**
* Define turn number to stop the game on. * Define turn number to stop the game on.
* @param turn
*/ */
void setStopOnTurn(int turn); void setStopOnTurn(int turn);
/** /**
* Define the turn number and step to stop the game on. * Define the turn number and step to stop the game on.
* @param turn
* @param step
*/ */
void setStopAt(int turn, PhaseStep step); void setStopAt(int turn, PhaseStep step);

View file

@ -0,0 +1,69 @@
/*
* 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.serverside.base;
import java.io.FileNotFoundException;
import mage.constants.MultiplayerAttackOption;
import mage.constants.RangeOfInfluence;
import mage.game.Game;
import mage.game.GameException;
import mage.game.TwoPlayerDuel;
import mage.player.ai.ComputerPlayer7;
import org.mage.test.player.TestPlayer;
import org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl;
/**
*
* @author LevelX2
*/
public abstract class CardTestPlayerBaseAI extends CardTestPlayerAPIImpl {
int skill = 9;
@Override
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
Game game = new TwoPlayerDuel(MultiplayerAttackOption.LEFT, RangeOfInfluence.ONE, 0, 20);
playerA = createPlayer(game, playerA, "PlayerA");
playerB = createPlayer(game, playerB, "PlayerB");
return game;
}
@Override
protected TestPlayer createPlayer(String name) {
if (name.equals("PlayerA")) {
return new TestPlayer(new ComputerPlayer7("PlayerA", RangeOfInfluence.ONE, skill));
}
return super.createPlayer(name);
}
public void setAISkill(int skill) {
this.skill = skill;
}
}

View file

@ -31,7 +31,6 @@ import java.io.FilenameFilter;
import java.util.*; import java.util.*;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import static org.mage.test.serverside.base.MageTestPlayerBase.currentGame;
/** /**
* Base class for all tests. * Base class for all tests.

View file

@ -28,6 +28,7 @@ import java.io.FilenameFilter;
import java.util.*; import java.util.*;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import mage.player.ai.ComputerPlayer;
/** /**
* Base class for all tests. * Base class for all tests.
@ -260,14 +261,15 @@ public abstract class MageTestPlayerBase {
} }
private TestPlayer getPlayer(String name) { private TestPlayer getPlayer(String name) {
if (name.equals("ComputerA")) { switch (name) {
return playerA; case "ComputerA":
} else if (name.equals("ComputerB")) { return playerA;
return playerB; case "ComputerB":
} else if (name.equals("ComputerC")) { return playerB;
return playerC; case "ComputerC":
} else if (name.equals("ComputerD")) { return playerC;
return playerD; case "ComputerD":
return playerD;
} }
throw new IllegalArgumentException("Couldn't find player for name=" + name); throw new IllegalArgumentException("Couldn't find player for name=" + name);
} }
@ -339,6 +341,7 @@ public abstract class MageTestPlayerBase {
} }
protected TestPlayer createPlayer(String name) { protected TestPlayer createPlayer(String name) {
return new TestPlayer(name, RangeOfInfluence.ONE); return new TestPlayer(new ComputerPlayer(name, RangeOfInfluence.ONE));
} }
} }

View file

@ -28,6 +28,7 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP
/** /**
* Default game initialization params for red player (that plays with Mountains) * Default game initialization params for red player (that plays with Mountains)
*/ */
@Override
public void useRedDefault() { public void useRedDefault() {
// *** ComputerA *** // *** ComputerA ***
// battlefield:ComputerA:Mountain:5 // battlefield:ComputerA:Mountain:5
@ -88,6 +89,7 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP
* @param player {@link Player} to add cards for. Use either playerA or playerB. * @param player {@link Player} to add cards for. Use either playerA or playerB.
* @param cardName Card name in string format. * @param cardName Card name in string format.
*/ */
@Override
public void addCard(Zone gameZone, TestPlayer player, String cardName) { public void addCard(Zone gameZone, TestPlayer player, String cardName) {
addCard(gameZone, player, cardName, 1, false); addCard(gameZone, player, cardName, 1, false);
} }
@ -100,6 +102,7 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP
* @param cardName Card name in string format. * @param cardName Card name in string format.
* @param count Amount of cards to be added. * @param count Amount of cards to be added.
*/ */
@Override
public void addCard(Zone gameZone, TestPlayer player, String cardName, int count) { public void addCard(Zone gameZone, TestPlayer player, String cardName, int count) {
addCard(gameZone, player, cardName, count, false); addCard(gameZone, player, cardName, count, false);
} }
@ -114,6 +117,7 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP
* @param tapped In case gameZone is Battlefield, determines whether permanent should be tapped. * @param tapped In case gameZone is Battlefield, determines whether permanent should be tapped.
* In case gameZone is other than Battlefield, {@link IllegalArgumentException} is thrown * In case gameZone is other than Battlefield, {@link IllegalArgumentException} is thrown
*/ */
@Override
public void addCard(Zone gameZone, TestPlayer player, String cardName, int count, boolean tapped) { public void addCard(Zone gameZone, TestPlayer player, String cardName, int count, boolean tapped) {
@ -179,6 +183,7 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP
* @param player {@link Player} to set life count for. * @param player {@link Player} to set life count for.
* @param life Life count to set. * @param life Life count to set.
*/ */
@Override
public void setLife(TestPlayer player, int life) { public void setLife(TestPlayer player, int life) {
if (player.equals(playerA)) { if (player.equals(playerA)) {
commandsA.put(Zone.OUTSIDE, "life:" + String.valueOf(life)); commandsA.put(Zone.OUTSIDE, "life:" + String.valueOf(life));
@ -190,16 +195,18 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP
/** /**
* Define turn number to stop the game on. * Define turn number to stop the game on.
*/ */
@Override
public void setStopOnTurn(int turn) { public void setStopOnTurn(int turn) {
stopOnTurn = turn == -1 ? null : Integer.valueOf(turn); stopOnTurn = turn == -1 ? null : turn;
stopAtStep = PhaseStep.UNTAP; stopAtStep = PhaseStep.UNTAP;
} }
/** /**
* Define turn number and step to stop the game on. * Define turn number and step to stop the game on.
*/ */
@Override
public void setStopAt(int turn, PhaseStep step) { public void setStopAt(int turn, PhaseStep step) {
stopOnTurn = turn == -1 ? null : Integer.valueOf(turn); stopOnTurn = turn == -1 ? null : turn;
stopAtStep = step; stopAtStep = step;
} }
@ -208,6 +215,7 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP
* *
* @param turn Expected turn number to compare with. 1-based. * @param turn Expected turn number to compare with. 1-based.
*/ */
@Override
public void assertTurn(int turn) throws AssertionError { public void assertTurn(int turn) throws AssertionError {
Assert.assertEquals("Turn numbers are not equal", turn, currentGame.getTurnNum()); Assert.assertEquals("Turn numbers are not equal", turn, currentGame.getTurnNum());
} }
@ -217,21 +225,28 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP
* *
* @param result Expected {@link GameResult} to compare with. * @param result Expected {@link GameResult} to compare with.
*/ */
@Override
public void assertResult(Player player, GameResult result) throws AssertionError { public void assertResult(Player player, GameResult result) throws AssertionError {
if (player.equals(playerA)) { if (player.equals(playerA)) {
GameResult actual = CardTestAPI.GameResult.DRAW; GameResult actual = CardTestAPI.GameResult.DRAW;
if (currentGame.getWinner().equals("Player PlayerA is the winner")) { switch (currentGame.getWinner()) {
actual = CardTestAPI.GameResult.WON; case "Player PlayerA is the winner":
} else if (currentGame.getWinner().equals("Player PlayerB is the winner")) { actual = CardTestAPI.GameResult.WON;
actual = CardTestAPI.GameResult.LOST; break;
case "Player PlayerB is the winner":
actual = CardTestAPI.GameResult.LOST;
break;
} }
Assert.assertEquals("Game results are not equal", result, actual); Assert.assertEquals("Game results are not equal", result, actual);
} else if (player.equals(playerB)) { } else if (player.equals(playerB)) {
GameResult actual = CardTestAPI.GameResult.DRAW; GameResult actual = CardTestAPI.GameResult.DRAW;
if (currentGame.getWinner().equals("Player PlayerB is the winner")) { switch (currentGame.getWinner()) {
actual = CardTestAPI.GameResult.WON; case "Player PlayerB is the winner":
} else if (currentGame.getWinner().equals("Player PlayerA is the winner")) { actual = CardTestAPI.GameResult.WON;
actual = CardTestAPI.GameResult.LOST; break;
case "Player PlayerA is the winner":
actual = CardTestAPI.GameResult.LOST;
break;
} }
Assert.assertEquals("Game results are not equal", result, actual); Assert.assertEquals("Game results are not equal", result, actual);
} }
@ -243,6 +258,7 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP
* @param player {@link Player} to get life for comparison. * @param player {@link Player} to get life for comparison.
* @param life Expected player's life to compare with. * @param life Expected player's life to compare with.
*/ */
@Override
public void assertLife(Player player, int life) throws AssertionError { public void assertLife(Player player, int life) throws AssertionError {
int actual = currentGame.getPlayer(player.getId()).getLife(); int actual = currentGame.getPlayer(player.getId()).getLife();
Assert.assertEquals("Life amounts are not equal", life, actual); Assert.assertEquals("Life amounts are not equal", life, actual);
@ -265,6 +281,7 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP
* @param scope {@link mage.filter.Filter.ComparisonScope} Use ANY, if you want "at least one creature with given name should have specified p\t" * @param scope {@link mage.filter.Filter.ComparisonScope} Use ANY, if you want "at least one creature with given name should have specified p\t"
* Use ALL, if you want "all creature with gived name should have specified p\t" * Use ALL, if you want "all creature with gived name should have specified p\t"
*/ */
@Override
public void assertPowerToughness(Player player, String cardName, int power, int toughness, Filter.ComparisonScope scope) public void assertPowerToughness(Player player, String cardName, int power, int toughness, Filter.ComparisonScope scope)
throws AssertionError { throws AssertionError {
int count = 0; int count = 0;
@ -298,6 +315,7 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override
public void assertAbilities(Player player, String cardName, List<Ability> abilities) public void assertAbilities(Player player, String cardName, List<Ability> abilities)
throws AssertionError { throws AssertionError {
int count = 0; int count = 0;
@ -326,6 +344,7 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP
* @param player {@link Player} which permanents should be counted. * @param player {@link Player} which permanents should be counted.
* @param count Expected count. * @param count Expected count.
*/ */
@Override
public void assertPermanentCount(Player player, int count) throws AssertionError { public void assertPermanentCount(Player player, int count) throws AssertionError {
int actualCount = 0; int actualCount = 0;
for (Permanent permanent : currentGame.getBattlefield().getAllPermanents()) { for (Permanent permanent : currentGame.getBattlefield().getAllPermanents()) {
@ -343,6 +362,7 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP
* @param cardName Name of the cards that should be counted. * @param cardName Name of the cards that should be counted.
* @param count Expected count. * @param count Expected count.
*/ */
@Override
public void assertPermanentCount(Player player, String cardName, int count) throws AssertionError { public void assertPermanentCount(Player player, String cardName, int count) throws AssertionError {
int actualCount = 0; int actualCount = 0;
for (Permanent permanent : currentGame.getBattlefield().getAllPermanents()) { for (Permanent permanent : currentGame.getBattlefield().getAllPermanents()) {

View file

@ -1,6 +1,8 @@
package org.mage.test.serverside.base.impl; package org.mage.test.serverside.base.impl;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.util.List;
import java.util.UUID;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.cards.Card; import mage.cards.Card;
import mage.cards.decks.Deck; import mage.cards.decks.Deck;
@ -18,20 +20,18 @@ import mage.filter.predicate.mageobject.NamePredicate;
import mage.game.ExileZone; import mage.game.ExileZone;
import mage.game.Game; import mage.game.Game;
import mage.game.GameException; import mage.game.GameException;
import mage.game.GameOptions;
import mage.game.command.CommandObject; import mage.game.command.CommandObject;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
import mage.game.permanent.PermanentCard; import mage.game.permanent.PermanentCard;
import mage.players.Player; import mage.players.Player;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before;
import org.mage.test.player.TestPlayer; import org.mage.test.player.TestPlayer;
import org.mage.test.serverside.base.CardTestAPI; import org.mage.test.serverside.base.CardTestAPI;
import org.mage.test.serverside.base.CardTestAPI.GameResult;
import org.mage.test.serverside.base.MageTestPlayerBase; import org.mage.test.serverside.base.MageTestPlayerBase;
import java.util.List;
import java.util.UUID;
import mage.game.GameOptions;
import org.junit.Before;
/** /**
* API for test initialization and asserting the test results. * API for test initialization and asserting the test results.
* *
@ -213,7 +213,6 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
* *
* @param player {@link Player} to remove all library cards from. * @param player {@link Player} to remove all library cards from.
*/ */
@Override
public void removeAllCardsFromLibrary(TestPlayer player) { public void removeAllCardsFromLibrary(TestPlayer player) {
getCommands(player).put(Zone.LIBRARY, "clear"); getCommands(player).put(Zone.LIBRARY, "clear");
} }
@ -235,7 +234,6 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
* @param player {@link Player} to add cards for. Use either playerA or playerB. * @param player {@link Player} to add cards for. Use either playerA or playerB.
* @param cardName Card name in string format. * @param cardName Card name in string format.
*/ */
@Override
public void addCard(Zone gameZone, TestPlayer player, String cardName) { public void addCard(Zone gameZone, TestPlayer player, String cardName) {
addCard(gameZone, player, cardName, 1, false); addCard(gameZone, player, cardName, 1, false);
} }
@ -248,7 +246,6 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
* @param cardName Card name in string format. * @param cardName Card name in string format.
* @param count Amount of cards to be added. * @param count Amount of cards to be added.
*/ */
@Override
public void addCard(Zone gameZone, TestPlayer player, String cardName, int count) { public void addCard(Zone gameZone, TestPlayer player, String cardName, int count) {
addCard(gameZone, player, cardName, count, false); addCard(gameZone, player, cardName, count, false);
} }
@ -263,7 +260,6 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
* @param tapped In case gameZone is Battlefield, determines whether permanent should be tapped. * @param tapped In case gameZone is Battlefield, determines whether permanent should be tapped.
* In case gameZone is other than Battlefield, {@link IllegalArgumentException} is thrown * In case gameZone is other than Battlefield, {@link IllegalArgumentException} is thrown
*/ */
@Override
public void addCard(Zone gameZone, TestPlayer player, String cardName, int count, boolean tapped) { public void addCard(Zone gameZone, TestPlayer player, String cardName, int count, boolean tapped) {
if (gameZone.equals(Zone.BATTLEFIELD)) { if (gameZone.equals(Zone.BATTLEFIELD)) {
@ -318,7 +314,6 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
* @param player {@link Player} to set life count for. * @param player {@link Player} to set life count for.
* @param life Life count to set. * @param life Life count to set.
*/ */
@Override
public void setLife(TestPlayer player, int life) { public void setLife(TestPlayer player, int life) {
getCommands(player).put(Zone.OUTSIDE, "life:" + String.valueOf(life)); getCommands(player).put(Zone.OUTSIDE, "life:" + String.valueOf(life));
} }

View file

@ -90,7 +90,6 @@ public class SacrificeEffect extends OneShotEffect{
// A spell or ability could have removed the only legal target this player // A spell or ability could have removed the only legal target this player
// had, if thats the case this ability should fizzle. // had, if thats the case this ability should fizzle.
if (amount > 0 && target.canChoose(source.getSourceId(), player.getId(), game)) { if (amount > 0 && target.canChoose(source.getSourceId(), player.getId(), game)) {
boolean abilityApplied = false;
while (!target.isChosen() && target.canChoose(player.getId(), game) && player.isInGame()) { while (!target.isChosen() && target.canChoose(player.getId(), game) && player.isInGame()) {
player.chooseTarget(Outcome.Sacrifice, target, source, game); player.chooseTarget(Outcome.Sacrifice, target, source, game);
} }
@ -99,11 +98,11 @@ public class SacrificeEffect extends OneShotEffect{
Permanent permanent = game.getPermanent(target.getTargets().get(idx)); Permanent permanent = game.getPermanent(target.getTargets().get(idx));
if ( permanent != null ) { if ( permanent != null ) {
abilityApplied |= permanent.sacrifice(source.getSourceId(), game); permanent.sacrifice(source.getSourceId(), game);
} }
} }
return abilityApplied; return true;
} }
return false; return false;
} }

View file

@ -2625,7 +2625,7 @@ public abstract class GameImpl implements Game, Serializable {
playerObject.abort(); playerObject.abort();
} }
} }
state.restore(restore); state.restoreForRollBack(restore);
// because restore uses the objects without copy each copy the state again // because restore uses the objects without copy each copy the state again
gameStatesRollBack.put(getTurnNum(), state.copy()); gameStatesRollBack.put(getTurnNum(), state.copy());
executingRollback = true; executingRollback = true;

View file

@ -175,11 +175,14 @@ public class GameState implements Serializable, Copyable<GameState> {
this.permanentOrderNumber = state.permanentOrderNumber; this.permanentOrderNumber = state.permanentOrderNumber;
} }
public void restoreForRollBack(GameState state) {
restore(state);
this.turn = state.turn;
}
public void restore(GameState state) { public void restore(GameState state) {
this.activePlayerId = state.activePlayerId; this.activePlayerId = state.activePlayerId;
this.priorityPlayerId = state.priorityPlayerId; this.priorityPlayerId = state.priorityPlayerId;
this.turn = state.turn;
this.stack = state.stack; this.stack = state.stack;
this.command = state.command; this.command = state.command;
this.exile = state.exile; this.exile = state.exile;

View file

@ -49,6 +49,7 @@ import mage.abilities.costs.AlternativeSourceCosts;
import mage.abilities.costs.VariableCost; import mage.abilities.costs.VariableCost;
import mage.abilities.costs.mana.ManaCost; import mage.abilities.costs.mana.ManaCost;
import mage.abilities.costs.mana.ManaCosts; import mage.abilities.costs.mana.ManaCosts;
import mage.abilities.mana.ManaOptions;
import mage.cards.Card; import mage.cards.Card;
import mage.cards.Cards; import mage.cards.Cards;
import mage.cards.decks.Deck; import mage.cards.decks.Deck;
@ -377,9 +378,11 @@ public interface Player extends MageItem, Copyable<Player> {
void phasing(Game game); void phasing(Game game);
void untap(Game game); void untap(Game game);
ManaOptions getManaAvailable(Game game);
List<Ability> getPlayable(Game game, boolean hidden); List<Ability> getPlayable(Game game, boolean hidden);
List<Ability> getPlayableOptions(Ability ability, Game game); List<Ability> getPlayableOptions(Ability ability, Game game);
Set<UUID> getPlayableInHand(Game game); Set<UUID> getPlayableInHand(Game game);
LinkedHashMap<UUID, ActivatedAbility> getUseableActivatedAbilities(MageObject object, Zone zone, Game game); LinkedHashMap<UUID, ActivatedAbility> getUseableActivatedAbilities(MageObject object, Zone zone, Game game);

View file

@ -1089,7 +1089,6 @@ public abstract class PlayerImpl implements Player, Serializable {
@Override @Override
public boolean activateAbility(ActivatedAbility ability, Game game) { public boolean activateAbility(ActivatedAbility ability, Game game) {
getManaPool().setStock(); // needed for the "mana already in the pool has to be used manually" option
boolean result; boolean result;
if (ability instanceof PassAbility) { if (ability instanceof PassAbility) {
pass(game); pass(game);
@ -1156,7 +1155,7 @@ public abstract class PlayerImpl implements Player, Serializable {
return true; return true;
} }
} }
game.restoreState(bookmark, source.getRule()); game.restoreState(bookmark, source.getRule()); // why restore is needed here?
return false; return false;
} }
@ -2149,7 +2148,8 @@ public abstract class PlayerImpl implements Player, Serializable {
return blockers; return blockers;
} }
protected ManaOptions getManaAvailable(Game game) { @Override
public ManaOptions getManaAvailable(Game game) {
ManaOptions available = new ManaOptions(); ManaOptions available = new ManaOptions();
List<Abilities<ManaAbility>> sourceWithoutCosts = new ArrayList<>(); List<Abilities<ManaAbility>> sourceWithoutCosts = new ArrayList<>();
@ -2207,7 +2207,7 @@ public abstract class PlayerImpl implements Player, Serializable {
} }
// returns only mana producers that require mana payment // returns only mana producers that require mana payment
protected List<Permanent> getAvailableManaProducersWithCost(Game game) { public List<Permanent> getAvailableManaProducersWithCost(Game game) {
List<Permanent> result = new ArrayList<>(); List<Permanent> result = new ArrayList<>();
for (Permanent permanent : game.getBattlefield().getAllActivePermanents(playerId)) { for (Permanent permanent : game.getBattlefield().getAllActivePermanents(playerId)) {
for (ManaAbility ability : permanent.getAbilities().getManaAbilities(Zone.BATTLEFIELD)) { for (ManaAbility ability : permanent.getAbilities().getManaAbilities(Zone.BATTLEFIELD)) {