Solved What's wrong with my Chance Math?

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

  1. 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));
                        itemsToGive.add(stack);
                        break;
                    }
                }
            }
    Since some of the items with a low % of 0.5 are getting thrown pretty frequently.
     
  2. Is LootData sorted?
     
  3. 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. 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. Can you show how you create your collection “items”?
     
  6. > [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. Yeah, for example:


    ItemStack legs = new ItemStack(Material.LEATHER_LEGGINGS);
    ItemMeta legMeta = legs.getItemMeta();
    legMeta.addItemFlags(ItemFlag.values());
    LeatherArmorMeta lLegMega = (LeatherArmorMeta) legMeta;
    lLegMega.setColor(Color.BROWN);
    lLegMega.setDisplayName("§aBrown Legs");
    lLegMega.setLore(Lang.getJumpSuitLeggings());
    legs.setItemMeta(lLegMega);
    items.add(new LootData(legs, 1.5));
     
  8. 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. What do you mean scale double?
     
  10. 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() {
        this(ThreadLocalRandom.current());
      }

      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();
      }

    }
    Just add elements with add(weight, element)
    then simply call next() to select the next weighted random element from the collection.
     
    • Like Like x 1
  11. 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. 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. This is resolved, put it through Excel and calculated out all the %s to notice that cumulatively the rare items equaled 30%.
     
  14. Any link to that class? Mucho love
     
  15. It‘s below that post
     
  16. It is not..???
     
  17. Do you not see that code-piece
    Code (Java):
    public class RandomWeightCollection<E> {
        //....
    }
    ?
     
    • Agree Agree x 1
  18. I wanted a link... not the code. I can't save code as favorite in my browser
    Whatever I'll save this post