Search code examples
swiftprobabilityarc4random

Increase random number probability swift


I'm trying to make a game where the hero damage is generated randomly within a range but the more luck the hero has, the higher probability the hero will have to hit with the max damage number within that range.

I'm using a Double extension to make it easier on myself.

public extension Double {

  public static func random(lower: Double = 0, _ upper: Double = 100) -> Double {

    return (Double(arc4random()) / 0xFFFFFFFF) * (upper - lower) + lower

}

//Assigning the random number to a constant
let heroDamage = Double.random(5, 15)

Let's say the hero has now an 80% of probability on getting the max damage(in this case 15), how would I approach this?, Thanks in advance.


Solution

  • For a given max damage probability of pMaxDamage (say, pMaxDamage = 0.80), one simple solution is to generate a random number, say r, in [0,1] and output:

    • maxDamage if r <= pMaxDamage,
    • a random damage in range [minDamage, maxDamage], otherwise.

    E.g.:

    class Hero {
        let heroName: String
        let pMaxDamage: Double // probability to generate max damage
    
        init(heroName: String, pMaxDamage: Double) {
            self.heroName = heroName
            self.pMaxDamage = pMaxDamage
        }
    
        func generateDamage(minDamage minDamage: Double, maxDamage: Double) -> Double {
            let r = (Double(arc4random()) / 0xFFFFFFFF)
    
            return r <= pMaxDamage ? maxDamage
                : round(100*((r-pMaxDamage)/(1-pMaxDamage)*(minDamage-maxDamage)+maxDamage))/100
        }
    }
    

    For the above implementation the r-damage (r uniform random number in [0, 1]) response curve looks as follows:

    enter image description here

    Example usage:

    let myHero = Hero(heroName: "Foo", pMaxDamage: 0.80)
    for _ in (1...10) {
        print("Slash! <\(myHero.generateDamage(minDamage: 5, maxDamage: 15)) damage>")
    }
    

    Example output:

    Slash! <15.0 damage>
    Slash! <12.68 damage>
    Slash! <15.0 damage>
    Slash! <15.0 damage>
    Slash! <5.72 damage>
    Slash! <15.0 damage>
    Slash! <15.0 damage>
    Slash! <15.0 damage>
    Slash! <15.0 damage>
    Slash! <15.0 damage>
    

    If you want your damage values to only take integer values, an alternative solution would be roulette wheel selection with

    • pMaxDamage probability of picking maxDamage,
    • uniform (1-pMaxDamage)/(numDamagePoints-1) probability of picking any of the remaining {minDamage, minDamage+1, ..., maxDamage-1} damage values.