mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-09-26 05:32:07 -04:00
157 lines
5.6 KiB
Lua
157 lines
5.6 KiB
Lua
#!/usr/bin/env luajit
|
|
-- Pret FireRed LCRNG + wild encounter stream smoke tests.
|
|
|
|
package.path = "./?.lua;./?/init.lua;" .. package.path
|
|
|
|
local failed = 0
|
|
local function check(cond, msg)
|
|
if cond then
|
|
print("[ok] " .. msg)
|
|
else
|
|
failed = failed + 1
|
|
print("[FAIL] " .. msg)
|
|
end
|
|
end
|
|
|
|
local function eq(a, b, msg)
|
|
check(a == b, string.format("%s (%s == %s)", msg, tostring(a), tostring(b)))
|
|
end
|
|
|
|
print("[test] 1. Golden Random() stream (seed 0x1234)")
|
|
local Rng = require("src.core.game3.rng")
|
|
Rng.SeedRng(0x1234)
|
|
-- Hand-verified first step + captured sequence from mulU32 ISO_RANDOMIZE1.
|
|
local GOLDEN_RANDOM = {
|
|
19915, 57697, 17216, 65521, 49689, 27070, 63088, 15005,
|
|
53261, 32661, 40992, 3727, 27521, 19775, 30255, 24698,
|
|
}
|
|
for i = 1, #GOLDEN_RANDOM do
|
|
eq(Rng.Random(), GOLDEN_RANDOM[i], "Random()[" .. i .. "]")
|
|
end
|
|
|
|
print("[test] 2. Golden WildEncounterRandom() stream (seed 0xABCD)")
|
|
Rng.SeedWildEncounterRng(0xABCD)
|
|
local GOLDEN_WILD = { 8751, 19765, 35009, 51321, 30994, 38996, 9941, 30164 }
|
|
for i = 1, #GOLDEN_WILD do
|
|
eq(Rng.WildEncounterRandom(), GOLDEN_WILD[i], "WildEncounterRandom()[" .. i .. "]")
|
|
end
|
|
|
|
print("[test] 3. Random32 / SeedRng / getState round-trip")
|
|
Rng.SeedRng(1)
|
|
local a = Rng.Random()
|
|
local b = Rng.Random()
|
|
Rng.SeedRng(1)
|
|
local c = Rng.Random32()
|
|
eq(c, a + b * 65536, "Random32 == Random | (Random<<16)")
|
|
local st = Rng.getState()
|
|
Rng.Random()
|
|
Rng.Random()
|
|
Rng.setState(st)
|
|
eq(Rng.getState().value, st.value, "setState restores value")
|
|
eq(Rng.setState({ value = 123 }), false, "setState rejects a partial state")
|
|
eq(Rng.getState().value, st.value, "and leaves the value untouched")
|
|
eq(Rng.restoreFromSession({ rng = { value = 1 } }), false,
|
|
"restoreFromSession rejects a partial rng (Game3 reseeds instead)")
|
|
|
|
print("[test] 4. seedNewGame wires wild from Random()")
|
|
Rng.SeedRng(0x55AA)
|
|
-- Manual: after SeedRng, seedNewGame reseeds from timer unless seed forced.
|
|
local tid = Rng.seedNewGame({ seed = 0x55AA })
|
|
eq(tid, 0x55AA, "seedNewGame returns trainer seed")
|
|
-- Wild stream was seeded with first Random() after SeedRng(0x55AA).
|
|
-- Replay: SeedRng(0x55AA); local w = Random(); compare wild state after seedNewGame.
|
|
Rng.SeedRng(0x55AA)
|
|
local first = Rng.Random()
|
|
local afterNew = Rng.getState().wild
|
|
Rng.SeedRng(0x55AA)
|
|
Rng.SeedWildEncounterRng(Rng.Random())
|
|
eq(Rng.getState().wild, first, "SeedWildEncounterRng(Random()) uses first Random")
|
|
eq(afterNew, first, "seedNewGame wild state matches")
|
|
|
|
print("[test] 5. Encounter rolls are deterministic (no math.random)")
|
|
local Encounters = require("src.core.game3.encounters")
|
|
Encounters._tables = {
|
|
TEST_MAP = {
|
|
land = {
|
|
rate = 21,
|
|
slots = {
|
|
{ species = 16, minLevel = 3, maxLevel = 5 },
|
|
{ species = 19, minLevel = 3, maxLevel = 5 },
|
|
{ species = 21, minLevel = 3, maxLevel = 5 },
|
|
{ species = 13, minLevel = 3, maxLevel = 5 },
|
|
{ species = 10, minLevel = 3, maxLevel = 5 },
|
|
{ species = 29, minLevel = 3, maxLevel = 5 },
|
|
{ species = 32, minLevel = 3, maxLevel = 5 },
|
|
{ species = 56, minLevel = 3, maxLevel = 5 },
|
|
{ species = 60, minLevel = 3, maxLevel = 5 },
|
|
{ species = 69, minLevel = 3, maxLevel = 5 },
|
|
{ species = 74, minLevel = 3, maxLevel = 5 },
|
|
{ species = 16, minLevel = 5, maxLevel = 5 },
|
|
},
|
|
},
|
|
},
|
|
}
|
|
Encounters._loaded = true
|
|
|
|
local function run_seq()
|
|
Rng.SeedRng(0xC0DE)
|
|
Rng.SeedWildEncounterRng(0xBEEF)
|
|
-- pret resets the encounter rate modifiers on map load / battle start; the
|
|
-- banked failure rate is history-dependent, so a replay needs the same reset.
|
|
Encounters.resetRateModifiers()
|
|
local out = {}
|
|
for i = 1, 40 do
|
|
local enc = Encounters.rollLand("TEST_MAP", nil, i == 1)
|
|
if enc then
|
|
out[#out + 1] = string.format("%d:%d", enc.species, enc.level)
|
|
else
|
|
out[#out + 1] = "-"
|
|
end
|
|
end
|
|
return table.concat(out, ",")
|
|
end
|
|
|
|
local seq1 = run_seq()
|
|
local seq2 = run_seq()
|
|
eq(seq1, seq2, "two identical seeds → identical encounter sequence")
|
|
check(seq1:find("%d+:%d+") ~= nil, "sequence includes at least one encounter")
|
|
-- First-step gate uses Random()%100; with seed 0xC0DE first roll is deterministic.
|
|
Rng.SeedRng(0xC0DE)
|
|
local gate = Rng.Random() % 100
|
|
check(gate >= 0 and gate < 100, "gate roll in 0..99")
|
|
|
|
print("[test] 6. Save schema persists rng")
|
|
local Schema = require("src.core.game3.save_schema_firered")
|
|
local session = Schema.newGame({ name = "ASH", rngSeed = 0x1111 })
|
|
check(type(session.rng) == "table", "newGame has rng table")
|
|
eq(session.trainerId, 0x1111, "newGame trainerId from seed")
|
|
local saved = Schema.toSaveTable(session)
|
|
check(saved.rng ~= nil and saved.rng.value ~= nil, "toSaveTable includes rng")
|
|
local loaded = Schema.fromSaveTable(saved)
|
|
eq(loaded.rng.value, saved.rng.value, "fromSaveTable keeps value")
|
|
eq(loaded.rng.wild, saved.rng.wild, "fromSaveTable keeps wild")
|
|
Rng.setState({ value = 0, value2 = 0, wild = 0 })
|
|
Rng.restoreFromSession(loaded)
|
|
eq(Rng.getState().value, loaded.rng.value, "restoreFromSession applies value")
|
|
eq(Rng.getState().wild, loaded.rng.wild, "restoreFromSession applies wild")
|
|
|
|
print("[test] 7. Battle State defaults to Rng.compat")
|
|
local State = require("src.core.game3.battle.state")
|
|
local st = State.new({
|
|
wild = true,
|
|
playerParty = { { species = 1, level = 5, hp = 20, maxHp = 20, moves = { 33 }, pp = { 35 } } },
|
|
foeMon = { species = 16, level = 3, hp = 15, maxHp = 15, moves = { 33 }, pp = { 35 } },
|
|
})
|
|
check(st.rng == Rng.compat, "State.new rng is Rng.compat")
|
|
Rng.SeedRng(42)
|
|
local r1 = st.rng(0, 255)
|
|
Rng.SeedRng(42)
|
|
local r2 = st.rng(0, 255)
|
|
eq(r1, r2, "compat is deterministic from SeedRng")
|
|
|
|
if failed > 0 then
|
|
print("[test] FAILED " .. failed)
|
|
os.exit(1)
|
|
end
|
|
print("[test] all passed")
|