mirror of
https://github.com/correl/mage.git
synced 2024-11-14 19:19:32 +00:00
Test framework: added support for flip coin tests (command: setFlipCoinResult);
This commit is contained in:
parent
fde24f349f
commit
c1dfbbda63
12 changed files with 162 additions and 24 deletions
|
@ -30,7 +30,7 @@ public final class WireflyHive extends CardImpl {
|
|||
public WireflyHive(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}");
|
||||
|
||||
// {3}, {tap}: Flip a coin. If you win the flip, create a 2/2 colorless Insect artifact creature token with flying named Wirefly.
|
||||
// {3}, {T}: Flip a coin. If you win the flip, create a 2/2 colorless Insect artifact creature token with flying named Wirefly.
|
||||
// If you lose the flip, destroy all permanents named Wirefly.
|
||||
Ability ability = new SimpleActivatedAbility(new FlipCoinEffect(
|
||||
new CreateTokenEffect(new WireflyToken()), new DestroyAllEffect(filter)
|
||||
|
|
|
@ -29,6 +29,7 @@ public class IdentityThiefTest extends CardTestPlayerBase {
|
|||
addCard(Zone.BATTLEFIELD, playerB, "Identity Thief"); // {2}{U}{U}
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Molten Sentry");
|
||||
setFlipCoinResult(playerA, true);
|
||||
|
||||
attack(2, playerB, "Identity Thief");
|
||||
setChoice(playerB, "Yes");
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
package org.mage.test.cards.flipcoin;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class FlipCoinTest extends CardTestPlayerBase {
|
||||
|
||||
@Test
|
||||
public void test_RandomResult() {
|
||||
// {3}, {T}: Flip a coin. If you win the flip, create a 2/2 colorless Insect artifact creature token with flying named Wirefly.
|
||||
// If you lose the flip, destroy all permanents named Wirefly.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Wirefly Hive");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
|
||||
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{3}");
|
||||
|
||||
//setStrictChooseMode(true); // normal play without errors
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
}
|
||||
|
||||
@Test(expected = AssertionError.class)
|
||||
public void test_StrictMode_MustFailWithoutResultSetup() {
|
||||
// {3}, {T}: Flip a coin. If you win the flip, create a 2/2 colorless Insect artifact creature token with flying named Wirefly.
|
||||
// If you lose the flip, destroy all permanents named Wirefly.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Wirefly Hive");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
|
||||
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{3}");
|
||||
|
||||
setStrictChooseMode(true); // no coinresult in choices, so it must fail
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_StrictMode_MustWorkWithResultSetup() {
|
||||
// {3}, {T}: Flip a coin. If you win the flip, create a 2/2 colorless Insect artifact creature token with flying named Wirefly.
|
||||
// If you lose the flip, destroy all permanents named Wirefly.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Wirefly Hive");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
|
||||
|
||||
// activates 5 times with same flip coin result
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{3}");
|
||||
setFlipCoinResult(playerA, true);
|
||||
activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{3}");
|
||||
setFlipCoinResult(playerA, true);
|
||||
activateAbility(5, PhaseStep.PRECOMBAT_MAIN, playerA, "{3}");
|
||||
setFlipCoinResult(playerA, true);
|
||||
activateAbility(7, PhaseStep.PRECOMBAT_MAIN, playerA, "{3}");
|
||||
setFlipCoinResult(playerA, true);
|
||||
activateAbility(9, PhaseStep.PRECOMBAT_MAIN, playerA, "{3}");
|
||||
setFlipCoinResult(playerA, true);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(9, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPermanentCount(playerA, "Wirefly", 5);
|
||||
}
|
||||
}
|
|
@ -8,13 +8,10 @@ package org.mage.test.cards.single.iko;
|
|||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
|
||||
|
@ -29,11 +26,14 @@ public class OboshThePreypiercerTest extends CardTestPlayerBase {
|
|||
// Companion — Your starting deck contains only cards with odd converted mana costs and land cards.
|
||||
// If a source you control with an odd converted mana cost would deal damage to a permanent or player, it deals double that damage to that permanent or player instead.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Obosh, the Preypiercer");
|
||||
|
||||
|
||||
// lose the flip
|
||||
setFlipCoinResult(playerA, false);
|
||||
|
||||
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
Assert.assertTrue("Life has to be 20 or 17 but was " + playerA.getLife() , playerA.getLife() == 17 || playerA.getLife() == 20);
|
||||
|
||||
assertLife(playerA, 20 - 3);
|
||||
}
|
||||
}
|
|
@ -12,7 +12,19 @@ import java.util.UUID;
|
|||
* @author JayDi85
|
||||
*/
|
||||
|
||||
// mock class to override to override AI logic for test
|
||||
/**
|
||||
* Mock class to override AI logic for test, cause PlayerImpl uses inner calls for other methods. If you
|
||||
* want to override that methods for tests then call it here.
|
||||
* <p>
|
||||
* It's a workaround and can be bugged (if you catch overflow error with new method then TestPlayer
|
||||
* class must re-implement full method code without computerPlayer calls).
|
||||
* <p>
|
||||
* Example 1: TestPlayer's code uses outer computerPlayer call to discard but discard's inner code must call choose from TestPlayer
|
||||
* Example 2: TestPlayer's code uses outer computerPlayer call to flipCoin but flipCoin's inner code must call flipCoinResult from TestPlayer
|
||||
* <p>
|
||||
* Don't forget to add new methods in another classes like TestComputerPlayer7 or TestComputerPlayerMonteCarlo
|
||||
*/
|
||||
|
||||
public class TestComputerPlayer extends ComputerPlayer {
|
||||
|
||||
private TestPlayer testPlayerLink;
|
||||
|
@ -27,10 +39,13 @@ public class TestComputerPlayer extends ComputerPlayer {
|
|||
|
||||
@Override
|
||||
public boolean choose(Outcome outcome, Target target, UUID sourceId, Game game) {
|
||||
// copy-paste for TestComputerXXX
|
||||
|
||||
// workaround for discard spells
|
||||
// reason: TestPlayer uses outer computerPlayer to discard but inner code uses choose
|
||||
return testPlayerLink.choose(outcome, target, sourceId, game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean flipCoinResult(Game game) {
|
||||
return testPlayerLink.flipCoinResult(game);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -9,10 +9,11 @@ import mage.target.Target;
|
|||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Copy paste methods from TestComputerPlayer, see docs in there
|
||||
*
|
||||
* @author JayDi85
|
||||
*/
|
||||
|
||||
// mock class to override AI logic in tests
|
||||
public class TestComputerPlayer7 extends ComputerPlayer7 {
|
||||
|
||||
private TestPlayer testPlayerLink;
|
||||
|
@ -27,10 +28,11 @@ public class TestComputerPlayer7 extends ComputerPlayer7 {
|
|||
|
||||
@Override
|
||||
public boolean choose(Outcome outcome, Target target, UUID sourceId, Game game) {
|
||||
// copy-paste for TestComputerXXX
|
||||
|
||||
// workaround for discard spells
|
||||
// reason: TestPlayer uses outer computerPlayer to discard but inner code uses choose
|
||||
return testPlayerLink.choose(outcome, target, sourceId, game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean flipCoinResult(Game game) {
|
||||
return testPlayerLink.flipCoinResult(game);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,10 +9,11 @@ import mage.target.Target;
|
|||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Copy paste methods from TestComputerPlayer, see docs in there
|
||||
*
|
||||
* @author JayDi85
|
||||
*/
|
||||
|
||||
// mock class to override AI logic in tests
|
||||
public class TestComputerPlayerMonteCarlo extends ComputerPlayerMCTS {
|
||||
|
||||
private TestPlayer testPlayerLink;
|
||||
|
@ -27,10 +28,11 @@ public class TestComputerPlayerMonteCarlo extends ComputerPlayerMCTS {
|
|||
|
||||
@Override
|
||||
public boolean choose(Outcome outcome, Target target, UUID sourceId, Game game) {
|
||||
// copy-paste for TestComputerXXX
|
||||
|
||||
// workaround for discard spells
|
||||
// reason: TestPlayer uses outer computerPlayer to discard but inner code uses choose
|
||||
return testPlayerLink.choose(outcome, target, sourceId, game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean flipCoinResult(Game game) {
|
||||
return testPlayerLink.flipCoinResult(game);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,6 +54,7 @@ import mage.players.net.UserData;
|
|||
import mage.target.*;
|
||||
import mage.target.common.*;
|
||||
import mage.util.CardUtil;
|
||||
import mage.util.RandomUtil;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Ignore;
|
||||
|
@ -83,6 +84,8 @@ public class TestPlayer implements Player {
|
|||
public static final String BLOCK_SKIP = "[block_skip]";
|
||||
public static final String ATTACK_SKIP = "[attack_skip]";
|
||||
public static final String NO_TARGET = "NO_TARGET"; // cast spell or activate ability without target defines
|
||||
public static final String FLIPCOIN_RESULT_TRUE = "[flipcoin_true]";
|
||||
public static final String FLIPCOIN_RESULT_FALSE = "[flipcoin_false]";
|
||||
|
||||
private int maxCallsWithoutAction = 400;
|
||||
private int foundNoAction = 0;
|
||||
|
@ -3304,6 +3307,25 @@ public class TestPlayer implements Player {
|
|||
return computerPlayer.flipCoin(source, game, true, appliedEffects);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean flipCoinResult(Game game) {
|
||||
assertAliasSupportInChoices(false);
|
||||
if (!choices.isEmpty()) {
|
||||
String nextResult = choices.get(0);
|
||||
if (nextResult.equals(FLIPCOIN_RESULT_TRUE)) {
|
||||
choices.remove(0);
|
||||
return true;
|
||||
} else if (nextResult.equals(FLIPCOIN_RESULT_FALSE)) {
|
||||
choices.remove(0);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
this.chooseStrictModeFailed("flipcoin result", game, "Use setFlipCoinResult to setup it in unit tests");
|
||||
|
||||
// implementation from PlayerImpl:
|
||||
return RandomUtil.nextBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int rollDice(Ability source, Game game, int numSides) {
|
||||
return computerPlayer.rollDice(source, game, numSides);
|
||||
|
|
|
@ -1876,6 +1876,17 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
|
|||
player.addModeChoice(choice);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set next result of next flipCoin's try (one flipCoin event can call multiple tries under some effects)
|
||||
* TestPlayer/ComputerPlayer always selects Heads in good winable events
|
||||
*
|
||||
* @param player
|
||||
* @param result
|
||||
*/
|
||||
public void setFlipCoinResult(TestPlayer player, boolean result) {
|
||||
player.addChoice(result ? TestPlayer.FLIPCOIN_RESULT_TRUE : TestPlayer.FLIPCOIN_RESULT_FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set target permanents
|
||||
*
|
||||
|
|
|
@ -629,6 +629,11 @@ public class PlayerStub implements Player {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean flipCoinResult(Game game) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int rollDice(Ability source, Game game, int numSides) {
|
||||
return 1;
|
||||
|
|
|
@ -444,6 +444,8 @@ public interface Player extends MageItem, Copyable<Player> {
|
|||
|
||||
boolean flipCoin(Ability source, Game game, boolean winnable, List<UUID> appliedEffects);
|
||||
|
||||
boolean flipCoinResult(Game game);
|
||||
|
||||
int rollDice(Ability source, Game game, int numSides);
|
||||
|
||||
int rollDice(Ability source, Game game, List<UUID> appliedEffects, int numSides);
|
||||
|
|
|
@ -2752,7 +2752,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
chosen = this.chooseUse(Outcome.Benefit, "Heads or tails?", "", "Heads", "Tails", source, game);
|
||||
game.informPlayers(getLogName() + " chose " + CardUtil.booleanToFlipName(chosen));
|
||||
}
|
||||
boolean result = RandomUtil.nextBoolean();
|
||||
boolean result = this.flipCoinResult(game);
|
||||
FlipCoinEvent event = new FlipCoinEvent(playerId, source, result, chosen, winnable);
|
||||
event.addAppliedEffects(appliedEffects);
|
||||
game.replaceEvent(event);
|
||||
|
@ -2762,7 +2762,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
boolean canChooseHeads = event.getResult();
|
||||
boolean canChooseTails = !event.getResult();
|
||||
for (int i = 1; i < event.getFlipCount(); i++) {
|
||||
boolean tempFlip = RandomUtil.nextBoolean();
|
||||
boolean tempFlip = this.flipCoinResult(game);
|
||||
canChooseHeads = canChooseHeads || tempFlip;
|
||||
canChooseTails = canChooseTails || !tempFlip;
|
||||
game.informPlayers(getLogName() + " flipped " + CardUtil.booleanToFlipName(tempFlip));
|
||||
|
@ -2789,6 +2789,15 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
return event.getResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return result for next flip coint try (can be contolled in tests)
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean flipCoinResult(Game game) {
|
||||
return RandomUtil.nextBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int rollDice(Ability source, Game game, int numSides) {
|
||||
return this.rollDice(source, game, null, numSides);
|
||||
|
|
Loading…
Reference in a new issue