From 41fd9eda4fff3697f5534f916960f4dbfcc8064a Mon Sep 17 00:00:00 2001 From: SirYwell Date: Sat, 16 Mar 2024 12:20:21 +0100 Subject: [PATCH] Allow repeated keys in RandomCollection --- .../extent/transform/RandomTransform.java | 28 +++++++------- .../util/collection/FastRandomCollection.java | 24 ++++++------ .../util/collection/RandomCollection.java | 32 +++++----------- .../collection/SimpleRandomCollection.java | 38 +++++++++---------- .../function/pattern/RandomPattern.java | 36 ++++++++---------- 5 files changed, 67 insertions(+), 91 deletions(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/transform/RandomTransform.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/transform/RandomTransform.java index 93ab1ac995..a839ec3237 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/transform/RandomTransform.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/transform/RandomTransform.java @@ -7,10 +7,10 @@ import com.sk89q.worldedit.extent.AbstractDelegateExtent; import com.sk89q.worldedit.extent.Extent; -import java.util.HashMap; -import java.util.LinkedHashSet; -import java.util.Map; +import java.util.ArrayList; +import java.util.List; import java.util.Set; +import java.util.stream.Collectors; import static com.google.common.base.Preconditions.checkNotNull; @@ -20,10 +20,9 @@ public class RandomTransform extends SelectTransform { private final SimpleRandom random; - private final Map weights = new HashMap<>(); + private final List> weights; private transient RandomCollection collection; - private transient LinkedHashSet extents = new LinkedHashSet<>(); public RandomTransform() { this(new TrueRandom()); @@ -36,27 +35,27 @@ public RandomTransform() { */ public RandomTransform(SimpleRandom random) { this.random = random; + this.weights = new ArrayList<>(); } @Override public AbstractDelegateExtent getExtent(int x, int y, int z) { - return collection.next(x, y, z); + return collection.next(this.random, x, y, z); } @Override public AbstractDelegateExtent getExtent(int x, int z) { - return collection.next(x, 0, z); + return collection.next(this.random, x, 0, z); } @Override public ResettableExtent setExtent(Extent extent) { if (collection == null) { - collection = RandomCollection.of(weights, random); - extents = new LinkedHashSet<>(weights.keySet()); + collection = RandomCollection.of(weights); } super.setExtent(extent); - for (ResettableExtent current : extents) { - current.setExtent(extent); + for (RandomCollection.Weighted current : this.weights) { + current.value().setExtent(extent); } return this; } @@ -72,13 +71,12 @@ public ResettableExtent setExtent(Extent extent) { */ public void add(ResettableExtent extent, double chance) { checkNotNull(extent); - weights.put(extent, chance); - collection = RandomCollection.of(weights, random); - this.extents.add(extent); + weights.add(new RandomCollection.Weighted<>(extent, chance)); + collection = RandomCollection.of(weights); } public Set getExtents() { - return extents; + return this.weights.stream().map(RandomCollection.Weighted::value).collect(Collectors.toSet()); } public RandomCollection getCollection() { diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/collection/FastRandomCollection.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/collection/FastRandomCollection.java index 61afe2dcd2..e332ad2311 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/collection/FastRandomCollection.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/collection/FastRandomCollection.java @@ -4,15 +4,14 @@ import com.fastasyncworldedit.core.util.MathMan; import java.util.ArrayList; -import java.util.Map; +import java.util.List; import java.util.Optional; -public class FastRandomCollection extends RandomCollection { +public final class FastRandomCollection implements RandomCollection { private final T[] values; - private FastRandomCollection(T[] values, SimpleRandom random) { - super(random); + private FastRandomCollection(T[] values) { this.values = values; } @@ -22,16 +21,15 @@ private FastRandomCollection(T[] values, SimpleRandom random) { * {@code Optional} in any case. * * @param weights the weight of the values. - * @param random the random generator to use for this collection. * @param the value type. * @return an {@link Optional} containing the new collection if it could be created, {@link * Optional#empty()} otherwise. * @see RandomCollection for API usage. */ - public static Optional> create(Map weights, SimpleRandom random) { + public static Optional> create(List> weights) { int max = 0; int[] counts = new int[weights.size()]; - Double[] weightDoubles = weights.values().toArray(new Double[0]); + double[] weightDoubles = weights.stream().mapToDouble(Weighted::weight).toArray(); for (int i = 0; i < weightDoubles.length; i++) { int weight = (int) (weightDoubles[i] * 100); counts[i] = weight; @@ -47,21 +45,21 @@ public static Optional> create(Map weights, S return Optional.empty(); } ArrayList parsed = new ArrayList<>(); - for (Map.Entry entry : weights.entrySet()) { - int num = (int) (100 * entry.getValue()); + for (Weighted entry : weights) { + int num = (int) (100 * entry.weight()); for (int j = 0; j < num / gcd; j++) { - parsed.add(entry.getKey()); + parsed.add(entry.value()); } } @SuppressWarnings("unchecked") T[] values = (T[]) parsed.toArray(); - FastRandomCollection fastRandomCollection = new FastRandomCollection<>(values, random); + FastRandomCollection fastRandomCollection = new FastRandomCollection<>(values); return Optional.of(fastRandomCollection); } @Override - public T next(int x, int y, int z) { - return values[getRandom().nextInt(x, y, z, values.length)]; + public T next(final SimpleRandom random, int x, int y, int z) { + return values[random.nextInt(x, y, z, values.length)]; } } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/collection/RandomCollection.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/collection/RandomCollection.java index 6214777e52..0988185587 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/collection/RandomCollection.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/collection/RandomCollection.java @@ -2,49 +2,35 @@ import com.fastasyncworldedit.core.math.random.SimpleRandom; -import java.util.Map; +import java.util.List; import static com.google.common.base.Preconditions.checkNotNull; /** * A RandomCollection holds multiple values that can be accessed by using - * {@link RandomCollection#next(int, int, int)}. The returned value is + * {@link RandomCollection#next(SimpleRandom, int, int, int)}. The returned value is * determined by a given {@link SimpleRandom} implementation. * * @param the type of values the collection holds. */ -public abstract class RandomCollection { - - private SimpleRandom random; - - protected RandomCollection(SimpleRandom random) { - this.random = random; - } +public sealed interface RandomCollection permits FastRandomCollection, SimpleRandomCollection { /** * Return a new RandomCollection. The implementation may differ depending on the * given arguments but there is no need to differ. * * @param weights the weighted map. - * @param random the random number generator. * @param the type the collection holds. * @return a RandomCollection using the given weights and the RNG. */ - public static RandomCollection of(Map weights, SimpleRandom random) { - checkNotNull(random); - return FastRandomCollection.create(weights, random) - .orElseGet(() -> new SimpleRandomCollection<>(weights, random)); + static RandomCollection of(List> weights) { + return FastRandomCollection.create(weights) + .orElseGet(() -> new SimpleRandomCollection<>(weights)); } - public void setRandom(SimpleRandom random) { - checkNotNull(random); - this.random = random; - } + T next(SimpleRandom random, int x, int y, int z); - public SimpleRandom getRandom() { - return random; - } - - public abstract T next(int x, int y, int z); + record Weighted(T value, double weight) { + } } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/collection/SimpleRandomCollection.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/collection/SimpleRandomCollection.java index dc21075598..c831bb8f1e 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/collection/SimpleRandomCollection.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/collection/SimpleRandomCollection.java @@ -2,41 +2,39 @@ import com.fastasyncworldedit.core.math.random.SimpleRandom; -import java.util.Map; +import java.util.List; import java.util.NavigableMap; import java.util.TreeMap; -public class SimpleRandomCollection extends RandomCollection { +public final class SimpleRandomCollection implements RandomCollection { - private final NavigableMap map = new TreeMap<>(); - private double total = 0; + private final NavigableMap map; + private final double total; /** * Create a {@link RandomCollection} from a weighted map and a RNG. - * It is recommended to use {@link RandomCollection#of(Map, SimpleRandom)} + * It is recommended to use {@link RandomCollection#of(List)} * instead of this constructor. * * @param weights the weighted map. - * @param random the random number generator. */ - public SimpleRandomCollection(Map weights, SimpleRandom random) { - super(random); - for (Map.Entry entry : weights.entrySet()) { - add(entry.getValue(), entry.getKey()); + public SimpleRandomCollection(List> weights) { + this.map = new TreeMap<>(); + double total = 0; + for (Weighted entry : weights) { + final double weight = entry.weight(); + if (weight <= 0) { + throw new IllegalArgumentException("Weights must be positive"); + } + total += weight; + this.map.put(total, entry.value()); } - } - - public void add(double weight, E result) { - if (weight <= 0) { - return; - } - total += weight; - map.put(total, result); + this.total = total; } @Override - public E next(int x, int y, int z) { - return map.ceilingEntry(getRandom().nextDouble(x, y, z) * this.total).getValue(); + public T next(final SimpleRandom random, int x, int y, int z) { + return map.ceilingEntry(random.nextDouble(x, y, z) * this.total).getValue(); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/RandomPattern.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/RandomPattern.java index 1d2df7223f..380051ec20 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/RandomPattern.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/RandomPattern.java @@ -27,10 +27,10 @@ import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.block.BaseBlock; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.Map; +import java.util.ArrayList; +import java.util.List; import java.util.Set; +import java.util.stream.Collectors; import static com.google.common.base.Preconditions.checkNotNull; @@ -39,11 +39,10 @@ */ public class RandomPattern extends AbstractPattern { - //FAWE start - SimpleRandom > Random, LHS

> List + //FAWE start - SimpleRandom > Random, RandomCollection private final SimpleRandom random; - private Map weights = new LinkedHashMap<>(); + private final List> weights; private RandomCollection collection; - private LinkedHashSet patterns = new LinkedHashSet<>(); //FAWE end //FAWE start @@ -53,6 +52,7 @@ public RandomPattern() { public RandomPattern(SimpleRandom random) { this.random = random; + this.weights = new ArrayList<>(); } /** @@ -63,9 +63,8 @@ public RandomPattern(SimpleRandom random) { */ public RandomPattern(SimpleRandom random, RandomPattern parent) { this.random = random; - this.weights = parent.weights; - this.collection = RandomCollection.of(weights, random); - this.patterns = parent.patterns; + this.weights = new ArrayList<>(parent.weights); + this.collection = RandomCollection.of(weights); } //FAWE end @@ -80,18 +79,15 @@ public RandomPattern(SimpleRandom random, RandomPattern parent) { */ public void add(Pattern pattern, double chance) { checkNotNull(pattern); - //FAWE start - Double, weights, patterns and collection - Double existingWeight = weights.get(pattern); - if (existingWeight != null) { - chance += existingWeight; - } - weights.put(pattern, chance); - collection = RandomCollection.of(weights, random); - this.patterns.add(pattern); + //FAWE start - Double, weights, repeating patterns, and collection + this.weights.add(new RandomCollection.Weighted<>(pattern, chance)); + this.collection = RandomCollection.of(weights); } public Set getPatterns() { - return patterns; + return this.weights.stream() + .map(RandomCollection.Weighted::value) + .collect(Collectors.toSet()); } public RandomCollection getCollection() { @@ -100,12 +96,12 @@ public RandomCollection getCollection() { @Override public BaseBlock applyBlock(BlockVector3 position) { - return collection.next(position.getBlockX(), position.getBlockY(), position.getBlockZ()).applyBlock(position); + return collection.next(this.random, position.getBlockX(), position.getBlockY(), position.getBlockZ()).applyBlock(position); } @Override public boolean apply(Extent extent, BlockVector3 get, BlockVector3 set) throws WorldEditException { - return collection.next(get.getBlockX(), get.getBlockY(), get.getBlockZ()).apply(extent, get, set); + return collection.next(this.random, get.getBlockX(), get.getBlockY(), get.getBlockZ()).apply(extent, get, set); } //FAWE end