From 1ae9fc883eeef2910df4716db64de677af8ea434 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Sat, 4 Jan 2020 20:22:49 +0400
Subject: [PATCH] * Replicate abilities - fixed that AI can freeze the game
 after play card with replicate (AI don't use it now);

---
 .../abilities/keywords/ReplicateTest.java     | 77 +++++++++++++++----
 .../main/java/mage/abilities/costs/Cost.java  | 10 ++-
 .../costs/OptionalAdditionalSourceCosts.java  |  4 +-
 .../abilities/keyword/ReplicateAbility.java   |  6 +-
 4 files changed, 74 insertions(+), 23 deletions(-)

diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ReplicateTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ReplicateTest.java
index a347456ec9..335d4d850f 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ReplicateTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ReplicateTest.java
@@ -1,14 +1,12 @@
-
-
 package org.mage.test.cards.abilities.keywords;
 
 import mage.constants.PhaseStep;
 import mage.constants.Zone;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.mage.test.serverside.base.CardTestPlayerBase;
 
 /**
- *
  * @author BetaSteward
  */
 public class ReplicateTest extends CardTestPlayerBase {
@@ -24,17 +22,16 @@ public class ReplicateTest extends CardTestPlayerBase {
      * replicate cost follows the rules for paying additional costs in rules 601.2b and 601.2e–g.
      * 702.55b If a spell has multiple instances of replicate, each is paid separately and triggers based on
      * the payments made for it, not any other instance of replicate.
-     * 
-     */
-
-    /** 
-     * 	Train of Thought  
-     *  Sorcery, 1U (2)
-     *  Replicate {1}{U} (When you cast this spell, copy it for each time you paid its replicate cost.)
-     *  Draw a card.
      *
      */
-    
+
+    /**
+     * Train of Thought
+     * Sorcery, 1U (2)
+     * Replicate {1}{U} (When you cast this spell, copy it for each time you paid its replicate cost.)
+     * Draw a card.
+     */
+
     @Test
     public void testReplicate1Time() {
         addCard(Zone.BATTLEFIELD, playerA, "Island", 6);
@@ -49,9 +46,8 @@ public class ReplicateTest extends CardTestPlayerBase {
 
         assertGraveyardCount(playerA, "Train of Thought", 1);
         assertHandCount(playerA, 2);
-
     }
-    
+
     @Test
     public void testReplicate2Times() {
         addCard(Zone.BATTLEFIELD, playerA, "Island", 6);
@@ -67,7 +63,6 @@ public class ReplicateTest extends CardTestPlayerBase {
 
         assertGraveyardCount(playerA, "Train of Thought", 1);
         assertHandCount(playerA, 3);
-
     }
 
     @Test
@@ -83,7 +78,55 @@ public class ReplicateTest extends CardTestPlayerBase {
 
         assertGraveyardCount(playerA, "Train of Thought", 1);
         assertHandCount(playerA, 1);
-
     }
-    
+
+    @Test
+    public void testReplicate_User() {
+        // Replicate {1}{R} (When you cast this spell, copy it for each time you paid its replicate cost. You may choose new targets for the copies.)
+        // Pyromatics deals 1 damage to any target.
+        addCard(Zone.HAND, playerA, "Pyromatics", 1); // {1}{R}
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 6);
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Pyromatics");
+        addTarget(playerA, playerB);
+        setChoice(playerA, "Yes"); // replicate 1
+        setChoice(playerA, "Yes"); // replicate 2
+        setChoice(playerA, "No"); // stop
+        //
+        setChoice(playerA, "No"); // don't change target 1
+        setChoice(playerA, "No"); // don't change target 2
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        execute();
+        assertAllCommandsUsed();
+
+        assertLife(playerB, 20 - 3); // 1 + 2 replicates
+    }
+
+    @Test
+    @Ignore // TODO: enable test after replicate ability will be supported by AI
+    public void testReplicate_AI() {
+        // Replicate {1}{R} (When you cast this spell, copy it for each time you paid its replicate cost. You may choose new targets for the copies.)
+        // Pyromatics deals 1 damage to any target.
+        addCard(Zone.HAND, playerA, "Pyromatics", 1); // {1}{R}
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 6);
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Pyromatics");
+        addTarget(playerA, playerB);
+        //setChoice(playerA, "Yes"); // replicate 1 - AI must choice max possible
+        //setChoice(playerA, "Yes"); // replicate 2 - AI must choice max possible
+        //setChoice(playerA, "No"); // stop - AI must choice max possible
+        //
+        //setChoice(playerA, "No"); // don't change target 1
+        //setChoice(playerA, "No"); // don't change target 2
+
+        //setStrictChooseMode(true); - AI must choice
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        execute();
+        assertAllCommandsUsed();
+
+        assertLife(playerB, 20 - 3); // 1 + 2 replicates
+    }
+
 }
diff --git a/Mage/src/main/java/mage/abilities/costs/Cost.java b/Mage/src/main/java/mage/abilities/costs/Cost.java
index a95e92bdc3..af3ffa182a 100644
--- a/Mage/src/main/java/mage/abilities/costs/Cost.java
+++ b/Mage/src/main/java/mage/abilities/costs/Cost.java
@@ -1,12 +1,12 @@
-
 package mage.abilities.costs;
 
-import java.io.Serializable;
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.game.Game;
 import mage.target.Targets;
 
+import java.io.Serializable;
+import java.util.UUID;
+
 public interface Cost extends Serializable {
 
     UUID getId();
@@ -15,6 +15,10 @@ public interface Cost extends Serializable {
 
     void setText(String text);
 
+    /**
+     * Check is it possible to pay
+     * For mana it checks only single color and amount available, not total mana cost
+     */
     boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game);
 
     boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana);
diff --git a/Mage/src/main/java/mage/abilities/costs/OptionalAdditionalSourceCosts.java b/Mage/src/main/java/mage/abilities/costs/OptionalAdditionalSourceCosts.java
index 80d5088455..7893c3505e 100644
--- a/Mage/src/main/java/mage/abilities/costs/OptionalAdditionalSourceCosts.java
+++ b/Mage/src/main/java/mage/abilities/costs/OptionalAdditionalSourceCosts.java
@@ -1,4 +1,3 @@
-
 package mage.abilities.costs;
 
 import mage.abilities.Ability;
@@ -6,7 +5,7 @@ import mage.game.Game;
 
 /**
  * Interface for abilities that add additional costs to the source.
- *
+ * <p>
  * Example of such additional source costs:
  * {@link mage.abilities.keyword.KickerAbility}
  *
@@ -14,6 +13,7 @@ import mage.game.Game;
  */
 public interface OptionalAdditionalSourceCosts {
 
+    // TODO: add AI support to use buyback, replicate and other additional costs (current version can't calc available mana before buyback use)
     void addOptionalAdditionalCosts(Ability ability, Game game);
 
     String getCastMessageSuffix();
diff --git a/Mage/src/main/java/mage/abilities/keyword/ReplicateAbility.java b/Mage/src/main/java/mage/abilities/keyword/ReplicateAbility.java
index 48de601e55..f870269abd 100644
--- a/Mage/src/main/java/mage/abilities/keyword/ReplicateAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/ReplicateAbility.java
@@ -88,8 +88,12 @@ public class ReplicateAbility extends StaticAbility implements OptionalAdditiona
                         int numActivations = additionalCost.getActivateCount();
                         times = (numActivations + 1) + (numActivations == 0 ? " time " : " times ");
                     }
+
+                    // test costs
+                    // TODO: add AI support to find max number of possible activations (from available mana)
+                    //  canPay checks only single mana available, not total mana usage
                     if (additionalCost.canPay(ability, sourceId, controllerId, game)
-                            && player.chooseUse(Outcome.Benefit, new StringBuilder("Pay ").append(times).append(additionalCost.getText(false)).append(" ?").toString(), ability, game)) {
+                            && player.chooseUse(/*Outcome.Benefit*/Outcome.AIDontUseIt, new StringBuilder("Pay ").append(times).append(additionalCost.getText(false)).append(" ?").toString(), ability, game)) {
                         additionalCost.activate();
                         for (Iterator it = ((Costs) additionalCost).iterator(); it.hasNext(); ) {
                             Cost cost = (Cost) it.next();