HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Error With Testing Functions

09-27-2006, 12:04 AM#1
corvy
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!

Test Code

Collapse JASS:
function GenerateRandomInt takes nothing returns nothing
  local integer index = 1
  local integer array gen
    
    loop 
    
      set gen[index] = GetRandomInt(udg_Memory[8], udg_Memory[9])
      
      exitwhen index >= udg_Memory[9]
      set index = index + 1
      
    endloop 
    
     set udg_Memory[1] = gen[GetRandomInt(1, udg_Memory[9])]
                                         
endfunction

function ArrayCompare takes nothing returns nothing
local integer index
local integer memory2 = udg_Memory[2]
local integer memory3 = udg_Memory[3]
local integer memory4 = udg_Memory[4]
local integer memory5 = udg_Memory[5]
local integer memory6 = udg_Memory[6]     
    
      loop 
     
        if memory2 == udg_GenerateRandom[index] and memory4 != index then
        
         set memory5 = memory5 + 1
        
        elseif memory3 == udg_GetRandom[index] and memory4 != index then
       
         set memory6 = memory6 + 1
        
        endif
       
        exitwhen index >= udg_Memory[7]
        set index = index + 1
       
      endloop
      
endfunction 

function RunRandomIntTest takes integer min, integer max returns nothing
  local integer index = 1
  local integer arrayindex = 1

    set udg_Memory[7] = max

    loop
     
      call ExecuteFunc("GenerateRandomInt")
     
     set udg_GenerateRandom[index] = udg_Memory[1]
     set udg_GetRandom[index] = GetRandomInt(min, max)
     
     exitwhen index >= max
     set index = index + 1
     
    endloop 

     set index = 1

    loop
    
       set udg_Memory[2] = udg_GenerateRandom[index]
       set udg_Memory[3] = udg_GetRandom[index]
       set udg_Memory[4] = index
       
        call ExecuteFunc("ArrayCompare")
      
      exitwhen index >= max
      set index = index + 1
      set arrayindex = 1
    
    endloop
    
      call DisplayTextToPlayer(Player(0), 0, 0, "Number of repeats using GetRandomInt(): " + I2S(udg_Memory[6]))
      call DisplayTextToPlayer(Player(0), 0, 0, "Number of repeats using GenerateRandomInt(): " + I2S(udg_Memory[5]))
    
endfunction 

function RunRandomIntTestStart takes nothing returns nothing
  set udg_Memory[8] = 0
  set udg_Memory[9] = 100
  
  call RunRandomIntTest(udg_Memory[8], udg_Memory[9])

endfunction

//===========================================================================
function InitTrig_Test takes nothing returns nothing
    set gg_trg_Test = CreateTrigger()
    call TriggerRegisterPlayerChatEvent( gg_trg_Test, Player(0), "test", true )
    call TriggerAddAction( gg_trg_Test, function RunRandomIntTestStart )
endfunction

09-27-2006, 01:56 AM#2
PipeDream
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:
Collapse 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:
Collapse 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
but it's basically completely untested. and it takes a lot of hammering to generate enough entropy for even a few dice rolls.
09-27-2006, 02:58 AM#3
corvy
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
Vexorian
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
corvy
Quote:
Originally Posted by corvy
Whatever, may just be my imagination
=]

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
Vexorian
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
PipeDream
You use index with out initializing it in ArrayCompare. And no, those things were written months ago.
09-27-2006, 04:04 AM#8
corvy
Ah thanks alot man!

Edit:
Quote:
Originally Posted by corvy
Now the test runs, but the values displayed at the end are both always 0, which is impossible!

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?