0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Last updated at Posted at 2025-04-28

ポケポケとは

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

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

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

シミュレータ

言語にはjavascriptを使用している。
3時間で適当に作ったからコードはかなり汚い。

// 配列のシャッフル
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)

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

さいごに

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

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?