performance: added day/night performance test for transform ability (disabled by default, see DayNightTest, related to #11285), added day/night rules ref

This commit is contained in:
Oleg Agafonov 2023-11-07 01:25:15 +04:00
parent 70c79fd6a6
commit d6c858ecaf
4 changed files with 68 additions and 0 deletions

View file

@ -4,6 +4,7 @@ import mage.constants.PhaseStep;
import mage.constants.Zone; import mage.constants.Zone;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase; import org.mage.test.serverside.base.CardTestPlayerBase;
@ -331,4 +332,48 @@ public class DayNightTest extends CardTestPlayerBase {
assertDayNight(true); assertDayNight(true);
assertLife(playerB, 20 - 1 - 3 - 3 - 1); assertLife(playerB, 20 - 1 - 3 - 3 - 1);
} }
@Test
@Ignore // debug only, use it to performance profiling only, can be slow
public void test_TransformDayboundPerformance() {
// day/night transform can take too much CPU usage, see https://github.com/magefree/mage/issues/11081
final int TEST_MAX_TURN = 300;
final int TEST_MAX_SIMPLE_CARDS = 50;
final int TEST_MAX_DAYBOUND_CARDS = 15;
// You have no maximum hand size.
playerA.setMaxCallsWithoutAction(10000);
playerB.setMaxCallsWithoutAction(10000);
addCard(Zone.BATTLEFIELD, playerA, "Graceful Adept", 1);
addCard(Zone.BATTLEFIELD, playerB, "Graceful Adept", 1);
// skip draw step
addCard(Zone.BATTLEFIELD, playerA, "Damia, Sage of Stone", 1);
addCard(Zone.BATTLEFIELD, playerB, "Damia, Sage of Stone", 1);
// simple cards
addCard(Zone.BATTLEFIELD, playerA, "Angelfire Crusader", TEST_MAX_SIMPLE_CARDS);
addCard(Zone.BATTLEFIELD, playerB, "Angelfire Crusader", TEST_MAX_SIMPLE_CARDS);
// day/night cards
addCard(Zone.BATTLEFIELD, playerA, "Baneblade Scoundrel", TEST_MAX_DAYBOUND_CARDS);
addCard(Zone.BATTLEFIELD, playerB, "Baneblade Scoundrel", TEST_MAX_DAYBOUND_CARDS);
for (int i = 10; i <= TEST_MAX_TURN; i++) {
if (i % 2 == 0) {
runCode("on turn " + i, i, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> {
// switch to night: auto on untap
// switch to day: here by workaround instead 2+ spells cast
game.setDaytime(!game.checkDayNight(true));
});
}
runCode("on turn " + i, i, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> {
System.out.println(String.format("turn %d, is day: %s", game.getTurnNum(), game.checkDayNight(true) ? "yes" : "no"));
});
}
showBattlefield("after", TEST_MAX_TURN, PhaseStep.PRECOMBAT_MAIN, playerA);
setStrictChooseMode(true);
setStopAt(TEST_MAX_TURN, PhaseStep.END_TURN);
execute();
Assert.assertEquals(TEST_MAX_TURN, currentGame.getTurnNum());
}
} }

View file

@ -51,6 +51,8 @@ class DayboundEffect extends ContinuousEffectImpl {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
if (!game.hasDayNight()) { if (!game.hasDayNight()) {
// 702.145d
// Any time a player controls a permanent with daybound, if its neither day nor night, it becomes day.
game.setDaytime(true); game.setDaytime(true);
} }
return true; return true;

View file

@ -57,6 +57,9 @@ class NightboundEffect extends ContinuousEffectImpl {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
if (!game.hasDayNight()) { if (!game.hasDayNight()) {
// 702.145f
// Any time a player controls a permanent that is back face up with nightbound and its day,
// that player transforms that permanent. This happens immediately and isnt a state-based action.
game.setDaytime(false); game.setDaytime(false);
} }
return true; return true;

View file

@ -29,7 +29,15 @@ public class UntapStep extends Step {
@Override @Override
public void beginStep(Game game, UUID activePlayerId) { public void beginStep(Game game, UUID activePlayerId) {
super.beginStep(game, activePlayerId); super.beginStep(game, activePlayerId);
// 726.2.
// As the second part of the untap step, the game checks the previous turn to see
// if the games day/night designation should change. See rule 502, Untap Step.
//
// Before a player untaps their permanents during the untap step, the game checks to see
// if the day/night designation should change. (2021-09-24)
handleDayNight(game); handleDayNight(game);
Player activePlayer = game.getPlayer(activePlayerId); Player activePlayer = game.getPlayer(activePlayerId);
//20091005 - 502.1/703.4a //20091005 - 502.1/703.4a
activePlayer.phasing(game); activePlayer.phasing(game);
@ -52,8 +60,18 @@ public class UntapStep extends Step {
.getWatcher(CastSpellLastTurnWatcher.class) .getWatcher(CastSpellLastTurnWatcher.class)
.getActivePlayerPrevTurnCount(); .getActivePlayerPrevTurnCount();
if (game.checkDayNight(true) && previousSpells == 0) { if (game.checkDayNight(true) && previousSpells == 0) {
// 726.2a
// If its day and the previous turns active player didnt cast any spells during that turn,
// it becomes night. Multiplayer games using the shared team turns option (see rule 805)
// use a modified rule: if its day and no player from the previous turns active team cast a
// spell during that turn, it becomes night.
game.setDaytime(false); game.setDaytime(false);
} else if (game.checkDayNight(false) && previousSpells >= 2) { } else if (game.checkDayNight(false) && previousSpells >= 2) {
// 726.2b
// If its night, and previous turns active player cast two or more spells during the previous turn,
// it becomes day. Multiplayer games using the shared team turns option (see rule 805) use a modified
// rule: if its night and any player from the previous turns active team cast two or more spells
// during that turn, it becomes day.
game.setDaytime(true); game.setDaytime(true);
} }
} }