| 09-27-2006, 12:04 AM | #1 | |
Okay, I was using GetRandomInt(), and I noticed that frequently I would get duplicate answers (meaning some kind of flaw with the internal algorithm or just not enough scrambling). Whatever, may just be my imagination or something, but I made a function for some extra randomness. Works fine, but I got bored and made this test to see how much it improved from GetRandomInt (or otherwise). BUT, wc3 crashes to a "memory could not be read" error when I start the test. Anyone know why this is? EDIT: OH wait! I know why xD. Sorry about that! EDIT2: Wait need help again =] Now the test runs, but the values displayed at the end are both always 0, which is impossible!
|
| 09-27-2006, 01:56 AM | #2 |
I did some testing of my own on GetRandomInt and it seemed to do ok. A lot better than what's in your C standard library. So I wouldn't worry about a custom implementation, especially something ad hoc. Designing RNGs is notoriously difficult. In JASS it's next to impossible since we have very few arithmetic operators and only signed integers, for which there is almost no literature. The only thing I could find was by the guy who wrote the diehard tests: JASS:globals real res integer array rng_state integer array rng_c integer rng_n integer rng_carry endglobals function rng_init takes nothing returns nothing local integer i = 0 loop exitwhen i>=8 set rng_state[i] = 1 set rng_c[i] = 1111*(i+1) set i = i + 1 endloop set rng_c[7] = 9272 set rng_n = 0 set rng_carry = 0 endfunction function mod takes integer n, integer d returns integer return n - (n/d)*d endfunction function rng_randint takes nothing returns integer local integer i = 0 local integer out = 0 loop exitwhen i>=8 set out = out + rng_c[mod(rng_n+i,8)]+rng_state[mod(rng_n+i,8)] set i = i + 1 endloop set out = out + rng_carry set rng_carry = out / 65536 set out = out - rng_carry*65536 set rng_state[rng_n] = out set rng_n = rng_n + 1 if rng_n > 7 then set rng_n = 0 endif return out endfunction function rng_randreal takes nothing returns real return rng_randint()/65536. endfunction function getreal takes nothing returns nothing set res = rng_randint()/65536. endfunction function GetRandomReal_safe takes real min, real max returns real call ExecuteFunc("getreal") return res*(max-min)+min endfunction If you want really random numbers you could try playing with this: JASS:constant function GC takes nothing returns gamecache return udg_gc endfunction function HtoI takes handle h returns integer return h return 0 endfunction function ItoT takes integer i returns timer return i return null endfunction function RtoI takes real r returns integer return r return 0 endfunction constant function ENTROPY_POOL_SIZE takes nothing returns integer return 1024 endfunction //Might be nice to grab n bits at once function entropy_pop_bit takes nothing returns integer local integer last local integer first local integer next loop set last = GetStoredInteger(GC(),"entropydaemon","last") set first = GetStoredInteger(GC(),"entropydaemon","first") set next = ModuloInteger(first+1,ENTROPY_POOL_SIZE()) exitwhen next != last //Is there a bit available? call TriggerSleepAction(0.) //No, try again later endloop call StoreInteger(GC(),"entropydaemon","first",next) return GetStoredInteger(GC(),"entropydaemon",I2S(next)) endfunction //Put entropy in a ring //Overwrite old entropy function entropy_push_bit takes integer bit returns nothing local integer last = GetStoredInteger(GC(),"entropydaemon","last") local integer first = GetStoredInteger(GC(),"entropydaemon","first") call StoreInteger(GC(),"entropydaemon",I2S(last),bit) set last = last + 1 set last = ModuloInteger(last,ENTROPY_POOL_SIZE()) if(first == last) then set first = first + 1 endif set first = ModuloInteger(first,ENTROPY_POOL_SIZE()) call StoreInteger(GC(),"entropydaemon","last",last) call StoreInteger(GC(),"entropydaemon","first",first) endfunction //Maybe this is less correlated? shrug function entropy_sum_bits takes integer rand returns integer local integer sum = 0 loop exitwhen rand == 0 set sum = sum + (rand - (rand/2)*2) set rand = rand/2 endloop return sum-(sum/2)*2 endfunction function entropy_gather takes nothing returns nothing local integer it = GetStoredInteger(GC(),"entropydaemon","timer") local string st = I2S(it) local real t local real dt local integer i if(it == 0) then set it = HtoI(CreateTimer()) call TimerStart(ItoT(it),1000.,true,null) call StoreInteger(GC(),"entropydaemon","timer",it) call StoreReal(GC(),"entropydaemon","lastmark",0.) return endif set t = TimerGetElapsed(ItoT(it)) set dt = t-GetStoredReal(GC(),"entropydaemon","lastmark") set i = RtoI(dt) if(i != 0) then //Throw out if the events occurred in same frame call entropy_push_bit(entropy_sum_bits(i)) endif call StoreReal(GC(),"entropydaemon","lastmark",t) endfunction //von Neumann debiasing. maybe too expensive. function entropy_get_bit_vonneumann takes nothing returns integer local integer i1 local integer i2 loop set i1 = entropy_pop_bit() set i2 = entropy_pop_bit() exitwhen (i1 != i2) endloop return i1 endfunction //Much cheaper debias function entropy_get_bit_xor takes nothing returns integer local integer bit = entropy_pop_bit()+entropy_pop_bit() return bit-(bit/2)*2 endfunction function entropy_rand_pow2 takes integer n returns integer local integer i = 0 local integer sum = 0 loop exitwhen i >= n // set sum = 2*sum+entropy_get_bit_vonneumann() set sum = 2*sum+entropy_get_bit_xor() set i = i + 1 endloop return sum endfunction function entropy_getnextpow takes integer i returns integer local integer n = 0 local integer p = 1 loop exitwhen p>=i set p = 2*p set n = n+1 endloop return n endfunction function entropy_rand takes integer max returns integer local integer bits = entropy_getnextpow(max) local integer randnum call BJDebugMsg("max: "+I2S(bits)) loop set randnum = entropy_rand_pow2(bits) exitwhen randnum <= max endloop return randnum endfunction function entropy_init takes nothing returns nothing local trigger t = CreateTrigger( ) local integer i = 0 set udg_gc = InitGameCache("gc.w3v") call StoreInteger(GC(),"entropydaemon","first",-1) call StoreInteger(GC(),"entropydaemon","last",0) loop exitwhen i>=12 call TriggerRegisterPlayerChatEvent( t, Player(i), "", false ) call TriggerRegisterPlayerUnitEvent( t,Player(i),EVENT_PLAYER_UNIT_SELECTED,null) call TriggerRegisterPlayerEvent(t,Player(i),EVENT_PLAYER_ARROW_LEFT_DOWN) call TriggerRegisterPlayerEvent(t,Player(i),EVENT_PLAYER_ARROW_RIGHT_DOWN) call TriggerRegisterPlayerEvent(t,Player(i),EVENT_PLAYER_ARROW_UP_DOWN) call TriggerRegisterPlayerEvent(t,Player(i),EVENT_PLAYER_ARROW_DOWN_DOWN) set i = i + 1 endloop call TriggerAddAction( t, function entropy_gather ) endfunction function entropy_test takes nothing returns nothing loop call BJDebugMsg(I2S(entropy_rand(5)+1)) endloop endfunction function Trig_init_Actions takes nothing returns nothing call entropy_init() call entropy_test() endfunction |
| 09-27-2006, 02:58 AM | #3 |
Did you just make that last set? Thanks loads for the references, but now I'm after what exactly is causing the error, because as I said now it's really just useless testing for fun. |
| 09-27-2006, 03:01 AM | #4 |
I don't have any issue with repeated numbers, the definition of random is that there is no rule on it so repeated numbers is something that should be possible. Also, have you by any chance been using "Test Map" to test your map that used the random natives? |
| 09-27-2006, 03:09 AM | #5 | |
Quote:
Now that I look a bit more, there are no problems with the native, except my code still doesn't work That's my problem now, I don't care about being random, just want to know why my code is causing errors. |
| 09-27-2006, 03:14 AM | #6 |
I can't seem to find any good reason for a crash out there give me some time |
| 09-27-2006, 03:54 AM | #7 |
You use index with out initializing it in ArrayCompare. And no, those things were written months ago. |
| 09-27-2006, 04:04 AM | #8 | |
Ah thanks alot man! Edit: Quote:
Same thing again =[ I must be missing something but I don't know what it is. Maybe because I'm using globals across the ExecuteFunc()? Could be overwriting by reexecuting the function before it can use the values? I used ExecuteFunc() because I could have massive loops there, is there an easier way to compare and add like I am doing? |
