# Solved What's wrong with my Chance Math?

Discussion in 'Spigot Plugin Development' started by NocturnalDev, Feb 3, 2020.

1. ### NocturnalDev

Hello,

I am currently using a chance system to calculate items.

LootData is a type of object that stores the item and chance of the item.

What's wrong with this math?

Code (Text):
// Calculate total chance
double totalChance = 0;
for (LootData item : items) totalChance += item.getChance();

for (int i = 0; i < numberOfItems; i++) {
// Select a 'random chance' from 0 to total chance
double randomChance = Math.random() * totalChance;

// Check which item the 'random chance' landed on
for (LootData item : items) {
randomChance -= item.getChance();

if (randomChance < 0) {

ItemStack stack = item.getItem().clone();
stack.setAmount((int)(Math.random() * stack.getAmount() + 1));
break;
}
}
}
Since some of the items with a low % of 0.5 are getting thrown pretty frequently.

2. ### Schottky

Is LootData sorted?

3. ### NocturnalDev

It's not sorted, but it's selecting a random item from the index and then taking the weight of the item from the list.

4. ### Schottky

Wait do you mean 5% or 0.5 with a low chance?
0.5 would be 50%, which is pretty big. What is your value for totalChance?

5. ### MightyOne

Can you show how you create your collection “items”?

6. ### NocturnalDev

> [17:21:27] [Server thread/INFO]: total chance 100.0 random chance 76.624444208109
> [17:21:27] [Server thread/INFO]: total chance 100.0 random chance 40.99656573063585
> [17:21:27] [Server thread/INFO]: total chance 100.0 random chance 35.33050808519198
> [17:21:27] [Server thread/INFO]: total chance 100.0 random chance 6.653425652325129
> [17:21:27] [Server thread/INFO]: total chance 100.0 random chance 20.435143770223252
> [17:21:27] [Server thread/INFO]: total chance 100.0 random chance 15.021589891489029
> [17:21:27] [Server thread/INFO]: total chance 100.0 random chance 62.16668566789282
> [17:21:27] [Server thread/INFO]: total chance 100.0 random chance 46.15101888815097
Code (Text):
for (int i = 0; i < numberOfItems; i++) {
// Select a 'random chance' from 0 to total chance
double randomChance = Math.random() * totalChance;

Console.log(" total chance " + totalChance + " random chance " + randomChance);

// Check which item the 'random chance' landed on
for (LootData item : items) {
> [17:21:27] [Server thread/INFO]: total chance 100.0 random chance 10.1608224744815

Some debug messages I threw here:

7. ### NocturnalDev

Yeah, for example:

ItemStack legs = new ItemStack(Material.LEATHER_LEGGINGS);
ItemMeta legMeta = legs.getItemMeta();
LeatherArmorMeta lLegMega = (LeatherArmorMeta) legMeta;
lLegMega.setColor(Color.BROWN);
lLegMega.setDisplayName("§aBrown Legs");
lLegMega.setLore(Lang.getJumpSuitLeggings());
legs.setItemMeta(lLegMega);

8. ### Schottky

I don‘t know if that is the issue, but once you scale a double, the probabilities are no longer equally distributed (due to the implementation of double)
Again, I don‘t know if that matters for small numbers that you have, but you might just try normalizing your chance-system to one...

9. ### NocturnalDev

What do you mean scale double?

10. ### 7smile7

I've wrote a neat little class for this purpose a while ago.
It maps values with a weight as key and selects a random element respecting its weight.

Code (Java):
public class RandomWeightCollection<E> {

private final NavigableMap<Double, E> map = new TreeMap<Double, E>();
private final Random random;
private double total = 0;

public RandomWeightCollection(Random random) {
this.random = random;
}

public RandomWeightCollection() {
}

public Collection<E> values() {
return this.map.values();
}

public void remove(E object) {
for (Entry<Double, E> entry : this.map.entrySet()) {
if (entry.getValue().equals(object)) {
this.map.remove(entry.getKey(), entry.getValue());
}
}
}

public RandomWeightCollection<E> add(double weight, E result) {
if (weight <= 0) {
return this;
}
total += weight;
map.put(total, result);
return this;
}

public E next() {
double value = random.nextDouble() * total;
Entry<Double, E> entry = map.higherEntry(value);
return entry == null ? null : entry.getValue();
}

}
then simply call next() to select the next weighted random element from the collection.

• Like x 1
11. ### MightyOne

You should post your full code with the part where you fill your “items” collection and everything. That way we can look what actually could’ve went wrong instead of guessing about what it might be.

12. ### Schottky

If you get a random double, it returns a double between 0 and 1. The problem with doubles is that they contain as many numbers between 0 and 1 as they contain between (I just made this up, but it's somewhat in the direction of that) 1 and 50. Again, a magic number, probably not exact. Thus, if you scale a value, values smaller than one are more likely to appear.
But then again, this is an effect, that I do not believe is applicable for you. But you might as well try it out.

13. ### NocturnalDev

This is resolved, put it through Excel and calculated out all the %s to notice that cumulatively the rare items equaled 30%.

14. ### Maxx_Qc

Any link to that class? Mucho love

15. ### Schottky

It‘s below that post

16. ### Maxx_Qc

It is not..???

17. ### Schottky

Do you not see that code-piece
Code (Java):
public class RandomWeightCollection<E> {
//....
}
?

• Agree x 1
18. ### Maxx_Qc

I wanted a link... not the code. I can't save code as favorite in my browser
Whatever I'll save this post