Weighted chances?

Is there a way to use weighted chances in Quest? EG., I want to give one thing a weight of 100, and another thing a weight of 200, so that out of a total of 300 rolls there's 100 results of A and 200 results of B.

I'd like to have it such that I can later go in and add C with a weight of 1, D with a weight of 80, etc etc with minimal extra work.

The current random commands don't seem up to the task. Randomint seems more fit for things with equal chance, RandomChance doesn't exactly stack well (elseifs have less than the listed value, as they aren't even evaluated if a previous randomchance evals true)...

I don't mind making my own function for it and hardcoding a list/dictionary of values, but I'd rather have the values be softcoded.


Like this?

roll = GetRandomInt(1, 20) + GetInt(weapon, attackatt) - target.defence

As in...?

if (firearm) {
  damageatt = "firearmdamage"
  attackatt = "firearmattack"
  weapon.ammo = weapon.ammo - 1
}
else {
  damageatt = "damage"
  attackatt = "attack"
}
roll = GetRandomInt(1, 20) + GetInt(weapon, attackatt) - target.defence
if (HasInt(weapon, damageatt)) {
  damage = GetInt(weapon, damageatt)
}
else {
  damage = DiceRoll(GetString(weapon, damageatt))
}
damage = damage * (100 - target.armour) / 100
if (damage < 1) {
  damage = 1
}
if (roll > 15) {
  damage = damage * 2
  AttackReport (weapon.critdesc, attacker, target, damage)
  target.hitpoints = target.hitpoints - damage
}
else if (roll > 5) {
  AttackReport (weapon.attackdesc, attacker, target, damage)
  target.hitpoints = target.hitpoints - damage
}
else {
  AttackReport (weapon.missdesc, attacker, target, 0)
}

That's.... a lot simpler than I thought it would be.


I might be confused, but that doesn't look like it does what you asked for.

The simplest way to do a weighted chance for small numbers of things is to use a list.

result = PickOneString (Split ("optionA;optionA;optionB"))

has a ⅔ chance of being option A and ⅓ option B.

If the relative probabilities are larger and that isn't practical, you can use RandomChance - but it involves some maths to work out the target numbers, so it isn't perfect.

Here's a function which will take a dictionary; the key will be returned, and the value is the weight.
(off the top of my head, I'm typing on my phone here, so this might have mistakes)

<function name="WeightedPickOne" parameters="options" type="string">
  weight = 0;
  foreach (item, options) {
    weight = weight + DictionaryItem (options, item)
  }
  choice = GetRandomInt (1, weight)
  foreach (item, options) {
    choice = choice - DictionaryItem (options, item)
    if (0 >= choice) {
      return (item)
    }
  }
</function>

If you're using it to choose a branch, you'd use it like this:

options = NewDictionary()
dictionary add (options, "A", 100)
dictionary add (options, "B", 200)
dictionary add (options, "C", 1)
dictionary add (options, "D", 80)
switch (WeightedPickOne (options)) {
  case ("A") {
    // code goes here
  }
  case ("B") {
    // code goes here
  }
  case ("C") {
    // code goes here
  }
  case ("D") {
    // code goes here
  }
}

or if there's only a few options, you could use QuickParams:

switch (WeightedPickOne (QuickParams ("A", 100, "B", 200, "C", 1))) {
  case ("A") {
    // code goes here
  }
  case ("B") {
    // code goes here
  }
  case ("C") {
    // code goes here
  }
}

Hold up....

A.weight = 1
B.weight = 2
C.weight = 3
D.weight = 4

And then this...

roll = GetRandomInt(1, 20) + this.weight

And then this...

if (roll > 5) {
  msg ("6")
}
else if (roll > 4) {
  msg ("5")
}
else if (roll > 3) {
  msg ("4")
}
else if (roll > 2) {
  msg ("3")
else {
  msg ("This is not enough.")
}

Is this what you wanted?


Try this...
(Just winging it...)
A=100
B=200
Total=A+B
R=DiceRoll("1d Total")
later...
C=1
D=80
Total=A+B+C+D
R=DiceRoll("1d" +T)
Drop "B"
Total=A+C+D
R=DiceRoll("1d" +T)
But I don't know if a variable could be used in DiceRoll()
Update: It will work...


@DarkLizerd
That's what I suggested; but I gave a function whose parameters are a dictionary of options and their weights, to automate the process.

Also, DiceRoll is an inefficient function. You're turning an int into a string, passing it to that function, which then has to walk over the string one character at a time to break it down into numbers before calling GetRandomInt to do the actual dice roll.

DiceRoll ("1d" + total) has exactly the same effect as GetRandomInt (1, total), but the DiceRoll version puts a much higher load on the server. And given the number of times people have complained here about the server being down, or games unexpectedly ending, I think that it's probably better to encourage efficient code where possible. Maybe it's a small thing, but if new coders get into good habits, then it's a small thing that will be repeated hundreds of times in dozens of games.

(DiceRoll is convenient and useful if you might have a sword whose damage is "2d6" and a bow whose damage is "1d8+4", so that you can store the damage rating in a single string attribute. That's where the additional load might be justified)


Didn't know that... Thanks.


This topic is now closed. Topics are closed after 60 days of inactivity.

Support

Forums