mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 11:18:01 +00:00
Guava migration - Add StreamUtils.random collectors
This commit is contained in:
@@ -2450,7 +2450,10 @@ public class ComputerUtil {
|
||||
// not enough good choices, need to fill the rest
|
||||
int minDiff = min - goodChoices.size();
|
||||
if (minDiff > 0) {
|
||||
goodChoices.addAll(Aggregates.random(CardLists.filter(validCards, ((Predicate<Card>) goodChoices::contains).negate()), minDiff));
|
||||
List<Card> choices = validCards.stream()
|
||||
.filter(((Predicate<Card>) goodChoices::contains).negate())
|
||||
.collect(StreamUtils.random(minDiff));
|
||||
goodChoices.addAll(choices);
|
||||
return goodChoices;
|
||||
}
|
||||
|
||||
|
||||
@@ -234,11 +234,11 @@ public abstract class DeckGeneratorBase {
|
||||
addSome(targetSize - actualSize, tDeck.toFlatList());
|
||||
}
|
||||
else if (actualSize > targetSize) {
|
||||
Predicate<PaperCard> exceptBasicLand = PaperCardPredicates.fromRules(CardRulesPredicates.NOT_BASIC_LAND);
|
||||
|
||||
for (int i = 0; i < 3 && actualSize > targetSize; i++) {
|
||||
Iterable<PaperCard> matchingCards = Iterables.filter(tDeck.toFlatList(), exceptBasicLand);
|
||||
List<PaperCard> toRemove = Aggregates.random(matchingCards, actualSize - targetSize);
|
||||
List<PaperCard> toRemove = tDeck.toFlatList().stream()
|
||||
.filter(PaperCardPredicates.fromRules(CardRulesPredicates.NOT_BASIC_LAND))
|
||||
.collect(StreamUtils.random(actualSize - targetSize));
|
||||
tDeck.removeAllFlat(toRemove);
|
||||
|
||||
for (PaperCard c : toRemove) {
|
||||
|
||||
@@ -21,12 +21,10 @@ package forge.item;
|
||||
import forge.StaticData;
|
||||
import forge.card.CardRulesPredicates;
|
||||
import forge.item.generation.BoosterGenerator;
|
||||
import forge.util.Aggregates;
|
||||
import forge.util.StreamUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public abstract class SealedProduct implements InventoryItemFromSet {
|
||||
|
||||
@@ -112,8 +110,9 @@ public abstract class SealedProduct implements InventoryItemFromSet {
|
||||
}
|
||||
|
||||
protected List<PaperCard> getRandomBasicLands(final String setCode, final int count) {
|
||||
Predicate<PaperCard> cardsRule = PaperCardPredicates.printedInSet(setCode)
|
||||
.and(PaperCardPredicates.fromRules(CardRulesPredicates.IS_BASIC_LAND));
|
||||
return Aggregates.random(StaticData.instance().getCommonCards().streamAllCards().filter(cardsRule).collect(Collectors.toList()), count);
|
||||
return StaticData.instance().getCommonCards().streamAllCards()
|
||||
.filter(PaperCardPredicates.printedInSet(setCode))
|
||||
.filter(PaperCardPredicates.fromRules(CardRulesPredicates.IS_BASIC_LAND))
|
||||
.collect(StreamUtils.random(count));
|
||||
}
|
||||
}
|
||||
|
||||
106
forge-core/src/main/java/forge/util/StreamUtils.java
Normal file
106
forge-core/src/main/java/forge/util/StreamUtils.java
Normal file
@@ -0,0 +1,106 @@
|
||||
package forge.util;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.*;
|
||||
import java.util.stream.Collector;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class StreamUtils {
|
||||
|
||||
private StreamUtils(){}
|
||||
|
||||
/**
|
||||
* Reduces a stream to a random element of the stream. Used with {@link Stream#collect}.
|
||||
* Result will be wrapped in an Optional, absent only if the stream is empty.
|
||||
*/
|
||||
public static <T> Collector<T, ?, Optional<T>> random() {
|
||||
return new RandomCollectorSingle<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects a number of items randomly from this stream. Used with {@link Stream#collect}.
|
||||
* @param count Number of elements to select from the stream.
|
||||
*/
|
||||
public static <T> Collector<T, ?, List<T>> random(int count) {
|
||||
return new RandomCollectorMulti<>(count);
|
||||
}
|
||||
|
||||
private static abstract class RandomCollector<T, O> implements Collector<T, RandomReservoir<T>, O> {
|
||||
private final int size;
|
||||
RandomCollector(int size) {
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Supplier<RandomReservoir<T>> supplier() {
|
||||
return () -> new RandomReservoir<>(size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BiConsumer<RandomReservoir<T>, T> accumulator() {
|
||||
return RandomReservoir::accumulate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BinaryOperator<RandomReservoir<T>> combiner() {
|
||||
return (first, second) -> {
|
||||
//There's probably a way to adapt the Random Reservoir method
|
||||
//so that two partially processed lists can be combined into one.
|
||||
//But I have no idea what that is.
|
||||
throw new UnsupportedOperationException("Parallel streams not supported.");
|
||||
};
|
||||
}
|
||||
|
||||
private final EnumSet<Characteristics> CHARACTERISTICS = EnumSet.of(Characteristics.UNORDERED);
|
||||
@Override
|
||||
public Set<Characteristics> characteristics() {
|
||||
return CHARACTERISTICS;
|
||||
}
|
||||
}
|
||||
|
||||
private static class RandomCollectorSingle<T> extends RandomCollector<T, Optional<T>> {
|
||||
RandomCollectorSingle() {
|
||||
super(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Function<RandomReservoir<T>, Optional<T>> finisher() {
|
||||
return (chosen) -> chosen.samples.isEmpty() ? Optional.empty() : Optional.of(chosen.samples.get(0));
|
||||
}
|
||||
}
|
||||
|
||||
private static class RandomCollectorMulti<T> extends RandomCollector<T, List<T>> {
|
||||
RandomCollectorMulti(int size) {
|
||||
super(size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Function<RandomReservoir<T>, List<T>> finisher() {
|
||||
return (chosen) -> chosen.samples;
|
||||
}
|
||||
}
|
||||
|
||||
private static class RandomReservoir<T> {
|
||||
final int maxSize;
|
||||
ArrayList<T> samples;
|
||||
int sampleCount = 0;
|
||||
|
||||
public RandomReservoir(int size) {
|
||||
this.maxSize = size;
|
||||
this.samples = new ArrayList<>(size);
|
||||
}
|
||||
|
||||
public void accumulate(T next) {
|
||||
sampleCount++;
|
||||
if(sampleCount < maxSize) {
|
||||
//Add the first [maxSize] items into the result list
|
||||
samples.add(next);
|
||||
return;
|
||||
}
|
||||
//Progressively reduce odds of adding an item into the reservoir
|
||||
int j = MyRandom.getRandom().nextInt(sampleCount);
|
||||
if(j < maxSize)
|
||||
samples.set(j, next);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -131,8 +131,9 @@ public class ChooseCardNameEffect extends SpellAbilityEffect {
|
||||
cpp = CardFacePredicates.valid(valid);
|
||||
}
|
||||
if (randomChoice) {
|
||||
final Iterable<ICardFace> cards = Iterables.filter(StaticData.instance().getCommonCards().getAllFaces(), cpp);
|
||||
chosen = Aggregates.random(cards).getName();
|
||||
chosen = StaticData.instance().getCommonCards().streamAllFaces()
|
||||
.filter(cpp).collect(StreamUtils.random()).get()
|
||||
.getName();
|
||||
} else {
|
||||
chosen = p.getController().chooseCardName(sa, cpp, valid, message);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package forge.game.ability.effects;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import forge.card.CardDb;
|
||||
import forge.card.CardStateName;
|
||||
import forge.card.GamePieceType;
|
||||
import forge.item.PaperCardPredicates;
|
||||
@@ -109,25 +109,28 @@ public class PlayEffect extends SpellAbilityEffect {
|
||||
}
|
||||
} else if (sa.hasParam("AnySupportedCard")) {
|
||||
final String valid = sa.getParam("AnySupportedCard");
|
||||
List<PaperCard> cards = null;
|
||||
Stream<PaperCard> cards;
|
||||
CardDb cardDb = StaticData.instance().getCommonCards();
|
||||
if (valid.startsWith("Names:")) {
|
||||
cards = new ArrayList<>();
|
||||
for (String name : valid.substring(6).split(",")) {
|
||||
name = name.replace(";", ",");
|
||||
cards.add(StaticData.instance().getCommonCards().getUniqueByName(name));
|
||||
}
|
||||
cards = Arrays.stream(valid.substring(6).split(","))
|
||||
.map(name -> name.replace(";", ","))
|
||||
.map(cardDb::getUniqueByName);
|
||||
} else if (valid.equalsIgnoreCase("sorcery")) {
|
||||
final Predicate<PaperCard> cpp = PaperCardPredicates.fromRules(CardRulesPredicates.IS_SORCERY);
|
||||
cards = StaticData.instance().getCommonCards().streamUniqueCards().filter(cpp).collect(Collectors.toList());
|
||||
cards = cardDb.streamUniqueCards()
|
||||
.filter(PaperCardPredicates.fromRules(CardRulesPredicates.IS_SORCERY));
|
||||
} else if (valid.equalsIgnoreCase("instant")) {
|
||||
final Predicate<PaperCard> cpp = PaperCardPredicates.fromRules(CardRulesPredicates.IS_INSTANT);
|
||||
cards = StaticData.instance().getCommonCards().streamUniqueCards().filter(cpp).collect(Collectors.toList());
|
||||
cards = cardDb.streamUniqueCards()
|
||||
.filter(PaperCardPredicates.fromRules(CardRulesPredicates.IS_INSTANT));
|
||||
} else {
|
||||
//Could just return a stream of all cards, but that should probably be a specific option rather than a fallback.
|
||||
//Could also just leave it null but there's currently nothing else that can happen that case.
|
||||
throw new UnsupportedOperationException("Unknown parameter for AnySupportedCard: " + valid);
|
||||
}
|
||||
if (sa.hasParam("RandomCopied")) {
|
||||
final CardCollection choice = new CardCollection();
|
||||
final String num = sa.getParamOrDefault("RandomNum", "1");
|
||||
int ncopied = AbilityUtils.calculateAmount(source, num, sa);
|
||||
for (PaperCard cp : Aggregates.random(cards, ncopied)) {
|
||||
int nCopied = AbilityUtils.calculateAmount(source, num, sa);
|
||||
for (PaperCard cp : cards.collect(StreamUtils.random(nCopied))) {
|
||||
final Card possibleCard = Card.fromPaperCard(cp, sa.getActivatingPlayer());
|
||||
if (sa.getActivatingPlayer().isAI() && possibleCard.getRules() != null && possibleCard.getRules().getAiHints().getRemAIDecks())
|
||||
continue;
|
||||
|
||||
@@ -22,9 +22,9 @@ import forge.model.FModel;
|
||||
import forge.screens.deckeditor.CDeckEditorUI;
|
||||
import forge.screens.deckeditor.SEditorIO;
|
||||
import forge.screens.deckeditor.views.VDeckgen;
|
||||
import forge.util.Aggregates;
|
||||
import forge.util.Iterables;
|
||||
import forge.util.StreamUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
|
||||
@@ -71,8 +71,10 @@ public enum CDeckgen implements ICDoc {
|
||||
final Deck randomDeck = new Deck();
|
||||
|
||||
final Predicate<PaperCard> notBasicLand = PaperCardPredicates.fromRules(CardRulesPredicates.NOT_BASIC_LAND);
|
||||
final Iterable<PaperCard> source = Iterables.filter(FModel.getMagicDb().getCommonCards().getUniqueCards(), notBasicLand);
|
||||
randomDeck.getMain().addAllFlat(Aggregates.random(source, 15 * 5));
|
||||
List<PaperCard> randomCards = FModel.getMagicDb().getCommonCards().streamUniqueCards()
|
||||
.filter(notBasicLand)
|
||||
.collect(StreamUtils.random(15 * 5));
|
||||
randomDeck.getMain().addAllFlat(randomCards);
|
||||
|
||||
for(final String landName : MagicColor.Constant.BASIC_LANDS) {
|
||||
randomDeck.getMain().add(landName, 1);
|
||||
|
||||
@@ -237,8 +237,7 @@ public class DuelScene extends ForgeScene {
|
||||
DeckProxy deckProxy = null;
|
||||
if (chaosBattle) {
|
||||
deckProxyMapMap = DeckProxy.getAllQuestChallenges();
|
||||
List<DeckProxy> decks = new ArrayList<>(deckProxyMapMap.keySet());
|
||||
deckProxy = Aggregates.random(decks);
|
||||
deckProxy = Aggregates.random(deckProxyMapMap.keySet());
|
||||
//playerextras
|
||||
List<IPaperCard> playerCards = new ArrayList<>();
|
||||
for (String s : deckProxyMapMap.get(deckProxy).getLeft()) {
|
||||
|
||||
@@ -9,6 +9,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import forge.item.PaperCardPredicates;
|
||||
import forge.util.*;
|
||||
@@ -463,25 +464,18 @@ public class DeckgenUtil {
|
||||
try {
|
||||
if (useGeneticAI) {
|
||||
if (!selection.isEmpty())
|
||||
deck = Aggregates.random(Iterables.filter(geneticAI, deckProxy -> deckProxy.getColorIdentity().sharesColorWith(ColorSet.fromNames(colors.toCharArray())))).getDeck();
|
||||
deck = geneticAI.stream()
|
||||
.filter(deckProxy -> deckProxy.getColorIdentity().sharesColorWith(ColorSet.fromNames(colors.toCharArray())))
|
||||
.collect(StreamUtils.random()).get().getDeck();
|
||||
else
|
||||
deck = Aggregates.random(geneticAI).getDeck();
|
||||
|
||||
} else {
|
||||
Predicate<DeckProxy> sizePredicate = deckProxy -> deckProxy.getMainSize() <= 60;
|
||||
if (!selection.isEmpty() && selection.size() < 4) {
|
||||
Predicate<DeckProxy> colorPredicate = deckProxy -> deckProxy.getColorIdentity().hasAllColors(ColorSet.fromNames(colors.toCharArray()).getColor());
|
||||
Predicate<DeckProxy> pred = sizePredicate.and(colorPredicate);
|
||||
if (isTheme)
|
||||
deck = Aggregates.random(Iterables.filter(advThemes, pred)).getDeck();
|
||||
else
|
||||
deck = Aggregates.random(Iterables.filter(advPrecons, pred)).getDeck();
|
||||
} else {
|
||||
if (isTheme)
|
||||
deck = Aggregates.random(Iterables.filter(advThemes, sizePredicate)).getDeck();
|
||||
else
|
||||
deck = Aggregates.random(Iterables.filter(advPrecons, sizePredicate)).getDeck();
|
||||
}
|
||||
Predicate<DeckProxy> predicate = deckProxy -> deckProxy.getMainSize() <= 60;
|
||||
if (!selection.isEmpty() && selection.size() < 4)
|
||||
predicate = predicate.and(deckProxy -> deckProxy.getColorIdentity().hasAllColors(ColorSet.fromNames(colors.toCharArray()).getColor()));
|
||||
List<DeckProxy> source = isTheme ? advThemes : advPrecons;
|
||||
deck = source.stream().filter(predicate).collect(StreamUtils.random()).get().getDeck();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
@@ -658,18 +652,15 @@ public class DeckgenUtil {
|
||||
|
||||
/** Generate a 2-5-color Commander deck. */
|
||||
public static Deck generateCommanderDeck(boolean forAi, GameType gameType) {
|
||||
final Deck deck;
|
||||
IDeckGenPool cardDb = FModel.getMagicDb().getCommonCards();
|
||||
PaperCard commander;
|
||||
ColorSet colorID;
|
||||
|
||||
// Get random multicolor Legendary creature
|
||||
final DeckFormat format = gameType.getDeckFormat();
|
||||
Predicate<CardRules> canPlay = forAi ? DeckGeneratorBase.AI_CAN_PLAY : CardRulesPredicates.IS_KEPT_IN_RANDOM_DECKS;
|
||||
Predicate<PaperCard> legal = format.isLegalCardPredicate().and(format.isLegalCommanderPredicate());
|
||||
Iterable<PaperCard> legends = cardDb.getAllCards(legal.and(PaperCardPredicates.fromRules(canPlay)));
|
||||
|
||||
commander = Aggregates.random(legends);
|
||||
PaperCard commander = FModel.getMagicDb().getCommonCards().streamAllCards()
|
||||
.filter(format.isLegalCardPredicate())
|
||||
.filter(format.isLegalCommanderPredicate())
|
||||
.filter(PaperCardPredicates.fromRules(canPlay))
|
||||
.collect(StreamUtils.random()).get();
|
||||
return generateRandomCommanderDeck(commander, format, forAi, false);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user