User File #638491436493000570

Upload All User Files

#638491436493000570 - Blue Rescue Team - dungeon bruteforce v1.3

PMD1_dungeon_bruteforce_v1.3.lua
42 downloads
Uploaded 4/19/2024 5:14 PM by ThunderAxe31 (see all 111)
fixed the bug that prevented for working on the second dungeon. now also logs the floor generation speed, yay!
--NDS Pokemon Mystery Dungeon: Blue Rescue Team - dungeon bruteforce v1.3 by ThunderAxe31
--launch this on at least 1 frame before that the dungeon seed (0x02115414) gets updated.

local floors         =      3 --amount of floors we want to bruteforce... probably later will automatically make it set the right value
local starting_floor =      1 --starting floor
local starting_adv   =      0 --starting amount of RNG advancements before loading the dungeon. lowest value allowed is 0
local iter           = 100000 --amount of seeds we want to test, starting from the value above

local addr_global_rng  = 0x0E9FC0
local addr_dungeon_rng = 0x115414
local addr_floor_rng   = 0x1C2790
local addr_floor       = 0x1153E5
local addr_fade_in     = 0x1C284C
local addr_player_x    = 0x126360
local addr_player_y    = 0x126362
local addr_tiles_start = 0x118260

local off_stairs =  1
local off_item   = 16
local off_tile   = 20
local dun_w = 56
local dun_h = 32

local waiting = 0

local current_floor     = 0
local current_floor_rng = 0

local file_log = io.open("log.txt", "a")
function log_update(text)
	file_log:write(text .. "\n")
	file_log:flush()
	console.log(text)
end

local function rng_adv(val, iter)
	for i=1, iter do
		local high = (val * 0x5D58) % 0x10000
		local low  = (val * 0x8B65) % 0x100000000
		val = (low + high * 0x10000) % 0x100000000 +1
		local high = (val * 0x5D58) % 0x10000
		local low  = (val * 0x8B65) % 0x100000000
		val = (low + high * 0x10000) % 0x100000000 +1
	end
	return val
end

local function change_floor()
	memory.write_u8(addr_floor, current_floor)
end

local function change_floor_rng()
	memory.write_u32_le(addr_floor_rng, rng_adv(memory.read_u32_le(addr_floor_rng), current_floor-1))
end

if not memory.usememorydomain("Main RAM") then
	log_update("ERROR: Can't set Main RAM as default memory domain.")
	while true do
		emu.frameadvance()
	end
end

if (memory.read_u8(addr_floor) ~= 0) and (memory.read_u8(addr_floor) ~= 255) then
	log_update("ERROR: Dungeon generation already started. Please start the script before entering the dungeon.")
	while true do
		emu.frameadvance()
	end
end

client.unpause()
while memory.read_u32_le(addr_dungeon_rng) ~= 0 do --let's wait until we're close enough
	emu.frameadvance()
end
local iter_state = memorysavestate.savecorestate() --we save before checking how long it takes to start generating

while memory.read_u32_le(addr_dungeon_rng) == 0 do --this tells it's the frame it starts generating
	waiting = waiting +1
	emu.frameadvance()
end

memorysavestate.loadcorestate(iter_state)
for i=1, waiting-1 do --let's wait the same, but 1 frame less.
	emu.frameadvance()
end

iter_state = memorysavestate.savecorestate() --start of dungeon generation

event.on_bus_read(change_floor, 0x02000000 +addr_floor)
event.on_bus_read(change_floor_rng, 0x02000000 +addr_floor_rng)

for rng_advancements = starting_adv, iter +starting_adv -1 do
	memorysavestate.loadcorestate(iter_state)
	
	local source_rng = rng_adv(memory.read_u32_le(addr_global_rng), rng_advancements) --we also save it for diagnostic purposes
	memory.write_u32_le(addr_global_rng, source_rng)
	
	log_update("RNG Advancements: " .. rng_advancements .. " Global RNG: " .. string.format("%08X", source_rng))
	
	local floor_state = memorysavestate.savecorestate() --start of floor generation
	
	local steps = 0
	local dungeon_frames = 0
	
	for f=starting_floor, floors do
		memorysavestate.loadcorestate(floor_state)
		
		current_floor = f
		
		local floor_frames = 0
		while memory.read_u8(addr_fade_in) == 0 do
			floor_frames = floor_frames +1 --count how many frames it takes to generate this floor
			emu.frameadvance()
		end
		
		local player_x = memory.read_u16_le(addr_player_x)
		local player_y = memory.read_u16_le(addr_player_y)
		local stairs_x = -1
		local stairs_y = -1
		
		for y=2, dun_h-2 do
			for x=2, dun_w-2 do
				local stairs = memory.read_u8(addr_tiles_start +y*dun_w*off_tile +x*off_tile +off_stairs)
				if stairs == 2 then
					stairs_x = x
					stairs_y = y
					break
				end
			end
			if stairs_x ~= -1 then
				break
			end
		end
		
		local distance = math.max(math.abs(player_x -stairs_x), math.abs(player_y -stairs_y))
		
		log_update("Floor " .. string.format("%02u", f) .. " seed " .. string.format("%08X", memory.read_u32_le(addr_floor_rng)) .. " distance " .. string.format("%2u", distance) .. " steps loading time " .. string.format("%3u", floor_frames) )
		
		steps = steps +distance
		
		if distance == 0 then
			distance = 4 --having the stairs directly under your feets is actually slower due to menuing
		end
		dungeon_frames = dungeon_frames +floor_frames +distance
		
		if f == floors then
			log_update("= Total steps: " .. steps .. " steps; Total time: " .. dungeon_frames)
		end
	end
	
	memorysavestate.removestate(floor_state)
	
end

memorysavestate.loadcorestate(iter_state)
client.pause()