1
1

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-01-06

前の記事

本文

前回は状況を考慮せずに画一的にカードを使用していたが、
今回はラルトスの有無でカードの使用順序が変わるのかどうかを調べたいと思う

先に結論

ラルトスの有無によらず常に「博士 → モンボ → 石板」がよい

想定するデッキ構成

ミュウツー サーナイト系 自由枠 石板 博士 モンボ その他
2 6 0 2 2 2 6

ラルトスがいる場合

ソースコード

ソースコード
// ### パラメータ ###

const ミュウツー_総数 = 2;
const サナ系_グループ数 = 2;
const 自由枠_総数 = 0;
const 石板_総数 = 2;
const 博士_総数 = 2;
const モンボ_総数 = 2;
const その他_総数 = 6;

const 優先度List = [
    ["石板", "博士", "モンボ"],
    ["石板", "モンボ", "博士"],
    ["博士", "石板", "モンボ"],
    ["博士", "モンボ", "石板"],
    ["モンボ", "石板", "博士"],
    ["モンボ", "博士", "石板"],
];

// ### メインロジック ###

const 試行回数 = 200_0000;

for (const 優先度 of 優先度List) {
    const ターン数List = [];
    let ターン数_合計 = 0;
    for (let i = 0; i < 試行回数; i++) {
        const ターン数 = サーナイトが完成したターン(優先度);
        ターン数List.push(ターン数);
        ターン数_合計 += ターン数;
    }
    const 平均 = ターン数_合計 / 試行回数;
    const 分散 = ターン数List.reduce((合計, ターン数) => 合計 + Math.pow(ターン数 - 平均, 2), 0) / 試行回数;
    console.log(`優先度:${優先度.join("")}`)
    console.log(`平均:${平均}`);
    console.log(`標準偏差:${Math.sqrt(分散)}`);
    console.log("");
}

// ### 関数群 ###

function 山札生成() {
    const 山札 = [];
    for (let i = 0; i < ミュウツー_総数; i++) {
        山札.push("ミュウツー");
    }
    for (let i = 0; i < サナ系_グループ数; i++) {
        山札.push("ラルトス");
        山札.push("キルリア");
        山札.push("サーナイト");
    }
    for (let i = 0; i < 自由枠_総数; i++) {
        山札.push("自由枠");
    }
    for (let i = 0; i < 石板_総数; i++) {
        山札.push("石板");
    }
    for (let i = 0; i < 博士_総数; i++) {
        山札.push("博士");
    }
    for (let i = 0; i < モンボ_総数; i++) {
        山札.push("モンボ");
    }
    for (let i = 0; i < その他_総数; i++) {
        山札.push("その他");
    }

    if (山札.length !== 20) {
        throw new Error(`山札の総数がおかしい:${山札.length}`);
    }

    return シャッフル(山札);
}

function 手札生成(山札) {
    const 手札 = [];
    手札.push(初手ラルトス取得(山札));
    手札.push(ドロー(山札));
    手札.push(ドロー(山札));
    手札.push(ドロー(山札));
    手札.push(ドロー(山札));
    return 手札;
}

function シャッフル(山札) {
    for (let i = 0; i < 山札.length; i++) {
        const j = Math.floor(Math.random() * 山札.length);
        const tmp = 山札[i];
        山札[i] = 山札[j];
        山札[j] = tmp;
    }
    return 山札;
}

function ドロー(山札) {
    return 山札.shift();
}

function 初手ラルトス以外をドロー(山札) {
    const index = 山札.findIndex(カード => カード !== "ラルトス");
    const カード = 山札.splice(index, 1)[0];

    シャッフル(山札);

    return カード;
}

function 初手ラルトス取得(山札) {
    const タネポケ = "ラルトス";
    const index = 山札.findIndex(カード => カード === タネポケ);
    山札.splice(index, 1);

    シャッフル(山札);

    return タネポケ;
}

function 初手ラルトス以外のタネポケ取得(山札) {
    const 対象のタネポケList = ["ミュウツー", "自由枠"];
    const 山札のタネポケList = 山札.filter(カード => 対象のタネポケList.includes(カード));

    const タネポケ = 山札のタネポケList[Math.floor(Math.random() * 山札のタネポケList.length)];
    const index = 山札.findIndex(カード => カード === タネポケ);
    山札.splice(index, 1);

    シャッフル(山札);

    return タネポケ;
}

function モンボ使用(山札) {
    const 対象のタネポケList = ["ミュウツー", "ラルトス", "自由枠"];
    const 山札のタネポケList = 山札.filter(カード => 対象のタネポケList.includes(カード));

    let タネポケ = "";
    if (山札のタネポケList.length > 0) {
        タネポケ = 山札のタネポケList[Math.floor(Math.random() * 山札のタネポケList.length)];

        const index = 山札.findIndex(カード => カード === タネポケ);
        山札.splice(index, 1);
    }

    シャッフル(山札);

    return タネポケ;
}

function 博士使用(山札) {
    return 山札.splice(0, 2);
}

function 石板使用(山札) {
    if (山札.length === 0) {
        return "";
    }

    const 対象のタネポケList = ["ミュウツー", "ラルトス", "キルリア", "サーナイト", "自由枠"];

    if (対象のタネポケList.includes(山札[0])) {
        return ドロー(山札);
    }
    else {
        const カード = ドロー(山札);
        山札.push(カード);
        return "";
    }
}

function サーナイトが完成したターン(優先度) {
    const 山札 = 山札生成();
    const 手札 = 手札生成(山札);

    let ターン数 = 0;
    let 場のサナ系のカード = "";

    while (場のサナ系のカード !== "サーナイト") {
        ターン数++;
        手札.push(ドロー(山札));

        for (const 使うカード of 優先度) {
            while (手札.includes(使うカード)) {
                カードを捨てる(手札, 使うカード);
            
                if (使うカード === "石板") {
                    const 引いたカード = 石板使用(山札);
                    if (引いたカード !== "") {
                        手札.push(引いたカード);
                    }
                }
                else if (使うカード === "博士") {
                    const 引いたカードList = 博士使用(山札);
                    手札.push(...引いたカードList);
                    break; // 博士は1ターンに1回しか使えないため
                }
                else if (使うカード === "モンボ") {
                    const 引いたカード = モンボ使用(山札);
                    if (引いたカード !== "") {
                        手札.push(引いたカード);
                    }
                }
            }
        }

        if (ターン数 === 1) {
            if (手札.includes("ラルトス")) {
                場のサナ系のカード = "ラルトス";
            }
        }
        else {
            if (場のサナ系のカード === "" && 手札.includes("ラルトス")) {
                場のサナ系のカード = "ラルトス";
            }
            else if (場のサナ系のカード === "ラルトス" && 手札.includes("キルリア")) {
                場のサナ系のカード = "キルリア";
            }
            else if (場のサナ系のカード === "キルリア" && 手札.includes("サーナイト")) {
                場のサナ系のカード = "サーナイト";
            }
        }
    }

    return ターン数;
}

function カードを捨てる(手札, カード) {
    const index = 手札.indexOf(カード);
    if (index !== -1) {
        手札.splice(index, 1);
    }
}

結果

「博士 → モンボ → 石板」がよい

優先度:石板 → 博士 → モンボ
平均:4.25869
標準偏差:1.7246296657266873

優先度:石板 → モンボ → 博士
平均:4.2571305
標準偏差:1.7275724025343788

優先度:博士 → 石板 → モンボ
平均:4.249411
標準偏差:1.7198459678427986

優先度:博士 → モンボ → 石板
平均:4.217528
標準偏差:1.6961068861156328

優先度:モンボ → 石板 → 博士
平均:4.229745
標準偏差:1.703528759644686

優先度:モンボ → 博士 → 石板
平均:4.2190335
標準偏差:1.700545155480819

ラルトスがいない場合

ソースコード

function 手札生成(山札)の箇所をいじっただけ

ソースコード
// ### パラメータ ###

const ミュウツー_総数 = 2;
const サナ系_グループ数 = 2;
const 自由枠_総数 = 0;
const 石板_総数 = 2;
const 博士_総数 = 2;
const モンボ_総数 = 2;
const その他_総数 = 6;

const 優先度List = [
    ["石板", "博士", "モンボ"],
    ["石板", "モンボ", "博士"],
    ["博士", "石板", "モンボ"],
    ["博士", "モンボ", "石板"],
    ["モンボ", "石板", "博士"],
    ["モンボ", "博士", "石板"],
];

// ### メインロジック ###

const 試行回数 = 200_0000;

for (const 優先度 of 優先度List) {
    const ターン数List = [];
    let ターン数_合計 = 0;
    for (let i = 0; i < 試行回数; i++) {
        const ターン数 = サーナイトが完成したターン(優先度);
        ターン数List.push(ターン数);
        ターン数_合計 += ターン数;
    }
    const 平均 = ターン数_合計 / 試行回数;
    const 分散 = ターン数List.reduce((合計, ターン数) => 合計 + Math.pow(ターン数 - 平均, 2), 0) / 試行回数;
    console.log(`優先度:${優先度.join("")}`)
    console.log(`平均:${平均}`);
    console.log(`標準偏差:${Math.sqrt(分散)}`);
    console.log("");
}

// ### 関数群 ###

function 山札生成() {
    const 山札 = [];
    for (let i = 0; i < ミュウツー_総数; i++) {
        山札.push("ミュウツー");
    }
    for (let i = 0; i < サナ系_グループ数; i++) {
        山札.push("ラルトス");
        山札.push("キルリア");
        山札.push("サーナイト");
    }
    for (let i = 0; i < 自由枠_総数; i++) {
        山札.push("自由枠");
    }
    for (let i = 0; i < 石板_総数; i++) {
        山札.push("石板");
    }
    for (let i = 0; i < 博士_総数; i++) {
        山札.push("博士");
    }
    for (let i = 0; i < モンボ_総数; i++) {
        山札.push("モンボ");
    }
    for (let i = 0; i < その他_総数; i++) {
        山札.push("その他");
    }

    if (山札.length !== 20) {
        throw new Error(`山札の総数がおかしい:${山札.length}`);
    }

    return シャッフル(山札);
}

function 手札生成(山札) {
    const 手札 = [];
    手札.push(初手ラルトス以外のタネポケ取得(山札));
    手札.push(初手ラルトス以外をドロー(山札));
    手札.push(初手ラルトス以外をドロー(山札));
    手札.push(初手ラルトス以外をドロー(山札));
    手札.push(初手ラルトス以外をドロー(山札));
    return 手札;
}

function シャッフル(山札) {
    for (let i = 0; i < 山札.length; i++) {
        const j = Math.floor(Math.random() * 山札.length);
        const tmp = 山札[i];
        山札[i] = 山札[j];
        山札[j] = tmp;
    }
    return 山札;
}

function ドロー(山札) {
    return 山札.shift();
}

function 初手ラルトス以外をドロー(山札) {
    const index = 山札.findIndex(カード => カード !== "ラルトス");
    const カード = 山札.splice(index, 1)[0];

    シャッフル(山札);

    return カード;
}

function 初手ラルトス取得(山札) {
    const タネポケ = "ラルトス";
    const index = 山札.findIndex(カード => カード === タネポケ);
    山札.splice(index, 1);

    シャッフル(山札);

    return タネポケ;
}

function 初手ラルトス以外のタネポケ取得(山札) {
    const 対象のタネポケList = ["ミュウツー", "自由枠"];
    const 山札のタネポケList = 山札.filter(カード => 対象のタネポケList.includes(カード));

    const タネポケ = 山札のタネポケList[Math.floor(Math.random() * 山札のタネポケList.length)];
    const index = 山札.findIndex(カード => カード === タネポケ);
    山札.splice(index, 1);

    シャッフル(山札);

    return タネポケ;
}

function モンボ使用(山札) {
    const 対象のタネポケList = ["ミュウツー", "ラルトス", "自由枠"];
    const 山札のタネポケList = 山札.filter(カード => 対象のタネポケList.includes(カード));

    let タネポケ = "";
    if (山札のタネポケList.length > 0) {
        タネポケ = 山札のタネポケList[Math.floor(Math.random() * 山札のタネポケList.length)];

        const index = 山札.findIndex(カード => カード === タネポケ);
        山札.splice(index, 1);
    }

    シャッフル(山札);

    return タネポケ;
}

function 博士使用(山札) {
    return 山札.splice(0, 2);
}

function 石板使用(山札) {
    if (山札.length === 0) {
        return "";
    }

    const 対象のタネポケList = ["ミュウツー", "ラルトス", "キルリア", "サーナイト", "自由枠"];

    if (対象のタネポケList.includes(山札[0])) {
        return ドロー(山札);
    }
    else {
        const カード = ドロー(山札);
        山札.push(カード);
        return "";
    }
}

function サーナイトが完成したターン(優先度) {
    const 山札 = 山札生成();
    const 手札 = 手札生成(山札);

    let ターン数 = 0;
    let 場のサナ系のカード = "";

    while (場のサナ系のカード !== "サーナイト") {
        ターン数++;
        手札.push(ドロー(山札));

        for (const 使うカード of 優先度) {
            while (手札.includes(使うカード)) {
                カードを捨てる(手札, 使うカード);
            
                if (使うカード === "石板") {
                    const 引いたカード = 石板使用(山札);
                    if (引いたカード !== "") {
                        手札.push(引いたカード);
                    }
                }
                else if (使うカード === "博士") {
                    const 引いたカードList = 博士使用(山札);
                    手札.push(...引いたカードList);
                    break; // 博士は1ターンに1回しか使えないため
                }
                else if (使うカード === "モンボ") {
                    const 引いたカード = モンボ使用(山札);
                    if (引いたカード !== "") {
                        手札.push(引いたカード);
                    }
                }
            }
        }

        if (ターン数 === 1) {
            if (手札.includes("ラルトス")) {
                場のサナ系のカード = "ラルトス";
            }
        }
        else {
            if (場のサナ系のカード === "" && 手札.includes("ラルトス")) {
                場のサナ系のカード = "ラルトス";
            }
            else if (場のサナ系のカード === "ラルトス" && 手札.includes("キルリア")) {
                場のサナ系のカード = "キルリア";
            }
            else if (場のサナ系のカード === "キルリア" && 手札.includes("サーナイト")) {
                場のサナ系のカード = "サーナイト";
            }
        }
    }

    return ターン数;
}

function カードを捨てる(手札, カード) {
    const index = 手札.indexOf(カード);
    if (index !== -1) {
        手札.splice(index, 1);
    }
}

結果

「博士 → モンボ → 石板」がよい

優先度:石板 → 博士 → モンボ
平均:4.696233
標準偏差:1.7600467635113153

優先度:石板 → モンボ → 博士
平均:4.744938
標準偏差:1.7502360915331232

優先度:博士 → 石板 → モンボ
平均:4.682066
標準偏差:1.760512417345648

優先度:博士 → モンボ → 石板
平均:4.6570425
標準偏差:1.7452563287876548

優先度:モンボ → 石板 → 博士
平均:4.727787
標準偏差:1.739180002942308

優先度:モンボ → 博士 → 石板
平均:4.7095575
標準偏差:1.737809441276489

チラ裏

ラルトスがいる場合も、博士がモンボより優先なのか…

バグってんのかな?
分からん

1
1
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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?