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.
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:
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.
Maybe add a luck to the random function? Pure linear implementation here. You could use some advanced curves though.
private let MaxLuck = 1000.0
public extension Double {
public static func random(lower: Double = 0, _ upper: Double = 100, luck: Double) -> Double {
let weight = min(luck / MaxLuck, 1)
return (Double(arc4random()) / 0xFFFFFFFF) * weight * (upper - lower) + lower
}
}
Related
I am trying to create a slider where the value gets changed exponentially.
Let’s say the minimum is 0 and the maximum is 100. The first half of the slider should change the value slowly, like 0-10 and afterwards faster. I looked up different sites on StackOverflow but none really made sense to me. There seems to be math operations like pow() and exp(). Best would be to put it all in one function so i can use it for different parameters with a function that looks like this:
function(slidervalue, min, max, factor)
and returns the value.
i have managed to create these 2 functions to solve my problem:
func setParamLog(sliderValue: Float, min: Float, max: Float)->Float
{
// Output will be between minv and maxv
var minv :Float = log(min);
var maxv :Float = log(max);
// Adjustment factor
var scale :Float = (maxv - minv) / (max - min);
return exp(minv + (scale * (sliderValue - min)));
}
func setSliderLog(wpm: Float, min: Float, max: Float)->Float
{
// Output will be between minv and maxv
var minv :Float = log(min);
var maxv :Float = log(max);
// Adjustment factor
var scale :Float = (maxv - minv) / (max - min);
return (((log(wpm) - minv) / scale) + min);
}
it would be great to figure out now how i can adjust the scale factor
You could create a custom UISlider that wraps the functionality you want.
import Foundation
class ExponentialSlider {
...
var exponentialValue: Double {
get {
return exp(Double(self.value))
}
}
}
And then call it directly from the slider like this:
yourSlider.exponentialValue
You could also use the min and max values of the slider to modify the result of the computed property.
what i come up with yet is this function:
func exponentSliderValue(slidervalue: Float, exp: Float, min: Float, max: Float)->Float
{
var diff: Float = max - min
var result: Float = pow(slidervalue / diff, exp)
var endresult: Float = ((result * diff) + min) * 10
endresult.round()
endresult = endresult / 10
if(endresult >= min)
{
endresult = endresult + 0
}
if(endresult < min)
{
endresult = min
}
if(endresult >= max)
{
endresult = max
}
return endresult
}
it works pretty good. what i havent figured out yet is to do the whole thing in the opposite direction. all my sliders have default values.. so.. when i have a value of 40. it should translate to the right position (which is 1000) on the slider by default.. i have no idea yet how to do that. it would be great if someone can give me a hint. thanks
I am running the following code with a round function.
let percentage = round((progress / maxValue) * 100)
However it keeps returning numbers like: 15.0, 25.0, 35.0 etc.
I want it to return: 15, 25, 35, basically 0 decimal places.
How can I do this?
Cheers! :D
That's because round() returns a floating point number, not an integer:
If you want an integer, you have to convert it:
let percentage = Int(round((progress / maxValue) * 100))
Cast it to an Int:
let percentage = round((progress / maxValue) * 100)
let percentageInt = Int(percentage)
round() returns a floating point number. You can convert the result
to an Int, or call lrint instead:
let percentage = lrint((progress / maxValue) * 100)
The functions
public func lrintf(_: Float) -> Int
public func lrint(_: Double) -> Int
return the integral value nearest to their argument as an integer.
I am trying to generate random floats between 1 and 100, but I keep getting errors everytime. Currently I am trying:
func returnDbl ()-> Double {
var randNum = Double(Float(arc4random(101) % 5))
return randNum
}
print(returnDbl())
but to no avail, would someone point me in the right direction?
arc4random is zero-based and returns values between 0 and n-1, pass 100 as the upper bounds and add 1
arc4random_uniform is easier to use, it returns an Int32 type which has to be converted to Float.
func randomFloat() -> Float {
return Float(arc4random_uniform(100) + 1)
}
or Double
func randomDouble() -> Double {
return Double(arc4random_uniform(100) + 1)
}
or generic
func returnFloatingPoint<T : FloatingPointType>()-> T {
return T(arc4random_uniform(100) + 1)
}
let float : Float = returnFloatingPoint()
let double : Double = returnFloatingPoint()
Edit
To return a non-integral Double between 1.000000 and 99.99999 with arc4random_uniform() use
func returnDouble()-> Double {
return Double(arc4random_uniform(UInt32.max)) / 0x100000000 * 99.0 + 1.0
}
0x100000000 is UInt32.max + 1
let a = 1 + drand48() * 99
drand48 is a C function that returns a double in the range [0, 1). You can call it directly from Swift. Multiplying by 99 gives you a double in the range [0, 99). Add one to get into the range [1, 100).
As drand48 returns a double, the Swift type will be Double.
As per the comment, drand48 will by default return the same sequence of numbers upon every launch. You can avoid that by seeding it. E.g.
seed48(UnsafeMutablePointer<UInt16>([arc4random(), arc4random()]))
func returnDbl ()-> Double {
var randNum = Double(Float(arc4random() % 101))
return randNum
}
Ok thank you everybody for all of your help, the setups you showed me helped me figure out how the setup should at least look, my end result is
func returnDbl ()-> Double{
var randNum = Double(Float(arc4random_uniform(99)+1)) / Double(UINT32_MAX)
return randNum
}
print(returnDbl())
it returns floats between 1 and 100.
If I have a function like:
func evaluateGraph(sender: GraphView, atX: Double) -> Double? {
return function?(atX)
}
Where function is a variable declared earlier and it is a mathematical expression (like x^2). How can I find the inverse of the univariate function in swift at a point (atX)?
Assuming that you just want to know the inverse function in your GraphView (which is hopefully not infinite) you can use something like this:
// higher percision -> better accuracy, start...end: Interval, f: function
func getZero(#precision: Int, var #start: Double, var #end: Double, f: Double -> Double) -> Double? {
let fS = f(start)
let fE = f(end)
let isStartNegative = fS.isSignMinus
if isStartNegative == fE.isSignMinus { return nil }
let fMin = min(fS, fE)
let fMax = max(fS, fE)
let doublePrecision = pow(10, -Double(precision))
while end - start > doublePrecision {
let mid = (start + end) / 2
let fMid = f(mid)
if fMid < fMin || fMax < fMid {
return nil
}
if (fMid > 0) == isStartNegative {
end = mid
} else {
start = mid
}
}
return (start + end) / 2
}
// same as above but it returns an array of points
func getZerosInRange(#precision: Int, #start: Double, #end: Double, f: Double -> Double) -> [Double] {
let doublePrecision = pow(10, -Double(precision))
/// accuracy/step count between interval; "antiproportional" performance!!!!
let stepCount = 100.0
let by = (end - start) / stepCount
var zeros = [Double]()
for x in stride(from: start, to: end, by: by) {
if let xZero = getZero(precision: precision, start: x, end: x + by, f) {
zeros.append(xZero)
}
}
return zeros
}
// using currying; all return values should be elements of the interval start...end
func inverse(#precision: Int, #start: Double, #end: Double, f: Double -> Double)(_ x: Double) -> [Double] {
return getZerosInRange(precision: precision, start: start, end: end) { f($0) - x }
}
let f = { (x: Double) in x * x }
// you would pass the min and max Y values of the GraphView
// type: Double -> [Double]
let inverseF = inverse(precision: 10, start: -10, end: 10, f)
inverseF(4) // outputs [-1.999999999953436, 2.000000000046564]
Interestingly this code rund in a playground in about 0.5 second which I didn't expect.
You could create an inverse function to do the opposite.
So f(x) = y inverse f' gives f'(y) = x.
So if your defined function is to square the input then the inverse is to return the square root and so on.
You might run into trouble with something like that though as f'(1) = 1 and -1 in the case where f(x) returns the square.
Short of actually writing the inverse method, the only way to actually inversely infer what input gave a provided output, the best we can do is write our program to make guesses until it's within a certain accuracy.
So, for example, let's say we have the square function:
func square(input: Double) -> Double {
return input * input
}
Now, if we don't want to right the inverse of this function (which actually has two inputs for any given output), then we have to write a function to guess.
func inverseFunction(output: Double, function: (Double)->Double) -> Double
This takes a double representing the output, and a function (the one that generated the output), and returns its best-guess at the answer.
So, how do we guess?
Well, the same way we do in pre-calculus and the early parts of any calculus 1 class. Pick a starting number, run it through the function, compare the result to the output we're looking for. Record the delta. Pick a second number, run it through the function, compare the result to the output we're looking for. Record the delta, compare it to the first delta.
Continually do this until you have minimized the delta to an acceptable accuracy level (0.1 delta okay? 0.01? 0.0001?). The smaller the delta, the longer it takes to calculate. But it's going to take a long time no matter what.
As for the guessing algorithm? That's a math question that I'm not capable of answering. I wouldn't even know where to begin with that.
In the end, your best bet is to simply write inverse functions for any function you'd want to inverse.
I'm trying to generate random values between two integers. I've tried this, which starts from 0,
let randomNumber = arc4random_uniform(10)
println(randomNumber)
But I need a value between 10 and 50.
try this
let randomNumber = arc4random_uniform(40) + 10
println(randomNumber)
in general form
let lower : UInt32 = 10
let upper : UInt32 = 50
let randomNumber = arc4random_uniform(upper - lower) + lower
println(randomNumber)
This is an option for Swift 4.2 and above using the random() method, which makes it easy!
let randomInt = Int.random(in: 10...50)
The range can be a closed (a...b) or half open (a..<b) range.
If you want a reusable function with simple parameters:
func generateRandomNumber(min: Int, max: Int) -> Int {
let randomNum = Int(arc4random_uniform(UInt32(max) - UInt32(min)) + UInt32(min))
return randomNum
}
more simple way of random number generator
func random(min: Int, max: Int) -> Int {
return Int(arc4random_uniform(UInt32(max - min + 1))) + min
}