From b9b69586dc16e8da5569d628739a672d37ab15b3 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Thu, 26 Sep 2024 18:49:00 -0400 Subject: [PATCH] add some new cardutil stuff --- .../src/mage/cards/c/ChromeReplicator.java | 21 +-- .../src/mage/cards/s/SphinxOfTheChimes.java | 159 +++++------------- Mage/src/main/java/mage/util/CardUtil.java | 60 +++++++ 3 files changed, 111 insertions(+), 129 deletions(-) diff --git a/Mage.Sets/src/mage/cards/c/ChromeReplicator.java b/Mage.Sets/src/mage/cards/c/ChromeReplicator.java index 20cd7226010..70e11d2469d 100644 --- a/Mage.Sets/src/mage/cards/c/ChromeReplicator.java +++ b/Mage.Sets/src/mage/cards/c/ChromeReplicator.java @@ -1,7 +1,6 @@ package mage.cards.c; import mage.MageInt; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.Condition; @@ -19,9 +18,6 @@ import mage.game.Game; import mage.game.permanent.token.Construct4Token; import mage.util.CardUtil; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; import java.util.UUID; /** @@ -66,16 +62,11 @@ enum ChromeReplicatorCondition implements Condition { @Override public boolean apply(Game game, Ability source) { - Map nameMap = new HashMap<>(); - return game - .getBattlefield() - .getActivePermanents( - filter, source.getControllerId(), source, game - ).stream() - .filter(Objects::nonNull) - .map(MageObject::getName) - .filter(Objects::nonNull) - .filter(s -> !s.isEmpty()) - .anyMatch(s -> nameMap.compute(s, CardUtil::setOrIncrementValue) >= 2); + return CardUtil + .checkAnyPairs( + game.getBattlefield().getActivePermanents( + filter, source.getControllerId(), source, game + ), (p1, p2) -> p1.sharesName(p2, game) + ); } } diff --git a/Mage.Sets/src/mage/cards/s/SphinxOfTheChimes.java b/Mage.Sets/src/mage/cards/s/SphinxOfTheChimes.java index b8fc0a5eb21..3faa7800b31 100644 --- a/Mage.Sets/src/mage/cards/s/SphinxOfTheChimes.java +++ b/Mage.Sets/src/mage/cards/s/SphinxOfTheChimes.java @@ -6,22 +6,21 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.DiscardTargetCost; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.keyword.FlyingAbility; -import mage.cards.*; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.common.FilterNonlandCard; -import mage.filter.predicate.mageobject.NamePredicate; import mage.game.Game; -import mage.players.Player; import mage.target.common.TargetCardInHand; import mage.util.CardUtil; -import java.util.HashSet; -import java.util.Map; import java.util.Set; import java.util.UUID; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * @author LevelX2 @@ -39,9 +38,9 @@ public final class SphinxOfTheChimes extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Discard two nonland cards with the same name: Draw four cards. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(4), new DiscardTwoNonlandCardsWithTheSameNameCost()); - this.addAbility(ability); - + this.addAbility(new SimpleActivatedAbility( + new DrawCardSourceControllerEffect(4), new DiscardTargetCost(new SphinxOfTheChimesTarget()) + )); } private SphinxOfTheChimes(final SphinxOfTheChimes card) { @@ -54,122 +53,54 @@ public final class SphinxOfTheChimes extends CardImpl { } } -class DiscardTwoNonlandCardsWithTheSameNameCost extends DiscardTargetCost { +class SphinxOfTheChimesTarget extends TargetCardInHand { - public DiscardTwoNonlandCardsWithTheSameNameCost() { - super(new TargetTwoNonLandCardsWithSameNameInHand()); + private static final FilterCard filter = new FilterNonlandCard("nonland cards with the same name"); + + public SphinxOfTheChimesTarget() { + super(2, 2, filter); } - private DiscardTwoNonlandCardsWithTheSameNameCost(final DiscardTwoNonlandCardsWithTheSameNameCost cost) { - super(cost); - } - - @Override - public DiscardTwoNonlandCardsWithTheSameNameCost copy() { - return new DiscardTwoNonlandCardsWithTheSameNameCost(this); - } - -} - -class TargetTwoNonLandCardsWithSameNameInHand extends TargetCardInHand { - - public TargetTwoNonLandCardsWithSameNameInHand() { - super(2, 2, new FilterNonlandCard("nonland cards with the same name")); - } - - private TargetTwoNonLandCardsWithSameNameInHand(final TargetTwoNonLandCardsWithSameNameInHand target) { + private SphinxOfTheChimesTarget(final SphinxOfTheChimesTarget target) { super(target); } @Override - public Set possibleTargets(UUID sourceControllerId, Game game) { - Set newPossibleTargets = new HashSet<>(); - Set possibleTargets = new HashSet<>(); - Player player = game.getPlayer(sourceControllerId); - if (player == null) { - return newPossibleTargets; - } - for (Card card : player.getHand().getCards(filter, game)) { - possibleTargets.add(card.getId()); - } - - Cards cardsToCheck = new CardsImpl(); - cardsToCheck.addAll(possibleTargets); - if (targets.size() == 1) { - // first target is laready chosen, now only targets with the same name are selectable - for (Map.Entry entry : targets.entrySet()) { - Card chosenCard = cardsToCheck.get(entry.getKey(), game); - if (chosenCard != null) { - for (UUID cardToCheck : cardsToCheck) { - if (!cardToCheck.equals(chosenCard.getId()) && chosenCard.getName().equals(game.getCard(cardToCheck).getName())) { - newPossibleTargets.add(cardToCheck); - } - } + public Set possibleTargets(UUID sourceControllerId, Ability source, Game game) { + Set possibleTargets = super.possibleTargets(sourceControllerId, source, game); + switch (this.getTargets().size()) { + case 0: + if (possibleTargets.size() < 2) { + possibleTargets.clear(); + break; } - } - } else { - for (UUID cardToCheck : cardsToCheck) { - Card card = game.getCard(cardToCheck); - if (card != null) { - String nameToSearch = CardUtil.getCardNameForSameNameSearch(card); - FilterCard nameFilter = new FilterCard(); - nameFilter.add(new NamePredicate(nameToSearch)); - - if (cardsToCheck.count(nameFilter, game) > 1) { - newPossibleTargets.add(cardToCheck); - } - } - } + Set set = CardUtil + .streamPairsWithMap( + possibleTargets, + (u1, u2) -> game.getCard(u1).sharesName(game.getCard(u2), game) + ? Stream.of(u1, u2) + : Stream.empty() + ) + .flatMap(Function.identity()) + .collect(Collectors.toSet()); + possibleTargets.clear(); + possibleTargets.addAll(set); + break; + case 1: + possibleTargets.removeIf( + uuid -> !game + .getCard(uuid) + .sharesName(game.getCard(this.getTargets().get(0)), game) + ); + break; + default: + possibleTargets.clear(); } - return newPossibleTargets; + return possibleTargets; } @Override - public boolean canChoose(UUID sourceControllerId, Game game) { - Cards cardsToCheck = new CardsImpl(); - Player player = game.getPlayer(sourceControllerId); - if (player == null) { - return false; - } - for (Card card : player.getHand().getCards(filter, game)) { - cardsToCheck.add(card.getId()); - } - int possibleCards = 0; - for (Card card : cardsToCheck.getCards(game)) { - String nameToSearch = CardUtil.getCardNameForSameNameSearch(card); - FilterCard nameFilter = new FilterCard(); - nameFilter.add(new NamePredicate(nameToSearch)); - - if (cardsToCheck.count(nameFilter, game) > 1) { - ++possibleCards; - } - } - return possibleCards > 0; - } - - @Override - public boolean canTarget(UUID id, Game game) { - if (super.canTarget(id, game)) { - Card card = game.getCard(id); - if (card != null) { - if (targets.size() == 1) { - Card card2 = game.getCard(targets.entrySet().iterator().next().getKey()); - return CardUtil.haveSameNames(card2, card); - } else { - String nameToSearch = CardUtil.getCardNameForSameNameSearch(card); - FilterCard nameFilter = new FilterCard(); - nameFilter.add(new NamePredicate(nameToSearch)); - - Player player = game.getPlayer(card.getOwnerId()); - return player != null && player.getHand().getCards(nameFilter, game).size() > 1; - } - } - } - return false; - } - - @Override - public TargetTwoNonLandCardsWithSameNameInHand copy() { - return new TargetTwoNonLandCardsWithSameNameInHand(this); + public SphinxOfTheChimesTarget copy() { + return new SphinxOfTheChimesTarget(this); } } diff --git a/Mage/src/main/java/mage/util/CardUtil.java b/Mage/src/main/java/mage/util/CardUtil.java index de249982049..829ef95a1ec 100644 --- a/Mage/src/main/java/mage/util/CardUtil.java +++ b/Mage/src/main/java/mage/util/CardUtil.java @@ -56,6 +56,8 @@ import java.net.URLDecoder; import java.net.URLEncoder; import java.text.SimpleDateFormat; import java.util.*; +import java.util.function.BiFunction; +import java.util.function.BiPredicate; import java.util.function.ToIntFunction; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -2167,6 +2169,64 @@ public final class CardUtil { return stream.filter(clazz::isInstance).map(clazz::cast).filter(Objects::nonNull); } + private static class IntPairIterator implements Iterator> { + private final int amount; + private int firstCounter = 0; + private int secondCounter = 1; + + IntPairIterator(int amount) { + this.amount = amount; + } + + @Override + public boolean hasNext() { + return firstCounter + 1 < amount; + } + + @Override + public AbstractMap.SimpleImmutableEntry next() { + AbstractMap.SimpleImmutableEntry value + = new AbstractMap.SimpleImmutableEntry(firstCounter, secondCounter); + secondCounter++; + if (secondCounter == amount) { + firstCounter++; + secondCounter = firstCounter + 1; + } + return value; + } + + public int getMax() { + // amount choose 2 + return (amount * amount - amount) / 2; + } + } + + public static boolean checkAnyPairs(Collection collection, BiPredicate predicate) { + return streamPairsWithMap(collection, (t1, t2) -> predicate.test(t1, t2)).anyMatch(x -> x); + } + + public static Stream streamPairsWithMap( + Collection collection, + BiFunction function) { + if (collection.size() < 2) { + return Stream.empty(); + } + List list; + if (collection instanceof List) { + list = (List) collection; + } else { + list = new ArrayList<>(collection); + } + IntPairIterator it = new IntPairIterator(list.size()); + return Stream + .generate(it::next) + .limit(it.getMax()) + .map(pair -> function.apply( + list.get(pair.getKey()), + list.get(pair.getValue()) + )); + } + public static void AssertNoControllerOwnerPredicates(Target target) { List list = new ArrayList<>(); Predicates.collectAllComponents(target.getFilter().getPredicates(), target.getFilter().getExtraPredicates(), list);