This came after playing a football manager game were a low quality striker was scoring goals for fun. Instead of his shooting attribute (lets say it was 5) been his limit (i.e RND(5), I wondered if it could be made to be his average.
This code lets you specify a dice (lets say 1-20) and allows you to weight it, so it is more likely to land on the specified number, but it can be higher or lower.
The code utilises Green Gandalfs natural log function (found on this forum - saved me a massive headache). I've reposted it here to make the code complete, I hope that OK.
The main function to call is _weighted_dice(mean,stddev,min,max), where:
mean = the value you want to be most likely
stddev = the spread of the values (lower = more likely to hit the mean value)
min = the lowest value allowed
max = the max value allowed
You can also call the _box_muller() function on it's own, but this will return a float, and depending on where you set your figures, can return a negative value. The _weighted_dice() function merely turn this into an int and prevent the numbers falling out of range
function _weighted_dice(mean# as float , stddev# as float , minnum as integer , maxnum as integer)
//mean# - the most likely outcome
//stddev# - the spead of the outcomes - smaller stddev = more likely to be around the mean
//minnum - the minimum the dice can hold - allows you to truncate impossible values
//maxnum - the maximum the dice can hold
goodroll=0
while goodroll=0
roll = int(_box_muller(mean#,stddev#))
if roll>minnum-1 and roll < maxnum+1 then goodroll=1
endwhile
endfunction roll
function _box_muller(mean# as float, stddev# as float)
r1# = rnd(1000)/1000.0
r2# = rnd(1000)/1000.0
z# = sqrt(-2*LN(r1#)) * sin((2*r2#)*(180))
res# = mean# + (z#*stddev#)
endfunction res#
function LN(x as float)
` Green Gandalf's natural log function
` accurate to about 6 decimal places
` (unless x is VERY large or small)
` written 20 May 2007, amended 21 May 2007
` NOTE - no checks for valid x values yet!!
` create memblock for float to dword conversion,
` replace 99 by another convenient memblock number if necessary,
` runs slightly faster if memblock is created in main program
` and following line removed
if memblock exist(99) = 0 then make memblock 99, 4
` split x into exponent and mantissa
d as dword
mant as dword
expon as integer
write memblock float 99, 0, x
d = memblock dword(99, 0)
` magic bit constants based on information provided by IanM
mant= (%00000000011111111111111111111111&&d)||%00111111100000000000000000000000
expon=((%01111111100000000000000000000000&&d)>>23) - 127
write memblock dword 99, 0, mant
` x should be in the range 1.0 to 2.0
x = memblock float( 99, 0)
` adjust range to 1/sqrt(2) to sqrt(2) for accuracy
if x > 1.414213562
inc expon
x = x / 2.0
endif
` calculate series approximation to LN(x)
arg# = (x - 1.0)/(x + 1.0)
arg2# = arg# * arg#
sum# = 1.0
fac# = 2.0 * arg#
arg# = arg2#
div# = 3.0
for i = 1 to 2 ` upper limit of 3 is slightly slower and more accurate
inc sum#, arg2#/div#
inc div#, 2.0
arg2# = arg2# * arg#
next i
result# = sum# * fac# + expon * 0.69314718
endfunction result#
I've also attached a picture showing some results. I ran the function 6000 times, counted the numbers rolled and plotted them. The results do look something like a normal distribution, which I was aimimg for. The mean was 10, stdev 3, min 1 and max 20.
I've not tested this to the limit, so if anyone uses it and finds a limitation, I'd be interested to know.
Cheers
Bluerinse