From ebe04dc3a67f51cf8d10c25235eb0476fbb5ea87 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Sun, 12 May 2024 14:37:10 +0400 Subject: [PATCH] tests: added tests for Chandra Ablaze card and some AI logic; --- .../java/mage/player/ai/ComputerPlayer.java | 1 + Mage.Sets/src/mage/cards/c/ChandraAblaze.java | 36 ++--- .../cards/single/zen/ChandraAblazeTest.java | 144 ++++++++++++++++++ 3 files changed, 163 insertions(+), 18 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/single/zen/ChandraAblazeTest.java 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 5939fd6e235..78fc8df8671 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 @@ -169,6 +169,7 @@ public class ComputerPlayer extends PlayerImpl { if (target.getOriginalTarget() instanceof TargetDiscard) { findPlayables(game); + // discard not playable first if (!unplayable.isEmpty()) { for (int i = unplayable.size() - 1; i >= 0; i--) { if (target.canTarget(abilityControllerId, unplayable.values().toArray(new Card[0])[i].getId(), null, game)) { diff --git a/Mage.Sets/src/mage/cards/c/ChandraAblaze.java b/Mage.Sets/src/mage/cards/c/ChandraAblaze.java index 813308966ed..a72c7d1d02c 100644 --- a/Mage.Sets/src/mage/cards/c/ChandraAblaze.java +++ b/Mage.Sets/src/mage/cards/c/ChandraAblaze.java @@ -41,8 +41,8 @@ public final class ChandraAblaze extends CardImpl { this.setStartingLoyalty(5); // +1: Discard a card. If a red card is discarded this way, Chandra Ablaze deals 4 damage to any target. - LoyaltyAbility ability = new LoyaltyAbility(new ChandraAblazeEffect1(), 1); - ability.addEffect(new ChandraAblazeEffect2()); + LoyaltyAbility ability = new LoyaltyAbility(new ChandraAblazeDiscardCardEffect(), 1); + ability.addEffect(new ChandraAblazeDamageEffect()); ability.addTarget(new TargetAnyTarget()); this.addAbility(ability); @@ -54,7 +54,7 @@ public final class ChandraAblaze extends CardImpl { this.addAbility(ability); // -7: Cast any number of red instant and/or sorcery cards from your graveyard without paying their mana costs. - ability = new LoyaltyAbility(new ChandraAblazeEffect5(), -7); + ability = new LoyaltyAbility(new ChandraAblazeCastCardsEffect(), -7); this.addAbility(ability); } @@ -68,20 +68,20 @@ public final class ChandraAblaze extends CardImpl { } } -class ChandraAblazeEffect1 extends OneShotEffect { +class ChandraAblazeDiscardCardEffect extends OneShotEffect { - public ChandraAblazeEffect1() { + public ChandraAblazeDiscardCardEffect() { super(Outcome.Discard); this.staticText = "Discard a card"; } - private ChandraAblazeEffect1(final ChandraAblazeEffect1 effect) { + private ChandraAblazeDiscardCardEffect(final ChandraAblazeDiscardCardEffect effect) { super(effect); } @Override - public ChandraAblazeEffect1 copy() { - return new ChandraAblazeEffect1(this); + public ChandraAblazeDiscardCardEffect copy() { + return new ChandraAblazeDiscardCardEffect(this); } @Override @@ -107,20 +107,20 @@ class ChandraAblazeEffect1 extends OneShotEffect { } } -class ChandraAblazeEffect2 extends OneShotEffect { +class ChandraAblazeDamageEffect extends OneShotEffect { - public ChandraAblazeEffect2() { + public ChandraAblazeDamageEffect() { super(Outcome.Damage); this.staticText = "If a red card is discarded this way, {this} deals 4 damage to any target"; } - private ChandraAblazeEffect2(final ChandraAblazeEffect2 effect) { + private ChandraAblazeDamageEffect(final ChandraAblazeDamageEffect effect) { super(effect); } @Override - public ChandraAblazeEffect2 copy() { - return new ChandraAblazeEffect2(this); + public ChandraAblazeDamageEffect copy() { + return new ChandraAblazeDamageEffect(this); } @Override @@ -143,7 +143,7 @@ class ChandraAblazeEffect2 extends OneShotEffect { } } -class ChandraAblazeEffect5 extends OneShotEffect { +class ChandraAblazeCastCardsEffect extends OneShotEffect { private static final FilterCard filter = new FilterInstantOrSorceryCard(); @@ -151,18 +151,18 @@ class ChandraAblazeEffect5 extends OneShotEffect { filter.add(new ColorPredicate(ObjectColor.RED)); } - public ChandraAblazeEffect5() { + public ChandraAblazeCastCardsEffect() { super(Outcome.PlayForFree); this.staticText = "Cast any number of red instant and/or sorcery cards from your graveyard without paying their mana costs"; } - private ChandraAblazeEffect5(final ChandraAblazeEffect5 effect) { + private ChandraAblazeCastCardsEffect(final ChandraAblazeCastCardsEffect effect) { super(effect); } @Override - public ChandraAblazeEffect5 copy() { - return new ChandraAblazeEffect5(this); + public ChandraAblazeCastCardsEffect copy() { + return new ChandraAblazeCastCardsEffect(this); } @Override diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/zen/ChandraAblazeTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/zen/ChandraAblazeTest.java new file mode 100644 index 00000000000..a54f91be9c3 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/zen/ChandraAblazeTest.java @@ -0,0 +1,144 @@ +package org.mage.test.cards.single.zen; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Ignore; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps; + +/** + * @author JayDi85 + */ +public class ChandraAblazeTest extends CardTestPlayerBaseWithAIHelps { + + @Test + public void test_PlusOneAbility_Manual() { + // +1: Discard a card. If a red card is discarded this way, Chandra Ablaze deals 4 damage to any target. + addCard(Zone.BATTLEFIELD, playerA, "Chandra Ablaze", 1); + addCard(Zone.HAND, playerA, "Lightning Bolt", 1); + // + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1); + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); + + // activate +1 and kill lion + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1:"); + addTarget(playerA, "Silvercoat Lion"); // damage + setChoice(playerA, "Lightning Bolt"); // discard + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerB, "Silvercoat Lion", 1); + } + + @Test + public void test_PlusOneAbility_AI_ChooseBetterTarget() { + // +1: Discard a card. If a red card is discarded this way, Chandra Ablaze deals 4 damage to any target. + addCard(Zone.BATTLEFIELD, playerA, "Chandra Ablaze", 1); + addCard(Zone.HAND, playerA, "Lightning Bolt", 1); + // + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1); + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); + + // AI must choose better action (kill lion) + aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerA); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerB, "Silvercoat Lion", 1); + } + + @Test + @Ignore // TODO: current choose logic uses first target, enable after new logic implemented (by simulations or improved choice) + public void test_PlusOneAbility_AI_ChooseBetterDiscardCard() { + // +1: Discard a card. If a red card is discarded this way, Chandra Ablaze deals 4 damage to any target. + addCard(Zone.BATTLEFIELD, playerA, "Chandra Ablaze", 1); + addCard(Zone.HAND, playerA, "Glittermonger", 10); // green + addCard(Zone.HAND, playerA, "Lightning Bolt", 1); // put after green, so AI must choose better, not first + // + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1); + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); + + // AI must choose better discard card (bolt as red, not green) + aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerA); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerB, "Silvercoat Lion", 1); + } + + @Test + public void test_PlusTwoAbility_Manual() { + // -2: Each player discards their hand, then draws three cards. + addCard(Zone.BATTLEFIELD, playerA, "Chandra Ablaze", 1); + // + addCard(Zone.HAND, playerA, "Grizzly Bears", 5); + addCard(Zone.HAND, playerB, "Silvercoat Lion", 1); + + // activate -2 and discard all + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "-2:"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertHandCount(playerA, 3); + assertHandCount(playerB, 3); + + assertGraveyardCount(playerA, "Grizzly Bears", 5); + assertGraveyardCount(playerB, "Silvercoat Lion", 1); + } + + @Test + public void test_PlusTwoAbility_AI_MustNotSeeOpponentHand_KeepBetterHand() { + // AI must calc battlefield score by visible data, so it do not know opponent's hand, + // but it is still able to cheat by look ahead in library (e.g. it "see" result of draw actions) + + // -2: Each player discards their hand, then draws three cards. + addCard(Zone.BATTLEFIELD, playerA, "Chandra Ablaze", 1); + // + // TODO: it's another bug - AI can play land in some actions chain and can't play on another, + // comment and research AI logs (possible reason: AI don't wait stack resolve) + addCard(Zone.HAND, playerA, "Mountain", 1); + // + addCard(Zone.HAND, playerA, "Grizzly Bears", 5); + addCard(Zone.HAND, playerB, "Silvercoat Lion", 1); + + // must not call any abilities + aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerA); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + // must keep better hand + assertHandCount(playerA, "Grizzly Bears", 5); + assertHandCount(playerB, "Silvercoat Lion", 1); + } + + @Test + public void test_PlusTwoAbility_AI_MustNotSeeOpponentHand_KeepWorseHand() { + // -2: Each player discards their hand, then draws three cards. + addCard(Zone.BATTLEFIELD, playerA, "Chandra Ablaze", 1); + addCard(Zone.HAND, playerA, "Mountain", 1); // see prev test comments + // + addCard(Zone.HAND, playerA, "Grizzly Bears", 1); + addCard(Zone.HAND, playerB, "Silvercoat Lion", 5); + + // must not call any abilities + aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerA); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + // must keep worse hand + assertHandCount(playerA, "Grizzly Bears", 1); + assertHandCount(playerB, "Silvercoat Lion", 5); + } +}