Guide: How to control the exact percent of chance of multiple outcomes, with just "if" and "random chance"

This is a guide on how to do it with the non-coding quest interface using only the “If” and “Random chance”.

Say you want to control the exact percentage of chance that a script will run. Right now that’s easy to do with 1 script using “if” and “random chance” but doing this with multiple scripts might be a bit harder.

For example you have a random candy machine. And you want it to have a 20% chance to give gum, 20% chance to give chocolate, 20% chance to give a peppermint and 40% chance to leave you crying.

Now if you’re a Richard you might say. “Well just say ‘random chance = 20%’ for the gum, and then create an ‘Else if’ with 20% chance for the next chocolate, and so on.”

But!!! think about, this means the gum has more chance to get picked because he’s first in line.

The gum now has a 20% chance to get picked, leaving an 80% chance that we’ll move on to the ‘Else if’. In other words we have an 80% chance to get a 20% chance to get the chocolate.
Calculating that:
20 : 100 x 80 = 16
Meaning we only have a 16% chance to get the chocolate.

Now how do we fix this. Well the trick is a bit difficult.
The first “If” is fine. He really has a 20 percent chance to get picked. But he is still Important, for he tells us how much percent is left.

Step one is determining how much chance we have:

100 – The Percent chance of the first “If” = The Value of “MainLeft”
(in this case) 100 – 20 = 80

Step two is calculating how much percent we need to give the “Else if”:

The percentage chance you want it to have : “MainLeft” x 100 = The percentage you have to enter in “Else if”.
(in this case) 20 : 80 x 100 = 25

See now that we have entered 25 percent it gives us: 80% chance to get 25% chance which equals 20% chance.

Now for the next one, and listen up buddy ‘cause here’s where it gets difficult. We have 80% chance to get a 75% chance to get to our 2nd “Else if” (the peppermint). Which it means is that the value of MainLeft doesn’t hold up for the next one. We need to change it.

Step three is changing the value of “Mainleft”

100 - The percentage you entered in the last random chance = Value of “GhostLeft”
(in this case) 100 – 25 = 75

GhostLeft : 100 x MainLeft = The new value of Mainleft
(in this case) 75 : 100 x 80 = 60

Great now we know how to calculate the percentage for our 2nd Else if. We do the same as we did before:

The percentage chance you want it to have : “MainLeft” x 100 = The percentage you have to enter in “Else if”.

(in this case) 20 : 60 x 100 = 33.33333333333

This means that we have 80% chance to get 75% chance to get 33.3333333% chance to get the peppermint, which translates to 20% chance to get the peppermint. And 80% chance to get 75% chance to get 66.666666% chance to get nothing. Which translates to 40% chance to get nothing.

Now I know Quest only allows you to put in simple numbers of 0 – 100. So just pick the number closest to it like 33%, just having a 0.3333 percent chance difference isn’t that much.

If you want to go deeper to four or five that’s fine just know that you have to calculate the new value of “MainLeft” each time before you calculate what percentage you have to enter in the next one.


OR...
Chance=GetRandomInt(1,100)
if(Chance<20){
candy="gum"
}
else if (Chance>19 and Chance<40){
candy="chocolate"
}
else if(Chance>39 and Chance<60){
candy="peppermint "
}
else {
candy="nothing"
}
You roll the random number only once.
If I understand what you are doing, you are re-rolling for a new number every time, and yes, that would throw off the values.
IE: roll for gum, if no gum, then
roll for chocolate, if no chocolate, then
roll for peppermint , if no peppermint, then
sorry, out of luck.
(Is that what you were doing?)


I think he's/she's just trying to account for the smaller percent of/due-to being after another chance roll:

30% of first roll succeeding (70% of first roll NOT succeeding)
30% for second roll x 70% of first roll NOT succeeding = 1/21 % of second roll succeeding
30% for third roll x (100 - 1/21) % of first+second rolls NOT succeeding = (1/xx or 1/xxx) % of third roll succeeding


you can avoid this by doing each as individual 'ifs' (as it's the 'else ifs' chain that connects them as a single 'if/if else' block), like so:

if (RandomChance (30)) {
  // 1st roll succeess scripting
}
if (RandomChance (30)) {
  // 2nd roll succeess scripting
}
if (RandomChance (30)) {
  // 3rd roll succeess scripting
}

however, the problem with this is that all 3 rolls occur, meaning you could succeed on all 3 rolls, as they're mutually exclusive to each other, which is probably not what you wanted.

thus, the poster's manual method of handling it, is probably the only way to do deal with the issue of smaller percents for each additional roll


P.S.

ah, DarkLizard's method would work too _ I should have looked at his/her post more closely... whoopsy

(it can be made a bit more efficient, see below)

if (Chance > 59) {
  // no item drop
} else if (Chance > 39) {
  // item 1 drops
} else if (Chance > 19) {
  // item 2 drops
} else {
  // item 3 drops
}

Personally... I have found that there is no code that cannot be improved when new eyes are used...
Yep, a little bit more efficient when counting down than up...
Altho, counting up would also work, but I've noticed that:
if (Chance <21){
select item#1
}
Quest thinks that "<21 " is a function...

But, could switch work here too???
Switch, from what I can tell, is the Quest version of Basic's Select case.
Select case is an improvement over chains of if / else if.
It is a single case "if" type of statement.


Switch could work...

switch (player.HP) {
case (11,12,13,14,15,16,17,18,19,20) {
msg ("Doing good")
}
case (6,7,8,9,10) {
msg ("feeling OK.")
}
case (1,2,3,4,5) {
msg ("HP getting low!"")
}
case (0) {
msg ("Sorry, but you died!"")
}
}

( kinda...)
But it does not understand ranges...
IE:
case (<5) {
msg ("HP getting low!"")
}
or
case (>5) {
msg ("feeling OK.")
}
unless I'm missing something...


if you are putting in code directly (non-GUI/Editor mode), you need to add in the:

<![CDATA[ scripting ]]>

tag, to tell quest that the '<' and '>' inside are to be seen as 'greater than' and 'lesser than' characters/symbols and not as ASLX tag characters/symbols

<function name="blah">
  <![CDATA[
    // blah missing scripting
    if (Chance > 59) {
      // no item drop
    } else if (Chance > 39) {
      // item 1 drops
    } else if (Chance > 19) {
      // item 2 drops
    } else {
      // item 3 drops
    }
  ]]>
</function>

or

<function name="blah">
  <![CDATA[
    // blah missing scripting
    if (Chance < 21) {
      // item 3 drops
    } else if (Chance < 41) {
      // item 2 drops
    } else if (Chance < 61) {
      // item 1 drops
    } else {
      // no item drop
    }
  ]]>
</function>

going from 'high-to-low' or 'low-to-high' probably doesn't make too much of a difference... without getting into more technical/deep/expert knowledge of coding efficiency/speed, which is well beyond my knowledge


a 'switch' block is actually identical to an 'if ' block, so it's just a matter of which design/structure you prefer and/or for what you're trying to do, as they're the exact same in terms of their functionality/capabilities.

I haven't tested whether quest's 'switch' includes above cases into the lower case, and I don't think it does... as you'd need 'break' Functions to not include the above cases into the lower case, when you don't want this to happen, and I don't think quest has the 'break' Functions for its 'switch', and quest was made to be non-coder friendly, and a non-coder isn't going to know about a more complex usage of 'switch' where you can decide whether all above cases are included in a lower case or not. I'm pretty sure that when a case condition is met, the 'switch' ends (after doing that case's scripting of course).


I think the 'case' can do ("complex") expressions:

switch (Count) {
  case (Count > 0 and Count < 100) { // 0 to 100
    // blah scripting
  }
  case (Count < 0 or Count > 100) { // 'neg infinity to zero' or 'zero to pos infinity', aka NOT: 0 to 100
    // blah scripting
  }
}

or

switch (Count) {
  case (Count > 100) {
    // blah scripting
  }
  case (Count > 50) {
    // blah scripting
  }
}

You need to compare you case statements to true:

switch (true) {
  case (Count > 100) {
    // blah scripting
  }
  case (Count > 50) {
    // blah scripting
  }
}

Only one case will get matched, so if Count is 120, the first case will fire, but not the second. To do that for percentages:

roll = GetRandomInt(1, 100)
switch (true) {
  case (roll > 80) {
    // blah scripting
  }
  case (roll > 60) {
    // blah scripting
  }
}

That said, I would do it Ryller's way, but work it out in reverse. Assuming n options, equally likely, then the percentage probability of picking the first is 100/n, and if is not picked, you need to pick equally from n-1 remaining.

For example, if you are choosing between five, the first will be 20. You are then picking between four, so it is 25, and then between three, so 33, and finally betwween two, so 50:

if (GenRandomChance(2o)) {
  msg("Five")
}
else if (GenRandomChance(25)) {
  msg("Four")
}
else if (GenRandomChance(33)) {
  msg("Three")
}
else if (GenRandomChance(50)) {
  msg("Two")
}
else {
  msg("One")
}

Log in to post a reply.

Support

Forums