【初心者】C++でアマツエルフのマリガンを考えてみた #2
みなさんこんにちは、エアーマンです。
こちらのnoteは前回の続きです。前回をご覧になってない方はぜひそちらもご覧ください。
↓↓↓
【初心者】C++でアマツエルフのマリガンを考えてみた
今回は前回実装できなかった「アマツ+αのセットキープ」について考えていこうと思います。
想定デッキ↑
アマツ+αのセットキープ
前回はアマツを単キープするか、アマツor悪戯の精霊を単キープするかの2通りのプログラムを実行しました。今回は「単キープするカード」「アマツとセットキープするカード」の2点の組み合わせをもとにマリガンの設定をしました。
単キープ対象
《フェアリーブレイダー・アマツ》
《悪戯の精霊》→「3枚生成」と表記
《フェアリーウィスパラー》《スピリイットシャイン》→上記にこれらを加えたものを「2枚生成」と表記
《ウォーターフェアリー》《萌芽の化身》《森荒らしへの報い》→上記にこれらを加えたものを「1枚生成」と表記
アマツを引けているときに同時にキープする対象
上記と同様に「3枚生成」「2枚生成」「1枚生成」と表記
これらの組み合わせを「(アマツ以外の単キープ対象)-(セットキープ対象)」のように表記します。
例. アマツと悪戯の精霊を単キープして、アマツがある時フェアリー1枚生成札をセットキープする場合⇒「3枚生成-1枚生成」と表記
この組み合わせのいくつかをC++で実装し、100万回ループを実行させて、それぞれのターンまでにアマツが起動する回数を計算しました。
今回実装したマリガン
なし-1枚生成
3枚生成-1枚生成
2枚生成-1枚生成
なし-2枚生成
3枚生成-2枚生成
2枚生成-2枚生成
1枚生成-1枚生成
なし-なし
マリガンなし
「なし-なし」はアマツのみを単キープし、アマツとセットキープするものはない状態のことで、前回の「マリガン①」に該当します(計算はし直しました)。
結果
計算結果を表にまとめました。
数字がいっぱい書いてあって脳が爆発された方、すみません。ではこれらをグラフで表したものがこちらになります。
少しは見やすくなりましたかね。実際序盤でアマツが起動するかどうかが大事で後半の部分はあまり意味がないので、こちらのグラフの8ターン目までを抜き出したグラフを作成しました。
だいぶ分かりやすくなりました。「マリガンなし」が圧倒的にアマツの起動が遅いことが分かります(当然ですね)。次点が「なし-なし」でしょうか。
一番アマツの起動が早いのはグラフの灰色で示された「2枚生成-1枚生成」だということが分かります。2番目は紺色の「1枚生成-1枚生成」ですね。1番と2番の差はかなり小さいです。
考察
2枚生成-1枚生成
0
3147
38892
134828
225242
322420
418453
507420
585370
1枚生成-1枚生成
0
2236
35232
129264
219344
316708
412057
500187
577485
例えば、5ターン目までにアマツが起動したのは「2枚生成-1枚生成」の時322420回、「1枚生成-1枚生成」の時316708回となっています。
つまり【アマツエルフ】において一番優れているとされるマリガンは、2枚生成札を単キープし、1枚生成札をセットキープすること。すなわち
アマツと悪戯の精霊とスピリットシャインとウィスパラーを単キープし、アマツがある時はフェアリー生成札はすべてセットキープする
となります。なんとなく悪戯の精霊は単キープしたほうがいいだろうなぁと思っていたのですが、シャインとウィスパラーも単キープすべきという結論が出たのには驚きました。「3枚生成-1枚生成」はいいマリガンの順としては3番目ですが、2番目との差ははっきりしていたのが見れましたね。また、アマツ+フェアリー生成札のセットキープがいいというイメージ通りでした。
終わりに
予想以上にはっきり結果が出たので面白かったです。実際にはコッコロやエルフクイーンのドローがあったり、PPの概念や序盤のテンポの意識等があったりするので少し変わってきますが、1枚多くドローするというだけならそのままの結果で対応可能なので参考になるかと思います。また機会があればこのようなこともやってみようと思いますので、ぜひ感想や指摘等お寄せください。
それでは、ここまで見てくださった方ありがとうございました。
ソースコード
長くなりすぎるので、マリガン6とマリガン7のみ掲載します(マリガン番号はエクセルの表の一番上に書いてあったやつで、マリガン6が「2枚生成-2枚生成」、マリガン7が「1枚生成-1枚生成」)。基本は前回のものと同じです。マリガン部分のみ変わっています。
デッキの中身
0~2 《ウォーターフェアリー》
3~5 《萌芽の化身》
6~8 《森荒らしへの報い》
9~11 《フェアリーウィスパラー》
12~14 《スピリットシャイン》
15~17 《悪戯の精霊》
18~20 《フェアリーブレイダー・アマツ》
21~39 フェアリー生成とは関係のないカード
//マリガン6
#include<iostream>
#include<vector>
#include<stdio.h>
#include<time.h>
#include <unistd.h>
#include <sys/time.h>
using namespace std;
//デッキの中身を列挙する関数
void print(vector<int> V) {
for (int i=0; i<V.size(); i++) {
cout << V[i] << " ";
}
cout << endl;
}
//乱数を初期化する関数
void InitRand() {
struct timeval tv;
gettimeofday(&tv, NULL);
srand(tv.tv_sec + tv.tv_usec);
//srand((unsigned int)time(NULL));
}
//ドローするカードを残りデッキから決める関数
int drowcard(vector<int> V) {
return rand() % V.size();
}
//メイン関数
int main() {
int T[39];
for (int i=0; i<40; i++) {T[i]=0;};
for(int i=0; i<1000000; i++) {
vector<int> V; //デッキの中身を列挙したベクトルを定義
//デッキVにカードを0から39まで設定
for (int i=0; i<40; i++) {
V.push_back(i);
}
//山上6枚のカードを決める
InitRand(); //乱数の初期化
int X1 = drowcard(V); //ドローするカードの位置を決定
int C1=V[X1]; //ドローしたカードの種類を保存
V.erase(V.begin() + X1); //ドローしたカードをデッキから消す
InitRand();
int X2 = drowcard(V);
int C2=V[X2];
V.erase(V.begin() + X2);
InitRand();
int X3 = drowcard(V);
int C3=V[X3];
V.erase(V.begin() + X3);
//cout << "マリガン前" << " " << C1 << " " << C2 << " " << C3 << endl; //マリガン前の手札を出力
InitRand();
int X4 = drowcard(V);
int C4=V[X4];
V.erase(V.begin() + X4);
InitRand();
int X5 = drowcard(V);
int C5=V[X5];
V.erase(V.begin() + X5);
InitRand();
int X6 = drowcard(V);
int C6=V[X6];
V.erase(V.begin() + X6);
//1枚目がアマツの時
if (17 < C1 && C1 < 21) {
if (17 < C2) {C2=C5;}; //2枚目がフェアリー生成以外ならマリガン
if (17 < C3) {C3=C6;}; //3枚目がフェアリー生成以外ならマリガン
}
else {//2枚目がアマツの時
if (17 < C2 && C2 < 21) {
if (17 < C1) {C1=C4;}; //1枚目がフェアリー生成以外ならマリガン
if (17 < C3) {C3=C6;}; //3枚目がフェアリー生成以外ならマリガン
}
else {//3枚目がアマツの時
if (17 < C3 && C3 < 21) {
if (17 < C1) {C1=C4;}; //1枚目がフェアリー生成以外ならマリガン
if (17 < C2) {C2=C5;}; //2枚目がフェアリー生成以外ならマリガン
}
//アマツがない時
else {if (C1 < 9 || 17 < C1) {C1=C4;}; //1枚目がフェアリー2枚生成以外ならマリガン
if (C2 < 9 || 17 < C2) {C2=C5;}; //2枚目がフェアリー2枚生成以外ならマリガン
if (C3 < 9 || 17 < C3) {C3=C6;}; //3枚目がフェアリー2枚生成以外ならマリガン
};
};
};
//cout << "マリガン後" << " " << C1 << " " << C2 << " " << C3 << endl; //マリガン後のハンドを出力
//デッキVを再定義
V.clear();
//デッキVにカードを0から39まで設定
for (int i=0; i<40; i++) {
V.push_back(i);
}
//特殊な操作が必要なカードを定義
int fairy = 0;
int amatu = 0;
int water = 0;
int water2 = 0;
int water3 = 0;
int seirei = 0;
int seirei2 = 0;
int seirei3 = 0;
int A = 0;
V.erase(V.begin() + C1); //1枚目はC1を引く
if (2 < C1 && C1 < 15) {fairy++;}; //それがフェアリーを生成するカードならフェアリーカウントを1進める
if (8 < C1 && C1 < 15) {fairy++;}; //それがフェアリーを2枚生成するカードならフェアリーカウントをさらに1進める
if (C1 == 0) {water++;}; //それがウォーターフェアリー1枚目の場合
if (C1 == 1) {water2++;}; //それがウォーターフェアリー2枚目の場合
if (C1 == 2) {water3++;}; //それがウォーターフェアリー3枚目の場合
if (C1 == 15) {seirei = seirei +3;}; //それが悪戯な精霊1枚目の場合
if (C1 == 16) {seirei2 = seirei2 +3;}; //それが悪戯な精霊2枚目の場合
if (C1 == 17) {seirei3 = seirei3 +3;}; //それが悪戯な精霊3枚目の場合
if (17 < C1 && C1 < 21) {amatu++;}; //それがアマツの場合
//2枚目について同様に行う
if (C1<C2) {V.erase(V.begin() + C2 -1);} else {V.erase(V.begin() + C2);};
if (2 < C2 && C2 < 15) {fairy++;};
if (8 < C2 && C2 < 15) {fairy++;};
if (C2 == 0) {water++;};
if (C2 == 1) {water2++;};
if (C2 == 2) {water3++;};
if (C2 == 15) {seirei = seirei +3;};
if (C2 == 16) {seirei2 = seirei2 +3;};
if (C2 == 17) {seirei3 = seirei3 +3;};
if (17 < C2 && C2 < 21) {amatu++;};
//3枚目について同様に行う
if (C1<C3 && C2<C3) {V.erase(V.begin() + C3 -2);}
else {if (C1<C3 || C2<C3) {V.erase(V.begin() + C3 -1);}
else {V.erase(V.begin() + C3);};};
if (2 < C3 && C3 < 15) {fairy++;};
if (8 < C3 && C3 < 15) {fairy++;};
if (C3 == 0) {water++;};
if (C3 == 1) {water2++;};
if (C3 == 2) {water3++;};
if (C3 == 15) {seirei = seirei +3;};
if (C3 == 16) {seirei2 = seirei2 +3;};
if (C3 == 17) {seirei3 = seirei3 +3;};
if (17 < C3 && C3 < 21) {amatu++;};
//print(V);
//cout << "フェアリーカウント" << " " << fairy << endl;
//フェアリーが6枚以上かつアマツが1枚以上になったら終了
while (fairy<6 || amatu<1) {
InitRand();
int XX = drowcard(V);
//cout << " " << endl;
//cout << "引いたカード" << " " << V[XX] << endl;
if (water > 0) {fairy++; water = water -1;};
if (water2 > 0) {fairy++; water2 = water2 -1;};
if (water3 > 0) {fairy++; water3 = water3 -1;};
if (seirei > 0) {fairy++; seirei = seirei -1;};
if (seirei2 > 0) {fairy++; seirei2 = seirei2 -1;};
if (seirei3 > 0) {fairy++; seirei3 = seirei3 -1;};
if (V[XX] == 0) {water++;};
if (V[XX] == 1) {water2++;};
if (V[XX] == 2) {water3++;};
if (2 < V[XX] && V[XX] < 15) {fairy++;};
if (8 < V[XX] && V[XX] < 15) {fairy++;};
if (V[XX] == 15) {seirei = seirei +3;};
if (V[XX] == 16) {seirei2 = seirei2 +3;};
if (V[XX] == 17) {seirei3 = seirei3 +3;};
if (17 < V[XX] && V[XX] < 21) {amatu++;};
V.erase(V.begin() + XX);
//print(V);
//cout << "フェアリーカウント" << " " << fairy << endl;
A++;
}
for (int i=39; i>A-1; i--) {T[i]++;};
};
//for (int i=0; i<38; i++) {cout << i << "T目アマツ " << T[i] << endl;};
for (int i=0; i<38; i++) {cout << T[i] << endl;};
return 0;
}
//マリガン7
//1枚目がアマツの時
if (17 < C1 && C1 < 21) {
if (17 < C2) {C2=C5;}; //2枚目がフェアリー生成以外ならマリガン
if (17 < C3) {C3=C6;}; //3枚目がフェアリー生成以外ならマリガン
}
else {//2枚目がアマツの時
if (17 < C2 && C2 < 21) {
if (17 < C1) {C1=C4;}; //1枚目がフェアリー生成以外ならマリガン
if (17 < C3) {C3=C6;}; //3枚目がフェアリー生成以外ならマリガン
}
else {//3枚目がアマツの時
if (17 < C3 && C3 < 21) {
if (17 < C1) {C1=C4;}; //1枚目がフェアリー生成以外ならマリガン
if (17 < C2) {C2=C5;}; //2枚目がフェアリー生成以外ならマリガン
}
//アマツがない時
else {if (17 < C1) {C1=C4;}; //1枚目がフェアリー生成以外ならマリガン
if (17 < C2) {C2=C5;}; //2枚目がフェアリー生成以外ならマリガン
if (17 < C3) {C3=C6;}; //3枚目がフェアリー生成以外ならマリガン
};
};
};