I've decompiled the PC versions, and the random function is the same in all of them.
random(byt LOWER, byt UPPER, var vRESULT);
A. The two values passed as LOWER and UPPER are stored on the stack. A third stack variable, DELTA is stored as UPPER-LOWER+1.
B. The AX register is loaded with a 8 bit pseudo-random number by function call rand() as follows:
-if the RNDSEED(unsigned 16 bit memory location) value is zero, it is loaded with current clock count (the DX value after a call to INT 1Ah), otherwise current RNDSEED value is used
-a constant value of 7C4Dh is multiplied (unsigned 16 bit) by the RNDSEED value; result stored as 32 bit value in DX:AX
-AX value (lower 16 bits of result) are incremented by 1
-AX is then stored as new RNDSEED value
-AL is XOR'ed with AH
-AH is cleared
-AX value returned to calling function (since AH is cleared, result is pseudo-random 8 bit unsigned integer)
C. The random value from B is divided (unsigned 16 bit) by DELTA. Quotient stored in AX, remainder stored in DX
D. Remainder (DX) moved to AX; LOWER added to AX
E. AL stored in vRESULT.
NOTES:
If DELTA is positive, result will be (pseudo)random number between (and including) LOWER and UPPER
If DELTA is zero (i.e. LOWER=UPPER+1), AGI will freeze due to attempted DIVIDE BY ZERO
If DELTA is negative, result will be (pseudo)random number between 0 and 255
The seed value (RNDSEED) is initially set using the cpu internal clock; unless you can manipulate the clock so AGI has the exact same clock count when the rand() function is called each time AGI is run, it's not possible to know the starting seed; thus the pattern of random numbers can't be guessed/calculated easily.
The AGI functions wander(obj oA) and follow.ego(obj oA, byt STEP, flg fDONE) also use the rand() function. Since these will change the seed value, even if you were able to control the initial seed value (by manipulating the internal clock) you need to make sure you account for calls to these functions as well.
NAGI is closest to the original.