前の記事
本文
前回は状況を考慮せずに画一的にカードを使用していたが、
今回はラルトスの有無でカードの使用順序が変わるのかどうかを調べたいと思う
先に結論
ラルトスの有無によらず常に「博士 → モンボ → 石板」がよい
想定するデッキ構成
ミュウツー | サーナイト系 | 自由枠 | 石板 | 博士 | モンボ | その他 |
---|---|---|---|---|---|---|
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
チラ裏
ラルトスがいる場合も、博士がモンボより優先なのか…
バグってんのかな?
分からん