diff --git a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/BlockBucket.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/BlockBucket.java index 071b2bfcd..5f2c371bc 100644 --- a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/BlockBucket.java +++ b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/BlockBucket.java @@ -3,6 +3,7 @@ package com.github.intellectualsites.plotsquared.plot.object; import com.github.intellectualsites.plotsquared.configuration.serialization.ConfigurationSerializable; import com.github.intellectualsites.plotsquared.plot.config.Captions; import com.github.intellectualsites.plotsquared.plot.config.Configuration; +import com.github.intellectualsites.plotsquared.plot.object.collection.RandomCollection; import com.google.common.collect.ImmutableMap; import lombok.EqualsAndHashCode; import lombok.Getter; @@ -20,12 +21,15 @@ import java.util.Map.Entry; implements Iterable, ConfigurationSerializable { private final Random random = new Random(); - private final Map ranges = new HashMap<>(); - private final Map blocks; + private final Map blocks; + private final BucketIterator bucketIterator = new BucketIterator(); private boolean compiled, singleItem; private PlotBlock head; + private RandomCollection randomBlocks; + private PlotBlock single; + public BlockBucket() { this.blocks = new HashMap<>(); } @@ -48,6 +52,11 @@ import java.util.Map.Entry; } public void addBlock(@NonNull final PlotBlock block, final int chance) { + addBlock(block, (double) chance); + } + + private void addBlock(@NonNull final PlotBlock block, double chance) { + if (chance == -1) chance = 1; this.blocks.put(block, chance); this.compiled = false; if (head == null) { @@ -56,9 +65,6 @@ import java.util.Map.Entry; } public boolean isEmpty() { - if (isCompiled()) { - return ranges.isEmpty(); - } return blocks.isEmpty(); } @@ -72,7 +78,7 @@ import java.util.Map.Entry; if (!isCompiled()) { this.compile(); } - return Collections.unmodifiableCollection(this.ranges.values()); + return Collections.unmodifiableCollection(this.blocks.keySet()); } /** @@ -112,80 +118,21 @@ import java.util.Map.Entry; return; } - if (blocks.size() == 0) { - this.compiled = true; - return; - } - - if (blocks.size() == 1) { - this.ranges.put(new Range(0, 100, true), blocks.keySet().toArray(new PlotBlock[1])[0]); - this.compiled = true; - this.singleItem = true; - return; - } - - final Map temp = new HashMap<>(blocks.size()); - final List unassigned = new ArrayList<>(blocks.size()); - - int sum = 0; - for (final Map.Entry entry : blocks.entrySet()) { - if (entry.getValue() == -1) { - unassigned.add(entry.getKey()); - } else { - sum += entry.getValue(); - temp.put(entry.getKey(), entry.getValue()); - } - } - // - // If this doesn't amount to 100 add it up to exactly 100. - // - if (sum < 100) { - final int remaining = 100 - sum; - if (unassigned.isEmpty()) { - // If there are no unassigned values, we just add it to the first value - final Entry entry = temp.entrySet().iterator().next(); - temp.put(entry.getKey(), (entry.getValue() + 1 + remaining)); - } else { - final int perUnassigned = remaining / unassigned.size(); - for (final PlotBlock block : unassigned) { - temp.put(block, perUnassigned); - sum += perUnassigned; - } - // Make sure there isn't a tiny difference remaining - if (sum < 100) { - final int difference = 100 - sum; - temp.put(unassigned.get(0), perUnassigned + difference); - sum = 100; - } - } - } else if (!unassigned.isEmpty()) { - Captions.BUCKET_ENTRIES_IGNORED.send(ConsolePlayer.getConsole()); - } - // - // If the sum adds up to more than 100, divide all values - // - if (sum > 100) { - final double ratio = 100D / sum; - for (final Map.Entry entry : blocks.entrySet()) { - if (entry.getValue() == -1) { - continue; - } - temp.put(entry.getKey(), (int) (entry.getValue() * ratio)); - } - } else { - temp.forEach(temp::put); - } - int start = 0; - for (final Map.Entry entry : temp.entrySet()) { - final int rangeStart = start; - final int rangeEnd = rangeStart + entry.getValue(); - start = rangeEnd + 1; - final Range range = - new Range(rangeStart, rangeEnd, unassigned.contains(entry.getKey())); - this.ranges.put(range, entry.getKey()); - } - this.blocks.clear(); this.compiled = true; + switch (blocks.size()) { + case 0: + single = null; + this.randomBlocks = null; + break; + case 1: + single = blocks.keySet().iterator().next(); + this.randomBlocks = null; + break; + default: + single = null; + this.randomBlocks = RandomCollection.of(blocks, random); + break; + } } @Override public Iterator iterator() { @@ -205,19 +152,13 @@ import java.util.Map.Entry; if (!isCompiled()) { this.compile(); } - if (this.isEmpty()) { - return StringPlotBlock.EVERYTHING; - } else if (this.hasSingleItem()) { - return this.head; + if (single != null) { + return single; } - final int number = random.nextInt(101); - for (final Map.Entry entry : ranges.entrySet()) { - if (entry.getKey().isInRange(number)) { - return entry.getValue(); - } + if (randomBlocks != null) { + return randomBlocks.next(); } - // Didn't find a block? Try again - return getBlock(); + return StringPlotBlock.EVERYTHING; } @Override public String toString() { @@ -225,14 +166,16 @@ import java.util.Map.Entry; compile(); } final StringBuilder builder = new StringBuilder(); - final Iterator> iterator = this.ranges.entrySet().iterator(); - while (iterator.hasNext()) { - final Entry entry = iterator.next(); - builder.append(entry.getValue().getRawId()); - if (!entry.getKey().isAutomatic()) { - builder.append(":").append(entry.getKey().getWeight()); + Iterator> iter = blocks.entrySet().iterator(); + while (iter.hasNext()) { + Entry entry = iter.next(); + PlotBlock block = entry.getKey(); + builder.append(block.getRawId()); + Double weight = entry.getValue(); + if (weight != 1) { + builder.append(":").append(weight.intValue()); } - if (iterator.hasNext()) { + if (iter.hasNext()) { builder.append(","); } } diff --git a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/collection/FlatRandomCollection.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/collection/FlatRandomCollection.java new file mode 100644 index 000000000..3255a80e7 --- /dev/null +++ b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/collection/FlatRandomCollection.java @@ -0,0 +1,45 @@ +package com.github.intellectualsites.plotsquared.plot.object.collection; + +import com.github.intellectualsites.plotsquared.plot.util.MathMan; + +import java.util.ArrayList; +import java.util.Map; +import java.util.Random; + +public class FlatRandomCollection extends RandomCollection { + private T[] values; + + public FlatRandomCollection(Map weights, Random random) { + super(weights, random); + int max = 0; + int[] counts = new int[weights.size()]; + Double[] weightDoubles = weights.values().toArray(new Double[weights.size()]); + for (int i = 0; i < weightDoubles.length; i++) { + int weight = (int) (weightDoubles[i] * 100); + counts[i] = weight; + if (weight != (weightDoubles[i] * 100)) { + throw new IllegalArgumentException("Too small"); + } + if (weight > max) { + max = weight; + } + } + int gcd = MathMan.gcd(counts); + if (max / gcd > 100000) { + throw new IllegalArgumentException("Too large"); + } + ArrayList parsed = new ArrayList<>(); + for (Map.Entry entry : weights.entrySet()) { + int num = (int) (100 * entry.getValue()); + for (int j = 0; j < num / gcd; j++) { + parsed.add(entry.getKey()); + } + } + this.values = (T[]) parsed.toArray(); + } + + @Override + public T next() { + return values[random.nextInt(values.length)]; + } +} diff --git a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/collection/RandomCollection.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/collection/RandomCollection.java new file mode 100644 index 000000000..ac136e9b4 --- /dev/null +++ b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/collection/RandomCollection.java @@ -0,0 +1,33 @@ +package com.github.intellectualsites.plotsquared.plot.object.collection; + +import java.util.Map; +import java.util.Random; + +import static com.google.common.base.Preconditions.checkNotNull; + +public abstract class RandomCollection { + protected Random random; + + public RandomCollection(Map weights, Random random) { + this.random = random; + } + + public static RandomCollection of(Map weights, Random random) { + try { + return new FlatRandomCollection<>(weights, random); + } catch (IllegalArgumentException ignore) { + return new SimpleRandomCollection<>(weights, random); + } + } + + public void setRandom(Random random) { + checkNotNull(random); + this.random = random; + } + + public Random getRandom() { + return random; + } + + public abstract T next(); +} diff --git a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/collection/SimpleRandomCollection.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/collection/SimpleRandomCollection.java new file mode 100644 index 000000000..7ea996613 --- /dev/null +++ b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/collection/SimpleRandomCollection.java @@ -0,0 +1,29 @@ +package com.github.intellectualsites.plotsquared.plot.object.collection; + +import java.util.Map; +import java.util.NavigableMap; +import java.util.Random; +import java.util.TreeMap; + +public class SimpleRandomCollection extends RandomCollection { + + private final NavigableMap map = new TreeMap<>(); + private double total = 0; + + public SimpleRandomCollection(Map weights, Random random) { + super(weights, random); + for (Map.Entry entry : weights.entrySet()) { + add(entry.getValue(), entry.getKey()); + } + } + + public void add(double weight, E result) { + if (weight <= 0) return; + total += weight; + map.put(total, result); + } + + public E next() { + return map.ceilingEntry(random.nextDouble()).getValue(); + } +} diff --git a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/util/MathMan.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/util/MathMan.java index 852521817..00ea391ba 100644 --- a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/util/MathMan.java +++ b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/util/MathMan.java @@ -37,6 +37,21 @@ public class MathMan { } } + public static final int gcd(int a, int b) { + if (b == 0) { + return a; + } + return gcd(b, a % b); + } + + public static final int gcd(int[] a) { + int result = a[0]; + for (int i = 1; i < a.length; i++) { + result = gcd(result, a[i]); + } + return result; + } + public static long pairInt(int x, int y) { return (((long) x) << 32) | (y & 0xffffffffL); }