DevonStudios / LuaScripts

Many Lua scripts useful for RNG abusing in Pokémon games

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[Suggestion] Switch to an O(log n) algo complexity to compute the distance between 2 PRNG states for LCGs.

StarfBerry opened this issue · comments

It's possible to calculate the distance between 2 LCG states in O(log n) complexity.
Here the source https://math.stackexchange.com/questions/2008585/computing-the-distance-between-two-linear-congruential-generator-states and an implementation in Python: https://github.com/Parzival5/Python-Scripts/blob/main/LCG.py#L50.
According to the person who found this algo, it should work with all main LCGs used in Pokémon games (mult = 1 mod 4, add is odd and the period is 2^32 or 2^64).
I tried the algo with several billion of PRNG states and different LCGs and I always get the correct result.

With that we can compute advances almost instantly with just the initial seed or with the previous prng state.
The Initial Seed Bot for FRLG can be optimized using this algo, I was able to get my target seed in an advances range < 10M in less than 15 seconds.
We can also lift the limit of 10k advances for Gen 4 games (the user will rarely reach such advancements but I think that's a good thing).
It's also possible to improve the savestate support like with RS on VBA where lua scripts can take a bunch of time to calculate advances when we reload states with different initial seeds.
Here is an application of the algo on Ruby using 5 savestates with different initial seeds for each:
c3wSJO4sSI
As you can see we keep the initial seed from the session where lua script was executed but info displayed are coherent and we don't have freezes between savestate loadings.
There's probably more applications that we can do with this algo.

An implementation of the algo for the LCRNG in lua if you want to try:

local rshift = bit.rshift
local bxor = bit.bxor
local band = bit.band

function mult32(a, b)
	local c = rshift(a, 16)
	local d = a % 0x10000
	local e = rshift(b, 16)
	local f = b % 0x10000
	local g = (c * f + d * e) % 0x10000
	local h = d * f
	local i = g * 0x10000 + h
	return i 
end

pmult = {
    0x41C64E6D, 0xC2A29A69, 0xEE067F11, 0xCFDDDF21, 0x5F748241, 0x8B2E1481, 0x76006901, 
    0x1711D201, 0xBE67A401, 0xDDDF4801, 0x3FFE9001, 0x90FD2001, 0x65FA4001, 0xDBF48001, 
    0xF7E90001, 0xEFD20001, 0xDFA40001, 0xBF480001, 0x7E900001, 0xFD200001, 0xFA400001, 
    0xF4800001, 0xE9000001, 0xD2000001, 0xA4000001, 0x48000001, 0x90000001, 0x20000001, 
    0x40000001, 0x80000001, 0x00000001, 0x00000001}
    
padd = {
    0x00006073, 0xE97E7B6A, 0x31B0DDE4, 0x67DBB608, 0xCBA72510, 0x1D29AE20, 0xBA84EC40, 
    0x79F01880, 0x08793100, 0x6B566200, 0x803CC400, 0xA6B98800, 0xE6731000, 0x30E62000, 
    0xF1CC4000, 0x23988000, 0x47310000, 0x8E620000, 0x1CC40000, 0x39880000, 0x73100000, 
    0xE6200000, 0xCC400000, 0x98800000, 0x31000000, 0x62000000, 0xC4000000, 0x88000000, 
    0x10000000, 0x20000000, 0x40000000, 0x80000000}

-- Calculate the distance between 2 LCRNG states in O(log n) complexity.
-- Credit: https://math.stackexchange.com/questions/2008585/computing-the-distance-between-two-linear-congruential-generator-states 
function calcDistance(seed1, seed2)
    p, i, d = 1, 1, 0

    while seed1 ~= seed2 do
        if band(bxor(seed1, seed2), p) ~= 0 then
            seed1 = (mult32(seed1, pmult[i]) + padd[i]) % 4294967296
            d = d + p
        end

        i = i + 1
        p = p * 2
    end

    return d
end

print(calcDistance(0xC0CAC01A, 0))
print(calcDistance(0xDEADBEEF, 0xBA0BAB))

Ok i completely missed this D:
This seems great, i'll take a look at it soon!

The gen4 10k limit has been already removed but i didn't push yet. The problem wasn't the distance, but the weather changing the init state