ポ雑記

RNG_Game攻略ツールを作ろう

この記事はPokémon RNG Advent Calendar 2020の7日目の記事です。
adventar.org


Pokémon RNG Advent Calendar 2017の2日目の記事でろいしん氏が公開したRNG_Gameですが、さき氏とoupo氏を除いてこのゲームで遊んだという話を耳にしません。
blastoise-x.hatenablog.com


私はこのゲームがとても好きなのですが、プレイ人口が少ないのは寂しいものです。
そこで、少しでも多くの人が楽しめるようRNG_Gameを攻略する為の情報を公開しようと思います。


注意事項
C#を使用していますが、指摘されそうな点が多々あります。目を瞑ってください。
※あえてツール未完成の状態で記事を公開しています。本記事が参考になるかはわかりませんが、ご自身で完成させてください。
(2日目の記事も合わせて、各自で良いツールを作りましょう。)
scrapbox.io


さき氏及びoupo氏の記事を参考にツールを作成し、クリアを目指したいと思います。予めそちらをお読みください。
xxsakixx.com
oupo.hatenadiary.com




早速ツールを作っていきましょう。


まず、seedを特定する必要があります。

seedを特定する為の手段として、以下の3つが候補に挙がります。
・捕まえたポケモンの情報
ペラップの音程
・ゲームの起動時刻


ゲームの起動時刻から特定するのが手っ取り早いですが、面白くないので今回は捕まえたポケモンの情報からseedを特定することにします。

一例として、さき氏の記事にある

オニドリル Lv.27
性格:がんばりや
個体値:17-4-5-30-25-0
この情報から、H個体値の生成seedである0x8EF97E97を計算してみたいと思います。

//個体値のみで適当に総当たり
static void SeedSearch()
{
    uint[] IVs = { 17, 4, 5, 30, 25, 0 };
    uint Seed, StartSeed = 0;
    long i;
    int j;
    for (i = 0; i <= 0xFFFFFFFF; i++)
    {
        Seed = StartSeed;
        //個体値の判定を行う
        for (j = 0; j < 6; j++)
        {
            if (((Seed >> 16) * 32 / 0x10000) == IVs[j])
            {
                if (j == 5)
                {
                    //個体値が全て一致したら出力
                    Console.WriteLine(StartSeed.ToString("X8"));
                }
                else
                {
                    Seed = SeedAdvance(Seed);
                }
            }
            else
            {
                break;
            }
        }
        StartSeed++;
     }
}
static uint SeedAdvance(uint Seed)
{
    return Seed * 0x41c64e6d + 0x6073;
}

これを実行すると、このような結果が出ます。

8848D488
8E09E649
8EF97E97
8F881D10
ちゃんと0x8EF97E97も含まれています。

ここから候補を1つに絞り込む為に、ポケモンの種族(出現スロット)や性格を考慮したり、連続してもう1匹捕まえて近い位置にいるかを判定したりといったことが考えられます。



さて、捕まえたポケモンの情報からH個体値の生成seedはわかりましたが、このままでは現在のseedがわからないので特定する必要があります。
特定のために、個体値決定後の消費について調べるか、ペラップをしばいて音程を調査するという手段が考えられます。

今回は調査が面倒だったのでさき氏と同じようにペラップの音程から現在のseedを特定してみます。

static void FrequencyList()
{
    uint Seed = 0x8EF97E97;
    uint Frequency;
    for (int i = 0; i < 100; i++)
    {
        Frequency = (((Seed >> 16) * 8192) >> 16) * 110 / 8192 + 440;
        //消費数、seed、音程の順で出力
        Console.WriteLine("{0} {1} {2} {3}", i, Seed.ToString("X8"), Frequency, Interval(Frequency));
        Seed = SeedAdvance(Seed);
    }
}
//わかりやすいように音程を3段階に分けてみる
static string Interval(uint Frequency)
{
    if (Frequency < 476)
        return "低";
    else if (Frequency < 512)
        return "中";
    return "高";
}

これを実行すると以下のような結果になります。

0 8EF97E97 501 中
1 249748BE 455 低



70 508A190D 474 低
71 0B7D00FC 444 低
72 B96E93BF 519 高
73 69D37AC6 485 中
74 B997FAC1 519 高
75 4662F2A0 470 低
76 65CE6E93 483 中
77 17483F0A 450 低
78 38B443B5 464 低
79 64605A84 483 中


ペラップの音程とリストを照らし合わせることで現在のseedが特定できます。



現在のseedを特定したら、2通り存在するクリアの条件を目指します。
何消費すれば目的のものが得られるかを予め計算すれば良いので、適当に出力します。

static void List()
{
    uint StartSeed = 0xAB8A3A9A, Seed;
    int i, j;
    string VendingMachine, Item;
    for (i = 0; i < 100; i++)
    {
        Seed = StartSeed;
        //自販機判定
        VendingMachine = (Seed >> 16) * 32 / 0x10000 == 0 ? "あたり" : "はずれ";
        //種族、個体値などの分を消費
        for (j = 0; j < 9; j++)
        {
            Seed = SeedAdvance(Seed);
        }
        //持ち物判定
        Item = (Seed >> 16) * 100 / 0x10000 < 5 ? "きんのたま" : "なし";
        //消費数、seed、自販機当たり判定、持ち物判定の順で出力
        Console.WriteLine("{0} {1} {2} {3}", i, StartSeed.ToString("X8"), VendingMachine, Item);
        StartSeed = SeedAdvance(StartSeed);
    }
}

0 AB8A3A9A はずれ きんのたま
1 94D24005 はずれ なし
2 35E52894 はずれ なし
3 BB67BF77 はずれ なし
4 B48D281E はずれ なし
5 87879939 はずれ なし
6 0081FBB8 あたり なし


あとはこれに従ってペラップをしばき、目的の行動をすればクリアすることができるはずです。



はずです・・・・



・・・・・・・・・・・


f:id:mahodoriproject:20201206194017p:plain
きんのたま」ではなく「するどいくちばし」を持っていました。どうやらオニドリルは「きんのたま」を持っていないようです。



是非より良いツールを作成し、クリアを目指してください。



では。





余談
何年か前に高個体値ベトベターを捕獲しましたが、クリアすることができませんでした。
f:id:mahodoriproject:20201109123356j:plain
(当時のメモによると初期seed 0x5B13463Dから狙ったらしい。)