ますたーです。あけましておめでとうございます。2021年もよろしくお願いいたします。
今回は、ポケモンキャンプで「カレー」を自動で調理するArduino Leonardo自動化の記事です。
なお、Arduino Leonardo自動化の導入と、機材構成については導入記事をご覧ください。
導入記事:【Arduino自動化01】Arduino開発環境の導入
※本ブログに初めてお越しの方は「本ブログについて」もぜひ、ご覧ください。
概要
本記事では、ポケモン剣盾の「ポケモンキャンプ」の「カレー」づくりを自動で行います。「クラボのみ」を6個ずつ消費(食材不要)して、マホミル級のカレーを完全放置で作ってくれます。カレーのあかしを持ったポケモンの厳選が効率的にできます。
2021/6/9追記:ポケモン捕獲までの完全自動化に成功しました。合わせてご参考ください⇒【Arduino自動化09ex】「カレーのあかし」持ちポケモンの完全自動厳選【キャンプでカレー】
それでは、今回の目次です。
つくったきっかけ
Twitterのタイムラインを眺めていたところ「カレーのあかし」を持ったポケモンが手に入る場所についてまとめた投稿を見かけて、何故か猛烈に「『カレーずきなモルペコ』が欲しい!」と思ってしまったので作りました。
調理後、まれに「カレーのあかし」を持ったポケモンが見つかる
最初にカレー作りの目的について紹介します。「目的なんかわかりきっている!使い方とソースコードだけ寄越せ!」という方は読み飛ばしてください⇒読み飛ばす
まず、ワイルドエリア・ヨロイじま・カンムリせつげん以外の道路上(例えば9番道路など)で、「ポケモンキャンプ」でカレーを作ると、時々、その香りに釣られてポケモンがやってきます。話しかけると仲間になってくれます。
さらに、カレー調理後にやってきたポケモンは「カレーのあかし」リボンを必ず持っているという特徴があります。
いわゆる「証厳選」をしたことがある人はご存知かと思いますが、「○○のあかし」を持つポケモンは野生でのみ出現します。ところが、そのポケモンが証を持っているかどうかは捕まえないと分かりません。しかも、その所持率はおおよそ2%で、なんと証の種類もランダムです。狙った証をつけるのは至難の業と言えます。
一方で、カレーを作った後の野生のポケモンは必ず「カレーのあかし」を持っているため、狙った証を100%つけることができます。今回のお目当ては、「カレーのあかし」を持ったモルペコです(モルペコの特性は「はらぺこスイッチ」ですし、「カレーずき」の二つ名はまさにピッタリですよね)。
なお、野生ポケモンが来てくれる確率は、手持ちポケモンのなつき度と、作ったカレーのランクによって変わります。
有志による検証によれば、最もポケモンが出やすいカレーのランクは「マホミル級」で、その確率は1/7(約14%)だそうです。ちなみに、左記は手持ちがなつき度MAX6匹の状態でのお話です。なつき度はカレーを作ると上がるので、カレーずかん埋めも兼ねて、とりあえず無限にカレーを作り続けるのが良いでしょう(適当)。
「マホミル級」のカレーづくり
完全に余談です。直接は関係無いので、事前準備まで読み飛ばしもOKです⇒読み飛ばす。
前述の通り、カレー調理後に最もポケモンが出やすいのは「マホミル級」なのですが、作中で無限に買うことができる「きのみ(クラボのみ・モモンのみ・オレンのみ・カゴのみ)」で「マホミル級」を出すのは結構シビアです。
具体的には、6個以上のきのみを入れて、一切のミスなく上手に調理できれば「マホミル級」になります。火起こしと真心はともかく、かき混ぜがかなり難しいです。
一応、食材屋でごくまれに購入できる2200円食材「しっぽのくんせい」「ゆでタマゴ」「フサパック」「モーモーチーズ」や、マックスレイドバトルでまれに手に入る「キョダイパウダー」、ワイルドエリアで拾える「ながねぎ」を使うと、完全放置(無操作)でも「マホミル級」が確定しますが、入手が困難だったり、試行回数を考えるとコスパが悪かったりします。
また、150円食材や400円食材、950円食材では「マホミル級」を幾分作りやすくなりますが、こちらは完全放置というわけには行きません。
なので、私にて色々試したり考えたりした結果、食材は入れず、調理は超一流のシェフ(Arduino Leonardo)におまかせすることにしました。
手持ちときのみを揃える(事前準備)
「カレーのあかし」を持ったポケモンを効率的に呼ぶためには、事前の準備として、「最高になついたポケモン6匹」と「大量のきのみ」が必要です。
なつき度(なかよし度)を最大ランクまで上げるには、キャンプでの触れ合いが不可欠です。カレーを沢山作って一緒に過ごしてください。なお、性格に合ったカレーの味だとなかよし度が上がりやすいそうです。それぞれのポケモンの好みに合わせてカレーを作ってあげてください。
続いて、きのみの調達です。
前述の通り平均して7調理ごとに1回しかポケモンには会えない上に、1回毎に少なくとも6個単位できのみを消費するため、大量のきのみが必要になります。地道に木を揺らしていくのは手間暇かかるため、余りがちなお金で無制限にきのみを購入できるブラッシータウンの「きのみショップ」を利用します。DLCを購入している人はヨロイじまのきのみショップでもOKです(ヨロイじまだと4種類から買えます)。
1個80円で買えるので、「クラボのみ」を999個買ってください。8万円を切ります。
※買ったきのみは「リュックの一番上」においてください。クラボのみはきのみの種類順で1番になるのでリュックの一番上に来ているはずですが、もし一番上じゃなかったら適宜並び替えてください。
※「クラボのみ」でなくても大丈夫です。本プログラムは「モモンのみ」「オレンのみ」「カゴのみ」のいずれも動作確認できています。無論、「リュックの一番上」においてください。
欲しいポケモンが出てくる道路に行こう (準備)
カレー作り後に現れるポケモンは、シンボルエンカウントではなく、草むら・水上で「!」と共に出現するポケモンから選ばれます。また、前述の通りワイルドエリアやDLCの島々では出現しません。これらを勘案して、自分の欲しいポケモンを図鑑の分布などを見ながら探してください。
なお、本記事ではモルペコが欲しいのでルートナイントンネルの右側の9番道路(=スパイクタウンはずれ)に行きます。
プログラムの使い方
下記が必要な条件です。特に重要な準備については前述の通りです。その他は、普通にプレイしていれば満たしているはずですが、念の為合わせて確認してください。
- なかよし度MAX のポケモン6匹が手持ちにいる
- 充分な量の「きのみ(クラボのみ999個など)」がリュックの一番上にある
- 食材を1つ以上持っている(一切消費しませんが、食材欄が空っぽだとうまく動きません)
- 充分な量の「モンスターボール」「スーパーボール」「ハイパーボール」いずれかを持っている(これらのいずれかにしか入りません。原則としてモンスターボールが優先されます)
- ボックスが空いている
これらを満たしたら、メニューから「ポケモンキャンプ」を開いてください。
そして、ポケモンキャンプが開いたら、手動で一度Xボタンを押し、カーソルを「料理」に合わせてからBボタンでメニューを閉じてください(重要)。
この状態でArduino Leonardoを挿したら、あとは勝手に調理をしてくれます。調理後、10秒ほどかけて自動でキョロキョロしてくれるので、目当てのポケモンがいたらArduino Leonardoを抜いてください。
ポケモンキャンプでカーソルが「料理」に合う状態でメニューを閉じてからArduinoを挿すだけの簡単設計です。全自動で「調理⇒調理後キョロキョロ」をひたすら繰り返してくれます。目当てのポケモンが見つかったら引っこ抜いてください。
2021/6/9追記:ポケモン捕獲までの完全自動化に成功しました。合わせてご参考ください⇒【Arduino自動化09ex】「カレーのあかし」持ちポケモンの完全自動厳選【キャンプでカレー】
所有する「クラボのみ」の数(1~999)と、1回の調理で消費するきのみの量(1~10)をプログラム中でそれぞれ「CHERI_BERRY」「USING_BERRY」で指定することができます。
#define CHERI_BERRY (999)
#define USING_BERRY (6)
残りの量が消費予定量を上回った場合にプログラムは自動で止まるように設計していますので、手持ちのきのみの個数を勘案の上、必要に応じて書き換えて使ってください。
なお、「USING_BERRY」の数ですが、5個以下だと確定で「ソーナンス級」になります。「マホミル級」のカレーを作るには、最低でも6個は必要です。また、6個にしていても、20回くらいに1回くらいの割合で何故かソーナンス級になることがあります。理由は分かりませんが、なんとなく心配な人や、試行回数よりも精度を求めるという人は「10個」にしてください。一応、筆者としては「6」または「10」のいずれかを推奨します。
それではソースコードです。
#include <SwitchControlLibrary.h>
#define HOLDTIME (95)
#define KAKIMAZE_CYCLE (435)
#define RENDA_CYCLE (150)
#define CHERI_BERRY (999)
#define USING_BERRY (6)
#define AIJO_BASETIME 3200
#define AIJO_OFFSET 60
void PushRL(int delay_time_ms);
int PushKey(char* keyname, int holdtime, int delaytime);
void TiltLeftStick(int direction_deg, double power, int holdtime, int delaytime);
int CheriBerryNum = 0;
void setup() {
for(int i=0;i<7;i++)PushRL(300);
delay(1000);
CheriBerryNum = CHERI_BERRY;
}
void loop() {
int i=0;
unsigned long int current_time=0;
unsigned long int start_time=0;
unsigned long int temp_time=0;
float holdA = 0;
int isholding = 0;
float temp_deg = 0;
int isfirsttime =0;
int aijo_kome = AIJO_BASETIME+AIJO_OFFSET*USING_BERRY;
if(CheriBerryNum < USING_BERRY){
for(;;) delay(1000);
}
PushKey("X", HOLDTIME, 1000);
PushKey("A", HOLDTIME, 750);
PushKey("A", HOLDTIME, 2500);
PushKey("A", HOLDTIME, 350);
PushKey("+", HOLDTIME, 550);
PushKey("A", HOLDTIME, 350);
#if(USING_BERRY == 10)
PushKey("A", HOLDTIME, 550);
PushKey("Down", HOLDTIME, 550);
PushKey("A", HOLDTIME, 1500);
#else
PushKey("A", HOLDTIME, 550);
for(i=0;i<USING_BERRY-1;i++)PushKey("UP",HOLDTIME, 150);
PushKey("A", HOLDTIME, 180*USING_BERRY);
PushKey("+", HOLDTIME, 350);
#endif
for( start_time=millis(), current_time=start_time,isfirsttime=1 ; current_time - start_time < 5000 ; current_time=millis() ){
if(isfirsttime){
PushKey("A", 100, 300);
isfirsttime=0;
}
}
for( start_time=millis(), current_time=start_time, isholding=0 ; current_time - start_time < 21000 ; current_time=millis() ){
temp_time = (current_time - start_time) % (RENDA_CYCLE+1);
holdA = (float)temp_time / (float)RENDA_CYCLE * 100.0;
if( holdA <= 35.0 ){
if(!isholding){
SwitchControlLibrary().PressButtonA();
isholding = 1;
}
}else{
if(isholding){
SwitchControlLibrary().ReleaseButtonA();
isholding = 0;
}
}
}
if(isholding) SwitchControlLibrary().ReleaseButtonA();
for( start_time=millis(), current_time=start_time ; current_time - start_time < 18000 ; current_time=millis() ){
temp_time = (current_time - start_time) % (KAKIMAZE_CYCLE+1);
temp_deg = (float)temp_time / (float)KAKIMAZE_CYCLE * 360.0;
TiltLeftStick( (int)temp_deg, 1.0, 0, 0);
}
TiltLeftStick( 0, 0.0, 0, 0);
for( start_time=millis(), current_time=start_time,isfirsttime=1 ; current_time - start_time < 20000 ; current_time=millis() ){
temp_time = (current_time - start_time);
if( temp_time > aijo_kome && isfirsttime==1){
if(isfirsttime) PushKey("A", 100, 300);
isfirsttime=0;
}
}
PushKey("A", 100, 300);
delay(13000);
PushKey("A", 100, 2000);
TiltLeftStick( 90, 0.5, 2000, 1000);
TiltLeftStick( 270, 0.5, 4000, 1000);
TiltLeftStick( 90, 0.5, 2000, 1000);
TiltLeftStick( 0, 0.0, 0, 0);
delay(3000);
CheriBerryNum -= 10;
}
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 TiltLeftStick(int direction_deg, double power, int holdtime, int delaytime){
double rad = (double)direction_deg*PI/180.0;
int x, y;
x = (double)128*sin(rad)*power;
y = (double)-128*cos(rad)*power;
x += 128; y += 128;
if(x >= 255) x=255; if(x <= 0) x=0;
if(y >= 255) y=255; if(y <= 0) y=0;
SwitchControlLibrary().MoveLeftStick(x,y);
if(holdtime> 0){
delay(holdtime);
SwitchControlLibrary().MoveLeftStick(128,128);
}
if(delaytime>0) delay(delaytime);
return;
}
あとがき
改めまして、新年あけましておめでとうございます(執筆:2021年1月2日)。昨年の11月にこのブログを始めて、なんとか更新を続けることができています。
アクセス数も順調に伸びてきていて、時には「はてなスター」をいただいたり、読者が増えたり、コメントで「マイコン導入のきっかけになった」と報告をいただいたり、 など、まだまだ駆け出しの当ブログですが、皆様にモチベーションを支えられて更新が続けられているなぁ、と感じます。これからもどうぞ応援のほどよろしくお願いいたします。
さて、今回はポケモンキャンプの「カレーづくり自動化」にフォーカスを当てた記事でしたが、やはり自分の思ったとおりにプログラムが動くのは気持ちが良いものですね。今回のプログラムは試行錯誤しながら作ったので、達成感もひとしおでした。
特に、自分がスティック入力をしていないのにカレーが自動でかき混ぜられている様子を見るのが楽しかったです。
前々回の記事では、スティック入力に対応した雛形をつくりましたが、1方向に倒し続ける動作はもちろん、回転させるなどの一連の動きをコントロールできるのは、より人間の作業に近いものを扱えるようで嬉しく思いますね。
もちろん、マクロ対応コントローラーでは絶対真似できない芸当ですし、人間でもこんなに正確に操作はできないので、今回の例はまさにArduino Leonardoならではの自動化とも言えると思います(言い過ぎかな)。
話が逸れましたが、以降も精進して参りますので今後ともどうぞよろしくお願いいたします。
ではではc⌒っ.ω.)っ
2021/6/9追記:ポケモン捕獲までの完全自動化に成功しました。合わせてご参考ください⇒【Arduino自動化09ex】「カレーのあかし」持ちポケモンの完全自動厳選【キャンプでカレー】
前回記事:【Arduino自動化08】バトルタワー自動周回【BP稼ぎ】
次回記事:【Arduino自動化10】きのみ・ぼんぐり自動回収
導入記事:【Arduino自動化01】Arduino開発環境の導入
おまけ(パッチルドンのはなみず)
この記事の中のどこにも入れられなかったのですが、どうしても紹介したかったことが1つあったので追伸として。
パッチルドンといえばいつも凍えていて鼻水を垂らしていますよね。
でも、カレーを食べている時だけ、パッチルドンの震えも鼻水も止まっているのです。
上でさんざんGIF動画の中に映っていましたが、気づきましたか?
以上です。今年もよろしくお願いいたします。c⌒っ.ω.)っ