[Solved] Randomly picking from a list of items with probability

Discussion in 'Plugin Development' started by LucasEmanuel, Jul 9, 2012.

Thread Status:
Not open for further replies.
  1. Offline

    LucasEmanuel

    Hi, im remaking the entire way of handling chests for our SurvivalGames plugin.

    I have a list of items that i want to randomly take any item out of. The only problem i have is that i also want to be able to set a probability level for certain items in that list to make them get picked either more or less frequently.

    Kind of like this:
    Code:
    Item            |  Chance of getting picked %
     
    Apple              64%
    Diamond Sword      5%
    Wooden Sword       15%
    Steak              50%
     
    etc etc
    Ive been sitting here for a few hours now trying to figure out whats the best way to do this while at the same time alowing my admins to just simply add more stuff to the list.

    Ive thought about taking the amount of items in the list, then make an string array with the length of 1000 where each entry will be a name of an item in that list. Then that name would be replicated for X times in that list based on their percentage level. But that would be memory consuming and very very ugly.

    It could be that im just tired but this is really giving me a headache. What would you do?
     
  2. Offline

    gjossep

    I had the same problem and asked it! Look at this:

    http://forums.bukkit.org/threads/help-with-fake-random.85467/
     
  3. Offline

    andf54

    I do something similar in my plugin. What you need is an array with increasing elements from 0.0 to 1.0. Higher probability means that the item has a larger range in the region [0.0...1.0], so a random has a better chance of hitting it. Then use a = random.nextDouble() and loop trough the array until weights > a. i is the index of your item.

    Lets say you want [20%, 40%, 40%] for [WOOD, GOLD, STONE]. Then you should get an array [0.2, 0.6, 1.0]. The random number may land anywhere between 0 and 1. It has a 0.2-0.0=20% chance of landing in the first region, 0.6-0.2=40% in the second and 1.0-0.6=40% in the third region. See what I did there?


    My plugin: https://github.com/andf54/Saga/blob/master/src/org/saga/config/EconomyConfiguration.java

    The methods that should interest you are createWeights() and createTradeDeal().
    createWeights() generates the [0...1] array from an array that has just numbers. Higher number means higher probability. Insert your percentages.
    First part of createTradeDeal() selects a material from the list. Ignore the second part.
     
  4. Offline

    LucasEmanuel

    Yea, I forgot to mention but this was also in my consideration, the problem is that all the items have to have a collected percentage of 100%. That means that if an admin wants to commit more items to the list, he/she has to rewrite the percentages of all the other items in that list. This becomes pretty annoying at around a few hundred items.



    EDIT:
    Ah, after a good nights sleep i just came up with this, based on what andf54 said.

    Splitting the items into groups, where each group has a constant weight and then there could be subgroups or items etc etc. For example:

    Code:
    Food - 60%:
      Apple - 50%
      Steak - 30%
      Golden apple - 20%
    Weapons - 40%:
      Bow - 10%
      Swords 40%:
        Wooden - 50%
        Diamond - 5%
      
    Although this still requires the groups and items of a specific level to add up to 100% this is alot more manageble even when the itemlist expands beyond a few hundreds.

    I would just loop through the list starting at the lowest level, randmize a number from 1-100 and pick the item/group that corresponds to that number. If i get an item, i spawn it, if i get a group i continue looping but inside of that group etc.
     
  5. Offline

    andf54

    No need to make things too complicated. There is a reason I used weights, instead of percentages in my plugin. You can put any positive numbers in there and it will generate the correct [0.0...1.0] array based on that. You need to divide everything by the sum of the weights (I do this under // Normalize: .).
     
    LucasEmanuel likes this.
  6. Offline

    LucasEmanuel

    Oh ofcourse, i missunderstood you :) Thanks ill use your method :)
     
  7. Offline

    desht

    I found a really elegant solution here, using a NavigableMap: http://stackoverflow.com/questions/6409652/random-weighted-selection-java-framework

    With this you should be able to (haven't tested myself) do something like:

    PHP:
    RandomCollection<Stringitems = new RandomCollection<String>();
     
    // add some stuff, weighted as you like
    items.add(0.1"diamond");
    items.add(1.0"stone");
    items.add(0.5"iron");
    // etc... this could easily be read from a config file
     
    // get a random item
    String item items.next();
     
    LucasEmanuel likes this.
  8. Offline

    LucasEmanuel

    Just tried it out and it works perfectly! And its so elegant :)
     
Thread Status:
Not open for further replies.

Share This Page