From d6c858ecaf02ea6ad4644e5333917797b8bc84a6 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Tue, 7 Nov 2023 01:25:15 +0400 Subject: [PATCH] performance: added day/night performance test for transform ability (disabled by default, see DayNightTest, related to #11285), added day/night rules ref --- .../abilities/keywords/DayNightTest.java | 45 +++++++++++++++++++ .../abilities/keyword/DayboundAbility.java | 2 + .../abilities/keyword/NightboundAbility.java | 3 ++ .../main/java/mage/game/turn/UntapStep.java | 18 ++++++++ 4 files changed, 68 insertions(+) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DayNightTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DayNightTest.java index 06d86f06bae..3ce078d3f4d 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DayNightTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DayNightTest.java @@ -4,6 +4,7 @@ import mage.constants.PhaseStep; import mage.constants.Zone; import mage.game.permanent.Permanent; import org.junit.Assert; +import org.junit.Ignore; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; @@ -331,4 +332,48 @@ public class DayNightTest extends CardTestPlayerBase { assertDayNight(true); 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()); + } } diff --git a/Mage/src/main/java/mage/abilities/keyword/DayboundAbility.java b/Mage/src/main/java/mage/abilities/keyword/DayboundAbility.java index 2971b12b291..c6dcfc78542 100644 --- a/Mage/src/main/java/mage/abilities/keyword/DayboundAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/DayboundAbility.java @@ -51,6 +51,8 @@ class DayboundEffect extends ContinuousEffectImpl { @Override public boolean apply(Game game, Ability source) { if (!game.hasDayNight()) { + // 702.145d + // Any time a player controls a permanent with daybound, if it’s neither day nor night, it becomes day. game.setDaytime(true); } return true; diff --git a/Mage/src/main/java/mage/abilities/keyword/NightboundAbility.java b/Mage/src/main/java/mage/abilities/keyword/NightboundAbility.java index 5269cd301bb..171ebf9de54 100644 --- a/Mage/src/main/java/mage/abilities/keyword/NightboundAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/NightboundAbility.java @@ -57,6 +57,9 @@ class NightboundEffect extends ContinuousEffectImpl { @Override public boolean apply(Game game, Ability source) { if (!game.hasDayNight()) { + // 702.145f + // Any time a player controls a permanent that is back face up with nightbound and it’s day, + // that player transforms that permanent. This happens immediately and isn’t a state-based action. game.setDaytime(false); } return true; diff --git a/Mage/src/main/java/mage/game/turn/UntapStep.java b/Mage/src/main/java/mage/game/turn/UntapStep.java index fcaf7db4c92..4f32d680476 100644 --- a/Mage/src/main/java/mage/game/turn/UntapStep.java +++ b/Mage/src/main/java/mage/game/turn/UntapStep.java @@ -29,7 +29,15 @@ public class UntapStep extends Step { @Override public void beginStep(Game game, UUID 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 game’s 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); + Player activePlayer = game.getPlayer(activePlayerId); //20091005 - 502.1/703.4a activePlayer.phasing(game); @@ -52,8 +60,18 @@ public class UntapStep extends Step { .getWatcher(CastSpellLastTurnWatcher.class) .getActivePlayerPrevTurnCount(); if (game.checkDayNight(true) && previousSpells == 0) { + // 726.2a + // If it’s day and the previous turn’s active player didn’t 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 it’s day and no player from the previous turn’s active team cast a + // spell during that turn, it becomes night. game.setDaytime(false); } else if (game.checkDayNight(false) && previousSpells >= 2) { + // 726.2b + // If it’s night, and previous turn’s 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 it’s night and any player from the previous turn’s active team cast two or more spells + // during that turn, it becomes day. game.setDaytime(true); } }