ポケポケのシミュレータを作った

# ポケポケとは

https://www.pokemontcgpocket.com/ja/

ポケモンカードというTCGを原作として、ルールなどがスマホ向けに大幅に変えられたスマホゲーム。

(正式名称: **Pokémon Trading Card Game Pocket**)

~~暇なときにやるけど、ランクマッチが全然勝てない。(カイリューではやはり辛いか？)~~

# シミュレータ
言語にはjavascriptを使用している。
~~3時間で適当に作ったからコードはかなり汚い。~~
```javascript
// 配列のシャッフル
const shuffle = (arr) => {
    for (let i = arr.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [arr[i], arr[j]] = [arr[j], arr[i]];
    }
    return arr;
};

// arrから特定のkeyの要素のインデックスを検索
const kiof = (arr, key, k = true) => {
    return arr.findIndex((c) => c[key] == k);
};

// from->toへfrom[i]を移動 (a回)
const move = (from, to, i, a = 1) => {
    if (from.length < 1) return;
    while (a--) {
        to.push(from[i]);
        from.splice(i, 1);
    }
};

// オーキド
const okido = {
    name: "okido",
    use() {
        move(hand, trush, kiof(hand, "name", this.name));
        move(stack, hand, 0, 2);
    },
};

// モンスターボール
const monbo = {
    name: "monbo",
    use() {
        move(hand, trush, kiof(hand, "name", this.name));
        shuffle(stack);
        if (kiof(stack, "isSeed") == -1) return;
        move(stack, hand, kiof(stack, "isSeed"));
    },
};

// ナンジャモ
const nanjamo = {
    name: "nanjamo",
    use() {
        move(hand, trush, kiof(hand, "name", this.name));
        const t = hand.length;
        move(hand, stack, 0, t);
        shuffle(stack);
        move(stack, hand, 0, t);
    },
};

// ポケモン通信
const poketsu = {
    name: "poketsu",
    use(i) {
        shuffle(stack);
        if (kiof(stack, "isPoke") == -1) {
            move(hand, trush, kiof(hand, "name", this.name));
            return;
        }
        [hand[i], stack[kiof(stack, "isPoke")]] = [
            stack[kiof(stack, "isPoke")],
            hand[i],
        ];
        move(hand, trush, kiof(hand, "name", this.name));
    },
};

// 不思議なアメ
const ame = {
    name: "ame",
    use() {
        move(hand, trush, kiof(hand, "name", this.name));
    },
};

// ミニリュー
const miniryu = {
    name: "miniryu",
    isPoke: true,
    isSeed: true,
    evo1: "hakuryu",
    evo2: "kairyu",
};

// ハクリュー
const hakuryu = {
    name: "hakuryu",
    isPoke: true,
    evo1: "kairyu",
};

// カイリュー
const kairyu = {
    name: "kairyu",
    isPoke: true,
};

// その他(トレーナーズ)
const trainer = {
    name: "trainer",
};

// その他(ポケモン)
const poke = {
    name: "poke",
    isPoke: true,
};

// その他(タネポケモン)
const seed = {
    name: "seed",
    isPoke: true,
    isSeed: true,
};

// 1ターン
const myturn = () => {
    //進化できない状態を解除
    for (let i = 0; i < bench.length; i++) {
        if (bench[i].newBorn) {
            delete bench[i].newBorn;
        }
    }

    // サポートカード(オーキド/ナンジャモ)は1ターン1回まで
    let support = true;

    //　ターン初めに1枚ドロー
    move(stack, hand, 0);

    // モンボ使用
    while (kiof(hand, "name", "monbo") != -1) {
        monbo.use();
    }

    // オーキド使用
    if (kiof(hand, "name", "okido") != -1) {
        okido.use();
        support = false;
    }

    // モンボ使用
    while (kiof(hand, "name", "monbo") != -1) {
        monbo.use();
    }

    // ベンチにタネを配置
    while (kiof(hand, "isSeed") != -1 && bench.length <= 4) {
        hand[kiof(hand, "isSeed")].newBorn = true;
        move(hand, bench, kiof(hand, "isSeed"));
    }

    // ポケモンの進化
    for (let i = 0; i < bench.length; i++) {
        const e1 = kiof(hand, "name", bench[i].evo1);
        const e2 = kiof(hand, "name", bench[i].evo2);
        if (!bench[i].newBorn && e2 != -1 && kiof(hand, "name", "ame") != -1) {
            bench[i] = hand[e2];
            hand.splice(e2, 1);
            ame.use();
        } else if (!bench[i].newBorn && e1 != -1) {
            hand[e1].newBorn = true;
            bench[i] = hand[e1];
            hand.splice(e1, 1);
        }
    }

    // ナンジャモ使用
    if (support && kiof(hand, "name", "nanjamo") != -1 && hand.length > 1) {
        nanjamo.use();
    }

    // モンボ使用
    while (kiof(hand, "name", "monbo") != -1) {
        monbo.use();
    }

    // ベンチにタネを配置
    while (kiof(hand, "isSeed") != -1 && bench.length <= 4) {
        hand[kiof(hand, "isSeed")].newBorn = true;
        move(hand, bench, kiof(hand, "isSeed"));
    }

    // ポケ通使用
    while (kiof(hand, "name", "poketsu") != -1) {
        let dup = -1;
        let seen = {};
        hand.forEach((c, i) => {
            if (seen[c.name]) dup = i;
            if (c.isPoke) seen[c.name] = true;
        });
        if (dup !== -1) {
            poketsu.use(dup);
        } else {
            break;
        }
    }

    // ポケモンの進化
    for (let i = 0; i < bench.length; i++) {
        const e1 = kiof(hand, "name", bench[i].evo1);
        const e2 = kiof(hand, "name", bench[i].evo2);
        if (!bench[i].newBorn && e2 != -1 && kiof(hand, "name", "ame") != -1) {
            bench[i] = hand[e2];
            hand.splice(e2, 1);
            ame.use();
        } else if (!bench[i].newBorn && e1 != -1) {
            hand[e1].newBorn = true;
            bench[i] = hand[e1];
            hand.splice(e1, 1);
        }
    }
};

// 1ゲーム
const game = () => {
    stack = [...deck];
    hand = [];
    trush = [];
    bench = [];

    shuffle(stack);

    // 手札にタネポケモンが来るまで抽選
    while (kiof(hand, "isSeed") == -1) {
        move(hand, stack, 0, hand.length);
        move(stack, hand, 0, 5);
    }

    // ターン数
    for (let i = 0; i < T; i++) {
        myturn();
    }
};

//
// 基本的にここから下をいじる
//

// 計測回数
const N = 100000;
let hit = 0;

// ターン数
const T = 4;
// 使用デッキ
const deck = [
    miniryu,
    miniryu,
    hakuryu,
    hakuryu,
    kairyu,
    kairyu,
    okido,
    okido,
    monbo,
    monbo,
    ame,
    ame,
    poketsu,
    nanjamo,
    seed,
    seed,
    trainer,
    trainer,
    trainer,
    trainer,
];

let stack = [];
let hand = [];
let trush = [];
let bench = [];

for (let i = 0; i < N; i++) {
    game();

    // 計測する確率の条件
    if (kiof(bench, "name", "kairyu") !== -1) {
        ++hit;
    }

    // 進捗を表示
    if (i % Math.floor(N / 1000) == 0) {
        process.stdout.write(`\rwip: ${Math.floor((i / N) * 1000) / 10}%`);
    }
}

process.stdout.write(`\rresult: ${Math.floor((hit / N) * 100000) / 1000}%\n`);
```

# 使ってみた
一応、このシミュレータで色々求めたので書いておく。
(博士2、モンボ2、アメ2、ポケ通1、ナンジャモ1、N=100000)

<table>
<tr><td></td><td>タネ2枚</td><td>タネ4枚</td><td>タネ5枚</td></tr>
<tr><td>2ターン目1進化</td><td>60.499%</td><td>51.025%</td><td>48.274%</td></tr>
<tr><td>3ターン目1進化</td><td>77.051%</td><td>71.381%</td><td>68.476%</td>
<tr><td>2ターン目2進化</td><td>36.69%</td><td>30.462%</td><td>29.01%</td></tr>
<tr><td>3ターン目2進化</td><td>67.704%</td><td>61.565%</td><td>58.42%</td></tr>
</table>

# さいごに
だいたいの使い方はコメントに書いたので、使いたい人は各自で使ってみてほしい。
ポケモン通信のロジックなどがかなり適当なので、精度はまあまあといったところだろうか？
~~(qiita見てる人ってポケポケやってるの？)~~