Add random collection

This commit is contained in:
Jesse Boyd 2019-04-10 17:10:44 +10:00
parent 533edc5f40
commit 6cea26f436
No known key found for this signature in database
GPG Key ID: 59F1DE6293AF6E1F
5 changed files with 162 additions and 97 deletions

View File

@ -3,6 +3,7 @@ package com.github.intellectualsites.plotsquared.plot.object;
import com.github.intellectualsites.plotsquared.configuration.serialization.ConfigurationSerializable; import com.github.intellectualsites.plotsquared.configuration.serialization.ConfigurationSerializable;
import com.github.intellectualsites.plotsquared.plot.config.Captions; import com.github.intellectualsites.plotsquared.plot.config.Captions;
import com.github.intellectualsites.plotsquared.plot.config.Configuration; import com.github.intellectualsites.plotsquared.plot.config.Configuration;
import com.github.intellectualsites.plotsquared.plot.object.collection.RandomCollection;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.Getter; import lombok.Getter;
@ -20,12 +21,15 @@ import java.util.Map.Entry;
implements Iterable<PlotBlock>, ConfigurationSerializable { implements Iterable<PlotBlock>, ConfigurationSerializable {
private final Random random = new Random(); private final Random random = new Random();
private final Map<Range, PlotBlock> ranges = new HashMap<>(); private final Map<PlotBlock, Double> blocks;
private final Map<PlotBlock, Integer> blocks;
private final BucketIterator bucketIterator = new BucketIterator(); private final BucketIterator bucketIterator = new BucketIterator();
private boolean compiled, singleItem; private boolean compiled, singleItem;
private PlotBlock head; private PlotBlock head;
private RandomCollection<PlotBlock> randomBlocks;
private PlotBlock single;
public BlockBucket() { public BlockBucket() {
this.blocks = new HashMap<>(); this.blocks = new HashMap<>();
} }
@ -48,6 +52,11 @@ import java.util.Map.Entry;
} }
public void addBlock(@NonNull final PlotBlock block, final int chance) { 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.blocks.put(block, chance);
this.compiled = false; this.compiled = false;
if (head == null) { if (head == null) {
@ -56,9 +65,6 @@ import java.util.Map.Entry;
} }
public boolean isEmpty() { public boolean isEmpty() {
if (isCompiled()) {
return ranges.isEmpty();
}
return blocks.isEmpty(); return blocks.isEmpty();
} }
@ -72,7 +78,7 @@ import java.util.Map.Entry;
if (!isCompiled()) { if (!isCompiled()) {
this.compile(); this.compile();
} }
return Collections.unmodifiableCollection(this.ranges.values()); return Collections.unmodifiableCollection(this.blocks.keySet());
} }
/** /**
@ -112,80 +118,21 @@ import java.util.Map.Entry;
return; return;
} }
if (blocks.size() == 0) {
this.compiled = true; this.compiled = true;
return; 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;
} }
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<PlotBlock, Integer> temp = new HashMap<>(blocks.size());
final List<PlotBlock> unassigned = new ArrayList<>(blocks.size());
int sum = 0;
for (final Map.Entry<PlotBlock, Integer> 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<PlotBlock, Integer> 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<PlotBlock, Integer> 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<PlotBlock, Integer> 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;
} }
@Override public Iterator<PlotBlock> iterator() { @Override public Iterator<PlotBlock> iterator() {
@ -205,19 +152,13 @@ import java.util.Map.Entry;
if (!isCompiled()) { if (!isCompiled()) {
this.compile(); this.compile();
} }
if (this.isEmpty()) { if (single != null) {
return single;
}
if (randomBlocks != null) {
return randomBlocks.next();
}
return StringPlotBlock.EVERYTHING; return StringPlotBlock.EVERYTHING;
} else if (this.hasSingleItem()) {
return this.head;
}
final int number = random.nextInt(101);
for (final Map.Entry<Range, PlotBlock> entry : ranges.entrySet()) {
if (entry.getKey().isInRange(number)) {
return entry.getValue();
}
}
// Didn't find a block? Try again
return getBlock();
} }
@Override public String toString() { @Override public String toString() {
@ -225,14 +166,16 @@ import java.util.Map.Entry;
compile(); compile();
} }
final StringBuilder builder = new StringBuilder(); final StringBuilder builder = new StringBuilder();
final Iterator<Entry<Range, PlotBlock>> iterator = this.ranges.entrySet().iterator(); Iterator<Entry<PlotBlock, Double>> iter = blocks.entrySet().iterator();
while (iterator.hasNext()) { while (iter.hasNext()) {
final Entry<Range, PlotBlock> entry = iterator.next(); Entry<PlotBlock, Double> entry = iter.next();
builder.append(entry.getValue().getRawId()); PlotBlock block = entry.getKey();
if (!entry.getKey().isAutomatic()) { builder.append(block.getRawId());
builder.append(":").append(entry.getKey().getWeight()); Double weight = entry.getValue();
if (weight != 1) {
builder.append(":").append(weight.intValue());
} }
if (iterator.hasNext()) { if (iter.hasNext()) {
builder.append(","); builder.append(",");
} }
} }

View File

@ -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<T> extends RandomCollection<T> {
private T[] values;
public FlatRandomCollection(Map<T, Double> 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<T> parsed = new ArrayList<>();
for (Map.Entry<T, Double> 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)];
}
}

View File

@ -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<T> {
protected Random random;
public RandomCollection(Map<T, Double> weights, Random random) {
this.random = random;
}
public static <T> RandomCollection<T> of(Map<T, Double> 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();
}

View File

@ -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<E> extends RandomCollection<E> {
private final NavigableMap<Double, E> map = new TreeMap<>();
private double total = 0;
public SimpleRandomCollection(Map<E, Double> weights, Random random) {
super(weights, random);
for (Map.Entry<E, Double> 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();
}
}

View File

@ -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) { public static long pairInt(int x, int y) {
return (((long) x) << 32) | (y & 0xffffffffL); return (((long) x) << 32) | (y & 0xffffffffL);
} }