-
-
Save taotao54321/146d584ce44f4a2e73e50ac7ca4a0717 to your computer and use it in GitHub Desktop.
NES Quarth (J): block chunk manipulation script for SubNesHawk
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[[ | |
クォース (FC) ツモ調整 for SubNesHawk | |
入力を微調整し、望みのツモ ($98) が出るパターンを探索する。 | |
使い方: | |
* 調整開始フレーム以降の入力を本スクリプトと同じディレクトリに manip.in として置く。 | |
* 調整開始フレームで本スクリプトを起動する。解は manip.out に出力される。 | |
--]] | |
local movie_ext = require("movie_ext") | |
local util = require("util") | |
-- プレイヤーの挙動に影響しない範囲で入力を変更できるかどうかを返す。 | |
local function can_mutate(input) | |
-- 1P/2Pの U 以外のキーが押されていれば変更不能とみなす。 | |
for i = 1, 2 do | |
for button, _v in pairs(input[i]) do | |
if button ~= "Up" then | |
return false | |
end | |
end | |
end | |
return true | |
end | |
-- プレイヤーの挙動に影響しない範囲で入力を確率的に変異させる。 | |
local function mutate_input(input) | |
-- 変更不能な入力はそのままにする。 | |
if not can_mutate(input) then | |
return input | |
end | |
local buttons1 = util.table_clone(input[1]) | |
local buttons2 = util.table_clone(input[2]) | |
-- 以下のいずれかの変更を確率的に施す: | |
-- | |
-- * 1Pの U を2Pの U に置き換える (元入力は1P側で U を押していると仮定) | |
-- * 1Pの S を押す | |
-- * 2Pの S を押す | |
local r = math.random() | |
if r < 0.1 then | |
if buttons1["Up"] then | |
buttons1["Up"] = false | |
buttons2["Up"] = true | |
end | |
elseif r < 0.2 then | |
buttons1["Select"] = true | |
elseif r < 0.3 then | |
buttons2["Select"] = true | |
end | |
return { buttons1, buttons2 } | |
end | |
-- inputs を微調整し、ツモ調整を試みる。 | |
-- 望みのツモ tumo_want が得られたら調整後の入力列を、さもなくば nil を返す。 | |
local function try_manipulate(inputs, tumo_want) | |
local inputs_manip = {} | |
local tumo = nil | |
local handle = event.on_bus_exec(function () | |
tumo = emu.getregister("A") | |
end, 0xAC7B) | |
for _i, input in ipairs(inputs) do | |
local input_manip = mutate_input(input) | |
table.insert(inputs_manip, input_manip) | |
util.run_frame(input_manip) | |
end | |
event.unregisterbyid(handle) | |
assert(tumo ~= nil) | |
if tumo == tumo_want then | |
return inputs_manip | |
else | |
return nil | |
end | |
end | |
local function main() | |
local PATH_IN = "manip.in" | |
local PATH_OUT = "manip.out" | |
-- 望みのツモ ($98)。 | |
--local TUMO_WANT = 0 | |
local TUMO_WANT = 0x09 | |
--local TUMO_WANT = 0x0D | |
-- 開始フレーム以降の入力列を読み込む。 | |
local inputs = movie_ext.parse_inputs_from_file(PATH_IN) | |
-- 開始時のステートをセーブ。 | |
local state = memorysavestate.savecorestate() | |
-- 成功するまで調整を繰り返す。 | |
for i = 1, 10000000 do | |
if i % 100 == 0 then | |
print(string.format("iteration %d", i)) | |
end | |
local inputs_manip = try_manipulate(inputs, TUMO_WANT) | |
if inputs_manip ~= nil then | |
-- 成功したら調整後の入力列を保存してループ終了。 | |
movie_ext.format_inputs_to_file(PATH_OUT, inputs_manip) | |
break | |
end | |
memorysavestate.loadcorestate(state) | |
end | |
client.pause() | |
end | |
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[[ | |
ムービーファイル中の入力列を自前で読み書きする。 | |
--]] | |
local movie_ext = {} | |
local BUTTON_CHARS = { "U", "D", "L", "R", "S", "s", "B", "A" } | |
local BUTTON_NAMES = { "Up", "Down", "Left", "Right", "Start", "Select", "B", "A" } | |
-- 文字列 s 内のバイトインデックス i (1-based) の文字を返す。ASCII 文字列を仮定している。 | |
local function str_at(s, i) | |
return string.sub(s, i, i) | |
end | |
local function panic(msg) | |
error(msg) | |
end | |
-- 1 人のプレイヤーの入力キー文字列 ("UDLRSsBA" 形式) をパースし、その入力を返す。 | |
local function parse_buttons(s) | |
local buttons = {} | |
for i = 1, 8 do | |
local c = str_at(s, i) | |
if c ~= "." then | |
buttons[BUTTON_NAMES[i]] = true | |
end | |
end | |
return buttons | |
end | |
-- 1 フレームの入力をパースし、1P/2P入力を返す。 | |
local function parse_input(line) | |
local PATTERN_BUTTONS = "[U.][D.][L.][R.][S.][s.][B.][A.]" | |
local PATTERN = string.format("^|[^|]*|(%s)|(%s)|$", PATTERN_BUTTONS, PATTERN_BUTTONS) | |
local input = {} | |
local s1, s2 = string.match(line, PATTERN) | |
local buttons1 = parse_buttons(s1) | |
local buttons2 = parse_buttons(s2) | |
table.insert(input, buttons1) | |
table.insert(input, buttons2) | |
return input | |
end | |
-- ファイル全体を入力列としてパースする。 | |
movie_ext.parse_inputs_from_file = function (path) | |
local rdr = io.open(path, "r") | |
if rdr == nil then | |
panic(string.format("can't open file '%s' to read", path)) | |
end | |
local inputs = {} | |
for line in rdr:lines() do | |
local input = parse_input(line) | |
table.insert(inputs, input) | |
end | |
rdr:close() | |
return inputs | |
end | |
-- 1 人のプレイヤーの入力を "UDLRSsBA" 形式にフォーマットする。 | |
local function format_buttons(buttons) | |
local s = "" | |
for i = 1, 8 do | |
if buttons[BUTTON_NAMES[i]] then | |
s = s .. BUTTON_CHARS[i] | |
else | |
s = s .. "." | |
end | |
end | |
return s | |
end | |
-- 1P/2P入力を 1 フレームの入力文字列としてフォーマットする。 | |
local function format_input(input) | |
local s1 = format_buttons(input[1]) | |
local s2 = format_buttons(input[2]) | |
return string.format("| 0,..|%s|%s|", s1, s2) | |
end | |
-- 入力列をムービーファイル用の形式にフォーマットし、ファイルに保存する。 | |
movie_ext.format_inputs_to_file = function (path, inputs) | |
local wtr = io.open(path, "w") | |
if wtr == nil then | |
panic(string.format("can't open file '%s' to write", path)) | |
end | |
for _i, input in ipairs(inputs) do | |
local line = format_input(input) | |
wtr:write(line) | |
wtr:write("\n") | |
end | |
wtr:close() | |
end | |
return movie_ext |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local util = {} | |
util.panic = function (msg) | |
error(msg) | |
end | |
-- テーブルをシャローコピーする。 | |
util.table_clone = function (tbl) | |
local res = {} | |
for k, v in pairs(tbl) do | |
res[k] = v | |
end | |
return res | |
end | |
-- 2 つの配列が等しいかどうかを返す。 | |
util.array_eq = function (lhs, rhs) | |
if #lhs ~= #rhs then | |
return false | |
end | |
for i = 1, #lhs do | |
if lhs[i] ~= rhs[i] then | |
return false | |
end | |
end | |
return true | |
end | |
-- 配列 ary の指定範囲のスライスを返す。idx は 1-based。 | |
util.array_slice = function (ary, idx, len) | |
local slice = {} | |
for i = idx, idx + len - 1 do | |
table.insert(slice, ary[i]) | |
end | |
return slice | |
end | |
-- 与えられた1P/2P入力で 1 フレーム進める。 | |
util.run_frame = function (input) | |
for i = 1, 2 do | |
joypad.set(input[i], i) | |
end | |
emu.frameadvance() | |
end | |
return util |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment