• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

Rerolling a Variable for Probability

W

Wardog

Guest
Good Evening,

I'm currently trying to code up a cave ore spawning system. Currently, the code asks three questions and rolls for each, assuming the previous condition passes:

1. Will a node spawn?
2. Is the node stone?
3. If not stone, what kind of node is it?

Code:
if(room = rm_cavelevel1 or rm_cavelevel2){
    spawnroll = random(1);
    if(spawnroll < min((0.30+(obj_player_stats.cavelevel/300)), 0.85)){ // Determines if a node spawns, better odds deeper you go, up to 85%.
        stoneroll = random(1);
        if(stoneroll < 0.800)            {nodetype = 17} // 80% chance of being a regular stone
        else {
            noderoll = (random_range(1,2)/5) + (obj_player_stats.cavelevel/300)); // Determines what kind of rarer resource it will be
            if(noderoll < 0.005)        {nodetype = 5}
            else if(noderoll < 0.010)    {nodetype = 2}
            else if(noderoll < 0.015)    {nodetype = 21}
            else if(noderoll < 0.020)    {nodetype = 15}
            else if(noderoll < 0.025)    {nodetype = 6}
            else if(noderoll < 0.030)    {nodetype = 13}
            else if(noderoll < 0.040)    {nodetype = 1}
            else if(noderoll < 0.050)    {nodetype = 3}
            else if(noderoll < 0.070)    {nodetype = 10}
            else if(noderoll < 0.090)    {nodetype = 11}
            else if(noderoll < 0.115)    {nodetype = 7}
            else if(noderoll < 0.145)    {nodetype = 16}
            else if(noderoll < 0.180)    {nodetype = 9}
            else if(noderoll < 0.250)    {nodetype = 0}
            else                        {nodetype = 4}
        }
    } else { instance_destroy(); } // Destroys Instance if Spawnroll Fails
}
The way question 3 is currently coded, it rolls noderoll once, and sees where the number falls. I suspect this isn't the best way to do probability, and would like suggestions on how to improve this. I was hoping for something simple like "Node 1 has a 5% chance, Node 2 has a 10% chance", but I wasn't successful in searching for GML coding like that.

For instance, let's say I order the list of possible nodes by rarity, and have noderoll re-roll for each item in the list, starting at the top. Would the repeat function allow this, or does it have to be some cumbersome multi-layered if-then statement?

Thanks again for your continued guidance and advice.
 
C

Catastrophe

Guest
TBH that is the best way of doing flat probability.

However, a better extendable method is to give your nodes a weighting instead, and grab based on that weighting. It would mean you can't hardcode X% chance, so your probability of choosing something would be affected as you add more things, but it would mean once you add a new node type you don't have to touch your other code again, just shove it in the list with another weight.

So for example:

common = 8
uncommon = 4
rare = 2


node1.weight = uncommon
node2.weight = uncommon
node3.weight = common

node3 would have a 50% chance and the others 25%

And if you wanted to have it increase/decrease based on cavelevel, you can give each node a LevelErosion value as well, and be like

node1.weight = getWeightForLevel(uncommon,node1.erosionLevel,caveLevel)

and getWeightForLevel would divide the weight by max(1,node1.caveLevel-erosionLevel+2) or something

Anyways, that's just how I'd do it xD

And I'd probably add a minlevel where weighting is 0 until you hit it, so it'd look like

getWeightForLevel(rarity,erosionLevel,minLevel,caveLevel)
 
Last edited by a moderator:

johnwo

Member
One way to do this is by populating a list according to chances of spawning, doing a shuffle then picking an element from said list:

Here's an example:
Code:
// At room start
var type = ds_list_create();
ds_list_add(drops,"rock","iron", "copper","silver","gold");
var chance = ds_list_create();
var chanceModifier = 1.2; // Lower chance of spawning a "common" due to some modifier
ds_list_add(chances,60/chanceModifier,25,10,4,1); // Chances set so that they sum up to 100, albeit 100 is an arbitrary number, and it will, in this case be affected by the chanceModifier.

spawnTable = ds_list_create();

for (var i=0;i<ds_list_size(type);i++) {
    for (var j=0;j<chances[|i];j++) {
        ds_list_add(spawnTable,type[|i]);
    }
}
ds_list_destroy(type);
ds_list_destroy(chances);

// Now just do the following for each ore you spawn
ds_list_shuffle(spawnTable);
oreType = spawnTable[0];
Code is not tested, just here as an example. Don't blame me if a c&p doesn't work...

Happy coding!

Edit:
Fixed chanceModifier error
 
Suggestions:

Use an enum instead of numbers for nodetype to increase readability among other things.

Code:
enum eNode
{
    Rock,
    Diamond,
    MAX
}

if (noderoll < 0.005)        {nodetype = eNode.Rock}
else if(noderoll < 0.010)    {nodetype = eNode.Diamond}
Code:
noderoll = (random_range(1,2)/5) + (obj_player_stats.cavelevel/300)
Your formula here, assuming max cavelevel of 300, will result in a range from 0.2 -> 1.4, which means most of the if()/else() checks you do after will never trigger.

I'd probably use an if/else chain, but put the material with the highest probability first, as an optimisation.
 
Top