ますたーです。こんにちは。
今回は、自動でボックスのポケモンをマジカル交換(旧:ミラクル交換)に出し続けます。
※本記事ではポケモンを「交換に出します」。利用に際してはくれぐれも自己責任でお願いします
Arduino自動化記事はこれで第16回。これからもよろしくお願いします。
なお、Arduino Leonardo自動化の導入・機材構成については導入記事を参考にしてください。
導入記事:【Arduino自動化01】Arduino開発環境の導入
※本ブログに初めてお越しの方は「本ブログについて」もぜひ、ご覧ください。
概要
本記事では、YY通信のマジカル交換(旧:ミラクル交換)を自動で繰り返し行います。「孵化余り」の有効活用や、ロトミIDくじ用のID集めなどにお役立てください。
※2021年2月19日AM9時追記:本稿中の「ミラクル交換」はすべて「マジカル交換」の誤りです。過去作品に引っ張られました。以降、適宜読み替えてください
では、目次です。
ソースコードのみ欲しいという方はどうぞコチラから⇒読み飛ばす。
ミラクル交換でID集め(ロトミIDくじを効率的に)
ミラクル交換の大きな特徴は、何よりも交換先が全国不特定多数のポケモントレーナーであることです。ポケモン剣盾での「おやID」は6桁なので、32ボックス960匹をランダムに交換し続けたとしても、これらの間で重複が起こる可能性はほぼ0です※。
※無作為の960匹について、これらでIDが重複する確率は単純計算で 1-(1-1/106)960=0.09595%
おやIDをたくさん集めることで、ロトミIDくじの当選確率もアップします。例えば、50種類のおやIDを入手しておくと「はずれ」が出る確率が1%を下回ったり、100種類のおやIDがあれば50%以上の確率で「ポイントアップ」が手に入るようになったりと、非常にお得です。【Arduino自動化14】ロトミIDくじ無限抽選では、このあたりの、IDの数と当選確率の話も纏めているので、合わせてご参考いただければと思います。
要するに、ミラクル交換で他人産IDのポケモンを50匹程度を集めておけば、ロトミIDくじが、まさにソシャゲの「ログインボーナス」のようになるのでおすすめです。
ほとんど毎日「ポイントアップ」が貰えて、時々「ポイントマックス」が貰えるとなれば、毎日のポケモンのモチベーションが大きく上がりますよね。
他人の孵化余りも手に入る
ミラクル交換のもう一つの魅力、それは、孵化余りを流すプレイヤーも多いということ。私も含め、孵化余りは積極的にGTSやポケモンHOMEでの交換、ミラクル交換に出していますが、ID集めはもちろん、これは何よりも他人の孵化余りを手に入れられるから。
現在、環境で流行っているポケモンの孵化余りが手に入ったり、有用なタマゴわざを持ったポケモン(うたかたのアリア持ちラプラス、じばく持ちゴンベなど)などが手に入りやすく、対戦ガチ勢にも嬉しい仕様ですね。
つまりは、高個体値のポケモンや夢特性のポケモン、タマゴわざを持っているポケモン、おしゃれボールに入ったポケモンなどを手に入れることもできます。特に海外産のポケモンも手に入りやすく、これらを活用するとタマゴ孵化による色違い厳選もはかどります。
ちなみに、全自動タマゴ孵化については本ブログでも取り扱っています。合わせてご参考いただければ幸いです⇒【Arduino自動化12】全自動タマゴ受け取り&孵化。
準備(プログラムの書き換え)
ミラクル交換をたくさん行うにあたっては、大量のポケモン準備する必要があります。また、効率的に通信交換をするためにも、図鑑は完成させておくと良いでしょう。
さて、本稿で扱う自動化プログラムについても、必要に応じて4箇所書き換える必要があります。
1箇所目は、何匹のポケモンを交換に出すかです。MAX_TRADE_POKEMONにて指定してください。必ずしも30の倍数である必要はありませんが、左詰めにあずけておいてください。
#define MAX_TRADE_POKEMON (30*3-5)
ミラクル交換の待ち時間
2箇所目は、ミラクル交換の待機時間です。具体的には、YY通信で「ミラクル交換」を選んでフィールドに戻ってから「交換完了!」の文字が出るまでの待機時間です。
概ね30秒あればマッチングすることが多いですが、時々マッチングに時間がかかり、50秒ほどかかることもあります。皆様の環境に合わせて数字を記入してください。
数字は、WAIT_TIME_FOR_MATCHINGに単位「秒」で記入してください。
#define WAIT_TIME_FOR_MATCHING (45)
なお、この時間指定が短すぎると、その次のループでうまく交換ができませんので注意しましょう。45秒で設定した筆者実証時には、85回の交換中3回、45秒以内での通信交換に失敗しています。もし、時間に余裕のある人は、60秒などの充分に大きな数字を設定しておくと良いかもしれません。
図鑑の状況と通信進化ポケモン
3箇所目・4箇所目は、ポケモン図鑑の完成状況(新たに登録される可能性があるかどうか)と、受け取ったポケモンが進化する可能性を考慮するかどうかです。これらは数字ではなく、trueまたはfalseを記入します。
ポケモン図鑑が完成している場合はPOKEDEX_COMPLETEDにtrueを指定してください。また、通信交換で進化するポケモンを無視する場合はIGNORE_EVOLVEをtrueにしてください。デフォルトでは、ポケモン図鑑は完成済(true)・通信進化ポケモンは無視しない(false)になっています。
#define POKEDEX_COMPLETED (true)
#define IGNORE_EVOLVE (false)
ポケモン図鑑に登録されていないポケモンを交換で受け取った場合、その図鑑説明の画面が出てきます。こちらは、たかだか5秒程度のロスなので、図鑑完成していない人も、あまり気にならないかもしれません。
一方で、バケッチャ(パンプジンに進化)などの通信進化するポケモンを受け取った場合、そのポケモンの進化画面が発生してしまいます。この進化ポケモン、数は20数匹と多めで、意外と受け取る機会が多いです。
通信進化は長くて20秒は時間を取られるので、よほど受け取らない自信が無い限りは、IGNORE_EVOLVEをfalseにしておきましょう。
特に、稀有な機会ではありますが、メタルコート持ちストライクの場合、進化時に「バレットパンチ」を覚えようとするため、そういうところもケアする必要があります。
実際に自動化ミラクル交換を試してみた
早速、85匹のパッチルドンを用意して、ミラクル交換のArduino自動化を実装&試してみました。
設定は1試行あたり45秒の待機時間、ポケモン図鑑は完成済、進化ポケモンは無視しない想定で、かかった時間は約3時間20分でした。1回の交換にかかる時間を平均すると約2分20秒となります。
なお、特筆すべき点として、待機時間を45秒に設定で、85回の通信交換中、3回はこの時間内に通信交換が成立せず、それぞれの翌試行分の交換が失敗しています。ゆえに、通信交換に成功したのは82匹です(いずれの試行も、通信交換に失敗した次の試行でループが復帰していました)。一応、これを避けるためには、待機時間を冗長に設定する他はありません。体感としては、60秒ほどあれば通信交換がほぼ成立すると考えます。
ちなみに、82匹の交換が成立したわけですが、そのうち1回のみ、通信交換するポケモン(バケッチャ)を受け取っています。約80回に1回(確率にすれば約1.25%)ですし、もし「全体的に時間効率を高めたい」という人は、IGNORE_EVOLVEを(true)にしても良いかもしれません。
さて、実際に使ったソースコードです。
使い方は簡単。ボックスを左詰めにした後、ポケモン剣盾をインターネット接続し、Arduinoを挿し込むだけ。
ただし、前述の通り、4箇所は各人のステータスに応じて書き換えてください。
#include <SwitchControlLibrary.h>
#define HOLDTIME (95)
#define MAX_TRADE_POKEMON (30*3-5)
#define WAIT_TIME_FOR_MATCHING (45)
#define POKEDEX_COMPLETED (true)
#define IGNORE_EVOLVE (false)
void PushRL(int delay_time_ms);
int PushKey(char* keyname, int holdtime, int delaytime);
void NextDayInCheatMode(void);
void YYconnection_start(int which_pokemon);
void YYconnection_receive(void);
void setup() {
for(int i=0;i<7;i++)PushRL(300);
delay(1000);
}
void loop() {
int num_pokemon;
for(num_pokemon=1; num_pokemon <= MAX_TRADE_POKEMON ; num_pokemon++){
YYconnection_start(num_pokemon);
delay( (unsigned long int)WAIT_TIME_FOR_MATCHING*1000UL );
YYconnection_receive();
}
for(;;) delay(100);
}
void PushRL(int delay_time_ms){
SwitchControlLibrary().PressButtonR();
SwitchControlLibrary().PressButtonL();
delay(HOLDTIME);
SwitchControlLibrary().ReleaseButtonR();
SwitchControlLibrary().ReleaseButtonL();
delay(delay_time_ms);
return;
}
int PushKey(char* keyname, int holdtime, int delaytime){
if(strlen(keyname)==1){
switch(keyname[0]){
case 'A': case 'a':
SwitchControlLibrary().PressButtonA(); delay(holdtime);
if(holdtime>0)SwitchControlLibrary().ReleaseButtonA(); delay(delaytime);
break;
case 'B': case 'b':
SwitchControlLibrary().PressButtonB(); delay(holdtime);
if(holdtime>0)SwitchControlLibrary().ReleaseButtonB(); delay(delaytime);
break;
case 'X': case 'x':
SwitchControlLibrary().PressButtonX(); delay(holdtime);
if(holdtime>0)SwitchControlLibrary().ReleaseButtonX(); delay(delaytime);
break;
case 'Y': case 'y':
SwitchControlLibrary().PressButtonY(); delay(holdtime);
if(holdtime>0)SwitchControlLibrary().ReleaseButtonY(); delay(delaytime);
break;
case 'L': case 'l':
SwitchControlLibrary().PressButtonL(); delay(holdtime);
if(holdtime>0)SwitchControlLibrary().ReleaseButtonL(); delay(delaytime);
break;
case 'R': case 'r':
SwitchControlLibrary().PressButtonR(); delay(holdtime);
if(holdtime>0)SwitchControlLibrary().ReleaseButtonR(); delay(delaytime);
break;
case 'H': case 'h':
SwitchControlLibrary().PressButtonHome(); delay(holdtime);
if(holdtime>0)SwitchControlLibrary().ReleaseButtonHome(); delay(delaytime);
break;
case '+': case 'p': case 'P':
SwitchControlLibrary().PressButtonPlus(); delay(holdtime);
if(holdtime>0)SwitchControlLibrary().ReleaseButtonPlus(); delay(delaytime);
break;
case '-': case 'm': case 'M':
SwitchControlLibrary().PressButtonMinus(); delay(holdtime);
if(holdtime>0)SwitchControlLibrary().ReleaseButtonMinus(); delay(delaytime);
break;
default:
break;
}
}else if(strlen(keyname)>=2){
switch(keyname[0]){
case 'z': case 'Z':
if(keyname[1]=='R'||keyname[1]=='r'){
SwitchControlLibrary().PressButtonZR(); delay(holdtime);
if(holdtime>0)SwitchControlLibrary().ReleaseButtonZR(); delay(delaytime);
}
if(keyname[1]=='L'||keyname[1]=='l'){
SwitchControlLibrary().PressButtonZL(); delay(holdtime);
if(holdtime>0)SwitchControlLibrary().ReleaseButtonZL(); delay(delaytime);
}
break;
case 'r': case 'R':
SwitchControlLibrary().MoveHat(2); delay(holdtime);
if(holdtime>0)SwitchControlLibrary().MoveHat(8); delay(delaytime);
break;
case 'l': case 'L':
SwitchControlLibrary().MoveHat(6); delay(holdtime);
if(holdtime>0)SwitchControlLibrary().MoveHat(8); delay(delaytime);
break;
case 'u': case 'U':
SwitchControlLibrary().MoveHat(0); delay(holdtime);
if(holdtime>0)SwitchControlLibrary().MoveHat(8); delay(delaytime);
break;
case 'd': case 'D':
SwitchControlLibrary().MoveHat(4); delay(holdtime);
if(holdtime>0)SwitchControlLibrary().MoveHat(8); delay(delaytime);
break;
case 'H': case 'h':
SwitchControlLibrary().PressButtonHome(); delay(holdtime);
if(holdtime>0)SwitchControlLibrary().ReleaseButtonHome(); delay(delaytime);
default:
break;
}
}else{
return -1;
}
return strlen(keyname);
}
void NextDayInCheatMode(){
PushKey("Home", HOLDTIME, 1000);
PushKey("down", HOLDTIME, 105);
PushKey("right", HOLDTIME, 105);
PushKey("right", HOLDTIME, 105);
PushKey("right", HOLDTIME, 105);
PushKey("right", HOLDTIME, 105);
PushKey("right", HOLDTIME, 105);
PushKey("A", HOLDTIME, 1500);
PushKey("down", 1500, 105);
PushKey("right", HOLDTIME, 105);
for(int i=0; i<4; i++){
PushKey("down", HOLDTIME, 55);
}
PushKey("A", HOLDTIME, 500);
PushKey("down", HOLDTIME, 105);
PushKey("down", HOLDTIME, 105);
PushKey("A", HOLDTIME, 500);
PushKey("right", HOLDTIME, 105);
PushKey("right", HOLDTIME, 105);
PushKey("up", HOLDTIME, 105);
PushKey("A", HOLDTIME, 105);
PushKey("A", HOLDTIME, 105);
PushKey("A", HOLDTIME, 105);
PushKey("A", HOLDTIME, 105);
PushKey("Home", HOLDTIME, 2000);
PushKey("A", HOLDTIME, 1000);
return;
}
void YYconnection_start(int which_pokemon){
static int shift_box = 0;
PushKey("Y", HOLDTIME, 1300);
PushKey("Down", HOLDTIME, 700);
PushKey("A", HOLDTIME, 2700);
int col=0, row=0;
col = (which_pokemon -1) % 6;
row = ((which_pokemon -1)%30) / 6;
if((which_pokemon -1) / 30 == 1+shift_box){
PushKey("R", HOLDTIME, 1600);
shift_box++;
}
for(int i = 0 ; i < col ; i++ ){
PushKey("Right", HOLDTIME, 170);
}
for(int i = 0 ; i < row ; i++ ){
PushKey("Down", HOLDTIME, 170);
}
PushKey("A", HOLDTIME, 700);
PushKey("A", HOLDTIME, 6000);
PushKey("A", HOLDTIME, 900);
PushKey("B", HOLDTIME, 900);
PushKey("B", HOLDTIME, 3000);
return;
}
void YYconnection_receive(void){
delay(100);
PushKey("Y", HOLDTIME, 1300);
PushKey("B", HOLDTIME, 1000);
PushKey("B", HOLDTIME, 1000);
delay(27*1000UL);
if(!IGNORE_EVOLVE){
delay(6*1000UL);
delay(7*1000UL);
PushKey("A", HOLDTIME, 13000);
PushKey("A", HOLDTIME, 3500);
PushKey("B", HOLDTIME, 1100);
PushKey("Down", HOLDTIME, 1100);
PushKey("A", HOLDTIME, 1400);
PushKey("B", HOLDTIME, 3000);
delay(4*1000UL);
}
PushKey("B", HOLDTIME, 300);
PushKey("B", HOLDTIME, 300);
PushKey("B", HOLDTIME, 300);
delay(2*1000UL);
if(POKEDEX_COMPLETED) return;
PushKey("B", HOLDTIME, 1000);
PushKey("B", HOLDTIME, 2000);
if(!IGNORE_EVOLVE){
PushKey("B", HOLDTIME, 3000);
PushKey("B", HOLDTIME, 1000);
PushKey("B", HOLDTIME, 2000);
}
PushKey("B", HOLDTIME, 300);
return;
}
あとがき
今回は16回目のArduino自動化記事として、ミラクル交換の自動化を紹介しました。
自分で実装してみて、思った以上にデバッグが大変だと思いました。長時間放置することが前提のプログラムゆえに、挙動を見守るのが大変で、コーディングをする時間よりも、本当に動くのか?を見守る時間のほうが10倍くらい長かった気がします。
私の本職は、プログラマでもデバッガーでもSEでも無いですが、本当にデバッグという作業は心がポキっと行くんだろうな、と漠然と感じました。
さて、話が逸れましたね。
今回のミラクル交換の実装により、今まで作ったプログラムが更に相互に利用しやすくなったな、と感じます。
具体的には、下記のようなサイクルでポケモンを遊び続けられるなぁと。
- 【Arduino自動化12】全自動タマゴ受け取り&孵化で色違い厳選など
- タマゴ孵化を経て、余ったポケモンたちを他人産ポケモンと交換(本稿)
- 集まったIDを使って【Arduino自動化14】ロトミIDくじ無限抽選
- 抽選後、不要なポケモンは【Arduino自動化13】全自動ポケモン逃がしでリリース
- オシャボや海外産ポケモンを活用し、タマゴ孵化厳選へ(1に戻る)
ほかにも、育成用アイテムは例えば【Arduino自動化11】5番道路で全自動羽集めなどで端数の努力値振りも対応できますし、「とくせいパッチ」などは【Arduino自動化06】完全放置「マックスこうせき」集め【ダイマックスアドベンチャー】で手に入れられるんですよね。
そういう意味で、Arduinoの自動化は、ポケモンの遊び方を深めることも、作業の代替もできるんだな、と改めて感じました。
皆様においても、このブログの記事ほかがお役立ていただければ幸いです。
ではではc⌒っ.ω.)っ
前記事:【Arduino自動化15】ラテラルタウン掘り出し物自動購入
次記事:【Arduino自動化17】ふしぎなおくりもの受け取り【あいことば自動入力】
導入記事:【Arduino自動化01】Arduino開発環境の導入