Guest User

Untitled

a guest
Jul 27th, 2016
291
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 9.54 KB | None | 0 0
  1. --[[
  2. ミニパット (FC) HIO探索スクリプト
  3.  
  4. カーソルを初期位置から動かしていない状態で起動すること
  5.  
  6. ログはサブディレクトリ log/ 内に出力される(予め mkdir すること)
  7. --]]
  8.  
  9.  
  10. ----------------------------------------------------------------------
  11. -- util
  12. ----------------------------------------------------------------------
  13.  
  14. local function mem_read_u8(addr)
  15.     return memory.readbyte(addr)
  16. end
  17.  
  18. local function mem_write_u8(addr, value)
  19.     memory.writebyte(addr, value)
  20. end
  21.  
  22. local function mem_read_u16_le(addr)
  23.     local lo = mem_read_u8(addr)
  24.     local hi = mem_read_u8(addr+1)
  25.     return bit.bor(lo, bit.lshift(hi, 8))
  26. end
  27.  
  28. local function mem_write_u16_le(addr, value)
  29.     local lo = bit.band(value, 0xFF)
  30.     local hi = bit.band(bit.rshift(value, 8), 0xFF)
  31.     mem_write_u8(addr, lo)
  32.     mem_write_u8(addr+1, hi)
  33. end
  34.  
  35. local function play(input, n)
  36.     if n == nil then n = 1 end
  37.     for i = 1, n do
  38.         joypad.set(1, input)
  39.         emu.frameadvance()
  40.     end
  41. end
  42.  
  43.  
  44. ----------------------------------------------------------------------
  45. -- log
  46. ----------------------------------------------------------------------
  47.  
  48. local out = nil
  49.  
  50. local function out_init()
  51.     local path = os.date("log/%Y%m%d-%H%M%S.log")
  52.  
  53.     out = io.open(path, "w")
  54.  
  55.     return out and true or false
  56. end
  57.  
  58. local function out_fin()
  59.     if out then
  60.         out:close()
  61.         out = nil
  62.     end
  63. end
  64.  
  65. local function info(msg)
  66.     out:write(msg)
  67.     out:write("\n")
  68.     out:flush()
  69. end
  70.  
  71.  
  72. ----------------------------------------------------------------------
  73. -- game
  74. ----------------------------------------------------------------------
  75.  
  76. local function get_weather()
  77.     return mem_read_u8(0x0545)
  78. end
  79.  
  80. local function get_course()
  81.     return mem_read_u8(0x04FC)
  82. end
  83.  
  84. local function get_hole()
  85.     return mem_read_u8(0x04FD)
  86. end
  87.  
  88. local function get_ball_pos()
  89.     local x = mem_read_u16_le(0xAB)
  90.     local y = mem_read_u16_le(0xAD)
  91.     return { x, y }
  92. end
  93.  
  94. local function set_power(power)
  95.     mem_write_u8(0x054E, power)
  96. end
  97.  
  98. local function set_curve(curve)
  99.     mem_write_u8(0x054D, curve)
  100. end
  101.  
  102. local function get_cursor()
  103.     local x = mem_read_u16_le(0x9B)
  104.     local y = mem_read_u16_le(0x9D)
  105.     return { x, y }
  106. end
  107.  
  108. local function set_cursor(cursor)
  109.     mem_write_u16_le(0x9B, cursor[1])
  110.     mem_write_u16_le(0x9D, cursor[2])
  111. end
  112.  
  113. --[[
  114. local function miniputt_sgn(value)
  115.     if value > 0 then
  116.         return 1
  117.     elseif value < 0 then
  118.         return 0xFF
  119.     else
  120.         return 0
  121.     end
  122. end
  123.  
  124. local function set_vec(vec)
  125.     for i = 0, 1 do
  126.         mem_write_u8(0x8B + i, miniputt_sgn(vec[1+i]))
  127.         mem_write_u16_le(0x97 + 2*i, math.abs(vec[1+i]))
  128.     end
  129. end
  130. --]]
  131.  
  132.  
  133. ----------------------------------------------------------------------
  134. -- 探索部
  135. ----------------------------------------------------------------------
  136.  
  137. local function shoot(power, curve, vec)
  138.     -- カーソル位置に vec を加算
  139.     local cursor = get_cursor()
  140.     cursor[1] = cursor[1] + vec[1]
  141.     cursor[2] = cursor[2] + vec[2]
  142.     set_cursor(cursor)
  143.  
  144.     -- 書き換えたカーソル位置を反映させる
  145.     play({})
  146.  
  147.     -- パワー、カーブ設定用フック
  148.     memory.registerexec(0xD30C, function()
  149.         set_power(power)
  150.     end)
  151.     memory.registerexec(0xD364, function()
  152.         set_curve(curve)
  153.     end)
  154.  
  155.     -- ボールを打つ操作
  156.     play({ A = 1 }) -- スイング開始
  157.     play({}, 10)
  158.     play({ A = 1 }) -- パワー決定
  159.     play({}, 6)
  160.     play({ A = 1 }) -- カーブ決定
  161.  
  162.     memory.registerexec(0xD364, nil)
  163.     memory.registerexec(0xD30C, nil)
  164. end
  165.  
  166. local function is_success()
  167.     local result = nil
  168.     local done = false
  169.  
  170.     memory.registerexec(0xDF1E, function()
  171.         result = mem_read_u8(0x89) == 0x8F
  172.         done = true
  173.     end)
  174.  
  175.     -- 4-2でボールが止まらないケースがあったので対処
  176.     -- 上限フレーム数を超えたら強制終了し、 nil を返す
  177.  
  178.     for frame = 1, 10000 do
  179.         play({})
  180.         if done then break end
  181.     end
  182.  
  183.     memory.registerexec(0xDF1E, nil)
  184.  
  185.     --[[
  186.     -- カーソル座標が書き換えられたらカップインしなかったとみなす
  187.     -- FIXME: これだとやや待ち時間が長い
  188.     memory.registerwrite(0x9B, function()
  189.         done = true
  190.     end)
  191.  
  192.     while not done do
  193.         -- ポインタ $047E が $E0E1 を指していればカップインとみなす
  194.         -- (左下の絵がカップイン時のものになっているはず)
  195.         local ptr_047E = mem_read_u16_le(0x047E)
  196.         if ptr_047E == 0xE0E1 then
  197.             result = true
  198.             done = true
  199.         end
  200.  
  201.         play({})
  202.     end
  203.  
  204.     memory.registerwrite(0x9B, nil)
  205.     --]]
  206.  
  207.     return result
  208. end
  209.  
  210. local function power_is_valid(power)
  211.     return 0x9D <= power and power <= 0xD3 and power%2 == 1
  212. end
  213.  
  214. local function curve_is_valid(curve)
  215.     return 0x10 <= curve and curve <= 0x40 and curve%2 == 0
  216. end
  217.  
  218. -- あまりにカーソルが近すぎると打てない
  219. local function vec_is_valid(vec)
  220.     local abs_x = math.abs(vec[1])
  221.     local abs_y = math.abs(vec[2])
  222.  
  223.     return (abs_x > 2 or abs_y > 2) or (abs_x == 2 and abs_y == 2)
  224. end
  225.  
  226. local function print_result(wait, power, curve, vec, n_frame, ball_pos, result)
  227.     -- ボールが止まらず強制終了した場合、ボール座標を (-1,-1) とする
  228.     if result == nil then
  229.         ball_pos = { -1, -1 }
  230.     end
  231.  
  232.     local line = string.format(
  233.         "%d\t0x%02X\t0x%02X\t%d\t%d\t%d\t%d\t%d\t%d",
  234.         wait, power, curve, vec[1], vec[2],
  235.         n_frame, ball_pos[1], ball_pos[2], result and 1 or 0
  236.     )
  237.  
  238.     info(line)
  239.     if result then print(line) end
  240. end
  241.  
  242. local function timestamp()
  243.     return os.date("%Y/%m/%d %H:%M:%S")
  244. end
  245.  
  246. local function search(power_range, curve_range, vecx_range, vecy_range, waits)
  247.     assert(power_is_valid(power_range[1]) and power_is_valid(power_range[2]))
  248.     assert(curve_is_valid(curve_range[1]) and curve_is_valid(curve_range[2]))
  249.  
  250.     if waits == nil then waits = { 0, 0, 1 } end
  251.  
  252.     info(string.format("# SEARCH START (%s)", timestamp()))
  253.     info("")
  254.  
  255.     local ball_pos = get_ball_pos()
  256.     info(string.format("# weather: %d", get_weather()))
  257.     info(string.format("# hole: %d-%d", 1+get_course(), 1+get_hole()))
  258.     info(string.format("# ball: (%d,%d)", ball_pos[1], ball_pos[2]))
  259.     info("")
  260.  
  261.     info(string.format("# waits: %d, %d, %d", waits[1], waits[2], waits[3]))
  262.     info(string.format("# power: [0x%02X,0x%02X]", power_range[1], power_range[2]))
  263.     info(string.format("# curve: [0x%02X,0x%02X]", curve_range[1], curve_range[2]))
  264.     info(string.format("# vecx: [%d,%d]", vecx_range[1], vecx_range[2]))
  265.     info(string.format("# vecy: [%d,%d]", vecy_range[1], vecy_range[2]))
  266.     info("")
  267.     info(string.format("# wait\tpower\tcurve\tvecx\tvecy\tnframe\tballx\tbally\tresult"))
  268.  
  269.     local count = 0
  270.     local frame_start = emu.framecount()
  271.  
  272.     local state = savestate.object()
  273.     savestate.save(state)
  274.  
  275.     for wait = waits[1], waits[2], waits[3] do
  276.         for vecx = vecx_range[1], vecx_range[2] do
  277.             for vecy = vecy_range[1], vecy_range[2] do
  278.                 local vec = { vecx, vecy }
  279.                 if vec_is_valid(vec) then
  280.                     for power = power_range[1], power_range[2], 2 do
  281.                         for curve = curve_range[1], curve_range[2], 2 do
  282.                             savestate.load(state)
  283.  
  284.                             play({}, wait)
  285.  
  286.                             shoot(power, curve, vec)
  287.                             local result = is_success()
  288.                             local n_frame = emu.framecount() - frame_start
  289.                             local ball_pos = get_ball_pos()
  290.                             print_result(wait, power, curve, vec,
  291.                                          n_frame, ball_pos, result)
  292.  
  293.                             count = count + 1
  294.                         end
  295.                     end
  296.                 end
  297.             end
  298.         end
  299.     end
  300.  
  301.     info("")
  302.     info(string.format("# SEARCH END (%s) count=%d", timestamp(), count))
  303. end
  304.  
  305.  
  306. ----------------------------------------------------------------------
  307. -- main
  308. ----------------------------------------------------------------------
  309.  
  310. local function init()
  311.     if not out_init() then
  312.         print("Can't open file")
  313.         return false
  314.     end
  315.  
  316.     emu.speedmode("maximum")
  317.     --emu.setrenderplanes(false, false)
  318.  
  319.     return true
  320. end
  321.  
  322. local function fin()
  323.     --emu.setrenderplanes(true, true)
  324.     emu.speedmode("normal")
  325.  
  326.     out_fin()
  327. end
  328.  
  329. local function main()
  330.     if not init() then
  331.         print("init() failed")
  332.         return
  333.     end
  334.     emu.registerexit(fin)
  335.  
  336.     -- カーソルが範囲外だとボールが止まらなくなることがあるので注意
  337.  
  338.     search(
  339.         { 0x9D, 0x9D }, -- power
  340.         { 0x10, 0x10 }, -- curve
  341.         { -10, 10 }, -- vecx
  342.         { -10, 10 }, -- vecy
  343.         { 0, 143, 1 } -- waits
  344.     )
  345.     --[[
  346.     search(
  347.         { 0x9D, 0x9D }, -- power
  348.         { 0x10, 0x10 }, -- curve
  349.         { -2, 2 }, -- vecx
  350.         { -2, 2 }, -- vecy
  351.         nil -- waits
  352.     )
  353.     --]]
  354.  
  355.     --[[
  356.     search(
  357.         { 0x9D, 0xD3 }, -- power
  358.         { 0x10, 0x40 }, -- curve
  359.         { -2, 2 }, -- vecx
  360.         { -2, 2 }, -- vecy
  361.         nil -- waits
  362.     )
  363.     --]]
  364.  
  365.     -- "Stop" を押さなくてもログ出力は完了してほしい
  366.     fin()
  367.  
  368.     emu.pause()
  369. end
  370.  
  371. main()
Add Comment
Please, Sign In to add comment