Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --[[
- ミニパット (FC) HIO探索スクリプト
- カーソルを初期位置から動かしていない状態で起動すること
- ログはサブディレクトリ log/ 内に出力される(予め mkdir すること)
- --]]
- ----------------------------------------------------------------------
- -- util
- ----------------------------------------------------------------------
- local function mem_read_u8(addr)
- return memory.readbyte(addr)
- end
- local function mem_write_u8(addr, value)
- memory.writebyte(addr, value)
- end
- local function mem_read_u16_le(addr)
- local lo = mem_read_u8(addr)
- local hi = mem_read_u8(addr+1)
- return bit.bor(lo, bit.lshift(hi, 8))
- end
- local function mem_write_u16_le(addr, value)
- local lo = bit.band(value, 0xFF)
- local hi = bit.band(bit.rshift(value, 8), 0xFF)
- mem_write_u8(addr, lo)
- mem_write_u8(addr+1, hi)
- end
- local function play(input, n)
- if n == nil then n = 1 end
- for i = 1, n do
- joypad.set(1, input)
- emu.frameadvance()
- end
- end
- ----------------------------------------------------------------------
- -- log
- ----------------------------------------------------------------------
- local out = nil
- local function out_init()
- local path = os.date("log/%Y%m%d-%H%M%S.log")
- out = io.open(path, "w")
- return out and true or false
- end
- local function out_fin()
- if out then
- out:close()
- out = nil
- end
- end
- local function info(msg)
- out:write(msg)
- out:write("\n")
- out:flush()
- end
- ----------------------------------------------------------------------
- -- game
- ----------------------------------------------------------------------
- local function get_course()
- return mem_read_u8(0x04FC)
- end
- local function get_hole()
- return mem_read_u8(0x04FD)
- end
- local function get_ball_pos()
- local x = mem_read_u16_le(0xAB)
- local y = mem_read_u16_le(0xAD)
- return { x, y }
- end
- local function set_power(power)
- mem_write_u8(0x054E, power)
- end
- local function set_curve(curve)
- mem_write_u8(0x054D, curve)
- end
- local function get_cursor()
- local x = mem_read_u16_le(0x9B)
- local y = mem_read_u16_le(0x9D)
- return { x, y }
- end
- local function set_cursor(cursor)
- mem_write_u16_le(0x9B, cursor[1])
- mem_write_u16_le(0x9D, cursor[2])
- end
- --[[
- local function miniputt_sgn(value)
- if value > 0 then
- return 1
- elseif value < 0 then
- return 0xFF
- else
- return 0
- end
- end
- local function set_vec(vec)
- for i = 0, 1 do
- mem_write_u8(0x8B + i, miniputt_sgn(vec[1+i]))
- mem_write_u16_le(0x97 + 2*i, math.abs(vec[1+i]))
- end
- end
- --]]
- ----------------------------------------------------------------------
- -- 探索部
- ----------------------------------------------------------------------
- local function shoot(power, curve, vec)
- -- カーソル位置に vec を加算
- local cursor = get_cursor()
- cursor[1] = cursor[1] + vec[1]
- cursor[2] = cursor[2] + vec[2]
- set_cursor(cursor)
- -- 書き換えたカーソル位置を反映させる
- play({})
- -- パワー、カーブ設定用フック
- memory.registerexec(0xD30C, function()
- set_power(power)
- end)
- memory.registerexec(0xD364, function()
- set_curve(curve)
- end)
- -- ボールを打つ操作
- play({ A = 1 }) -- スイング開始
- play({}, 10)
- play({ A = 1 }) -- パワー決定
- play({}, 6)
- play({ A = 1 }) -- カーブ決定
- memory.registerexec(0xD364, nil)
- memory.registerexec(0xD30C, nil)
- end
- local function is_success()
- local result = false
- local done = false
- -- カーソル座標が書き換えられたらカップインしなかったとみなす
- -- FIXME: これだとやや待ち時間が長い
- memory.registerwrite(0x9B, function()
- done = true
- end)
- while not done do
- -- ポインタ $047E が $E0E1 を指していればカップインとみなす
- -- (左下の絵がカップイン時のものになっているはず)
- local ptr_047E = mem_read_u16_le(0x047E)
- if ptr_047E == 0xE0E1 then
- result = true
- done = true
- end
- play({})
- end
- memory.registerwrite(0x9B, nil)
- return result
- end
- local function power_is_valid(power)
- return 0x9D <= power and power <= 0xD3 and power%2 == 1
- end
- local function curve_is_valid(curve)
- return 0x10 <= curve and curve <= 0x40 and curve%2 == 0
- end
- -- あまりにカーソルが近すぎると打てない
- local function vec_is_valid(vec)
- local abs_x = math.abs(vec[1])
- local abs_y = math.abs(vec[2])
- return (abs_x > 2 or abs_y > 2) or (abs_x == 2 and abs_y == 2)
- end
- local function print_result(power, curve, vec, result)
- info(string.format(
- "0x%02X\t0x%02X\t%d\t%d\t%d",
- power, curve, vec[1], vec[2], result and 1 or 0
- ))
- end
- local function timestamp()
- return os.date("%Y/%m/%d %H:%M:%S")
- end
- local function search(power_range, curve_range, vecx_range, vecy_range)
- assert(power_is_valid(power_range[1]) and power_is_valid(power_range[2]))
- assert(curve_is_valid(curve_range[1]) and curve_is_valid(curve_range[2]))
- info(string.format("# SEARCH START (%s)", timestamp()))
- local ball_pos = get_ball_pos()
- info(string.format("# hole: %d-%d", 1+get_course(), 1+get_hole()))
- info(string.format("# ball: (%d,%d)", ball_pos[1], ball_pos[2]))
- info("")
- info(string.format("# power: [0x%02X,0x%02X]", power_range[1], power_range[2]))
- info(string.format("# curve: [0x%02X,0x%02X]", curve_range[1], curve_range[2]))
- info(string.format("# vecx: [%d,%d]", vecx_range[1], vecx_range[2]))
- info(string.format("# vecy: [%d,%d]", vecy_range[1], vecy_range[2]))
- info("")
- local count = 0
- local state = savestate.object()
- savestate.save(state)
- for vecx = vecx_range[1], vecx_range[2] do
- for vecy = vecy_range[1], vecy_range[2] do
- local vec = { vecx, vecy }
- if vec_is_valid(vec) then
- for power = power_range[1], power_range[2], 2 do
- for curve = curve_range[1], curve_range[2], 2 do
- savestate.load(state)
- shoot(power, curve, vec)
- local result = is_success()
- print_result(power, curve, vec, result)
- count = count + 1
- end
- end
- end
- end
- end
- info("")
- info(string.format("# SEARCH END (%s) count=%d", timestamp(), count))
- end
- ----------------------------------------------------------------------
- -- main
- ----------------------------------------------------------------------
- local function init()
- if not out_init() then
- print("Can't open file")
- return false
- end
- emu.speedmode("maximum")
- --emu.setrenderplanes(false, false)
- return true
- end
- local function fin()
- --emu.setrenderplanes(true, true)
- emu.speedmode("normal")
- out_fin()
- end
- local function main()
- if not init() then
- print("init() failed")
- return
- end
- emu.registerexit(fin)
- -- カーソルが範囲外だとボールが止まらなくなることがあるので注意
- search(
- { 0x9D, 0x9D }, -- power
- { 0x10, 0x10 }, -- curve
- { -41, 50 }, -- vecx
- { -50, 50 } -- vecy
- )
- --[[
- search(
- { 0x9D, 0x9D }, -- power
- { 0x10, 0x10 }, -- curve
- { -2, 2 }, -- vecx
- { -2, 2 } -- vecy
- )
- --]]
- --[[
- search(
- { 0x9D, 0xD3 }, -- power
- { 0x10, 0x40 }, -- curve
- { -2, 2 }, -- vecx
- { -2, 2 } -- vecy
- )
- --]]
- -- "Stop" を押さなくてもログ出力は完了してほしい
- fin()
- emu.pause()
- end
- main()
Add Comment
Please, Sign In to add comment