diff --git a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java index df39977f5a8..39af757837f 100644 --- a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java +++ b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java @@ -552,11 +552,45 @@ public class PlayerControllerAi extends PlayerController { @Override public CardCollectionView londonMulliganReturnCards(final Player mulliganingPlayer, int cardsToReturn) { - // TODO This is awful. Don't do this. + // TODO This is better than it was before, but still suboptimal (but fast). // Maybe score a bunch of hands based on projected hand size and return the "duds" - CardCollectionView hand = player.getCardsIn(ZoneType.Hand); + CardCollection hand = new CardCollection(player.getCardsIn(ZoneType.Hand)); + CardCollection landsInHand = CardLists.filter(hand, Presets.LANDS); + int numLandsInHand = landsInHand.size(); + int numLandsDesired = (mulliganingPlayer.getStartingHandSize() - cardsToReturn) / 2; - return CardCollection.getView(hand.subList(0, cardsToReturn)); + CardCollection toReturn = new CardCollection(); + for (int i = 0; i < cardsToReturn; i++) { + // If we're flooding with lands, get rid of the worst land we have + if (numLandsInHand > 0 && numLandsInHand > numLandsDesired) { + CardCollection producingLands = CardLists.filter(landsInHand, Presets.LANDS_PRODUCING_MANA); + CardCollection nonProducingLands = CardLists.filter(landsInHand, Predicates.not(Presets.LANDS_PRODUCING_MANA)); + Card worstLand = nonProducingLands.isEmpty() ? ComputerUtilCard.getWorstLand(producingLands) + : ComputerUtilCard.getWorstLand(nonProducingLands); + toReturn.add(worstLand); + continue; + } + + // See if we'd scry something to the bottom in this situation. If we want to, probably get rid of it. + CardCollection scryBottom = new CardCollection(); + for (Card c : hand) { + // Lands are evaluated separately above, factoring in the number of cards to be returned to the library + if (!c.isLand() && !toReturn.contains(c) && !willPutCardOnTop(c)) { + scryBottom.add(c); + } + } + if (!scryBottom.isEmpty()) { + CardLists.sortByCmcDesc(scryBottom); + toReturn.add(scryBottom.getFirst()); // assume the max CMC one is worse since we're not guaranteed to have lands for it + continue; + } + + // If we don't want to scry anything to the bottom, remove the worst card that we have in order to satisfy + // the requirement + toReturn.add(ComputerUtilCard.getWorstAI(hand)); + } + + return CardCollection.getView(toReturn); } @Override diff --git a/forge-gui/res/cardsfolder/upcoming/goatnap.txt b/forge-gui/res/cardsfolder/upcoming/goatnap.txt index f45b7e7429f..0c21954562a 100644 --- a/forge-gui/res/cardsfolder/upcoming/goatnap.txt +++ b/forge-gui/res/cardsfolder/upcoming/goatnap.txt @@ -1,8 +1,7 @@ Name:Goatnap ManaCost:2 R Types:Sorcery -A:SP$ GainControl | Cost$ 2 R | ValidTgts$ Creature | TgtPrompt$ Select target creature | Untap$ True | LoseControl$ EOT | SubAbility$ DBPumpHaste | SpellDescription$ Gain control of target creature until end of turn. Untap that creature. It gains haste until end of turn. If that creature is a, it also gets +3/+0 until end of turn. -SVar:DBPumpHaste:DB$ Pump | Defined$ Targeted | KW$ Haste | SubAbility$ DBPump +A:SP$ GainControl | Cost$ 2 R | ValidTgts$ Creature | TgtPrompt$ Select target creature | Untap$ True | AddKWs$ Haste | LoseControl$ EOT | SubAbility$ DBPump | SpellDescription$ Gain control of target creature until end of turn. Untap that creature. It gains haste until end of turn. If that creature is a, it also gets +3/+0 until end of turn. SVar:DBPump:DB$ Pump | Defined$ Targeted | NumAtt$ 3 | ConditionCheckSVar$ X | ConditionSVarCompare$ GE1 | References$ X SVar:X:Targeted$Valid Goat Oracle:Gain control of target creature until end of turn. Untap that creature. It gains haste until end of turn. If that creature is a goat, it also gets +3/+0 until end of turn.