PR
[PR]アフィリエイト広告を利用しています
[PR]アフィリエイト広告を利用しています

【中級★★☆】RAM Searchで候補が絞れない時の原因と再検索の組み立て方|Unknown Initial Value・変化量・Domainの見直し6項目

RAM Searchで候補が絞れないときの原因切り分けフロー概念図 エミュレーター制御

【難易度】中級★★☆【対象環境】Windows 10(64bit)・Windows 11/BizHawk 2.9系(EmuHawk)・RAM Search/検索・検証するのは自分で購入・所有しているソフトのみ・オフライン環境

【PR】本記事はアフィリエイトリンク(Amazonアソシエイト)を含みます。Amazonのアソシエイトとして、当ブログは適格販売により収入を得ています。

【技術検証に関する法的注意事項・免責事項】
本記事に掲載されているメモリ解析、Luaスクリプト実行、およびAIコード生成に関する技術情報は、自らが所有するゲームソフトおよびオフライン環境における技術研究・教育目的の検証記録です。著作権法第47条の3に配慮し、複製ROMの配布、コピーガードの回避(DRM解除)、オンライン環境での不正操作(BOT化)等に関する情報は一切取り扱いません。掲載情報の実行によって生じた損害について、当ブログは一切の責任を負いかねます。

結論から書きます。RAM Searchで候補が減らない・むしろ増える場合、原因の大半は「探し方」ではなく「検索条件の設定」にあります。具体的には、値の型(サイズ)の思い込み、Unknown Initial Valueの使いっぱなし、変化量指定のズレ、フレームの進みすぎ、RAM Domainの選び間違い、そしてポインタ経由の再配置。この6つを疑う順番さえ覚えれば、闇雲な再検索から抜け出せます。

この記事は、RAM Searchでアドレスを特定する基本手順(記事末の「次に読む」参照)を一通り試したうえで「候補が1つに絞れない」「検索するたびに候補が増える」という詰まりに直面した人向けのトラブル解決編です。基本操作の説明はここでは繰り返さず、絞れない原因の切り分けと、再検索の組み立て直し方だけに絞って書きます。

「候補が絞れない」症状別の当たりの付け方フロー図
候補が絞れないときに疑う順番。①型とサイズ ②Unknown Initial Valueの使い方 ③変化量指定 ④フレームの古さ ⑤RAM Domain ⑥ポインタ再配置。

1. 「絞れない」の2パターンを先に区別する

対処を始める前に、自分がどちらの状態にいるかを確認してください。原因の探し方が変わります。

症状 意味 まず疑うべき場所
候補が数百〜数千のまま減らない 検索条件が緩すぎて絞り込みが効いていない 型・サイズ/Unknown Initial Valueの使い方
一度減ったのに次の検索で候補が増える・0件になる 基準がズレた状態で条件を重ねている フレームの古さ/変化量指定/RAM Domain

前者は「検索条件が甘い」、後者は「検索条件が食い違っている」という別の失敗です。両方を同時に疑うと余計に混乱するので、まずどちらかに絞ってください。

2. 原因別チェック(6項目・上から順に)

ここからは症状 → 原因候補 → 確認手順 → 直し方の順で6項目たどります。飛ばさず上から確認してください。

チェック1:値の型(サイズ)を思い込みで決めている

症状:候補が数百件から減らない。あるいは、増えているはずの値なのに候補一覧の数値が明らかにおかしい。

原因候補:RAM SearchのDisplay Type(1 Byte / 2 Byte / 4 Byte、Signed / Unsigned)を初期値のまま検索していると、実際の値のサイズと一致せず、無関係なアドレスまで候補に残り続けます。HPや所持金のような小さい数値は1〜2バイトのことが多い一方、座標やスコアは4バイトのことがあります。

確認手順:候補一覧の数値を見て、ゲーム内の実際の値と桁が合っているかを確認します。実際は「30」のはずなのに候補の値が「7680」のような不自然な数字になっている場合、サイズの取り違えです(バイト境界がズレて隣接データを巻き込んでいます)。

直し方:Display Typeを1バイト→2バイト→4バイトの順に切り替えながら同じ操作(ダメージを受ける/回復するなど)を再現し、候補の増減が実際の値の増減と一致するサイズを探します。合致するサイズが見つかった時点で、それ以外のサイズでの検索結果は捨てて構いません。

チェック2:Unknown Initial Valueのまま検索し続けている

症状:最初の検索で候補が数千〜数万件のまま、何度絞り込んでも減り方が鈍い。

原因候補:RAM Searchの初回検索は「Unknown Initial Value(値不明)」で始めるのが基本ですが、これは母数が最大になる検索方法です。ここで一度に絞り込もうとせず、次の検索を焦って重ねると、条件同士が矛盾して有効な候補まで消してしまうことがあります。

確認手順:直近2〜3回の検索で使った条件(Equal To/Not Equal To/Greater Than など)を思い出してください。「値が変わった」ことだけを条件にした検索を2回以上連続で使っていないかを確認します。同じ性質の条件を繰り返しても情報量は増えません。

直し方異なる性質の条件を交互に使うのが基本です。①Unknown Initial Valueで初回検索 →②値が「変化した(Not Equal To Previous)」で1回絞る →③次はあえて「変化していない(Equal To Previous)」で、変化させたくない別の場面(メニューを開いた瞬間など)を使って絞る、という具合に、「変わった」と「変わらなかった」を交互に踏むと収束が速くなります。

チェック3:変化量の指定が実際の増減と合っていない

症状:「10ダメージ受けたはず」で Decreased by 10 のような条件を使ったのに、候補が0件になる。

原因候補:ゲーム内表示のダメージと、メモリ上の実際の増減量が一致しないケースがあります。防御力の計算が挟まっていたり、表示上は10でも内部値は10.0ではなく別スケール(例:×100して整数で保持)で扱われていたりします。

確認手順Decreased by のような固定値の条件で0件になったら、いったん条件を緩めます。Decreased by をやめて Less Than Previous(前回より小さくなった、程度の条件)に切り替えて再検索し、候補が復活するか確認してください。復活するなら、固定値の見積もりが間違っていたことが確定します。

直し方:最初から固定値の変化量で絞ろうとせず、「増えた/減った」という方向だけの条件で複数回絞り込み、最後の1〜2件だけを固定値条件で確定させる順番にします。方向条件は誤差の影響を受けにくいため、無関係な候補を安全に減らせます。

チェック4:フレームが進みすぎて基準値が古くなっている

症状:数十秒〜数分操作してから検索をかけると、明らかに無関係な候補ばかり残る。

原因候補:RAM Searchの「前回の検索結果」は検索を実行した瞬間のメモリ状態が基準です。ゲームを長く進めるほど、探している値と無関係な一時変数(タイマー、アニメーションのフレームカウンタなど)まで「変化した値」として拾われやすくなります。

確認手順:1回の検索から次の検索までに、どれくらいゲームを進めたかを振り返ります。目的の操作(ダメージを受ける、アイテムを拾うなど)以外に、画面が何度も切り替わっていたら基準が古くなっている可能性が高いです。

直し方1回の検索と次の検索の間は、目的の操作だけを行い、他の操作を挟まないのが鉄則です。BizHawkのFrame Advance(コマ送り)を使い、操作の直前・直後のフレームで検索を挟むと、無関係な値の混入を最小限にできます。

チェック5:RAM Domainが対象と食い違っている

症状:条件は合っているはずなのに、候補が最初から極端に少ない、または探している値が一覧に一度も出てこない。

原因候補:BizHawkは対象ハードによって複数のメモリ領域(RAM Domain)を切り替えられます。既定のDomainが目的のワークRAMではなく、別領域(例:System Bus、あるいは拡張RAM)を指している場合、目的の値がそもそも検索範囲に入っていません。

確認手順:RAM SearchウィンドウのDomain選択欄を確認します。ハードごとに名称が異なるため、まずは候補数が最も多く出るDomainを選び直して検索をやり直してみてください。

直し方:Domainを切り替えて同じ初回検索(Unknown Initial Value)をやり直し、候補数が明らかに増えるDomainがあれば、そちらが正しい探索範囲です。Domainの選択ミスは、条件をどれだけ工夫しても解決しません。他のチェックより先に疑って構わない項目です。

チェック6:値がポインタ経由で再配置されている

症状:一度アドレスを1つに絞れたのに、セーブ&ロードやエリア移動のあとに同じアドレスを見ても値が反映されない。

原因候補:一部のゲームは、対象の値を毎回同じアドレスに置かず、実行のたびに確保し直したメモリ領域(動的確保)に置きます。この場合、見えているアドレスは「その回だけ」有効な一時的な場所で、次に起動し直すと別の場所に移動します。

確認手順:一度絞り込んだアドレスを覚えておき、タイトル画面からもう一度同じ状況を再現したときに、同じアドレスで値が正しく変化するかを確認します。再現するたびにアドレスが変わる、または値が更新されないなら、動的確保が濃厚です。

直し方:これは検索条件の見直しでは解決しません。値そのものではなく「値を指しているアドレス(ポインタ)」を別途たどる必要があり、扱う手法が変わります。この記事のスコープでは扱いません(ポインタ解析は難易度・法務面でも別枠の話題のため、対応が固まり次第あらためて記事化します)。ここでは「これに当たった場合、検索条件の工夫では解決しない」と分かるだけでも、無駄な再検索を止められます。

3. 再検索の組み立て方(決定木)

6項目を毎回全部確認するのは非効率なので、次の順番で当たりを付けてください。

  1. 候補数が最初から極端に少ない/0件 → チェック5(RAM Domain)を最優先で疑う
  2. 候補が数百〜数千のまま減らない → チェック1(型・サイズ)→チェック2(検索条件の交互化)の順
  3. 減っていたのに急に0件になった → チェック3(変化量指定)→チェック4(フレームの古さ)の順
  4. 1件に絞れたのに再現性がない → チェック6(ポインタ再配置)を疑う

この順番で当たりを付けると、無関係な項目を潰す時間を減らせます。

4. AIに直させる:絞り込み条件を整理させる

ここからは当ブログの本題であるAIとの試行錯誤の記録です。目的は、「今までに何を試したか」を自分で整理するのが面倒なときに、検索ログをAIに渡して次の一手を提案させることです。

目的

RAM Searchで実行した検索条件と、そのつど残った候補数を手元のメモに残しておき、それをClaudeに渡して「次に試すべき条件」を提案させる。あわせて、検索ログを整理して記録するための簡易スクリプトの叩き台を書かせる。

Claudeへ渡した実際のプロンプト

BizHawkのRAM Searchで候補を絞り込んでいます。
これまでの検索ログは次の通りです(値の型=2Byte Unsigned/Domainは選択済み)。

1回目: Unknown Initial Value → 候補 4820件
2回目: Not Equal To Previous(ダメージを受けた直後) → 候補 96件
3回目: Decreased by 10(10ダメージのつもり) → 候補 0件

次に試すべき検索条件を、なぜそれを勧めるかの理由つきで3つ提案してください。
また、この検索ログをテキストファイルに追記していく
簡易ロガー用のPythonスクリプトの叩き台も書いてください。

1回目の提案:条件だけで、ロガーが未完成

# 提案の一部(検索条件のみ)
- 3回目が0件なのは Decreased by 10 の見積もりミスの可能性が高い。
  Less Than Previous に緩めて再実行する。
- 2回目の96件に対し、別の非戦闘操作(メニューを開くなど)で
  Equal To Previous を1回挟み、変化しない値を除外する。

ロガー側のコードが「関数を作っただけ」で、実行しても何もファイルに書き込まれませんでした。呼び出し部分が抜けていたのが原因です。よくある落とし方で、AIが「使い方の説明」を省略して定義だけ返してくることがあります。

修正のためにAIへ渡した追加情報

関数は定義されていますが、呼び出している箇所がないため
実行しても何も起きません。
コマンドラインから「検索回数」「候補数」「使った条件」を
その場で入力してログに1行追記できる、実行可能な形にしてください。

最終コード(コピペ可・検索ログの簡易記録用)

# RAM Searchの検索ログを1行ずつ追記していく簡易ロガー。
# 実行するたびに対話形式で条件・候補数を尋ね、log.csv に追記する。
import csv, datetime, os

LOG_FILE = "ramsearch_log.csv"

def append_log(condition: str, count: int, note: str = ""):
    is_new = not os.path.exists(LOG_FILE)
    with open(LOG_FILE, "a", newline="", encoding="utf-8-sig") as f:
        w = csv.writer(f)
        if is_new:
            w.writerow(["timestamp", "condition", "candidate_count", "note"])
        w.writerow([datetime.datetime.now().isoformat(timespec="seconds"), condition, count, note])

def main():
    print("RAM Search 検索ログ記録(終了は空Enter)")
    while True:
        condition = input("使った条件(例: Not Equal To Previous): ").strip()
        if not condition:
            break
        try:
            count = int(input("残った候補数: ").strip())
        except ValueError:
            print("数値で入力してください。もう一度。")
            continue
        note = input("メモ(任意): ").strip()
        append_log(condition, count, note)
        print(f"  記録しました({LOG_FILE})")

if __name__ == "__main__":
    main()

これを使うと、検索を1回試すたびに条件と候補数を記録でき、あとからClaudeに「このログを見て次を提案して」と渡すだけで済みます。手作業のメモより、条件と候補数の対応がずれにくいのが利点です。

※実環境でのログ蓄積結果(実際に絞り込めたアドレスの実例)は、検証のうえ後日追記します。当ブログでは、未検証の値をそれらしく書くことをしない方針です。値を伏せる箇所は 0x00XXXX のようなプレースホルダで統一しています。

この試行錯誤から得られた再利用可能な教訓

  • AIに検索ログを渡すと、次の一手を機械的に提案してくれる。ただし「なぜその条件を勧めるか」の理由まで読んで、自分の状況と合っているか確認する
  • AIはコードの「定義」と「実行」を分けて考えず、定義だけ返すことがある。「実行しても何も起きない」と感じたら、呼び出し部分の有無を疑う
  • ログを構造化しておくと、あとからAIに渡すときの説明コストが下がる。口頭で説明し直すより、CSVを渡すほうが正確に伝わる


Luaを腰を据えて学ぶなら
Programming in Lua(プログラミング言語Lua公式解説書)

Lua作者陣による公式解説書。BizHawkのLua APIは断片的な情報が多く、言語そのものの仕様(テーブル・メタテーブル・コルーチン)を一度通しで押さえておくと、スクリプトが動かないときの原因切り分けが速くなる。

  • 言語作者による公式解説
  • テーブルとメタテーブルの理解が進む
  • 断片的なコピペから抜け出せる

Amazonで詳細を見る

※価格・在庫・仕様は変動します。最新の情報はAmazonの商品ページでご確認ください。

よくある質問

Q1. Unknown Initial Valueと普通の検索、どちらから始めるべきですか

基本はUnknown Initial Valueから始めてください。目的の値の初期値が分からない状態から始めるのがRAM Searchの前提で、ここを飛ばして憶測の数値で検索を始めると、母数を絞りすぎて正解を除外してしまうことがあります。

Q2. 候補が1件に絞れたのに、値を書き換えても画面に反映されません

チェック1(型・サイズの不一致)とチェック6(ポインタ再配置)の両方を疑ってください。表示上は変化しているように見えても、実際に参照されているアドレスが別にあるケースがあります。

Q3. これは「チート」や「改造」の話ですか

いいえ、違います。この記事が扱っているのはメモリ解析における検索条件の組み立て方のみで、当ブログが一貫して扱うメモリ解析と状態検証Luaによる挙動デバッグの一部です。自らが所有するソフトをオフライン環境で検証する前提であり、オンライン環境での不正操作、コピーガード回避(DRM解除)、ROM・BIOS・改変データ・セーブデータの配布や入手先の案内は一切取り扱いません。

Q4. どのDomainを選べばいいか分かりません

チェック5の手順のとおり、候補数が最も多く出るDomainからあたるのが確実です。ハードによって名称が異なるため、本記事では特定のハード名は挙げず、選び方の考え方のみを扱っています。


Ghidraを触るなら
リバースエンジニアリングツール Ghidra実践ガイド

本ブログで扱うGhidraそのものを主題にした日本語書籍。逆アセンブル結果の読み方や関数の当たりの付け方が体系立っているので、記事の手順をなぞるだけの段階から一歩進める。

  • Ghidraを主題にした日本語書籍
  • 逆アセンブル結果の読み方が体系的
  • 記事の手順と地続きで学べる

Amazonで詳細を見る

※価格・在庫・仕様は変動します。最新の情報はAmazonの商品ページでご確認ください。

まとめ

  • 候補が減らない系の症状は、まずチェック1(型・サイズ)とチェック2(Unknown Initial Valueの使い方)を疑う
  • 急に0件になる系の症状は、チェック3(変化量指定)とチェック4(フレームの古さ)を疑う
  • 候補が最初から極端に少ないときはチェック5(RAM Domain)を最優先で疑う。条件の工夫では直らない
  • 絞れたのに再現しないときはチェック6(ポインタ再配置)。この記事のスコープでは扱わず、別記事で扱う予定
  • 再検索は「変わった」条件と「変わらなかった」条件を交互に使うと収束が速い
  • 検索ログをCSVで記録しておくと、AIに次の一手を提案させるときの説明コストが下がる

教材について:この記事のようなRAM Search絞り込みの型を含む「コピペLua集+環境構築キット」の事前登録枠を準備中です。価格・配布日は未定のため、詳細が決まり次第あらためてお知らせします。

次に読む

基本のRAM Search手順をまだ試していない方は、まずこちらから。

【中級】BizHawk RAM SearchでゲームのHP・座標アドレスを特定する手順|AIにアドレス候補を絞らせる試行錯誤ログ付き
BizHawkのRAM Searchで、ゲーム内のHPやキャラの座標が入っているメモリアドレスを自分で特定する手順を解説。AIに絞り込み条件を設計させ、候補がヒットしない失敗の立て直しまで記録した中級向けのメモリ解析・状態検証ガイドです。

アドレスが特定できたら、値を画面に常時表示する方法はこちら。

【中級】BizHawk LuaでゲームのHPバーを画面オーバーレイ表示する|取得したメモリアドレスをgui.textで常時表示(試行錯誤ログ)
RAM Searchで特定したHPアドレスをBizHawkのLuaで読み取り、gui.textとgui.drawRectangleで画面左上にHPバーとして常時オーバーレイ表示する最小手順。emu.frameadvance()のループ設計と、座標ズレ・チラつきの直し方を、Claudeとの試行錯誤ログ付きで解説します。改造ではなくメモリ解析・状態検証の技術記録です。

メモリ解析の5ステップ全体像から見直したい方は、こちらの旗クラスタ親ハブへ。

【中級★★☆】ゲームのメモリ解析は5ステップで内部の数値が読める|BizHawk×Lua×AI 手順の全体像
ゲームのメモリ解析は5工程しかありません。①読み出せる状態をつくる ②目的の数値がどこにあるかを絞り込む ③読んだ数値を画面に出し続ける ④AIにコードを書かせて往復を削る ⑤自動化して実験する。この記事は手順そのものではなく「いまどの工程にいるか」「次にどの記事を開くか」を示す地図です。開始地点を事実だけで判定する4問チャート付き。自己所有ソフト・オフライン環境が前提です。

コメント