User File #638491436493000570

Upload All User Files

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

PMD1_dungeon_bruteforce_v1.3.lua
10 downloads
Uploaded 13 days ago 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()