console.clear()
--famidash v1.2
--This script tells you, starting with an RNG value either specified or read from memory, whether Lucky Draw will eventually be successful or end in an infinite loop (modes 1&2), or alternatively whether the first attempt is successful or fails a particular check (modes 3&4).
--mode 1: Starting with initial RNG given below, determine whether Lucky Draw will eventually be successful and on which attempt, or end up in an infinite loop and state which attempt repeats which attempt, and what the RNG value is when it repeats.
--mode 2: Same as mode 1, but with RNG value read from memory address 0x1B (4-byte little-endian)
--mode 3: Starting with initial RNG given below, determine whether the first attempt is successful, and if not, which of the 788 checks it fails, and the final RNG value upon death (this RNG value is the value for the next attempt).
--mode 4: Same as mode 2, but with RNG value read from memory address 0x1B (4-byte little-endian)
local mode=1
local initial_rng_value=0xBABF9847 --only applicable if mode is 1 or 3
--this RNG is a 32-bit linear feedback shift register (LFSR)
--also called "newrand" in the source
local function rand32(thisrng, numtimes)
local rng=thisrng
for i=1,(8*numtimes) do
if rng>=0x80000000 then rng = ((rng*2)&0xFFFFFFFF)~0xC5 else rng = rng*2 end
end
return rng
end
--RNG may be modified by the following function (8-bit LFSR only applying to the lowest byte)
--also called "rand1" in the source
local function rand8(thisrng, numtimes)
local lbyte=thisrng&0xFF
for i=1,numtimes do
if lbyte>=0x80 then lbyte=((lbyte*2)&0xFF)~0xCF else lbyte=lbyte*2 end
end
return ((thisrng&0xFFFFFF00)|lbyte)
end
--function combining uses of both rand32 and rand8
local function rand8rand32(thisrng, numtimesrand8, numtimeswhole)
local rng=thisrng
for i=1,numtimeswhole do
rng=rand8(rng,numtimesrand8)
rng=rand32(rng,1)
end
return rng
end
--check whether player survives in Lucky Draw - there are 788 such checks
local function check(rng1,rng2)
return ((rng1&0x3F)~=(rng2&0x3F))
end
local crng_temp=0
local check_count=1
local dist=0
local crng=0
if mode==1 or mode==3 then
crng=initial_rng_value
else
crng=mainmemory.read_u32_le(0x1B)
end
local prev_values={}
local exitvalue=-1
local successflag=false
local i=0
console.write(string.format("Testing RNG value %08X:\n", crng))
--this is the main loop
--limited to 50000 loops, but there are no known instances of reaching 50000 without resolution, and there is not expected to be one
for i=1,50000 do
if prev_values[crng] then
exitvalue=i
break
end
prev_values[crng]=i
for fakeloop=1,1 do
crng=rand32(crng,24)
crng=rand8rand32(crng,16,2)
crng=rand32(crng,2)
crng=rand8rand32(crng,16,3)
crng=rand32(crng,1) --selects random character
crng=rand8rand32(crng,16,24)
--check #1
crng_temp=rand32(crng,1)
crng=rand32(crng_temp,1)
if check(crng,crng_temp) then check_count=check_count+1 else
crng=rand8(crng,16)
break
end
crng=rand8rand32(crng,16,2)
crng=rand8rand32(crng,15,3)
crng=rand8rand32(crng,9,1)
--check #2
crng_temp=rand32(crng,1)
crng=rand32(crng_temp,1)
if check(crng,crng_temp) then check_count=check_count+1 else
crng=rand8(crng,9)
break
end
crng=rand8rand32(crng,9,4)
--the following is responsible for checks #3-#786
dist=7840
local nextcheckdist=8448
local get_out=false
while dist<3219328 do
if dist>=nextcheckdist then
--check
crng_temp=rand32(crng,1)
crng=rand32(crng_temp,1)
if check(crng,crng_temp) then
check_count=check_count+1
nextcheckdist=nextcheckdist+4096
else
get_out=true
dist=dist+708
break
end
end
dist=dist+708
crng=rand32(crng,1)
end
if get_out then break end
--now at dist 3219328
crng=rand8rand32(crng,2,1)
--check #787
crng_temp=rand32(crng,1)
crng=rand32(crng_temp,1)
if check(crng,crng_temp) then check_count=check_count+1 else
crng=rand8(crng,2)
break
end
crng=rand8rand32(crng,2,4)
crng=rand8rand32(crng,4,2)
--check #788 (final)
crng_temp=rand32(crng,1)
crng=rand32(crng_temp,1)
if check(crng,crng_temp) then check_count=check_count+1 else
crng=rand8(crng,4)
break
end
crng=rand8rand32(crng,4,1)
successflag=true
end
if successflag or mode>=3 then break end
end
if successflag then
if mode==1 or mode==2 then
console.write(string.format("Success after %d attempts", i))
else
console.write("Attempt is successful")
end
else
if mode==1 or mode==2 then
if exitvalue~=-1 then
console.write(string.format("Exited due to repetition at attempt %d of attempt %d (period %d), repeated RNG value is %08X", exitvalue,prev_values[crng],exitvalue-prev_values[crng],crng))
else
console.write(string.format("Exited after max attempts (50000)")) --this is not expected to happen
end
else
console.write(string.format("Failed at check #%d with final RNG value: %08X", check_count, crng))
end
end