# PokemonCoRNGLibraryの雑な取説 https://github.com/yatsuna827/PokemonCoRNGLibrary ### なにができるの? ポケモンコロシアムの乱数関連の処理がだいたいできます。 今のところ戦闘中の瞬きと追加要素限定ポケモン3体の生成には対応していません。 ### 依存ライブラリについて `PokemonPRNG`…ポケモンの乱数調整で使う基本的な疑似乱数生成処理を詰め込んであるライブラリです. LCGのほかにMTとSFMTも実装してあります. `PokemonStandardLibrary`…ポケモンの種族情報, 性格・性別・性別比等の列挙子を提供するライブラリです. 前者はGithubで公開していますが後者は未完成なので非公開かもしれません. 同梱してあるやつを使ってください. ### 基本的な使い方 #### ダークポケモンの情報を取得する ```csharp= var poke = CoDarkPokemon.GetDarkPokemon("ヌオー"); ``` - 引数には`int`もしくは`string`を指定します. - `int`の場合はダークポケモンの内部ID, `string`の場合はポケモン名を指定します. 対象のダークポケモンが存在しない場合は例外が投げられます. #### seedを指定してダークポケモンの個体を生成する ```csharp= uint seed = 0xBEEFBEEF; var poke = CoDarkPokemon.GetDarkPokemon("ヌオー"); var result = poke.Generate(seed); Console.WriteLine($"{string.Join("-", result.IVs)} {result.PID:X8} {result.Nature.ToJapanese()} {result.Gender.ToSymbol()}"); // 29-3-2-10-23-17 064FAC2B ひかえめ ♀ ``` - resultにはPID, 個体値, 性格, 性別, 特性, GC特性などが入っています. - 本ライブラリではギンザルさんのツールで言うところの『強制消費2』『強制消費3』を生成過程に含めています. #### 個体値を指定して該当する個体を生成するseedを取得する ```csharp= var poke = CoDarkPokemon.GetDarkPokemon("ヌオー"); foreach(var(seed, individual) in poke.CalcBack(31,31,31,31,31,31)) Console.WriteLine($"{seed:X8} {string.Join("-", individual.IVs)} {individual.PID:X8} {individual.Nature.ToJapanese()} {individual.Gender.ToSymbol()}"); // A872A1CB 31-31-31-31-31-31 C351DEF2 おくびょう ♂ // 2872A1CB 31-31-31-31-31-31 43515EF2 うっかりや ♂ // 8FE9F3FE 31-31-31-31-31-31 6872803F きまぐれ ♀ // 0FE9F3FE 31-31-31-31-31-31 E872003F やんちゃ ♀ // 77614631 31-31-31-31-31-31 0D94218D がんばりや ♂ // F7614631 31-31-31-31-31-31 8D94A18D おっとり ♂ ``` - 『該当する個体を生成するseed』と『生成される個体』のタプルを返すイテレータを返します. - イテレータよくわからんって人は『foreachで全部取得できる』って思えば大丈夫です. - 個体値の後ろに`bool`を引数で渡すことができます. デフォルトでは`false`が指定されていますが, `true`を指定することで重複する個体を生成するseedが1つだけ返されるようになります. - グライガーやヘラクロスなど, ダークポケモンより前に通常ポケモンが生成される場合(いわゆる『生成順』が2以降のポケモン)は再計算により異なるseedから同じ個体が生成されることがあります. - 逆に再計算があるせいで実現不可能な個体もあります. 例えばヘラクロスは6V個体が存在しません. #### seedを指定してIDとエーフィ/ブラッキーを生成する ```csharp= uint seed = 0xbeefbeef; var generator = CoStarterGenerator.Instance; var result = generator.Generate(seed); Console.WriteLine($"{result.TID} {result.SID} {result.Espeon.Nature.ToJapanese()} {result.Umbreon.Nature.ToJapanese()}"); // 36115 18798 いじっぱり のんき ``` ### ちょっとかっこいい使い方 #### 現在seedを指定してリストを生成する ```csharp= uint currentSeed = 0xbeefbeef; var poke = CoDarkPokemon.GetDarkPokemon("ヌオー"); foreach(var (index, seed, result) in currentSeed.EnumerateSeed().EnumerateGeneration(poke).Take(10)) Console.WriteLine($"{index} {seed:X8} {string.Join("-", result.IVs)} {result.PID:X8} {result.Nature.ToJapanese()} {result.Gender.ToSymbol()}"); // 0 BEEFBEEF 29-3-2-10-23-17 064FAC2B ひかえめ ♀ // 1 AEDBDDF6 17-10-23-2-8-29 AC2BC4B0 ゆうかん ♂ // 2 F46A5CE1 29-2-8-18-1-15 C4B0E946 ひかえめ ♀ // 3 7A364C20 15-18-1-1-11-11 E9464BC0 ひかえめ ♂ // 4 E21C3A63 11-1-11-5-17-16 4BC02392 てれや ♂ // 5 887D3B9A 16-5-17-10-26-6 23920E90 うっかりや ♂ // 6 5D51D3F5 6-10-26-30-18-0 0E9065A8 のんき ♂ // 7 205D36E4 0-30-18-28-8-18 65A889DE わんぱく ♂ // 8 064F8A17 18-28-8-20-3-16 89DE0BB1 ゆうかん ♂ // 9 AC2B1C7E 16-20-3-13-25-8 0BB13A05 さみしがり ♀ ``` - `EnumerateSeed(this uint seed)`は`PokemonPRNG`ライブラリに定義されている拡張メソッドです. - `EnumerateGeneration(this IEnumerable<uint> e, IGeneratable<TResult> igenerator)`は『`uint`を返すイテレータから取得した`uint`の値を`igenerator.Generate()`に渡して得られる`TResult`の値を返すイテレータを得るメソッド』です. - 『`foreach`で列挙できる』くらいの認識で大丈夫ですが, Linqの`Take`メソッドと組み合わせないと無限に結果を返し続けます. - `IGeneratable<TResult>`も`PokemonPRNG`ライブラリに定義されています. `TResult Generate(uint seed)`メソッドのみが定義されている, 『LCGのseedを与えると何らかの生成処理が行われるモノ』であることを表現するインタフェースです. - `Where`や`Skip`と組み合わせることもできます. #### 不定消費を考慮したリストを生成する ```csharp= uint currentSeed = 0xbeefbeef; var poke = CoDarkPokemon.GetDarkPokemon("ライコウ"); foreach(var (index, seed, result) in currentSeed.EnumerateSeedAtCipherLab().EnumerateGeneration(poke).Take(10)) Console.WriteLine($"{index}F {seed:X8} {string.Join("-", result.IVs)} {result.PID:X8} {result.Nature.ToJapanese()}"); // 0F BEEFBEEF 29-3-2-10-23-17 064FAC2B ひかえめ // 1F 738CFF03 12-16-9-15-26-3 5BC3ACB3 まじめ // 2F 18FA30DF 17-14-2-12-7-20 0A378E9E がんばりや // 3F 0A37A207 26-27-1-18-6-16 EF21432A すなお // 4F A75B3735 30-1-16-26-29-17 3B308F03 わんぱく // 5F 8E044656 14-16-4-13-21-19 6E02A975 ようき // 6F 19D30BEB 20-0-19-20-11-21 EFA53B57 ずぶとい // 7F F923E96F 10-20-14-11-24-10 3BD41EA5 おとなしい // 8F 61EE80BF 23-22-7-31-26-21 E7968D88 いじっぱり // 9F 8D88C10E 16-27-21-31-11-25 4482A121 れいせい ``` - 不定消費に従ってseedを返すイテレータを取得する拡張メソッドは`EnumerateSeedAtCipherLab`, `EnumerateSeedAtOutskirtStand`, `EnumerateSeedAtNamingScreen`の3つが用意されています. それぞえダークポケモン研究所B2F(ボルグのいるフロア), 町外れのスタンド屋外, 名前入力画面の不定消費に対応しています. - 戻り値の`index`は経過フレームを表します. 消費数は戻り値に含まれていないので, `PokemonPRNG`の拡張メソッド`GetIndex()`を使うなどする必要があります. #### 個体に条件を指定してフィルタリングする ```csharp= uint currentSeed = 0xbeefbeef; var poke = CoDarkPokemon.GetDarkPokemon("ライコウ"); var builder = new IndividualCriteriaBuilder(); builder.AddNatureCriteria(Nature.Timid, Nature.Modest); builder.AddHiddenPowerTypeCriteria(PokeType.Ice, PokeType.Grass, PokeType.Fire); builder.AddHiddenPowerCriteria(70); var criteria = builder.Build(); foreach (var (index, seed, result) in currentSeed.EnumerateSeedAtCipherLab().EnumerateGeneration(poke).Take(30000).Where(_ => criteria.Check(_.result))) Console.WriteLine($"{index}F {seed:X8} {string.Join("-", result.IVs)} {result.PID:X8} {result.Nature.ToJapanese()} {result.HiddenPowerType.ToKanji()}{result.HiddenPower}"); // 20678F ADE9741B 11-7-19-11-19-6 9E07E925 ひかえめ 氷70 // 26794F 13FF44AD 31-7-7-19-19-26 0F985012 おくびょう 氷70 ``` - 個体の条件を指定できる`IndividualCriteria`クラスと, その生成を行う`IndividualCriteriaBuilder`クラスが提供されています. - 一般になんらかのクラスに対して条件を指定できる`Criteria`抽象クラスも提供しています. `IndividualCriteria`はその実装です. - `Where`と組み合わせる際に条件を長ったらしく記述する必要が無くなります. - 上の例で`Where`と`Take`の順番を入れ替えると大変なことになるので注意しましょう. - 上の例は『30000F未満の待機時間で出現する個体の内条件を満たすものを返す』ですが, 順番を入れ替えると『条件を満たす個体を30000個返す』になります. - `AddNatureCriteria`および`AddHiddenPowerType`の引数は`param`指定されているので並べて記述していますが, 普通に配列として与えることもできます. - `AddShinyTypeCriteria`は引数を1つしか渡せませんが, `ShinyType.Square | ShinyType.Star`とすることで『菱形または星型』すなわち単に色違いかどうかを条件に指定することができます. - `ShinyType.NotShiny`は無視されます. `|`で`Star`や`Square`と結合して渡した場合でも無視されます. ### その他 #### 瞬きの間隔からseedを検索する ```csharp= uint currentSeed = 0xbeefbeef; var blanks = GetBlanks(); // 瞬きの間隔を取得する架空のメソッド. foreach (var seed in SeedFinder.FindCurrentSeedByBlink(currentSeed, 0, 1000000, blanks, 20, 4)) Console.WriteLine($"{seed:X8}"); ``` - 頑張って高速化させました. - これも戻り値は`IEnumerable<uint>`です. - 範囲を1億くらい取っても数秒 ~ 十数秒で処理が終わるはずですが, メモリの消費量を気にしていないのでオススメはしません. 最悪落ちます. - 引数の`allowanceLimitOfError`(第5引数)は検索時に許容する瞬き間隔の誤差を指定します. 85とか指定するとめちゃくちゃになるので適切な値を入れましょう. ### あっぴるぽいんと 最後に持ってくる項目か? - 乱数ツールの生成部分を『`EnumerateSeed`して`EnumerateGeneration`して`Skip`や`Take`で適当な範囲を切り取って`Where`に`Criteria`渡してフィルタリングして列挙する』という記述に集約できるようにしました. ツール側は検索処理をいつでも同じように・非常に短く記述するだけでリスト出力に繋げることができます. - `for`で回したり`if`で条件を指定したりする必要がありません. - 逆算処理や検索処理を頑張って高速化しました. - アルゴリズムは別途記事にする予定です. - XDの個体逆算はダークポケモンの固定の有無やプレイヤーのIDが関わってくるのでもうちょっとややこしくなります. 気が重い.