From 62d6675be67d2614372c75203d9ed23db66f32ac Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Thu, 1 Jul 2021 18:21:15 +0400 Subject: [PATCH] AI: fixed that computer can't play commanders (#7955); --- .../src/mage/player/ai/ComputerPlayer6.java | 8 ++--- .../java/mage/player/ai/ComputerPlayer.java | 4 ++- .../cards/continuous/CommandersCastTest.java | 33 +++++++++++++++++-- .../java/mage/game/command/Commander.java | 3 +- 4 files changed, 40 insertions(+), 8 deletions(-) diff --git a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer6.java b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer6.java index e9873e6ec9..672de57a5a 100644 --- a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer6.java +++ b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer6.java @@ -215,7 +215,7 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ { logger.trace("Add Action [" + depth + "] " + node.getAbilities().toString() + " a: " + alpha + " b: " + beta); } Game game = node.getGame(); - if (ALLOW_INTERRUPT + if (COMPUTER_DISABLE_TIMEOUT_IN_GAME_SIMULATIONS && Thread.interrupted()) { Thread.currentThread().interrupt(); logger.debug("interrupted"); @@ -435,7 +435,7 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ { pool.execute(task); try { int maxSeconds = maxThink; - if (!ALLOW_INTERRUPT) { + if (!COMPUTER_DISABLE_TIMEOUT_IN_GAME_SIMULATIONS) { maxSeconds = 3600; } logger.debug("maxThink: " + maxSeconds + " seconds "); @@ -460,7 +460,7 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ { } protected int simulatePriority(SimulationNode2 node, Game game, int depth, int alpha, int beta) { - if (ALLOW_INTERRUPT + if (COMPUTER_DISABLE_TIMEOUT_IN_GAME_SIMULATIONS && Thread.interrupted()) { Thread.currentThread().interrupt(); logger.info("interrupted"); @@ -480,7 +480,7 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ { int bestValSubNodes = Integer.MIN_VALUE; for (Ability action : allActions) { counter++; - if (ALLOW_INTERRUPT + if (COMPUTER_DISABLE_TIMEOUT_IN_GAME_SIMULATIONS && Thread.interrupted()) { Thread.currentThread().interrupt(); logger.info("Sim Prio [" + depth + "] -- interrupted"); diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java index 4ccf02ddfe..a65e789930 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java @@ -70,7 +70,9 @@ public class ComputerPlayer extends PlayerImpl implements Player { private long lastThinkTime = 0; // msecs for last AI actions calc protected int PASSIVITY_PENALTY = 5; // Penalty value for doing nothing if some actions are available - protected boolean ALLOW_INTERRUPT = true; // change this for test to false / debugging purposes to false to switch off interrupts while debugging + + // debug only: set TRUE to debug simulation's code/games (on false sim thread will be stopped after few secs by timeout) + protected boolean COMPUTER_DISABLE_TIMEOUT_IN_GAME_SIMULATIONS = false; private transient Map unplayable = new TreeMap<>(); private transient List playableNonInstant = new ArrayList<>(); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/CommandersCastTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/CommandersCastTest.java index c69a8f9172..1eef649caf 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/CommandersCastTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/CommandersCastTest.java @@ -6,14 +6,15 @@ import mage.cards.AdventureCard; import mage.constants.CommanderCardType; import mage.constants.PhaseStep; import mage.constants.Zone; +import mage.game.GameState; import org.junit.Assert; import org.junit.Test; -import org.mage.test.serverside.base.CardTestCommander4Players; +import org.mage.test.serverside.base.CardTestCommander4PlayersWithAIHelps; /** * @author JayDi85 */ -public class CommandersCastTest extends CardTestCommander4Players { +public class CommandersCastTest extends CardTestCommander4PlayersWithAIHelps { @Test public void test_CastToBattlefieldOneTime() { @@ -611,4 +612,32 @@ public class CommandersCastTest extends CardTestCommander4Players { assertPermanentCount(playerA, "Swamp", 1); assertGraveyardCount(playerA, "Uro, Titan of Nature's Wrath", 1); // sacrificed } + + @Test + public void test_AI_MustPlayCommander() { + // Player order: A -> D -> C -> B + + addCard(Zone.COMMAND, playerA, "Balduvian Bears", 1); // {1}{G}, 2/2, commander + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + + // possible bug: wrong copy of commander objects + runCode("test", 1, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> { + GameState copied = game.getState().copy(); + Assert.assertEquals("original commander must have 1 ability", 1, game.getState().getCommand().get(0).getAbilities().size()); + Assert.assertEquals("copied commander must have 1 ability", 1, copied.getCommand().get(0).getAbilities().size()); + }); + + // ai must play commander + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {G}", 2); + aiPlayStep(1, PhaseStep.PRECOMBAT_MAIN, playerA); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertCommandZoneCount(playerA, "Balduvian Bears", 0); + assertPermanentCount(playerA, "Balduvian Bears", 1); + assertTappedCount("Forest", true, 2); + } } diff --git a/Mage/src/main/java/mage/game/command/Commander.java b/Mage/src/main/java/mage/game/command/Commander.java index 9dcb2af84f..0593fa83ec 100644 --- a/Mage/src/main/java/mage/game/command/Commander.java +++ b/Mage/src/main/java/mage/game/command/Commander.java @@ -104,9 +104,10 @@ public class Commander implements CommandObject { } private Commander(final Commander commander) { - this.sourceObject = commander.sourceObject; + this.sourceObject = commander.sourceObject.copy(); this.copy = commander.copy; this.copyFrom = (commander.copyFrom != null ? commander.copyFrom.copy() : null); + this.abilities.addAll(commander.abilities.copy()); } @Override