【Arduino自動化16】自動ミラクル交換(ID集めほか)

ますたーです。こんにちは。

今回は、自動でボックスのポケモンをマジカル交換(旧:ミラクル交換)に出し続けます。

※本記事ではポケモンを「交換に出します」。利用に際してはくれぐれも自己責任でお願いします

 Arduino自動化記事はこれで第16回。これからもよろしくお願いします。

 

なお、Arduino Leonardo自動化の導入・機材構成については導入記事を参考にしてください。

導入記事:【Arduino自動化01】Arduino開発環境の導入

※本ブログに初めてお越しの方は「本ブログについて」もぜひ、ご覧ください。

 

概要

本記事では、YY通信のマジカル交換(旧:ミラクル交換)を自動で繰り返し行います。「孵化余り」の有効活用や、ロトミIDくじ用のID集めなどにお役立てください。

※2021年2月19日AM9時追記:本稿中の「ミラクル交換」はすべて「マジカル交換」の誤りです。過去作品に引っ張られました。以降、適宜読み替えてください

f:id:tangential_star:20210213121954g:plain
f:id:tangential_star:20210213121616p:plain
マジカル交換(ミラクル交換)を自動で行う。待機時間など含め1回あたり約2分20秒ほど

では、目次です。

ソースコードのみ欲しいという方はどうぞコチラから⇒読み飛ばす

 

ラクル交換で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の数と当選確率の話も纏めているので、合わせてご参考いただければと思います。

f:id:tangential_star:20210204135040g:plain
f:id:tangential_star:20210203180947p:plain
たくさんのIDがあれば、ロトミIDくじ当選確率もアップ!

要するに、ミラクル交換で他人産IDのポケモンを50匹程度を集めておけば、ロトミIDくじが、まさにソシャゲの「ログインボーナス」のようになるのでおすすめです。

ほとんど毎日「ポイントアップ」が貰えて、時々「ポイントマックス」が貰えるとなれば、毎日のポケモンのモチベーションが大きく上がりますよね。

 

他人の孵化余りも手に入る

ラクル交換のもう一つの魅力、それは、孵化余りを流すプレイヤーも多いということ。私も含め、孵化余りは積極的にGTSポケモンHOMEでの交換、ミラクル交換に出していますが、ID集めはもちろん、これは何よりも他人の孵化余りを手に入れられるから。

現在、環境で流行っているポケモンの孵化余りが手に入ったり、有用なタマゴわざを持ったポケモンうたかたのアリア持ちラプラス、じばく持ちゴンベなど)などが手に入りやすく、対戦ガチ勢にも嬉しい仕様ですね。

f:id:tangential_star:20210218224023p:plain
f:id:tangential_star:20210218224028p:plain
有用なタマゴわざを覚えていたり、希少価値の高いオシャボに入っていたりも多い

つまりは、個体値ポケモン夢特性ポケモンタマゴわざを持っているポケモンおしゃれボールに入ったポケモンなどを手に入れることもできます。特に海外産ポケモンも手に入りやすく、これらを活用するとタマゴ孵化による色違い厳選もはかどります

ちなみに、全自動タマゴ孵化については本ブログでも取り扱っています。合わせてご参考いただければ幸いです⇒【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にしておきましょう。

特に、稀有な機会ではありますが、メタルコート持ちストライクの場合、進化時に「バレットパンチ」を覚えようとするため、そういうところもケアする必要があります。

f:id:tangential_star:20210218233952g:plain

バケッチャなど、通信交換で進化するポケモンを受け取る機会は意外と多い。注意しよう

 

実際に自動化ミラクル交換を試してみた

早速、85匹のパッチルドンを用意して、ミラクル交換のArduino自動化を実装&試してみました。

 

f:id:tangential_star:20210218234741g:plain
f:id:tangential_star:20210218234922g:plain
85匹のパッチルドンでミラクル交換!所要時間3時間20分の大半は待ち時間だ

設定は1試行あたり45秒の待機時間ポケモン図鑑は完成済、進化ポケモンは無視しない想定で、かかった時間は約3時間20分でした。1回の交換にかかる時間を平均すると約2分20秒となります。

なお、特筆すべき点として、待機時間を45秒に設定で、85回の通信交換中、3回はこの時間内に通信交換が成立せず、それぞれの翌試行分の交換が失敗しています。ゆえに、通信交換に成功したのは82匹です(いずれの試行も、通信交換に失敗した次の試行でループが復帰していました)。一応、これを避けるためには、待機時間を冗長に設定する他はありません。体感としては、60秒ほどあれば通信交換がほぼ成立すると考えます。

ちなみに、82匹の交換が成立したわけですが、そのうち1回のみ、通信交換するポケモンバケッチャ)を受け取っています。約80回に1回(確率にすれば約1.25%)ですし、もし「全体的に時間効率を高めたい」という人は、IGNORE_EVOLVEを(true)にしても良いかもしれません。

f:id:tangential_star:20210218234725g:plain

ラクル交換後のボックス。オシャボ入が7匹、準伝説3匹など上々の結果

 

ソースコード

さて、実際に使ったソースコードです。

使い方は簡単。ボックスを左詰めにした後、ポケモン剣盾をインターネット接続し、Arduinoを挿し込むだけ。

ただし、前述の通り、4箇所は各人のステータスに応じて書き換えてください。

/* 
 *  マジカル交換自動化
 *  
 *  ボックスは交換を始める先頭&左詰めにしておくこと。
 *  野生ポケモンが近くに発生しない環境で使うこと!
 *  (重くならない、ワイルドエリア「外」+オブジェクトが少ない場所を推奨)⇒シュートシティのタワー前とか?
 *  あとはYY通信がオンラインになっている状態でArduino差し込むだけ!
 *  (c) 2021 ますたーの忘備録
 *  https://tangential-star.hatenablog.jp/
*/

#include <SwitchControlLibrary.h>

#define HOLDTIME (95) // 1回のキー入力の長押し時間


// 実測値メモ 45秒指定で、85匹交換=合計3時間14分45秒[1試行平均137秒]
// [うち、交換失敗3回・改造と思しき個体3匹]

#define MAX_TRADE_POKEMON (30*3-5)
// ★何匹通信交換するか?【重要】
// ボックスの中で交換するポケモンの数を入れましょう。
// ※無論、ボックスの中身は左詰めでお願いします。

#define WAIT_TIME_FOR_MATCHING (45) 
// ★通信交換を何秒待つか?(単位:秒)
// 実際の待ち時間が上記指定時間をを超えた場合など、短すぎるとずれる可能性あり。
// 2順目でズレを吸収し、3順目から正式に進行するはずだが、この事象が発生したタイミング
// 如何によってはボックスの遷移が正しく行われない可能性あり。
// ※時間に余裕がある場合は、確実に交換できるよう「充分に長い時間(75など)」にしておくと安心。
// 特に深夜帯25:30を回ると45では心許なくなる。

#define POKEDEX_COMPLETED (true) 
// ★ポケモン図鑑は完成済か?【重要】
// 完成済の人は「true」にしてください
// 新規ポケモン受取時に図鑑登録ダイアログ出現を考慮するかどうかです。
// ※未完成の挙動は筆者未検証です!(そのへんの動画見て時間合わせしてるので動かないかも)

#define IGNORE_EVOLVE (false)
// ★通信進化ポケモンは来ない前提とするか?【重要】
// 進化後のポケモン例:フーディン・カイリキー・ゲンガー・ヤドキング・ニョロトノ・ハガネール・ハッサム・
// キングドラ・ポリゴン2・ミロカロス・ポリゴンZ・ドサイドン・エレキブル・ブーバーン・ヨノワール・
// ギガイアス・ローブシン・フレフワン・ペロリーム・オーロット・パンプジン・シュバルゴ・アギルダー
// ※コイツら↑を受け取らない前提なら「true」にしてください(非推奨)
// ※参考までに、筆者は本デバッグ中にバケッチャを受け取りました(パンプジンに進化しました)。わずか33試行目でした。


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() {
  // コントローラーとして認識されるためにRLを7回ほどカチャカチャする
  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){
  // ホームボタン・方向キーはRight, Left, Up, Down, Homeなど2文字以上で入力。
  // その他ボタン入力は1文字(A,B,X,Y,R,L,+,-)ZR・ZLにも対応
  // 同時押しは非対応
  
  if(strlen(keyname)==1){
    switch(keyname[0]){
      case 'A': case 'a': // A
        SwitchControlLibrary().PressButtonA(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonA(); delay(delaytime);
      break;
      case 'B': case 'b': // B
        SwitchControlLibrary().PressButtonB(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonB(); delay(delaytime);
      break;
      case 'X': case 'x': // X
        SwitchControlLibrary().PressButtonX(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonX(); delay(delaytime);
      break;
      case 'Y': case 'y': // Y
        SwitchControlLibrary().PressButtonY(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonY(); delay(delaytime);
      break;
      case 'L': case 'l': // L
        SwitchControlLibrary().PressButtonL(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonL(); delay(delaytime);
      break;
      case 'R': case 'r': // R
        SwitchControlLibrary().PressButtonR(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonR(); delay(delaytime);
      break;
      case 'H': case 'h': // Home
        SwitchControlLibrary().PressButtonHome(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonHome(); delay(delaytime);
      break;
      case '+': case 'p': case 'P': // Plus
        SwitchControlLibrary().PressButtonPlus(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonPlus(); delay(delaytime);
      break;
      case '-': case 'm': case 'M': // Minus
        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': // ZR/ZL
        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': // right
        SwitchControlLibrary().MoveHat(2); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().MoveHat(8); delay(delaytime);
      break;
      case 'l': case 'L': // left
        SwitchControlLibrary().MoveHat(6); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().MoveHat(8); delay(delaytime);
      break;
      case 'u': case 'U': // up
        SwitchControlLibrary().MoveHat(0); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().MoveHat(8); delay(delaytime);
      break;
      case 'd': case 'D': // down
        SwitchControlLibrary().MoveHat(4); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().MoveHat(8); delay(delaytime);
      break;
      case 'H': case 'h': // Home
        SwitchControlLibrary().PressButtonHome(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonHome(); delay(delaytime);
      default:
      break;  
    }
  }else{
    return -1;
  }
  return strlen(keyname);
}
void NextDayInCheatMode(){
  // ★日付変更
  // Homeボタンを押して設定の画面へ移動
  PushKey("Home", HOLDTIME, 1000);

  // Home画面で「設定」を選ぶ
  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); // 1.5秒「↓」長押し
  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){
  // (注)which_pokemonはボックスの何匹目を交換に出すか?
  // 1 <= which_pokemon <= 30 (0<29ではないので注意)

  static int shift_box = 0; // ボックスで「R(次のボックスに変える)」を押すタイミング見計らい
  
  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;

  // 31匹目以降は必要に応じてRキーで次のボックスを参照する
  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); // 予備

  // 通信交換(27秒待つ)
  delay(27*1000UL);

  // 通信進化ポケモン受取時のループ破綻防止   
  if(!IGNORE_EVOLVE){
    delay(6*1000UL); // 暗転画面で6秒
    delay(7*1000UL); // 鳴き声含め5秒
    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でも無いですが、本当にデバッグという作業は心がポキっと行くんだろうな、と漠然と感じました。

 

さて、話が逸れましたね。

今回のミラクル交換の実装により、今まで作ったプログラムが更に相互に利用しやすくなったな、と感じます。

具体的には、下記のようなサイクルでポケモンを遊び続けられるなぁと。

  1. 【Arduino自動化12】全自動タマゴ受け取り&孵化で色違い厳選など
  2. タマゴ孵化を経て、余ったポケモンたちを他人産ポケモンと交換(本稿)
  3. 集まったIDを使って【Arduino自動化14】ロトミIDくじ無限抽選
  4. 抽選後、不要なポケモン【Arduino自動化13】全自動ポケモン逃がしでリリース
  5. オシャボや海外産ポケモンを活用し、タマゴ孵化厳選へ(1に戻る)

ほかにも、育成用アイテムは例えば【Arduino自動化11】5番道路で全自動羽集めなどで端数の努力値振りも対応できますし、「とくせいパッチ」などは【Arduino自動化06】完全放置「マックスこうせき」集め【ダイマックスアドベンチャー】で手に入れられるんですよね。

そういう意味で、Arduinoの自動化は、ポケモンの遊び方を深めることも、作業の代替もできるんだな、と改めて感じました。

 

皆様においても、このブログの記事ほかがお役立ていただければ幸いです。

 

ではではc⌒っ.ω.)っ

 

前記事:【Arduino自動化15】ラテラルタウン掘り出し物自動購入

導入記事:【Arduino自動化01】Arduino開発環境の導入 

 

【Arduino自動化15】ラテラルタウン掘り出し物自動購入

ますたーです。こんにちは。

今回は、いわゆるランクマバグ状態で「掘り出し物」を買い続ける自動化記事です。

第15回目のArduino自動化記事。引き続きよろしくお願いします。

 

なお、Arduino Leonardo自動化の導入・機材構成については導入記事を参考にしてください。

導入記事:【Arduino自動化01】Arduino開発環境の導入

※本ブログに初めてお越しの方は「本ブログについて」もぜひ、ご覧ください。

 

概要

本記事では、ランクマバグ状態による自動「ほりだしもの」購入を実装します。
※便宜上「ランクマバグ」と表記しますが、当ブログではランクマッチを使用しません。通信切断・初手降参の推奨もしません。

 

NintendoSwitch Ver.11.0アップデート対応済です。

f:id:tangential_star:20210211120145g:plain
f:id:tangential_star:20210211120226p:plain
ほりだしもの市で自動購入。「ぼうごパット」はここでしか手に入らない超レアアイテム

 

では、目次です。

相変わらず余談多めですので、ソースコードだけ欲しいという方は⇒読み飛ばす

 

ほりだしもの市でしか手に入らない「ぼうごパット」

余談を挟む余地もなく、最初に結論から話します。なぜ掘り出し物市を自動化するのか、その理由は、ここでしか買えないアイテム「ぼうごパット」があるからです。

また、ここでの入手が現実的なアイテムも複数あるのも魅力ですね。

f:id:tangential_star:20210211125124p:plain
f:id:tangential_star:20210211125118p:plain
「ぼうごパット」の他、野生のワンリキーがたまに(5%)持っている「きあいのハチマキ」なども買える

ここからはすこしだけ、余談をはさみます。

「『ぼうごパット』についてはいい、他のアイテムは何が手に入るんだ」という方は⇒読み飛ばす

どうぐ「ぼうごパット」の希少性

ガラル地方では本編(DLC含まず)において入手が困難なアイテムがいくつかあります。その中の筆頭として挙がるのはおそらく、「かえんだま」と「どくどくだま」※1、「マスターボール※2」「各種ガンテツボール※3」などでしょうか。なぜかは言わずもがな、これらは原則として「運」でしか手に入らないからです。

※1「かえんだま」「どくどくだま」はDLC・配布を除くと、トーナメントでの勝利報酬でしか入手できません
※2「マスターボール」はシナリオ内で1個+IDくじの特等で入手できます
※3「各種ガンテツボール」はシナリオ内で各1個+トーナメントでの勝利報酬で入手できます

さて、前述の通り「ぼうごパット」は、ラテラルタウンの「ほりだしもの市」でしか入手できない貴重なアイテムです。

しかも、上記「かえんだま」「どくどくだま」「ガンテツボール」などとは違い、剣盾発売から1年以上経った2021年2月11日現在において1度たりとも配布されたりレイド報酬になったりしたことがない「正真正銘」ここでしか手に入らないアイテムです。また、DLCで拾えたり作れたりするようになった「かえんだま」等とも違い、DLCを含めても入手方法がここだけです。如何に「ぼうごパット」がレアかがお分かりいただけたかと思います。

f:id:tangential_star:20210211144958p:plain
f:id:tangential_star:20210211144818g:plain
鎧の孤島では、ウッウロボで「たいようのいし」4つを合成すると「かえんだま」になる

どうぐ「ぼうごパット」の効果

そんな幻の超激レアアイテム「ぼうごパット」は、多少ピンポイントながら効果も優秀。その驚きの効果は「直接攻撃に対して発動するわざ・とくせい・どうぐの効果を受けない」というもの。

メジャーどころでは「せいでんき」「ほうし」「さめはだ」「てつのトゲ」など、直接攻撃した際に発動する特性をほぼシャットアウトできます。また、追加効果が非常に厄介な守備技「ブロッキング(防御2段階ダウン)」「トーチカ(どく状態付与)」「ニードルガード(最大HPの1/8ダメージ)」などの追加効果を無効化できます。

さらに、どうぐゴツゴツメット」の効果も無効にできるので、例えばナットレイには2重の意味で強く出られますし、ゴツメサンダー@夢(せいでんき)にも強く出られるかもしれませんね。

f:id:tangential_star:20210211145003p:plain
f:id:tangential_star:20210211143034g:plain
わざ「ブロッキング」の厄介な防御2段階ダウン効果を「ぼうごパット」で無効化できる
f:id:tangential_star:20210211144850g:plain
f:id:tangential_star:20210211143000g:plain
「ぼうごパット」があれば、相手の特性「てつのトゲ」「さめはだ」などを無効化できる

ただ、ここまで持ち上げておいてなんですが、皆様もよくご存知の通り対戦ではほとんど使われません。つまりは読まれにくい、ということだと思うので誰か使ってみてください。

 

ほりだしもの市で入手できる道具

さて、大きく話が逸れたので、一旦ラテラルタウンの話題に戻します。

ほりだしもの市では、1日に1回、露天商のお兄さんから3,000円でその日のほりだしものを購入することができます。また、前日に購入していなかった場合は、前日のほりだしものも5,000円で購入することができます。

目玉商品は前述の「ぼうごパット」ですが、有用なアイテムや他の入手方法が困難or現実的ではないアイテムも売られることがあるため、そちらも狙い目です。

いちおう、筆者的に狙い目のものには、黄色で網掛けしています。入手方法について特にコメントしていないものは、道中で拾えたり貰えたりするものです。

ラテラルタウンほりだしもの市で買えるもの一覧
店頭に並ぶ頻度 なまえ 用途 入手方法または筆者コメント
非常に多い われたポット ヤバチャ(がんさくフォルム)の進化用 実はガラル地方のどこにも落ちていない。DLC2弾「巨人の寝床」でドロップ報告あり。
多い するどいツメ 急所率が1段階上がる。夜にはニューラの進化にも 進化に使うが、ニューラは持っていない。ちなみにピントレンズと同じ効果。
多い プロテクター サイドンの通信進化用 第4世代では1個しか手に入らない貴重アイテムだったのに…
多い れいかいのぬの サマヨールの通信進化用 DPではギラティナ捕獲後、最奥の部屋にあるアイテムだった。でもギラティナは関係ない
ときどき せんせいのツメ 20%で先制攻撃できる ニューラが5%で持っている。ここで手に入れよう。
ときどき メタルコート はがねタイプのわざの威力が1.2倍に。イワーク等の通信進化にも。 穴掘り兄弟が掘り当ててくれるので結構余りがち。進化アイテムなのに効果がある。
まれ かけたポット ヤバチャ(しんさくフォルム)の進化用 ポケセンで売ると19,000円に化ける。まさに文字通りの掘り出し物。
まれ きあいのハチマキ ひんし相当の被ダメージ時、10%の確率でHP1で耐える ワンリキーが5%で持っている。ここで手に入れよう。
まれ きせきのタネ くさタイプのわざの威力が1.2倍になる 特にコメントが思いつかない。
まれ くろいヘドロ どくタイプは毎ターン1/16回復。それ以外は1/8ダメージ ドヒドイデのイメージが強いが、野生で持っているのはグレッグル系列とダストダス
まれ しめつけバンド わざ「しめつける」の毎ターンダメージが1/8⇒1/6になる かつては幻の超激レアアイテムだったDLC第2段冠の雪原で開放。
まれ しんぴのしずく みずタイプのわざの威力が1.2倍になる 実はラプラスが100%持っている。2番道路でランダムに会えるぞ。
まれ どくバリ どくタイプのわざの威力が1.2倍になる ハリーセンロゼリア系列・スコルピ系列が5%で持っている。
まれ ねらいのまと 相性無効タイプの被ダメージが等倍で当たるようになる DLC第1段ウッウロボ以外では入手不可。こいつも幻の超激レアアイテムだった
まれ のろいのおふだ ゴーストタイプのわざの威力が1.2倍になる ヨマワル系列が5%で持っている。ここで手に入れよう。
まれ ぼうごパット 直接攻撃に対して発動するわざ・とくせい・どうぐの効果を受けない 幻の超激レアアイテム。文字通りここでしか手に入らない!ちなみに「パッド」ではない
まれ まがったスプーン エスパータイプのわざの威力が1.2倍になる ケーシィが5%で持っている。ここで手に入れよう。
まれ メトロノーム 同じわざを繰り返し使うと威力アップ(+20%ずつ/最大+100%) かつては幻の超激レアアイテムだったDLC第2段冠の雪原で開放。
まれ もくたん ほのおタイプのわざの威力が1.2倍になる 金銀では9,800円で買えた=売価4,900円だったが、今作では500円にしかならない。
まれ りゅうのキバ ドラゴンタイプのわざの威力が1.2倍になる DLC第1段ウッウロボ以外では入手不可。こいつも幻の超激レアアイテムだった
ごくまれ ぎんのこな むしタイプのわざの威力が1.2倍になる コメント特になし。4番道路で拾えるそうです。
ごくまれ するどいくちばし ひこうタイプのわざの威力が1.2倍になる コメント特になし。これも4番道路で拾えるそうです。
ごくまれ くろいメガネ あくタイプのわざの威力が1.2倍になる RSEでカナシダトンネルを抜けたところで拾ったときのイベントが思い出です。
ごくまれ くろおび かくとうタイプのわざの威力が1.2倍になる 金銀ではいかりのみずうみで水曜にミズオから貰えたが、いあいぎり必須なので不便。
ごくまれ じしゃく でんきタイプのわざの威力が1.2倍になる 金銀では日曜日のニチオから貰えた。個人的に初めて出会った曜日兄弟でした。
ごくまれ シルクのスカーフ ノーマルタイプのわざの威力が1.2倍になる RSEではマッスグマにもたせていました。まさか今作であくタイプになるとは…
ごくまれ せいれいプレート フェアリータイプのわざの威力が1.2倍になる 唯一残ったプレート。アルセウスはノーマルかフェアリーの二者択一を迫られることに。
ごくまれ とけないこおり こおりタイプのわざの威力が1.2倍になる バイバニラが50%の確率で持っている。バニプッチは5%だ。

※注:上記の「幻の超激レアアイテム」とは、このラテラルタウンでしか入手できないことを指します 

 

プログラムの使い方(ランクマバグの入り方)

前述の項目などを通して、ラテラルタウンの「ほりだしもの市」が如何に重要かがわかったと思います。特に、DLCを買っていない人には唯一の入手経路となるアイテムが5つもあるわけですから、無視するわけにもいきませんね。

さて、おまたせしました。肝心のプログラムの使い方です。

本稿でも、前回の【Arduino自動化14】ロトミIDくじ無限抽選と同じく、いわゆるランクマバグ状態でプログラムを動かします。やり方については、何も難しいことはなく、ランクマッチで1戦するだけなのですが、ランクマッチ以外の方法だと下記もあります。画像は過去記事の使いまわしです。あしからず。

  1. YY通信で「通信対戦」を行う(ローカル通信)
  2. もう一台のSwitch・剣盾を使ってその通信対戦に応じる
  3. 戦闘が始まったらHomeボタン長押し→「機内モード」をONに変更する
  4. エラーが発生するのでそれを閉じて、「にげる」選択
  5. フィールド画面に戻ってくる(ランクマバグ状態に移行完了)
f:id:tangential_star:20201122213022g:plain
f:id:tangential_star:20201122213037g:plain
YY通信でローカル対戦を募る。バトルが始まったらHomeボタン長押しで機内モード
f:id:tangential_star:20201122213148g:plain
f:id:tangential_star:20201122213205g:plain
エラーが出たら「にげる」で対戦終了。この状態で時刻変更すると一瞬画面が暗転する

 

ソースコード

使い方は、ラテラルタウンの露天商のお兄さんの前で、ランクマバグ状態でArduinoを挿し込むだけの簡単仕様です。1試行あたり25秒(実測値)でした。1回で3,000円取られるので、ちゃんと金額は準備しておきましょう。

なお、新規アイテム(初めて入手したアイテム)があっても、そのままループは破綻しませんので、そのまま放置でOKのはずです。

NintendoSwitch Ver.11.0アップデート対応済です。

/* 
 *  ラテラルタウン掘り出し物市 自動購入
 *  
 *  【ランクマバグ】状態で使うこと!
 *  掘り出し物市の前でArduino差し込むだけ!
 *  (c) 2021 ますたーの忘備録
 *  https://tangential-star.hatenablog.jp/
*/

#include <SwitchControlLibrary.h>

#define HOLDTIME (95) // 1回のキー入力の長押し時間

void PushRL(int delay_time_ms);
int PushKey(char* keyname, int holdtime, int delaytime);
void NextDayInCheatMode(void);

void setup() {
  // コントローラーとして認識されるためにRLを7回ほどカチャカチャする
  for(int i=0;i<7;i++)PushRL(300);
  delay(1000);
}


void loop() {

  // ★日付を回すやつ
  NextDayInCheatMode();

  PushKey("A", HOLDTIME, 700);  // いらっしゃい… ほりだしもの みていくかい? 
  PushKey("A", HOLDTIME, 1500); // ⇒本日の ほりだし (※シークタイムあり)
  PushKey("A", HOLDTIME, 700);  // 本日の ほりだしものは ○○ だな…… ▼
  PushKey("A", HOLDTIME, 700);  // ○○円だが 買っていくかい? ⇒はい
  PushKey("B", HOLDTIME, 2100); // まいど どうも ▼ (※SEチャリーン音あり)
  PushKey("B", HOLDTIME, 3000); // ○○は ○○を 手に入れた! ▼
  PushKey("B", HOLDTIME, 700);  // ○○は ○○を ○○ポケットに しまった ▼
  PushKey("B", HOLDTIME, 700);  // ほかに 用は あるかい? ▼
  PushKey("B", HOLDTIME, 700);  // 気が 変わったら また 来な…… ▼
  PushKey("B", HOLDTIME, 700);  // 
  
  PushKey("B", HOLDTIME, 300); // 予備
  PushKey("B", HOLDTIME, 300);

  //// 以下、新規購入のダイアログ出現時に効率よく回せるようになるので、「きあいのハチマキ」「ぼうごパッド」など新規アイテムが多い人はつけよう(↓3行の//を消そう)
  // PushKey("B", HOLDTIME, 500);
  // PushKey("B", HOLDTIME, 500);
  // PushKey("B", HOLDTIME, 500);

}

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){
  // ホームボタン・方向キーはRight, Left, Up, Down, Homeなど2文字以上で入力。
  // その他ボタン入力は1文字(A,B,X,Y,R,L,+,-)ZR・ZLにも対応
  // 同時押しは非対応
  
  if(strlen(keyname)==1){
    switch(keyname[0]){
      case 'A': case 'a': // A
        SwitchControlLibrary().PressButtonA(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonA(); delay(delaytime);
      break;
      case 'B': case 'b': // B
        SwitchControlLibrary().PressButtonB(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonB(); delay(delaytime);
      break;
      case 'X': case 'x': // X
        SwitchControlLibrary().PressButtonX(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonX(); delay(delaytime);
      break;
      case 'Y': case 'y': // Y
        SwitchControlLibrary().PressButtonY(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonY(); delay(delaytime);
      break;
      case 'L': case 'l': // L
        SwitchControlLibrary().PressButtonL(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonL(); delay(delaytime);
      break;
      case 'R': case 'r': // R
        SwitchControlLibrary().PressButtonR(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonR(); delay(delaytime);
      break;
      case 'H': case 'h': // Home
        SwitchControlLibrary().PressButtonHome(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonHome(); delay(delaytime);
      break;
      case '+': case 'p': case 'P': // Plus
        SwitchControlLibrary().PressButtonPlus(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonPlus(); delay(delaytime);
      break;
      case '-': case 'm': case 'M': // Minus
        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': // ZR/ZL
        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': // right
        SwitchControlLibrary().MoveHat(2); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().MoveHat(8); delay(delaytime);
      break;
      case 'l': case 'L': // left
        SwitchControlLibrary().MoveHat(6); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().MoveHat(8); delay(delaytime);
      break;
      case 'u': case 'U': // up
        SwitchControlLibrary().MoveHat(0); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().MoveHat(8); delay(delaytime);
      break;
      case 'd': case 'D': // down
        SwitchControlLibrary().MoveHat(4); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().MoveHat(8); delay(delaytime);
      break;
      case 'H': case 'h': // Home
        SwitchControlLibrary().PressButtonHome(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonHome(); delay(delaytime);
      default:
      break;  
    }
  }else{
    return -1;
  }
  return strlen(keyname);
}
void NextDayInCheatMode(){
  // ★日付変更
  // Homeボタンを押して設定の画面へ移動
  PushKey("Home", HOLDTIME, 1000);

  // Home画面で「設定」を選ぶ
  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); // 1.5秒「↓」長押し
  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;
}

一応、ループが破綻せずに2周目でズレを吸収する様子を↓下記GIF画像にて。

f:id:tangential_star:20210211170119g:plain
f:id:tangential_star:20210211125124p:plain
新規アイテム入手時のダイアログで動作がズレても、次の試行で吸収する設計

 

 

あとがき

今回で、Arduino自動化記事も15本目となります。

皆様に支えられ、本ブログも開設から3ヶ月が経過し、月間PV数も平均で2,537PV/月にまで成長しました(2021/2/8 0:00計算)。引き続きよろしくお願いいたします。

 

さて、DLCも第2弾まで出ているのに「なんで今更ラテラルタウンの記事?」と思った方も、ここまで読んでいただいて、「ここでしか入手できないアイテムなんてあったのか!」と驚いた方も多いのでは無いでしょうか。また、「『ぼうごパット』というアイテムなんて存在すら知らなかった!」という方もいらっしゃると思います。

 

f:id:tangential_star:20210211174915p:plain

落書き:パッチルドン@ぼうごパット

上述しましたが、超激レアアイテムの「ぼうごパット」は、メタとしては強力な一方、汎用性には乏しくかなりピンポイントな運用になると思います。が、それ故にきっと意表を突けるアイテム枠として採用できるポテンシャルは充分にあると思います。

本ブログ記事が、皆様にとって「ぼうごパット」を今一度認識する機会となったり、Arduino自動化の一助となったりすれば幸いです。

 

ではではc⌒っ.ω.)っ

 

前記事:【Arduino自動化14】ロトミIDくじ無限抽選

次の記事:【Arduino自動化16】自動ミラクル交換(ID集めほか)

導入記事:【Arduino自動化01】Arduino開発環境の導入 

 

【Arduino自動化14】ロトミIDくじ無限抽選

ますたーです。こんにちは。

今回は、いわゆるランクマバグ状態で「ロトミIDくじ」を引き続ける自動化記事です。

Arduino自動化記事もこれで第14回。これからもよろしくお願いします。

 

なお、Arduino Leonardo自動化の導入・機材構成については導入記事を参考にしてください。

導入記事:【Arduino自動化01】Arduino開発環境の導入

※本ブログに初めてお越しの方は「本ブログについて」もぜひ、ご覧ください。

 

概要

本記事では、ランクマバグ状態を利用した、ロトミIDくじ無限抽選を実装します。
※便宜上「ランクマバグ」と表記しますが、当ブログではランクマッチを使用しません。通信切断・初手降参の推奨もしません。

【!】ロトミIDくじは、その特性上、大量の「レポート」を伴います。必然的にmicroSDや本体メモリへの書き込みが増えますので、セーブデータ破損のリスクや動作不良のリスクなど、充分に危険性を理解した上でご参考ください。

 

NintendoSwitch Ver.11.0アップデート対応済です。

f:id:tangential_star:20210130135029g:plain
f:id:tangential_star:20210201212827p:plain
無限にIDくじを引き続けることができる。目指せ、特賞「マスターボール

では、目次です。確率計算とかもしました!(理系っぽい!)

ロトミIDくじの当選確率を知りたいという方はコチラ⇒読み飛ばす

ソースコードだけほしい方はコチラ⇒読み飛ばす

 

ポケモンセンターでロトミIDくじが引ける

まずは前提、というか余談です。

ポケモン剣盾では、ポケモンセンター各店舗のパソコンライクな機械「ロトミ」で1日1回「IDくじ」を引くことができます。

「IDくじ」をご存じない方にも説明をすると、手持ち・ボックスにいる全ポケモンの親IDと、ランダムで発生した5桁の乱数とを比較し、一致する桁数に応じて賞品が貰える、という毎作品お馴染みのおまけ要素です。

古くは第二世代(金銀)時代に始まり、ID完全一致の特等景品「マスターボール」は当時からの憧れの的でした(当時は、1日1回ではなく1週間に1回だったので引ける回数は大きく増えましたね)。完全に余談ですが、この「IDくじ」、何故か第五世代(BW)だけなかったんですよね。毎日のゲームモチベが減ったものです。

f:id:tangential_star:20210202103550g:plain
f:id:tangential_star:20210202085422p:plain
ポケモンRSEではミナモデパートでIDくじを引くことができた

前作までは、わざわざコガネシティ(ラジオとう)やミナモシティ(ミナモデパート)、コトブキシティ(テレビコトブキ)などの街に移動する、いわゆる「IDくじのための移動」が必要でした。

今作は、なんと、どこの街のポケモンセンターでもIDくじが引けますDLCでも、鎧の孤島なら拠点にロトミを連れてくることができるため、大変手軽にくじを回すことができるようになりました。また、今作のIDくじについても例外なく、特等(=ID下5桁一致)の賞品は「マスターボール」になります。

 

ロトミIDくじで特等「マスターボール」を狙う

ロトミIDくじは、ランダムに生成された5桁の数字を、手持ち・ボックスの全ポケモン(ポケジョブ除く)の親IDそれぞれの下5桁とを比較し、一致した桁数に応じて賞品がもらえます。

さて、肝心の特等「マスターボール」ですが、前述の通り親IDの下5桁が完全一致する必要があります。親IDが1つの場合(例えば一切他人と交換していない場合)の「マスターボール」当選確率を計算すると、「10の5乗分の1」になります。すなわち、わずか0.001%です

※一説によれば、今作は先頭5桁の一致も含むという噂?があるようです。それに則れば単純計算で当選確率は2倍になりますが、本ブログでは7回「特等」を当選していますが「上5桁一致」での当選はありませんでした。そのため、下5桁のみ当選する前提にて確率計算・表記しています

※ところが、後述の通り、実測値で計算した確率が「理論値の2倍」になります

f:id:tangential_star:20210203092440p:plain
f:id:tangential_star:20210203092444p:plain
f:id:tangential_star:20210203092449p:plain
「特等」当選時のメンバーの一例。いずれも下5ケタ一致だ(205575, 101082, 590328
f:id:tangential_star:20210203003331g:plain
f:id:tangential_star:20210203003339g:plain
f:id:tangential_star:20210203003348g:plain
筆者は7回「特等」を引いたが、いずれも「下5ケタ すべてが おんなじロ」と評される

 

ロトミIDくじの当選確率

上述の通り、「マスターボール」の当選確率はただでさえ低いですので、ミラクル交換やポケモンHOME(スマホ版)の交換機能などを活用し、できるだけ親IDを集めておくと良いでしょう。

ボックスいっぱいにすると30匹×32ボックス=960匹預けることができますので、これらのIDに一切の重複が無いと仮定すれば、マスターボールの当選確率が約1%まで跳ね上がります

一応、理論値としては、手持ち6匹+960匹すべて違うポケモンで0.9614%が上限です。

300匹で特等の確率は0.3%程度になるので、マスターボール狙いなら、少なくとも10ボックス程度は他人の親IDのポケモンで埋めておきたいです。どうしても難しい場合でも、ハズレの確率が1%を下回る50種類までは少なくとも集めておきたいです。

なお、IDの一致桁数に応じた賞品の当選確率は下記の通りです(筆者試算)。

 

所持ID数と各賞品の当選確率(小数点第3位を四捨五入)
ID数 【特等】5桁
マスターボール
【1等】4桁
ふしぎなあめ
【2等】3桁
ポイントマックス
【3等】2桁
ポイントアップ
【4等】1桁
モーモーミルク
はずれ
1 0.00% 0.01% 0.09% 0.90% 9.00% 90.00%
10 0.01% 0.08% 0.81% 7.75% 52.41% 38.94%
25 0.02% 0.20% 2.00% 18.00% 70.31% 9.46%
50 0.05% 0.40% 3.95% 31.96% 62.74% 0.90%
100 0.10% 0.80% 7.71% 50.90% 40.48% 0.01%
300 0.30% 2.36% 21.01% 69.69% 6.64% 0.00%
500 0.50% 3.90% 31.85% 62.66% 1.09% 0.00%
700 0.70% 5.41% 40.65% 53.07% 0.18% 0.00%
960 0.96% 7.32% 49.59% 42.11% 0.02% 0.00%
966 0.96% 7.37% 49.77% 41.89% 0.02% 0.00%

 

所持ID数と各賞品の当選確率のグラフ

狙う賞品によって最適なID数は違うが、はずれが出にくくなる50種は少なくとも欲しい

f:id:tangential_star:20210208165840p:plain

特等(マスターボール)・1等(ふしぎなあめ)は、ID数に応じて当たりやすくなる

言わずもがな、ポイントマックスはもちろんマスターボールやふしぎなあめの当選確率は、持っているID数が多ければ多いほど上がります。ユニークなのはポイントアップとモーモーミルクの当選確率で、途中で極大値を取ります。

具体的には、ポイントアップは283匹の時に69.764%、モーモーミルクは28匹の時に70.504%でそれぞれ極大値を取ります。現実的に960匹違うIDでボックスを埋めるのはツライと思いますので、このあたりを参考に、狙う道具を検討しつつ、事前に用意するポケモンの数を検討すべしですね。

 

マスターボール」の当選確率は2倍?

9時間46分放置でマスターボールを7個入手しました

筆者は過去作でGTS交換したものなども含めて12ボックス分(360匹)、ポケモンHOMEで連れてきて、自動化プログラムを使用しました。結果、プログラムを9時間46分稼働させ続け、推定試行回数888抽選で「マスターボール」7つを入手できました。

※プログラムソースコード後述します

f:id:tangential_star:20210204135040g:plain
f:id:tangential_star:20210130135029g:plain
他人産ポケモンで12ボックスを埋めて、いざ自動化!
f:id:tangential_star:20210204130023p:plain
f:id:tangential_star:20210204130028p:plain
9時間46分稼働させ、「マスターボール」7個を入手(左:実施前/右:実施後)

マスターボールの当選確率を計算(考察)

さて、確率計算です。完全に余談ですので、読み飛ばしOKです→読み飛ばす

888試行に対して7回の「特等」当選ですが、これは確率に表すと0.7883%になります。一方、12ボックス分(360匹)での、1試行あたりの入手確率は計算上、0.3593%になります。すなわち、実績値が理論値のおおよそ2倍になるという、かなり稀有な事象が起こっています。

もし、噂通り「上5桁も当選する仕様」なら理論値は上記2倍の0.7187%になるので理論値とかなり近しくなりますが、これはかなり不思議です。というのも、筆者が特等を引いた7回とも「下5桁」で当選しているからです

※7回続けて「下5桁」で当選するのは統計学的に有意水準1%で有意な偏り(符号検定よりp<0.01)と言えます。要するに偶然の範疇を超えています

なので、もしかしたら、ロトミは内部的に2回「下5桁完全一致」の抽選を行っているのかもしれませんし、上5桁で当選した時に、下5桁で言い直しているのかもしれません。

いずれにしても、実績値から考えるに当選確率は、単純な「下5桁完全一致」で計算した理論値の2倍と考えるのが妥当なので、ロトミIDくじの各賞品の当選確率は下表になるかもしれません。どちらを信じるかは読者の判断に委ねます。

 

所持ID数と各賞品の当選確率(特等の当選確率が2倍で仮定した時の試算)
ID数 【特等】5桁
マスターボール
【1等】4桁
ふしぎなあめ
【2等】3桁
ポイントマックス
【3等】2桁
ポイントアップ
【4等】1桁
モーモーミルク
はずれ
1 0.00% 0.01% 0.09% 0.90% 9.00% 90.00%
10 0.02% 0.06% 0.82% 7.75% 52.41% 38.94%
25 0.05% 0.15% 2.03% 18.00% 70.31% 9.46%
50 0.10% 0.30% 4.00% 31.96% 62.74% 0.90%
100 0.20% 0.60% 7.81% 50.90% 40.48% 0.01%
300 0.60% 1.77% 21.30% 69.69% 6.64% 0.00%
500 1.00% 2.93% 32.33% 62.66% 1.09% 0.00%
700 1.39% 4.06% 41.31% 53.07% 0.18% 0.00%
960 1.90% 5.49% 50.48% 42.11% 0.02% 0.00%
966 1.91% 5.52% 50.66% 41.89% 0.02% 0.00%

 

プログラムの使い方(ランクマバグの入り方)

おまたせしました。

肝心のプログラムの使い方ですが、ロトミの前でソースコードを書き込んだArduinoを挿し込むだけの簡単仕様です。ただし、いわゆる「ランクマバグ」状態で使う必要がありますので、ランクマッチで1戦してください。

なお、ランクマッチを使用しない方法での「ランクマバグ状態」移行は下記手順でできます。画像は過去記事の使いまわしです。あしからず。

  1. YY通信で「通信対戦」を行う(ローカル通信)
  2. もう一台のSwitch・剣盾を使ってその通信対戦に応じる
  3. 戦闘が始まったらHomeボタン長押し→「機内モード」をONに変更する
  4. エラーが発生するのでそれを閉じて、「にげる」選択
  5. フィールド画面に戻ってくる(ランクマバグ状態に移行完了)
f:id:tangential_star:20201122213022g:plain
f:id:tangential_star:20201122213037g:plain
YY通信でローカル対戦を募る。バトルが始まったらHomeボタン長押しで機内モード
f:id:tangential_star:20201122213148g:plain
f:id:tangential_star:20201122213205g:plain
エラーが出たら「にげる」で対戦終了。この状態で時刻変更すると一瞬画面が暗転する

 

ソースコード

今回も例のごとく、「ランクマバグ」状態で使います。使い方は、最寄りのポケモンセンターのロトミの前でArduinoを挿し込むだけの簡単仕様です。

30試行あたり19分47秒(実測値)だったので、ざっくり1時間放置で90回抽選できます。

NintendoSwitch Ver.11.0アップデート対応済です。

/* 
 *  ロトミIDくじ自動抽選c⌒っ.ω.)っ
 *  
 *  【ランクマバグ】状態で使うこと!
 *  ロトミの前でArduino差し込むだけ!
 *  (c) 2021 ますたーの忘備録
 *  https://tangential-star.hatenablog.jp/
*/

#include <SwitchControlLibrary.h>

#define HOLDTIME (95) // 1回のキー入力の長押し時間



void PushRL(int delay_time_ms);
int PushKey(char* keyname, int holdtime, int delaytime);
void NextDayInCheatMode(void);

void setup() {
  // コントローラーとして認識されるためにRLを7回ほどカチャカチャする
  for(int i=0;i<7;i++)PushRL(300);
  delay(1000);
 
}


void loop() {

  // ★日付を回すやつ
  NextDayInCheatMode();

  PushKey("A", HOLDTIME, 700); // こんにちロ~! なにを しますロミ? ▼
  PushKey("A", HOLDTIME, 700); // 
  PushKey("down", HOLDTIME, 350); // IDくじ
  PushKey("A", HOLDTIME, 700); // ただいま IDくじセンターの 抽選コーナーに つないだロミ!▼
  PushKey("A", HOLDTIME, 700); // 引いた くじの ナンバーと ○○さんの ポケモンのIDが▼
  PushKey("A", HOLDTIME, 700); // みごと あってると ステキな 賞品を もらえちゃうんだロ!▼
  PushKey("A", HOLDTIME, 700); // 運試しに レポートを 書いて 引いてみるのは どうロミ?
  PushKey("A", HOLDTIME, 2000); // はい⇒レポートを書き込んでいます⇒しっかり書き残した▼
  PushKey("B", HOLDTIME, 800); // かしこまりロ~! 抽選 スタート ロミ!▼
  PushKey("B", HOLDTIME, 1200); // …… …… ……▼
  PushKey("B", HOLDTIME, 700); // ハイ! でたロミ! くじの ナンバーは *****!▼
  PushKey("B", HOLDTIME, 700); // ○○さんの ポケモンのIDと どれだけ あってるか▼
  PushKey("B", HOLDTIME, 700); // 調べて 見るロ!▼
  PushKey("B", HOLDTIME, 3000); // おめでロ~~~!!!!▼
  PushKey("B", HOLDTIME, 700); // ボックスに 預けている ○○ちゃんの IDが みごと▼
  PushKey("B", HOLDTIME, 700); // くじの ナンバーと ぴったしロミ!▼
  PushKey("B", HOLDTIME, 700); // ロミ! ○ケタが おんなじロ!▼
  PushKey("B", HOLDTIME, 700); // そんな スペシャルな 奇跡には 2等の 賞品▼
  PushKey("B", HOLDTIME, 700); // ポイントマックスを プレゼントだロ!!▼
  
  PushKey("B", HOLDTIME, 3000); // ○○は ポイントマックスを 手に入れた!▼
  PushKey("B", HOLDTIME, 700); // ○○は ポイントマックスを どうぐポケットに しまった▼
  PushKey("B", HOLDTIME, 900); // それじゃあ またの 挑戦を お待ちしてるロ~~!▼
  PushKey("B", HOLDTIME, 700); // ▼
  
  PushKey("B", HOLDTIME, 300); // 予備
  PushKey("B", HOLDTIME, 300);
  PushKey("B", HOLDTIME, 300);
  

}

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){
  // ホームボタン・方向キーはRight, Left, Up, Down, Homeなど2文字以上で入力。
  // その他ボタン入力は1文字(A,B,X,Y,R,L,+,-)ZR・ZLにも対応
  // 同時押しは非対応
  
  if(strlen(keyname)==1){
    switch(keyname[0]){
      case 'A': case 'a': // A
        SwitchControlLibrary().PressButtonA(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonA(); delay(delaytime);
      break;
      case 'B': case 'b': // B
        SwitchControlLibrary().PressButtonB(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonB(); delay(delaytime);
      break;
      case 'X': case 'x': // X
        SwitchControlLibrary().PressButtonX(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonX(); delay(delaytime);
      break;
      case 'Y': case 'y': // Y
        SwitchControlLibrary().PressButtonY(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonY(); delay(delaytime);
      break;
      case 'L': case 'l': // L
        SwitchControlLibrary().PressButtonL(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonL(); delay(delaytime);
      break;
      case 'R': case 'r': // R
        SwitchControlLibrary().PressButtonR(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonR(); delay(delaytime);
      break;
      case 'H': case 'h': // Home
        SwitchControlLibrary().PressButtonHome(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonHome(); delay(delaytime);
      break;
      case '+': case 'p': case 'P': // Plus
        SwitchControlLibrary().PressButtonPlus(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonPlus(); delay(delaytime);
      break;
      case '-': case 'm': case 'M': // Minus
        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': // ZR/ZL
        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': // right
        SwitchControlLibrary().MoveHat(2); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().MoveHat(8); delay(delaytime);
      break;
      case 'l': case 'L': // left
        SwitchControlLibrary().MoveHat(6); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().MoveHat(8); delay(delaytime);
      break;
      case 'u': case 'U': // up
        SwitchControlLibrary().MoveHat(0); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().MoveHat(8); delay(delaytime);
      break;
      case 'd': case 'D': // down
        SwitchControlLibrary().MoveHat(4); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().MoveHat(8); delay(delaytime);
      break;
      case 'H': case 'h': // Home
        SwitchControlLibrary().PressButtonHome(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonHome(); delay(delaytime);
      default:
      break;  
    }
  }else{
    return -1;
  }
  return strlen(keyname);
}
void NextDayInCheatMode(){
  // ★日付変更
  // Homeボタンを押して設定の画面へ移動
  PushKey("Home", HOLDTIME, 1000);

  // Home画面で「設定」を選ぶ
  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); // 1.5秒「↓」長押し
  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;
}

 

あとがき

今回は、「ロトミIDくじ」の自動化について紹介しました。

ロトミってRotom Information Serviceの意味だったんですね。この記事を書くまで知りませんでした。

 

さて、本稿でも結構考察しましたが、様々なWebサイトを調べてもマスターボールの当選について「5桁一致」とは書いてあるものの「下5桁」のみならず「上5桁」も含むのかが分からず、自分で検証してしまいました。

結論、ロトミは「下5桁」でしか当選を教えてくれないが、確率は単純計算時の2倍で設定されている、という仮説ができましたね。そういう意味では、ある意味「自動化記事」でありながら「疑問だし記事」にもなってしまいました(笑)。

誰か、数学に強い人か、ロトミに詳しいポケモン博士にぜひともコメントしていただきたいものです(もしかしたらそもそも私の試算が間違っている、なんて本末転倒かもしれませんし)。

 

話が逸れましたが、これで「マスターボール」の自動入手もできるようになりました。マスターボールは「孵化厳選」では絶対に引き継げない、とても希少なボールですし、ボールエフェクトもカッコイイんですよね。そういう意味でも、すごく需要があるボールなのに…。もう少し手軽に入手できれば良いのにな、と思います。そういった意味でも、需要のある自動化だったのではないでしょうか。

 

本ブログが皆様の自動化ライフをより豊かなものにできれば幸いです。

ではではc⌒っ.ω.)っ

 

前記事:【Arduino自動化13】全自動ポケモン逃がし

導入記事:【Arduino自動化01】Arduino開発環境の導入 

次の記事:【Arduino自動化15】ラテラルタウン掘り出し物自動購入

 

2021/2/19追記:IDを効率よく集められる「ミラクル交換」の自動化記事を執筆しました。

参考:【Arduino自動化16】自動ミラクル交換(ID集めほか)

【Arduino自動化 番外編3】筆者の使用機材一覧【紹介記事】

こんにちは。ますたーです。

今回は、番外編その3として、Arduinoでの自動化で私が「実際」に使っている機器類を紹介します。 

 

なお、機器全般・ソフトウェアの構成・セットアップは【Arduino自動化01】Arduino開発環境の導入をご覧ください。

※本ブログに初めてお越しの方は「本ブログについて」もぜひ、ご覧ください。

 

概要

今回の記事では、実際に筆者が使っている機材を紹介します。参考になるよう、私が実際に購入した際の、購入日・購入金額・Amazonのリンクも載せます。

金額は私が購入した当時のものなので、あくまでも参考金額として参照ください(最新の金額はAmazonのリンク先で記載のものでご確認をお願いします)
※有線コントローラーのみAmazonではなく実店舗で購入しています。同型番のものを紹介します。

 

目次です。

私の使っている機材の一覧は最後に表で纏めています。

「説明はいらん」というかたは読み飛ばしてください⇒読み飛ばす

 

自動化の基本機材構成

まず、導入記事の一部繰り返しになりますが、Nintendo Switchの操作を自動化するにあたり、基本的な機材構成としては下記のものを準備する必要があります。

さらに、給電しながら自動化をする場合には、追加で

  • Switchドック(Switchの場合)または USBスタンド(Switch Liteの場合)
  • 充電アダプタ

が必要です。筆者としては、長時間放置できるからこそ自動化の意義があると考えるので、上記の下2つも合わせて使うことをおすすめします。

f:id:tangential_star:20201107181011p:plain
f:id:tangential_star:20201107181445p:plain
機材構成イメージ(左:おすすめ/右:最低限の構成)

 

必須ではないが、あると便利な機材

導入記事では紹介しておりませんでしたが、Nintendo Switchの操作を自動化するにあたって、前述の機材構成に加えて、あると便利だったものは下記の通りです。

なお、これらはいずれも筆者が実際に使っているものです

ただ、これらはあれば重宝しますが、「必須ではない」ので、無理に揃える必要はありません。まずは上記基本構成で自動化ができるようになってから必要に応じて購入すればOKです。

  • Arduino Leonardoのケース(カバー)
  • Switchドック用USBハブスタンド
  • USBスイッチ付きコネクタ
  • 有線コントローラー

以上に述べたこれらの機材について、次項からは1つずつ紹介します。 

 

 

実際に使用している機材の紹介

【前提】Arduino Leonardo(マイコン

まずは、ポケモン自動化の要となるマイコンArduino Leonardo」の紹介です。

自動化方法の調査記事でも紹介している通り、Arduinoには「Arduino UNO」系列と「Arduino Leonardo」系列の2種類があります。

見た目は似ていますが、これらの間でポケモン自動化の方法は大きく異なるので、UNOとLeonardoを間違えないように購入しましょう。

本ブログでは、導入記事でも紹介している通り、後者(Leonardo)を利用しています。

このArduino Leonardoには様々な互換機や類似品が売っています。もし、しっかりとした理解が無い状態で選ぶのであれば、筆者としては互換機ではなく純正の「Leonardo」を購入することを強くおすすめします。

ちなみに購入して1年経ちますが、全く壊れる気配がありません。

(筆者購入日:2020/1/28;購入時の金額:3,190円)

 

【前提】通信対応USBケーブル(USB-Aオス・USB-microBオス)

これは、すでにお持ちの方も多いと思います。

具体的には、少し前のAndroidスマホのPC通信ケーブルがまさにUSB-A・USB-microBですので、これが使えるかもしれません。

なお、SwitchまたはSwitch Liteと直接つなぐ(給電しない)場合は、USB-microB・USB-Cの通信対応USBケーブルを購入するか、別途USB-Aメス・USB-Cオスの変換口を購入してください。

念の為、USB-microB側をArduino Leonardoに挿し込み、USB-A側をSwitchのドックまたはUSBスタンドに挿し込みます。このケーブルはPCからArduinoにプログラムを書き込む際にも使うので、Arduino側(USB-microB側)は基本的に挿しっぱなしの運用になると思います。ケーブルが長すぎると邪魔になるので、自身のちょうどよい長さのものを買い揃えましょう。

ちなみに、よくあるミスのひとつとして「充電専用ケーブル」は使えないので、「通信対応」などと書かれているものを購入しましょう

(筆者購入日:2020/2/5;購入時の金額:598円)

 

【あると便利】Arduino用ケース(エンクロージャー)

Arduinoはその性質上、基盤がむき出しになることが多いです。そのため、不意に素手でベタベタ触っているときに誤って汚損・破損することもあります。しかし、Arduino Leonardoにはその専用のケースがありません

そこで、Arduino Leonardoに「UNO用」エンクロージャーをあてがうと、なんとなく良い感じに仕上がります(紹介していてアレですが、この使い方は公式に推奨されるものではありません!)。

本来は、Arduinoの基盤に空いた穴と付属のネジの位置が合致するためエンクロージャー内部で基盤が固定されるのですが、UNOとLeonardoでは基盤の配置や穴の位置が若干異なるため、これができません。

なので、私は、側面のアクリルを使わずに基盤を直接挟んでネジ固定する荒業で使っています。買って約1年経ちますが、壊れる気配が無いので安心しています。

(筆者購入日:2020/2/5;購入時の金額:340円)

f:id:tangential_star:20210128100318j:plain
f:id:tangential_star:20210128100139j:plain

筆者の使用しているマイコンArduino Leonardo」の外観
「UNO用」エンクロージャーでLeonardoを挟む荒業だが約1年間、問題なく使えている

 

【Liteの場合は必須】USBハブスタンド

Switch Liteを利用している方のうち、充電しながら自動化する場合には必須です(もちろん、すでに代替ドックなどのUSBポートと給電が同時にできるものを持っている方はそちらでもOKですし、そもそも給電しないのであれば不要です)。

純正ドックが使えるSwitch(Liteではない)では原則不要です。ただし、長時間使うことが前提になるので、「ドックに挿すと本体に熱がこもるのでは?」「ディスプレイが画面焼けするのでは?」などが気になる人は使ってみても良いかもしれません(テーブルモードになります)。私は、Switch Liteで自動化をするために買っています。ポータブルUSBハブスタンドの良いところは、なんと言ってもSwitch・Switch Liteいずれも安定してテーブルモードで利用できること。

筆者はこれの2ポート版も持っていますが、やっぱり横に広くなる4ポート版のほうが卓上で安定するのでなんとなく安心しています。

f:id:tangential_star:20210128004650j:plain
f:id:tangential_star:20210128004639j:plain
Nintendoライセンス商品の、ホリの「ポータブルUSBハブスタンド(4ポート)」。
Arduinoと充電アダプタを同時に挿せるので、給電しながら自動化ができる

なお、重要な欠点として、純正でありながら「Switch保護ケース」をつけたSwitchやSwitch Liteとは充電部分が干渉してしまい、使えないという点があります。

もし、普段は携帯モードで遊ぶ機会が多く、テーブルモードで遊ばないという人は注意が必要です。

(筆者購入日:2020/11/6;購入時の金額:3,550円)

 

【あると便利】Switchドック用USBハブスタンド

前述の通り、Switch(Liteではない)を利用する場合、純正のドックをUSBポートとして利用できます。

しかし、ドックの置き方にもよりますが、USBポートはドックの側面に付いているため、Switch本体を挿すとその裏側になり、Arduinoや有線コントローラーを挿しにくいという欠点があります。

また、別途冷却用ファンを使っている人などは、そもそもUSBポートが不足している、ということもあるかもしれません。

そこで、側面のUSBポートを1つ使い、Switchドックの「正面」にUSBポートを4つ拡張できるドック用のUSBハブスタンドを使うと便利です。特に、Arduinoを抜き差しして3日後の巣穴厳選をひたすら繰り返す【Arduino自動化05】乱数調整レイドの自動化では、横にあるよりも正面にある方が便利でした。

(筆者購入日:2020/11/14;購入時の金額:2,600円)

f:id:tangential_star:20201123010702g:plain

USBポートが正面にあるため抜き差しが楽々できるように

 

【あると便利】USBスイッチ付きコネクタ

スイッチひとつでON・OFFが切り替えられるコネクタです。

Arduino自動化で一番「買ってよかった」と感じている「個人的MVP機材」です。前述のドック用USBハブスタンドで、正面にUSBポートができたので、Arduinoの抜き差しも幾分手軽にできるようになったのですが、いかんせん、その抜き差しがめんどくさくなったのです(省力志向)。

ArduinoでSwitch操作を自動化する場合、他のコントローラーを予め抜いておく(無線コントローラーの場合は切断しておく)必要があります。

すなわち、「何かしらの自動化をするたびにコントローラーを抜いてArduinoを挿す」「自動化が終わったらArduinoを抜いてコントローラーを挿す」作業が必要になります。この抜き差しが大変に面倒くさいのです。

そこで、筆者はこれをなんと4個も買い(アホです)、上述のSwitchドック用USBハブスタンドにすべて挿し込み、ON・OFF切り替え対応機能をドックにつけました。

f:id:tangential_star:20210128150338j:plain
f:id:tangential_star:20210128150349j:plain
Switchドック用USBハブスタンドに、スイッチ付きUSBコネクタを4本挿し

結論、Arduinoもコントローラーも両方ともSwitchに挿しっぱなしで、スイッチでON・OFFが切り替えられるので、「今はコントローラーで操作したい!」というタイミングだけ有線コントローラー側をONにする、みたいなことができるように。

ちょっと操作が終わったらON・OFFを逆にするだけ、という極限の省力化に成功しました。しかも、Arduinoを普段ずっと挿しっぱなしにしても誤動作しなくなったので、Arduinoの収納場所を考えなくて済むようになりました(ただのズボラ)。

(筆者購入日:2020/12/5;購入時の金額:485円)

f:id:tangential_star:20210128161917j:plain

有線コントローラーとArduinoの2本刺しが可能。抜き差しせずに入力を切り替えられる

 

【余談】Switch本体のJoy-conカバーと有線コントローラー

最後に余談がてら、筆者の使っているNintendoSwitch周辺機器の紹介です。

もちろん、Switch本体の種類(Switch・Switch Lite)は自動化部分(ソフトウェア部分)とは本来、直接の関係はありません。前述の通り、私はSwitchとSwitch Liteの2台持ちですが、どちらも同じプログラムで動作しています。

f:id:tangential_star:20210127233334j:plain
f:id:tangential_star:20210127233348j:plain
筆者が愛用するSwitch本体。
ピカブイカラーのJoy-conと、Amazonで買ったシリコンカバーがお気に入りだ

筆者はピカブイセットでNintendo Switchを購入したので、Joy-conがピカブイカラーにちなんだ茶色と黄色になっています。

そこで、普段からさらにテンションを上げるために、別途購入したシリコンカバーをつけています。デザインがとてつもなくかわいいので気に入っています。

ただ、スティック部のシリコンカバーがずれやすかったり、単純に切り抜いただけのシリコンなのでボタンが埋もれて押しにくくなったりと、実用には多少ネックとなる部分もあります。

普段から別途コントローラーを使ったり、Arduinoで操作を自動化したりする場合にはこのデメリットは気にならないので、普段Switch用ドックに挿しっぱなし、という人は買ってみても良いかも?(私は欲しい物は多少値が張っても買っちゃうタイプなのでいらない人はやめましょう)

(筆者購入日:2020/12/16;購入時の金額:1,399円)

 

また、有線コントローラーも、もちろん、ピカブイの物にそろえて購入しています。淡い色合いにピカチュウイーブイが単色で入っているシンプルなデザインですが、とにかくかわいいです。スティックやボタンが茶色いのもアクセントになっていてすごく好みです。

やっぱり、普段遣いのものこそ、自分の好きなものを使ってテンションを上げていきたいですもんね。

 (筆者購入日:2020/7/13;購入時の金額:2,728円)

 

 

まとめ(購入リンク集)

本稿では、実際に私が使っている機材について紹介しました。

一覧で表示したものも準備しました。下記表を御覧ください。

 

筆者の使用機材一覧

必須 商品名 画像 Amazonリンク

備考(筆者コメント)

必須 Arduino Leonardo

https://amzn.to/3pvglNJ

筆者の使用マイコン。これがないと始まらない。

必須 通信対応USBケーブル(A-microB)

https://amzn.to/3aaSJrk

探せば家に5本くらいありそう。
  Arduino用ケース

https://amzn.to/36l50IC

本当はUNO用だが、Leonardoでも騙し騙し使っている。
  USBハブスタンド

https://amzn.to/2MfLFBD

Switch Liteでは必要。
  Switchドック用USBハブスタンド

https://amzn.to/39oyxTy

純正ドックのUSBポートをより使いやすくできる。おすすめ。
  USBスイッチ付きコネクタ

https://amzn.to/2NFc0tf

USBポートにON・OFFスイッチが付く。マジでおすすめ。
  Joy-conカバー

https://amzn.to/3ckmbO3

自動化には全く不要。だが、かわいい。

  有線コントローラー

https://amzn.to/2YnN6R6

あると便利だが、これでなくても良い。かわいい。

 

あとがき

思えば、今まで記事で使用機材を紹介する機会がなかったので、自動化記事も13件と増えてきましたので、良い機会かと思ってまとめてみました。

実は、私がArduino Leonardoを購入したのが2020年1月28日です。今日が2021年1月28日ですので、Arduino Leonardoを購入してから今日でちょうど1年なんですよね

1年前の今頃は、こうやってブログを作ろうと決意するなんて考えてもなかったですし、こんなにコロナが流行るなんて夢にも思ってなかったですから、世の中ってわからないなぁ、と感じます。

 

話が逸れましたが、少なくともArduino Leonardoが1年間壊れずに動いてくれたことに感謝しつつ、これからも使い倒していきます(笑)

 

皆様も、上記機材の一覧が役に立てば何よりですし、自分に合う機材構成で自動化ライフを楽しんでいただければ幸いです。

ではではc⌒っ.ω.)っ

 

導入記事:【Arduino自動化01】Arduino開発環境の導入

番外編1: 【Arduino自動化 番外編】マイコン導入にあたって気をつけるべきと思ったこと【ポケモン自動化 方法調査】

番外編2: 【Arduino自動化 番外編2】NintendoSwitchControllライブラリの導入と、自動化できる作業の一覧【ポケモン剣盾自動化】

 

【Arduino自動化13】全自動ポケモン逃がし

ますたーです。こんにちは。

今回は、ボックスのポケモンを全自動で逃がし続ける自動化記事です。前回の全自動タマゴ受け取り&孵化とは対称的ですね。

※本記事ではポケモンを「逃がします」。利用に際してはくれぐれも自己責任でお願いします

 

Arduino自動化記事も本稿で第13回。これからもよろしくお願いします。

なお、Arduino Leonardo自動化の導入・機材構成については導入記事を参考にしてください。

導入記事:【Arduino自動化01】Arduino開発環境の導入

※本ブログに初めてお越しの方は「本ブログについて」もぜひ、ご覧ください。

 

概要

本記事では、ボックスのポケモンを自動で逃がすプログラムを紹介します。孵化厳選などで溢れたボックスを整理できます。

f:id:tangential_star:20210121234428g:plain
f:id:tangential_star:20210121233732p:plain
完全放置でボックスのポケモンを逃がせる。1ボックスあたり約2分半だ

 

目次です。プログラムまで読み飛ばす方は⇒コチラからどうぞ

 

 

「いちらん」を使って歯抜け状態のポケモンボックスを左詰めに

効率的に逃がすためにも、まずは余談でボックス整理方法から。

タマゴ孵化で色厳選をすると、どうしてもボックスが孵化したポケモンで溢れてしまいます。そんなとき、1匹1匹を手動で逃がすのはとても大変です。

そんな時こそ自動化プログラムの出番…と言いたいところですが、前提として、自動化プログラムは「ボックスが歯抜け状態だと使えない」という欠点があります。

孵化したポケモンの中で「高個体値」「理想個体」「色違い」のポケモンを残そうと別ボックスに移動させてしまうと、どうしてもボックスの中が歯抜け状態になります。つまり、いわゆる「自動ポケモン逃がし」プログラムが使えなくなってしまいます。そこで、手動でポケモンを整列させる必要がありますが、1匹ずつずらすのも大変です。

f:id:tangential_star:20210122001659p:plain
f:id:tangential_star:20210122001952g:plain
左写真のように「歯抜け」があると自動化できない。右写真のように整列させる必要がある

実は、ボックスの機能「いちらん」を使うことで、効率的にポケモンたちを左詰めにできます。具体的には、他のボックスから「はんい」モード(緑のカーソル状態)で「いちらん」を経由して移動させると、空いている箇所に左詰めに自動で挿入してくれます。もし、今まで1匹ずつずらしていた人には、目からウロコのテクニックです。

f:id:tangential_star:20210121235612g:plain

ボックスの「いちらん」機能を使えば、1匹ずつずらさなくてもポケモンを左詰めにできる

 

逃がすポケモンの数をプログラムに書き込み

自動化プログラムの使い方に話を移します。

まずは、逃がすポケモンの数をプログラムに書き込みます。具体的には、「NUM_RELEASE_POKEMON」で指定しますので、ここに数字を入力してください。

例えば、8ボックス埋まっている場合は「30*8」または「240」と入力すればOKです。もちろん、30の倍数以外にも対応できます。

// 何匹、逃がしますか?
#define NUM_RELEASE_POKEMON (30*8+21) 

f:id:tangential_star:20210122215552g:plain

この画像なら、ボックス10~17までの8ボックスと21匹なので「30*8+21」と書けばOK

なお、前述の通り、ボックスのポケモンは「歯抜け」が無い状態でかつ、左詰めに整列させておいてください。

これが終わったら、B連打でボックスを閉じ、メニューを閉じ、フィールド画面に戻ってください(メニュー画面でカーソル位置が「ポケモン」なら準備完了です)。 

f:id:tangential_star:20210122221406p:plain
f:id:tangential_star:20210122221839g:plain
Xボタンでポケモンにカーソルが合った状態なら、自動でボックスを開いて逃がしてくれる

 

ソースコード

ポケモンをボックスに左詰めで整列させておき、Xでポケモンにカーソルが合う状態で、Arduinoを挿すだけの簡単設計です。

/* 
 *  ボックスのポケモン全自動逃がし
 *  【ボックスは30匹左詰めで埋まっていること】
 *  Xボタンでメニュー開いた時に「ポケモン」にカーソルが合っていること
 *  
 *  (c) 2021 ますたーの忘備録
 *  https://tangential-star.hatenablog.jp/
*/
#include <SwitchControlLibrary.h>

#define HOLDTIME (95) // 1回のキー入力の長押し時間

// 何匹、逃がしますか?
#define NUM_RELEASE_POKEMON (30*8+21) 

void PushRL(int delay_time_ms);
int PushKey(char* keyname, int holdtime, int delaytime);

// 現在、何匹逃したか
int current_poke = 0;

void setup() {
  // コントローラーとして認識されるためにRLを7回ほどカチャカチャする
  for(int i=0;i<7;i++)PushRL(300);
  delay(1000);

  current_poke = 0;

  // Xでメニュー画面を開く
  PushKey("X", HOLDTIME, 700);  // メニューを開き
  // ポケモンを開く
  PushKey("A", HOLDTIME, 1500); // ポケモンを開く
  PushKey("R", HOLDTIME, 1800); // ボックスへ
  
}


void loop() {

  if(current_poke >= NUM_RELEASE_POKEMON) for(;;); // 逃がす上限になったらプログラム終了

  PushKey("A", HOLDTIME, 350);  // ○○をどうする?
  PushKey("up", HOLDTIME, 250); // 
  PushKey("up", HOLDTIME, 250); // ⇒にがす
  PushKey("A", HOLDTIME, 800);  // 本当に逃がしますか?
  PushKey("up", HOLDTIME, 350); // ⇒はい
  PushKey("A", HOLDTIME, 1100);  // ○○を 逃がしてあげた ばいばい ○○
  PushKey("A", HOLDTIME, 300);  // ▼

  current_poke++; // 逃がしたポケモンの数をカウント


  if( current_poke %6 == 0 ){
    for(int i=0;i<5;i++)PushKey("left", HOLDTIME, 250); // 左にカーソルを戻し
    PushKey("down", HOLDTIME, 350); // 次のポケモンへ
  }else{
    PushKey("right", HOLDTIME, 350); // 次のポケモンへ
  }
  if( current_poke %30 == 0){
    PushKey("down", HOLDTIME, 350); // カーソルを左上に
    PushKey("down", HOLDTIME, 350); // もどして、
    PushKey("R", HOLDTIME, 800); // 30匹逃がし終わったら次のボックスへ
  }

}

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){
  // ホームボタン・方向キーはRight, Left, Up, Down, Homeなど2文字以上で入力。
  // その他ボタン入力は1文字(A,B,X,Y,R,L,+,-)ZR・ZLにも対応
  // 同時押しは非対応
  
  if(strlen(keyname)==1){
    switch(keyname[0]){
      case 'A': case 'a': // A
        SwitchControlLibrary().PressButtonA(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonA(); delay(delaytime);
      break;
      case 'B': case 'b': // B
        SwitchControlLibrary().PressButtonB(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonB(); delay(delaytime);
      break;
      case 'X': case 'x': // X
        SwitchControlLibrary().PressButtonX(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonX(); delay(delaytime);
      break;
      case 'Y': case 'y': // Y
        SwitchControlLibrary().PressButtonY(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonY(); delay(delaytime);
      break;
      case 'L': case 'l': // L
        SwitchControlLibrary().PressButtonL(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonL(); delay(delaytime);
      break;
      case 'R': case 'r': // R
        SwitchControlLibrary().PressButtonR(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonR(); delay(delaytime);
      break;
      case 'H': case 'h': // Home
        SwitchControlLibrary().PressButtonHome(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonHome(); delay(delaytime);
      break;
      case '+': case 'p': case 'P': // Plus
        SwitchControlLibrary().PressButtonPlus(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonPlus(); delay(delaytime);
      break;
      case '-': case 'm': case 'M': // Minus
        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': // ZR/ZL
        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': // right
        SwitchControlLibrary().MoveHat(2); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().MoveHat(8); delay(delaytime);
      break;
      case 'l': case 'L': // left
        SwitchControlLibrary().MoveHat(6); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().MoveHat(8); delay(delaytime);
      break;
      case 'u': case 'U': // up
        SwitchControlLibrary().MoveHat(0); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().MoveHat(8); delay(delaytime);
      break;
      case 'd': case 'D': // down
        SwitchControlLibrary().MoveHat(4); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().MoveHat(8); delay(delaytime);
      break;
      case 'H': case 'h': // Home
        SwitchControlLibrary().PressButtonHome(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonHome(); delay(delaytime);
      default:
      break;  
    }
  }else{
    return -1;
  }
  return strlen(keyname);
}

 

あとがき

前回(第12回)全自動タマゴ受け取り&孵化の記事ではタマゴを受け取っては孵すを繰り返していましたが、今回の記事では打って変わって、ひたすらポケモンを逃がす記事でした。

私は孵化厳選した際に余ったポケモンたちを逃がすのが本当にめんどくさく、いつもポケモンバンクポケモンHomeに預けるのを繰り返していました。ところが、これらにも容量が上限あることに最近気づいてしまい、止む無く「逃がす」ことの重要性に気づいたのです。また、前回実装したタマゴ自動孵化プログラムのおかげで、気分が乗らなくてもタマゴ孵化が捗ってしまい、気づいたらボックスが埋まってしまうということも多くなったため、現在は重宝しています。

 

こうやって自分の手で「作って」「使って」みると、「世の中にすでにある自動化」はニーズがあるんだなぁ、と真の意味で納得できるということに気づかされます。本ブログが、皆様の需要にも応えられるように、世の中のものもしっかりと調べて行こうかなと思った次第です。

個人的には、第9回のポケモンキャンプでカレー自動調理は非常に満足の行く出来だったのですが、本当にニーズがないのか本記事にはなかなかアクセスがありません(ちなみに、「ポケモン カレー 自動化」で調べると、まだまだ駆け出しの本ブログでは珍しく、検索上位にランクインします。よかったら見てね←)

 

話が逸れましたが、本記事ならびに本ブログが皆様のポケモン自動化ライフの一助となれば幸いです。引き続き応援のほど、よろしくお願いいたします。

 

ではではc⌒っ.ω.)っ

 

導入記事:【Arduino自動化01】Arduino開発環境の導入

前回記事:【Arduino自動化12】全自動タマゴ受け取り&孵化

次の記事:【Arduino自動化14】ロトミIDくじ無限抽選

 

【Arduino自動化12】全自動タマゴ受け取り&孵化

ますたーです。こんにちは。

今回は、預かり屋からタマゴを自動で受け取り、孵化までを自動で行う記事です。ついに全自動記事も第12回。引き続きよろしくお願いします。

 

なお、Arduino Leonardo自動化の導入・機材構成については導入記事を参考にしてください。

導入記事:【Arduino自動化01】Arduino開発環境の導入

※本ブログに初めてお越しの方は「本ブログについて」もぜひ、ご覧ください。

 

概要

本記事では、ワイルドエリアの「預かり屋」でタマゴの自動受け取り&自動孵化を紹介します。完全放置で孵化厳選や色厳選作業をできるようにしてみました。

f:id:tangential_star:20210116214618g:plain
f:id:tangential_star:20210116214601p:plain
完全自動でタマゴの受け取りと孵化をひたすら行ってくれる

では、目次です。

ソースコードだけほしい方は読み飛ばしてください⇒読み飛ばす

 

本ブログの紹介環境で自動孵化を作りたかった

始めに、本ブログで「自動孵化」を取り扱おうと思ったきっかけについてです。

今まで、本ブログもまだまだ駆け出しということもあり、世間で公開されているArduino自動化には「無いもの」「新しいもの」をとにかく作ってきました(個人的には、第9回の全自動カレーづくりは大変な力作でした)。そして、本ブログも公開してから3ヶ月が過ぎ、自動化に関わる記事数も10を超え、月間1000アクセスも超えるようになりました。

本ブログでポケモン剣盾のArduino自動化にチャレンジを始めた人もいらっしゃるようなので、せっかくなら「同じ導入環境」で「使えるもの」を増やしたほうが親切かと思い、本ブログでも比較的メジャーな「孵化作業の自動化」に踏み切った次第です。

すなわち、本ブログの「導入記事」での環境で動いていることが(少なくとも筆者は)確認済のプログラムになります。そういった意味では需要があるのかと思います。

 

ほのおのからだ」を持つポケモンで孵化時間は半減

まず、孵化厳選で重要なことですが、必ず「ほのおのからだ」「じょうききかん」「マグマのよろい」のいずれかの特性を持つポケモンをパーティーに加えて孵化作業をしましょう。これらの特性をもつポケモンは、手持ちにいるだけで孵化に必要な歩数が半分になります。10匹以上孵化するなら、今から捕まえに行ってもお釣りが来るほど時間効率が変わります。持っていない人は今すぐ捕まえてください。

f:id:tangential_star:20210117010932g:plain
f:id:tangential_star:20210117011001g:plain

同じタマゴ・条件で「ほのおのからだ」を手持ちに加えた場合、孵化時間が半減する
※かかった時間はそれぞれ53秒、1分35秒だった(注:計測値/タマゴ:ニャビー

f:id:tangential_star:20210117011309p:plain
f:id:tangential_star:20210117011314p:plain
上記検証に使った手持ちポケモン。「ほのおのからだ」は高速孵化に効果てきめんだ

孵化の歩数を半減できる特性3つのうちいずれかを持っており、かつ、剣盾内(DLCなし)で捕まえやすいポケモンと言えば、タンドン系(タンドン・トロッゴン・セキタンザン)でしょうか。一応、他にもエンジンシティのジムミッションで出会える「ヒトモシ」もいますが、「もらいび」と確率半々なので、DLC無しで確実に捕まえるにはタンドン系が最安定と思います。なお、同特性を持つ剣盾内で出現するポケモンは、ポケモン徹底攻略で検索できますので合わせて参考にしてください。

ところで、剣盾にはDLC含めて「マグマのよろい」を持つポケモンマグマッグおよびマグカルゴ)は登場しませんでしたね。悲しい…。マグカルゴは私の好きなポケモンの1匹でした。当時、対戦はもちろん、ウルガモスファイアローが登場してからも孵化のお供に使っており、思い入れ深いポケモンでした。次作では帰ってきてほしいです。

 

受け取り作業と孵化作業の自動化

自動化に限らず、孵化作業において特筆して準備すべきものは、上記の通り「ほのおのからだ」持ちポケモンだけです。今回の自動化はタマゴを1匹ずつ「受け取り」「孵化」を繰り返すので、1回ごとの試行回数を増やすべく、手持ちポケモンは「ほのおのからだ」持ち1匹のみにしておいてください。

 

一応、効率的な孵化作業には「自転車」「連続した空きボックス」は必須かと思いますので、本ブログで紹介するプログラムもそれに準じます。なお、本ブログで扱う自動孵化は「ワイルドエリア」の預かり屋を活用します。理由は自転車でくるくると一周できるからです。

f:id:tangential_star:20210117014822p:plain
f:id:tangential_star:20210117014830p:plain
作業としての準備は「自転車」を準備し、「空きボックス」を連続させるだけだ

 

プログラムの使い方と修正箇所

本ブログのプログラムを使うにあたっては、下記冒頭箇所を必要に応じて修正してください。プログラム全文は後述します

// ↓タマゴ孵化にかかる時間[秒](実測でお願いします)
#define HATCH_TIME (57) // 何秒でタマゴが割れるかを計測して入れてください
#define ADV_OFFSET (3) // 育て屋前から何秒間はずれたところで自転車くるくるするか

// 何試行するか?
#define MAX_HATCH_POKEMON (30*9) 

// Xでメニュー開いたときのボタン配置
#define TOWNMAP_ROW (1) // 「タウンマップ」の行
#define TOWNMAP_COL (0) // 「タウンマップ」の列
#define POKEMON_ROW (0) // 「ポケモン」の行
#define POKEMON_COL (1) // 「ポケモン」の列

■1匹の孵化にかかる時間を入力する

計測は難しいですが、ポケモンの孵化歩数などを参考に入力しましょう。数字は「HATCH_TIME」に指定してください。現在デフォルトで入っている57はニャビー(孵化歩数:3840歩?に対して「ほのおのからだ」適用の数字)ですが、かなりざっくりアバウトな数字なので気にせず入力ください。その下の「ADV_OFFSET」は変更しなくて大丈夫です。

// ↓タマゴ孵化にかかる時間[秒](実測でお願いします)
#define HATCH_TIME (57) // 何秒でタマゴが割れるかを計測して入れてください
#define ADV_OFFSET (3) // 育て屋前から何秒間はずれたところで自転車くるくるするか

■空きボックスの数を指定

まず、前述の通り、空きボックス(30匹空白のボックス)を連続して配置してください。その個数を「MAX_HATCH_POKEMON」に適用します。具体的には、例えば、9ボックス連続で空っぽなら、30×9=「270」で指定すればOKです(あるいは計算式で「30*9」でもOK)。

// 何試行するか? 
#define MAX_HATCH_POKEMON (30*9)  

■Xボタンメニュー画面の位置を入力

Xで開くメニュー画面の「タウンマップ」「ポケモン」のボタンの位置情報を必要に応じて修正(プログラム全文は後述)してください。行(英語でrow)は、上の段が0、下の行が1という決まりで入れています。列(英語でcolumn)は、左端から順番に0, 1, 2, 3, 4になります。

具体的には、タウンマップの行列はそれぞれ「TOWNMAP_ROW」「TOWNMAP_COL」、ポケモンの行列は「POKEMON_ROW」「POKEMON_COL」で指定しているので、これらを修正ください。

 

// Xでメニュー開いたときのボタン配置 
#define TOWNMAP_ROW (1) // 「タウンマップ」の行
#define TOWNMAP_COL (0) // 「タウンマップ」の列
#define POKEMON_ROW (0) // 「ポケモン」の行
#define POKEMON_COL (1) // 「ポケモン」の列

よくわからない人は、プログラムではなくゲームのメニュー画面を、下記画像の順番で並び替えればOKです(この順番は、剣盾のシナリオを進めた際のそのままです)。

書き換え作業が終わったら、「タウンマップ」にカーソルをあわせてください。

f:id:tangential_star:20210117020954p:plain

「タウンマップ」「ポケモン」の位置(行列)をプログラムの指定箇所に書き込もう

 

ソースコード

使う前に、前述の通りXボタンで開くメニュー画面はプログラム上で編集しておき、Xボタンで開いたメニューのカーソルは「タウンマップ」に合う状態にしておいてください。

ワイルドエリアの「預かり屋」にポケモンを預け、手持ちは1匹・自転車に乗った状態で、かつ、「タマゴができている状態」でArduinoを挿すだけで、ひたすら「タマゴを受け取っては孵す」作業を繰り返してくれます。

/* 
 *  タマゴ自動受取@ワイルドエリア
 *  【手持ち1匹、ボックスは空き指定のこと】タウンマップにカーソルをあわせて
 *  自転車に乗り、タマゴが出来上がっている状態でワイルドエリア預かり屋手前で
 *  Arduinoを挿すだけで準備OK。
 *  
 *  (c) 2021 ますたーの忘備録
 *  https://tangential-star.hatenablog.jp/
*/

#include <SwitchControlLibrary.h>

#define HOLDTIME (95) // 1回のキー入力の長押し時間
#define ROW 0
#define COL 1


// ↓タマゴ孵化にかかる時間[秒](実測でお願いします)
#define HATCH_TIME (57) // 何秒でタマゴが割れるかを計測して入れてください
#define ADV_OFFSET (3) // 育て屋前から何秒間はずれたところで自転車くるくるするか

// 何試行するか?
#define MAX_HATCH_POKEMON (30*9) 

// Xでメニュー開いたときのボタン配置
#define TOWNMAP_ROW (1) // 「タウンマップ」の行
#define TOWNMAP_COL (0) // 「タウンマップ」の列
#define POKEMON_ROW (0) // 「ポケモン」の行
#define POKEMON_COL (1) // 「ポケモン」の列


// ★下表を参考に、↑の数字を書き換えてください★
// (Xでメニューを開いたときのボタンの位置のイメージ)
//  +     +     +     +     +  ←0段目(ROW=0)
//  +     +     +     +     +  ←1段目(ROW=1)
// 左橋から何個ずれるか(COLを0~4に)
// COL=0 COL=1 COL=2 COL=3 COL=4
//
// 例えば、メニューで「タウンマップ」が「下の段の1番目」にあるなら
// TOWNMAP_ROWを「1」に、TOWNMAP_COLを「0」に書き換えてください

void getEgg(void);
void hatchEgg(void);
int *SetCursor(int *cursor_position, int row, int col);
void SoratobuTaxi(int cursor_position[]);
void SendBox(int* cursor_position, int *hatched_num);

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);
void TiltRightStick(int direction_deg, double power, int holdtime, int delaytime);
void TiltStick(int leftdeg, double leftpower, int rightdeg, double rightpower, int holdtime, int delaytime);
void NextDayInCheatMode(void);



int current_cursor[2]; // Xボタンを開いたときのカーソルの位置
int hatched_num;       // タマゴからかえしたポケモンの数


void setup() {
  // コントローラーとして認識されるためにRLを7回ほどカチャカチャする
  for(int i=0;i<7;i++)PushRL(300);
  delay(1000);

  // Xボタンカーソルの位置を登録
  //(初期位置は、開いた時にタウンマップに合っている想定)
  current_cursor[ROW] = TOWNMAP_ROW;
  current_cursor[COL] = TOWNMAP_COL;
  // ★上記は環境に応じて書き換えてください★

  hatched_num = 0; // 何匹預けているか
  // もし、ボックスの続きからやる場合は、上記調整してください(非推奨)
}


void loop() {

  // 1匹タマゴ受け取り
  getEgg();
  // 1匹タマゴ孵化
  hatchEgg();
  // 孵化したポケモンの数を追加する
  hatched_num++;

  //上記5回繰り返すたびにボックス送り
  if(hatched_num>0&&hatched_num%5==0){ 
    SendBox(current_cursor, &hatched_num);
  }

  // 上限数に達したら処理終了
  if(hatched_num > MAX_HATCH_POKEMON)for(;;);
  delay(1000);

}

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){
  // ホームボタン・方向キーはRight, Left, Up, Down, Homeなど2文字以上で入力。
  // その他ボタン入力は1文字(A,B,X,Y,R,L,+,-)ZR・ZLにも対応
  // 同時押しは非対応
  
  if(strlen(keyname)==1){
    switch(keyname[0]){
      case 'A': case 'a': // A
        SwitchControlLibrary().PressButtonA(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonA(); delay(delaytime);
      break;
      case 'B': case 'b': // B
        SwitchControlLibrary().PressButtonB(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonB(); delay(delaytime);
      break;
      case 'X': case 'x': // X
        SwitchControlLibrary().PressButtonX(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonX(); delay(delaytime);
      break;
      case 'Y': case 'y': // Y
        SwitchControlLibrary().PressButtonY(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonY(); delay(delaytime);
      break;
      case 'L': case 'l': // L
        SwitchControlLibrary().PressButtonL(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonL(); delay(delaytime);
      break;
      case 'R': case 'r': // R
        SwitchControlLibrary().PressButtonR(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonR(); delay(delaytime);
      break;
      case 'H': case 'h': // Home
        SwitchControlLibrary().PressButtonHome(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonHome(); delay(delaytime);
      break;
      case '+': case 'p': case 'P': // Plus
        SwitchControlLibrary().PressButtonPlus(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonPlus(); delay(delaytime);
      break;
      case '-': case 'm': case 'M': // Minus
        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': // ZR/ZL
        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': // right
        SwitchControlLibrary().MoveHat(2); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().MoveHat(8); delay(delaytime);
      break;
      case 'l': case 'L': // left
        SwitchControlLibrary().MoveHat(6); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().MoveHat(8); delay(delaytime);
      break;
      case 'u': case 'U': // up
        SwitchControlLibrary().MoveHat(0); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().MoveHat(8); delay(delaytime);
      break;
      case 'd': case 'D': // down
        SwitchControlLibrary().MoveHat(4); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().MoveHat(8); delay(delaytime);
      break;
      case 'H': case 'h': // Home
        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){ // holdtime=0のときは押しっぱなし。
    delay(holdtime);
    SwitchControlLibrary().MoveLeftStick(128,128); // 傾きを直す
  }
  if(delaytime>0) delay(delaytime);
  return;
}
void TiltRightStick(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().MoveRightStick(x,y);
  if(holdtime> 0){ // holdtime=0のときは押しっぱなし。
    delay(holdtime);
    SwitchControlLibrary().MoveRightStick(128,128); // 傾きを直す
  }
  if(delaytime>0) delay(delaytime);
  return;
}

void TiltStick(int leftdeg, double leftpower, int rightdeg, double rightpower, int holdtime, int delaytime){
  double rad;
  int x, y, rx, ry, lx, ly;

  rad = (double)leftdeg*PI/180.0; // 弧度法(ラジアン)変換
  x = (double)128*sin(rad)*leftpower;
  y = (double)-128*cos(rad)*leftpower;
  x += 128; y += 128;
  if(x >= 255) x=255; if(x <= 0) x=0;
  if(y >= 255) y=255; if(y <= 0) y=0;
  lx=x;ly=y;
  rad = (double)rightdeg*PI/180.0; // 弧度法(ラジアン)変換
  x = (double)128*sin(rad)*rightpower;
  y = (double)-128*cos(rad)*rightpower;
  x += 128; y += 128;
  if(x >= 255) x=255; if(x <= 0) x=0;
  if(y >= 255) y=255; if(y <= 0) y=0;
  rx=x;ry=y;

  SwitchControlLibrary().MoveLeftStick(lx,ly);
  SwitchControlLibrary().MoveRightStick(rx,ry);
  if(holdtime> 0){ // holdtime=0のときは押しっぱなし。
    delay(holdtime);
    SwitchControlLibrary().MoveLeftStick(128,128); // 傾きを直す
    SwitchControlLibrary().MoveRightStick(128,128); // 傾きを直す
  }
  if(delaytime>0) delay(delaytime);
  return;
}


void NextDayInCheatMode(){
  // ★日付変更
  // Homeボタンを押して設定の画面へ移動
  PushKey("Home", HOLDTIME, 1000);

  // Home画面で「設定」を選ぶ
  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); // 1.5秒「↓」長押し
  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;
}
int *SetCursor(int *cursor_position, int row, int col){

  if(cursor_position[COL] < col){
     for( ; cursor_position[COL] != col ; cursor_position[COL]++ )PushKey("right", HOLDTIME, 100);  
  }else if(cursor_position[COL] > col){
     for( ; cursor_position[COL] != col ; cursor_position[COL]-- )PushKey("left", HOLDTIME, 100);  
  }
  if(cursor_position[ROW] < row){
     for( ; cursor_position[ROW] != row ; cursor_position[ROW]++ )PushKey("down", HOLDTIME, 100);  
  }else if(cursor_position[ROW] > row){
     for( ; cursor_position[ROW] != row ; cursor_position[ROW]-- )PushKey("up", HOLDTIME, 100);  
  }

  return cursor_position;
}


void SoratobuTaxi(int* cursor_position){

  // タウンマップを開く
  PushKey("X", HOLDTIME, 700);  // メニューを開き

  // カーソルをタウンマップに揃える
  SetCursor(cursor_position, TOWNMAP_ROW,  TOWNMAP_COL);
  PushKey("A", HOLDTIME, 2700); // タウンマップを開く

  // TiltLeftStick( 45, 1.0, 50, 170); // カーソル合わせが必要な場所なら調整ください。

  // 空を飛ぶ(A)
  PushKey("A", HOLDTIME, 650);  // ○○まで そらとぶタクシーで 移動しますか?
  PushKey("A", HOLDTIME, 3800); // はい

  return;
}

void getEgg(void){
  SoratobuTaxi(current_cursor);
  TiltLeftStick( 185, 1.0, 50, 200); // ブリーダーおばさんの方向を向く
  PushKey("L", HOLDTIME, 190); // チラッと
  TiltLeftStick( 0, 1.0, 500, 240); // ブリーダーおばさんに向かう
  PushKey("A", HOLDTIME, 420);  // きみのポケモンがタマゴを持っていたんです!
  PushKey("B", HOLDTIME, 420);  // ほしいですよね?
  PushKey("A", HOLDTIME, 2800); // はい
  PushKey("B", HOLDTIME, 1300); // ○○は 預かり屋さん から タマゴを もらった!
  PushKey("B", HOLDTIME, 1100); // タマゴを 手持ちに 加えました!
  PushKey("B", HOLDTIME, 300); // 予備
  PushKey("B", HOLDTIME, 1600); // 大事に 育ててくださいね!
  PushKey("B", HOLDTIME, 300); // 予備

  return;
}


void hatchEgg(){
  // 時間を扱うための変数たち
  unsigned long int current_time=0;
  unsigned long int start_time=0;
  unsigned long int temp_time=0;
  
  // 向き修正(ブリーダーおばさんの反対側を向く)
  TiltLeftStick( 195, 1.0, 50, 200); // ちょっとだけ右側を向くと野生ポケモンと戦闘になりにくい
  PushKey("L", HOLDTIME, 190); // チラッと
  // SoratobuTaxi(current_cursor); // ※タクシーでも可

  TiltStick(0, 1.0, 0, 0, ADV_OFFSET*1000, 100); // 前にOFFSET秒だけ進んで(3秒ほど推奨)
  TiltStick(0, 1.0, 90, 1.0, 0, 0); // スティック傾けて
  for( start_time=millis(), current_time=start_time ; current_time - start_time < (HATCH_TIME-ADV_OFFSET)*1000UL ; current_time=millis() );
  TiltStick(0, 0, 0, 0, 0, 0); // 時間が経ったらスティックを初期位置に戻す

  // ・・・おや? ▼

  PushKey("A", HOLDTIME, 18500); // タマゴが かえって ○○が うまれた!
  PushKey("B", HOLDTIME, 4200); 

  return;
}

void SendBox(int* cursor_position, int *hatched_num){

  // ポケモンを開く
  PushKey("X", HOLDTIME, 700);  // メニューを開き
  // カーソルをポケモンに揃える
  SetCursor(cursor_position, POKEMON_ROW,  POKEMON_COL);
  PushKey("A", HOLDTIME, 1500); // ポケモンを開く
  PushKey("R", HOLDTIME, 1800); // ボックスへ
  
  if(*hatched_num > 30 && *hatched_num %30 == 5){
    PushKey("R", HOLDTIME, 800); // 30匹埋まっていれば次のボックスへ
  }

  PushKey("left", HOLDTIME, 150); // 手持ちにカーソルをあわせて
  PushKey("down", HOLDTIME, 150); // 手持ち2匹目にカーソル
  PushKey("Y", HOLDTIME, 150);
  PushKey("Y", HOLDTIME, 150); // 範囲指定モードに
  PushKey("A", HOLDTIME, 150);
  PushKey("down", HOLDTIME, 150);
  PushKey("down", HOLDTIME, 150);
  PushKey("down", HOLDTIME, 150);
  PushKey("down", HOLDTIME, 150);
  PushKey("A", HOLDTIME, 150); // ホールド

  PushKey("right", HOLDTIME, 150); 
  PushKey("down", HOLDTIME, 150);
  PushKey("down", HOLDTIME, 150);
  PushKey("down", HOLDTIME, 150);
  PushKey("down", HOLDTIME, 150);
  PushKey("A", HOLDTIME, 800); // いちらん をクリック
  PushKey("A", HOLDTIME, 400); // 今いるボックスに左詰めで5匹を入れる。

  PushKey("B", HOLDTIME, 600);  // いちらんを閉じ、ボックスに戻る
  PushKey("B", HOLDTIME, 2000); // ボックスを閉じ、手持ち画面に戻る
  PushKey("B", HOLDTIME, 2000); // 手持ち画面を閉じ、メニューに戻る
  PushKey("B", HOLDTIME, 2000); // メニュー画面を閉じ、フィールドに戻る
  
  return;
}

f:id:tangential_star:20210117025454g:plain

30匹でボックスが埋まっても右のボックスに自動に送るように実装しているので安心だ


あとがき

今回は、タマゴの自動受取と自動孵化を実装しました。これでよりこのブログも、偉大なる先駆者たちに近づけたかなぁと思います。何よりも自分の手でちゃんと実装ができてよかったです。これからも皆様、応援のほどよろしくお願いいたします

 

 

ところで、今回は、予想外のところでプログラミングにつまずきました。

あまり本ブログではプログラムの実装に関する話をする気は無いのですが、今後のプログラミングでも気をつけなきゃなと感じたのでここに「あとがき」として載せます。

Arduinoのint型は2バイトなので32767までしか扱えない

C言語で組み込みやマイコン以外しか触ってこなかった人にとってはかなり衝撃的というか、違和感があるのですが、なんと、int型なのに2バイト(16bit)しかありません。つまり、扱える整数は216=65536通りしかないため、unsigned(マイナスを考えないモード)でも65535までしか扱えないことになります。無論、プログラム中のリテラル(定数)も同様なので、演算の結果intの範囲を超えるとオーバーフローします。

具体的に何に困ったかというと、要するにそのままだと、Arduinoでは「32767ミリ秒以上」待機させることができない(⇒32秒までしか待機できない)ということでした。

…というか10本以上記事を書くだけプログラムしておきながら、今まで一切オーバーフローの可能性に気がつかなかった(=その桁数を扱わずに済んでいた)というのは、奇跡と言わざるを得ません。 一応、ケーススタディとして下記に具体例を載せて解説します。

うまく行かないプログラムソース例:「57秒待機させようとしたらオーバーフローする」

#define WAITTIME (57) // 57秒待機を予定

loop(){
  // 時間を扱うための変数たち
  unsigned long int current_time=0, start_time=0;

  // 57秒待機
  for( start_time=millis(), current_time=start_time ; current_time - start_time < WAITTIME*1000 ; current_time=millis() );
  // ↑このソースコードだとうまく行かずに無限ループする
}

このソースでは、「WAITTIME*1000」の計算結果が「57000」になりますが、これが「int型」で演算され、最大値32768以上になることで意図した結果とは異なる動きをしてしまいます。

結論:long intにキャストすればArduinoでも4バイトになる

前述のオーバーフローについて、原因は「文字定数もint型で演算されていること」です。結論、「long型(4バイト)」にキャストしておけば期待した通りに動作します。すなわち、リテラルに接尾辞「L」を付け加えることで解決できます。

改善したプログラムソース例(うまく動く例):「57秒待機するにはlong型にキャストしておく」

#define WAITTIME (57) // 57秒待機を予定

loop(){
  // 時間を扱うための変数たち
  unsigned long int current_time=0, start_time=0;

  // 57秒待機
  for( start_time=millis(), current_time=start_time ; current_time - start_time < WAITTIME*1000UL ; current_time=millis() );
  // ↑このソースコードだとうまく行く(1000を1000ULに変えただけ)
}

上記例では、1000の後に「UL」をつけてunsigned long int型にキャストしています。

接尾辞「L」をつけることで「long型」として扱われます。ちなみに「U」は「unsigned」の意なので、負の数を扱わないことを示します。

 

 *

 

以上、Arduinoのハードウェア仕様に関する内容にも触れたあとがきでした。この知見も、見る人が見るとためになる内容だと思います。

 

いずれにしても、皆様のポケモン自動化ライフがよりよいものになることを願っております。

  

追記:ニャビーの色違い無事に出せました! (1/17 11:27追記)

本ブログを執筆したのが本日1/17の午前4:09。ブログ投稿からわずか6時間、色違いに出会えました。また、このプログラムを動かし続けて7時間以上、ループの破綻なく、無事に動いておりました。これは個人的に安心できました。

なお、今回はわずか189試行で出会えました。これは強運。

f:id:tangential_star:20210117112445g:plain
f:id:tangential_star:20210117112553p:plain
7時間、ループ破綻なくプログラムは動き続け、その最中、無事に色違いに出会えました

 

改めて、皆様も良い自動化ライフを。

ではではc⌒っ.ω.)っ

 

導入記事: 【Arduino自動化01】Arduino開発環境の導入

前記事:【Arduino自動化11】5番道路で全自動羽集め【DLC不要】

次記事:【Arduino自動化13】全自動ポケモン逃がし

 

【Arduino自動化11】5番道路で全自動羽集め【DLC不要】

ますたーです。こんにちは。

今回は、「5番道路」の橋の上でひたすら努力値振りに使える「ハネ」系アイテムの自動回収を行う記事です。

なお、Arduino Leonardo自動化の導入・機材構成については導入記事を参考にしてください。

導入記事:【Arduino自動化01】Arduino開発環境の導入

※本ブログに初めてお越しの方は「本ブログについて」もぜひ、ご覧ください。

 

概要

本記事では、ランクマバグ状態を利用した、羽系アイテムの自動回収を実装します。
※便宜上「ランクマバグ」と表記しますが、当ブログではランクマッチを使用しません。通信切断・初手降参の推奨もしません。

NintendoSwitch Ver.11.0アップデート対応済です。

f:id:tangential_star:20210111155633p:plain
f:id:tangential_star:20210111121951g:plain
完全自動で5番道路の育て屋から羽を10枚回収する。1試行あたり約56秒だ


では、目次です。

ソースコードだけほしい方は読み飛ばしてください⇒読み飛ばす

 

羽集めで端数の努力値振りも簡単に

今回の記事で集める「たいりょくのハネ」「ちりょくのハネ」などのいわゆる「ハネ系アイテム」は、 ポケモンの基礎ポイント(努力値)を1増やすことができます。

ポケモンの育成をする上で努力値振りという作業をしますが、極振り(252振り)以外の端数で努力値を扱う場合には、10単位でしか努力値が増やせない各種ドーピングアイテム(タウリンブロムヘキシンなど)では対応できないため、1単位で努力値を増やせる「○○のハネ」は大変重宝します。

f:id:tangential_star:20210111131402p:plain
f:id:tangential_star:20210111131407p:plain
羽系アイテムなら両刀の配分や、素早さ抜き調整で例えばS28振りなど柔軟に対応できる

 

日付変更+マップ切り替えで無限に羽が集まる

「○○のハネ」はドロップアイテムとしてガラル地方の様々な場所に隠しアイテムとして落ちています。

これらの場所で拾える羽系アイテムは、1日経過で「同じ場所」に復活するため、これらの拾えるポイントを事前に覚えておけば、毎日これらのポイントを周回することでコツコツと集めることができます(例えば「5ばんどうろ」の橋の上や「エンジンシティはずれ」の橋の上、ヨロイじまの海上など)。

f:id:tangential_star:20210111122110p:plain
f:id:tangential_star:20210111155817p:plain
羽系アイテムは、橋の上や海の上など、様々な場所に隠しアイテムとして落ちている

条件についてより厳密には、日付の切り替え+マップ切り替え(そらとぶタクシー含む)の条件2つを満たすと復活するため、巣穴が近くにある場合には、いわゆる「日付バグ」との併用で無限に回収し続けることができます。

手動でやる場合は、ヨロイじまの「ワークアウトのうみ」の岩(一礼野原の駅を出て目の前に見える海上の岩)がおすすめです。岩場を1周するだけで羽が集められ、ポケモンの巣穴もあるため日付変更ペナルティの回避もできます。前述の通り羽の落ちている場所は常に一定なので、場所を覚えれば効率的に回収できます。

f:id:tangential_star:20210111151804g:plain

日付変更+そらとぶタクシーで同じ場所に羽系アイテムが復活する。無限に回収可能だ

 

ランクマバグで効率的な日付変更が可能に

前回の記事でも紹介しましたが、ランクマッチ1戦の直後や、YY通信のカジュアルバトルで機内モードに変更した際に、日付変更ペナルティが解除されるバグ(通称:ランクマバグ)があります。これを活用することで、短い時間で場所を選ばず効率的に日付変更ができるようになります。

なお、ランクマッチを使用しない方法での「ランクマバグ状態」移行は下記手順でできます。画像は過去記事の使いまわしです。あしからず。

  1. YY通信で「通信対戦」を行う(ローカル通信)
  2. もう一台のSwitch・剣盾を使ってその通信対戦に応じる
  3. 戦闘が始まったらHomeボタン長押し→「機内モード」をONに変更する
  4. エラーが発生するのでそれを閉じて、「にげる」選択
  5. フィールド画面に戻ってくる(ランクマバグ状態に移行完了)
f:id:tangential_star:20201122213022g:plain
f:id:tangential_star:20201122213037g:plain
YY通信でローカル対戦を募る。バトルが始まったらHomeボタン長押しで機内モード
f:id:tangential_star:20201122213148g:plain
f:id:tangential_star:20201122213205g:plain
エラーが出たら「にげる」で対戦終了。この状態で時刻変更すると一瞬画面が暗転する

 

羽集めの自動化は「5番道路」が最適

前述の通り、羽を集めるには、大きく「日付変更+マップ切り替え」と「羽を拾う動作」の2つを繰り返す必要があります。自動化する上で、この「日付変更+マップ切り替え」が結構な曲者です。

上記でおすすめした「ワークアウトのうみ」では、近くに「そらとぶタクシー」で移動できるポイントがあるため都合が良いのですが、海上に位置するため、道中、ポケモンに遭遇する可能性が高いです。具体的には、サメハダーに襲われたりタマンタが跳ねてきたり、他にもキャモメが空を飛んでいたりなどで不意な戦闘が発生するため、一定の動きでプログラミングしてしまうと、ループが途切れてしまいます。

そこで、羽集めの自動化には、「そらとぶタクシー」で近くにアクセスできて、ポケモンとの遭遇がほぼ発生しない「5番道路」を活用します。

「そらとぶタクシー」でこれらが選択できるよう、あらかじめ「5番道路」のキャンプ場と「預り屋さん」に足を運んでおきましょう。

f:id:tangential_star:20210111161229p:plain
f:id:tangential_star:20210111161238p:plain
「キャンプ場」「預り屋さん」を訪れておけば、そらとぶタクシーで移動できるようになる

ちなみに、5番道路を活用することで「DLCが不要」 という副次的なメリットもあります。剣盾ビギナーの方にはそういう意味でもおすすめと言えます。

 

自動化プログラムの使い方

今回のプログラム(ソースコードは後述の通り)は、上記の通り5番道路で羽集めを自動で行うものです。できるだけ効率化を図るため、下記条件で動くようにしました。

この状態でArduinoを挿すと、「日付変更⇒そらとぶタクシーで預り屋に移動⇒10枚の羽を拾う」をひたすらループしてくれます。

  1. ランクマバグ状態であること
  2. そらとぶタクシーで「5ばんどうろ」「あずかりやさん」が選択可能なこと
  3. 「話の速さ」が「はやい」になっていること
  4. Xボタンでメニューを開いた時に「タウンマップ」にカーソルが合うこと
  5. 5番道路で自転車に乗っていること
f:id:tangential_star:20210111162925p:plain
f:id:tangential_star:20210111162931p:plain
Xボタンでタウンマップがカーソルにあうこと・自転車に乗っていることを確認しよう

 

ソースコード

ランクマバグ状態・5番道路で自転車に乗ってArduinoを挿すだけの簡単仕様です。

/* 
 *  羽集め@5番道路(育て屋)c⌒っ.ω.)っ 
 *  【ランクマバグ状態で使うこと!】
 *  Xボタンで「タウンマップ」にカーソルが合っていること、自転車に乗っていること
 *  
 *  (c) 2021 ますたーの忘備録
 *  https://tangential-star.hatenablog.jp/
*/

#include <SwitchControlLibrary.h>

#define HOLDTIME (95) // 1回のキー入力の長押し時間

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);

void setup() {
  // コントローラーとして認識されるためにRLを7回ほどカチャカチャする
  for(int i=0;i<7;i++)PushRL(300);
  delay(1000);
}


void loop() {

  // ★日付を回すやつ
  NextDayInCheatMode();

  // ★5番道路の預かり屋に空を飛ぶ。
  PushKey("X", HOLDTIME, 700);  // メニューを開き
  PushKey("A", HOLDTIME, 2700); // タウンマップを開く
  TiltLeftStick( 45, 1.0, 50, 170); // 5番道路から預かり屋にカーソル合わせ
  // 空を飛ぶ(A)
  PushKey("A", HOLDTIME, 650);  // 5番道路まで そらとぶタクシーで 移動しますか?
  PushKey("A", HOLDTIME, 3500); // はい


  // ★羽集め(育て屋前からスタート)
  // 1個め
  TiltLeftStick( 93, 1.0, 3000, 500);
  PushKey("A", HOLDTIME, 100);
  PushKey("B", HOLDTIME, 100);
  PushKey("B", HOLDTIME, 300);

  // 2個め
  TiltLeftStick( 72, 1.0, 1950, 500);
  PushKey("A", HOLDTIME, 100);
  PushKey("B", HOLDTIME, 100);
  PushKey("B", HOLDTIME, 300);

  // 3個め
  TiltLeftStick( 103, 1.0, 1460, 500);
  PushKey("A", HOLDTIME, 100);
  PushKey("B", HOLDTIME, 100);
  PushKey("B", HOLDTIME, 300);

  // 4個め
  TiltLeftStick( 105, 1.0, 1250, 500);
  PushKey("A", HOLDTIME, 100);
  PushKey("B", HOLDTIME, 100);
  PushKey("B", HOLDTIME, 300);

  // 5個め
  TiltLeftStick( 59, 1.0, 750, 500);
  PushKey("A", HOLDTIME, 100);
  PushKey("B", HOLDTIME, 100);
  PushKey("B", HOLDTIME, 300);


  // 6個め
  TiltLeftStick( 100, 1.0, 2690, 500);
  PushKey("A", HOLDTIME, 100);
  PushKey("B", HOLDTIME, 100);
  PushKey("B", HOLDTIME, 300);


  // 7個めnew
  TiltLeftStick( 63, 1.0, 1620, 500);
  PushKey("A", HOLDTIME, 100);
  PushKey("B", HOLDTIME, 100);
  PushKey("B", HOLDTIME, 300);

  // 8個めnew
  TiltLeftStick( 120, 1.0, 1950, 500);
  PushKey("A", HOLDTIME, 100);
  PushKey("B", HOLDTIME, 100);
  PushKey("B", HOLDTIME, 300);

  // 9個めnew
  TiltLeftStick( 69, 1.0, 1800, 500);
  PushKey("A", HOLDTIME, 100);
  PushKey("B", HOLDTIME, 100);
  PushKey("B", HOLDTIME, 300);

  // 10個めnew
  TiltLeftStick( 125, 1.0, 900, 500);
  PushKey("A", HOLDTIME, 100);
  PushKey("B", HOLDTIME, 100);
  PushKey("B", HOLDTIME, 300);

  delay(1000);

}

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){
  // ホームボタン・方向キーはRight, Left, Up, Down, Homeなど2文字以上で入力。
  // その他ボタン入力は1文字(A,B,X,Y,R,L,+,-)ZR・ZLにも対応
  // 同時押しは非対応
  
  if(strlen(keyname)==1){
    switch(keyname[0]){
      case 'A': case 'a': // A
        SwitchControlLibrary().PressButtonA(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonA(); delay(delaytime);
      break;
      case 'B': case 'b': // B
        SwitchControlLibrary().PressButtonB(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonB(); delay(delaytime);
      break;
      case 'X': case 'x': // X
        SwitchControlLibrary().PressButtonX(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonX(); delay(delaytime);
      break;
      case 'Y': case 'y': // Y
        SwitchControlLibrary().PressButtonY(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonY(); delay(delaytime);
      break;
      case 'L': case 'l': // L
        SwitchControlLibrary().PressButtonL(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonL(); delay(delaytime);
      break;
      case 'R': case 'r': // R
        SwitchControlLibrary().PressButtonR(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonR(); delay(delaytime);
      break;
      case 'H': case 'h': // Home
        SwitchControlLibrary().PressButtonHome(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonHome(); delay(delaytime);
      break;
      case '+': case 'p': case 'P': // Plus
        SwitchControlLibrary().PressButtonPlus(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonPlus(); delay(delaytime);
      break;
      case '-': case 'm': case 'M': // Minus
        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': // ZR/ZL
        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': // right
        SwitchControlLibrary().MoveHat(2); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().MoveHat(8); delay(delaytime);
      break;
      case 'l': case 'L': // left
        SwitchControlLibrary().MoveHat(6); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().MoveHat(8); delay(delaytime);
      break;
      case 'u': case 'U': // up
        SwitchControlLibrary().MoveHat(0); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().MoveHat(8); delay(delaytime);
      break;
      case 'd': case 'D': // down
        SwitchControlLibrary().MoveHat(4); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().MoveHat(8); delay(delaytime);
      break;
      case 'H': case 'h': // Home
        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){ // holdtime=0のときは押しっぱなし。
    delay(holdtime);
    SwitchControlLibrary().MoveLeftStick(128,128); // 傾きを直す
  }
  if(delaytime>0) delay(delaytime);
  return;
}

void NextDayInCheatMode(){
  // ★日付変更
  // Homeボタンを押して設定の画面へ移動
  PushKey("Home", HOLDTIME, 1000);

  // Home画面で「設定」を選ぶ
  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); // 1.5秒「↓」長押し
  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;
}

日付変更処理部分を関数にしたので、loop()の中が多少読みやすくなりました。

 

あとがき

今回の記事では、対応する努力値を「1」だけ増やすことができる、いわゆる「羽系アイテム」の自動回収について紹介しました。

前回の「【Arduino自動化10】きのみ・ぼんぐり自動回収」を使えば、対応する努力値を「10」下げられるので、これらを併用することで、努力値調整がかなり簡便に行えるようになりました。

さらに、技レコードやミント、各種カプセルなどは、それぞれ「ワット(【Arduino自動化03】ワット稼ぎ・自動回収)」、「バトルポイント(【Arduino自動化08】バトルタワー自動周回【BP稼ぎ】)」や「マックスこうせき(【Arduino自動化06】完全放置「マックスこうせき」集め)」で引き換えられるため、今までのものも含めて育成環境の整備がしっかりとできるようになってきたかな、と感じています。

 

アイテムなどの回収作業をArduinoで自動化でき、さらに副次的に、育成論の検討や対戦に時間を割けるようになるのは、まさに一石二鳥なのではないかと思います。皆様のプレイスタイルに応じてうまくご活用いただければ幸いです。

ではではc⌒っ.ω.)っ

 

前記事:【Arduino自動化10】きのみ・ぼんぐり自動回収

導入記事:【Arduino自動化01】Arduino開発環境の導入 

次の記事:【Arduino自動化12】全自動タマゴ受け取り&孵化

【Arduino自動化10】きのみ・ぼんぐり自動回収

ますたーです。こんにちは。

今回は、「きのみがとれる木」を自動で揺らし、完全放置で「努力値下げきのみ」や「ぼんぐり」などを集めるArduino Leonardo自動化の記事です。
記念すべき10個目の「Arduino自動化」に関する記事です。引き続きよろしくお願いします!

なお、Arduino Leonardo自動化の導入・機材構成については導入記事を参考にしてください。

導入記事:【Arduino自動化01】Arduino開発環境の導入

※本ブログに初めてお越しの方は「本ブログについて」もぜひ、ご覧ください。 

概要

本記事では、ランクマバグ状態を利用した、きのみの自動回収を実装します。
※便宜上「ランクマバグ」と表記しますが、当ブログではランクマッチを使用しません。通信切断・初手降参の推奨もしません。

NintendoSwitch Ver.11.0アップデート対応済です。

f:id:tangential_star:20210105151958g:plain
f:id:tangential_star:20210105151934p:plain
木を揺らして日付を変えるまで約28秒。ぼんぐり・きのみの高速回収が可能だ


では、目次です。

 

つくったきっかけ

きのみ・ぼんぐりの自動回収については、すでに沢山のブログで紹介・掲載されていると思います。一方で、当ブログへの検索流入を調べたところ「ぼんぐり 自動化」で検索してアクセスされる方が一定数いらっしゃるため、おそらくまだまだ需要があるかと思いましたので、更新するに至った次第です。

また、他のブログでは、先月のNintendo SwitchのVer.11のアップデート(Nintendo OnlineボタンがHome画面に実装された奴)に対応していないものも一定数見受けられるため、そういう意味でも需要があるかな、と思います。

一応、ベーシックな「きのみ高速回収のやりかた」に沿って執筆していますので、Arduinoをお持ちでない方にも楽しく読めるように心がけています

なお、「きのみ回収の使い方も手順も知っとるわ!ソースコードだけくれ!」 という方は読み飛ばしてください⇒読み飛ばす

 

集めたいきのみ・ぼんぐりが出やすい木を揺らそう

きのみ・ぼんぐりを効率的に集めるにあたって、「どこの木を揺らせば良いのか?」が大変重要です。

例えば、「こうげき」の基礎ポイント(努力値)を「10」下げる効果を持つ「ネコブのみ」を集める場合、7番道路やナックル丘陵の木からランダムに入手できますが、ヨロイじまの「ワークアウトの海」の木(「並ぶ島の海」側)であれば「ネコブのみ」が確定で落ちてきます(それ以外のきのみは落ちてきません)。

すなわち、揺らして手に入るきのみの種類や量が場所によって違うため、狙ったきのみが出やすい木で揺らし続けることが重要です。

特に需要が大きいかと思いますので、各種努力値下げきのみについて、下表におすすめの木の場所を纏めました(結構頑張りました)。ご参考ください。

 

狙う「きのみ」が落ちてくる木の場所の一覧(アルファベット表記は「ポケモン徹底攻略」に準じる)

「きのみ」の名前 きのみの効果 おすすめの場所 備考
ぼんぐり全種 ガンテツボールの
材料になる
集中の森
(ヨロイじま)
当エリア内の
どの木でもOK。
※出やすさに偏りあり
努力値下げきのみ全種
※個別に集める場合は
それぞれ下記推奨
対応する努力値
10だけ下げる
巨人の鏡池
(ワイルドエリア)
DLC不要。
当エリアには1本しか
木が無いため迷わない
ザロクのみ 「HP」努力値
10だけ下げる

離れ島海域(ヨロイじま)

f:id:tangential_star:20210108171554g:plain
離れ島海域の木。
チャレンジビーチから岩の間を通り左奥へ進もう。
ネコブのみ 「こうげき」努力値
10だけ下げる

ボールレイクの湖畔(カンムリせつげん)

f:id:tangential_star:20210105180104g:plain
ボールレイクの湖畔「C」の木。
ダイ木の丘から池を左回りに進もう。
タポルのみ 「ぼうぎょ」努力値
10だけ下げる

ボールレイクの湖畔(カンムリせつげん)

f:id:tangential_star:20210105194230g:plain
ボールレイクの湖畔「F」の木。
ダイ木の丘から池を半周分、左へ進もう。
ロメのみ 「とくこう」努力値
10だけ下げる

ボールレイクの湖畔(カンムリせつげん)

f:id:tangential_star:20210105194352g:plain
ボールレイクの湖畔「H」の木。
ダイ木の丘から右に行き、下りたら左に進もう。
ウブのみ 「とくぼう」努力値
10だけ下げる

鍛錬平原(ヨロイじま)

f:id:tangential_star:20210108121838g:plain
鍛錬平原「巣穴G側」の木。
掘り出しおやじの逆方向へまっすぐ進もう。
マトマのみ 「すばやさ」努力値
10だけ下げる

ボールレイクの湖畔(カンムリせつげん)

f:id:tangential_star:20210105194555g:plain
ボールレイクの湖畔「A」の木。
ダイ木の丘から左に進み、登ったら右奥に進もう。

 

ランクマバグ状態で効率的にきのみを回収する

ランクマバグ状態ならきのみが即復活

きのみ・ぼんぐりの効率的な回収に関しては、ランクマッチ後やYY通信のカジュアルバトルで機内モードに変更した際に日付変更ペナルティが解除されるバグ(通称:ランクマバグ)を活用します。ランクマバグ状態であれば、きのみを回収してすぐにHome画面から日付を変更することで、再度きのみを復活させることができます

f:id:tangential_star:20210105165439g:plain

ランクマバグ状態なら、日付を進めるだけできのみが即復活する

Switch2台あればランクマッチ不要で「ランクマバグ」状態に

いわゆる「ランクマバグ状態」への移行は、ランクマッチ1戦の代わりに、下記手順でも可能です。Nintendo Online未加入など、ランクマッチができない場合に重宝します。

なお、Switchを2台用意できないなどでこの方法が使えない人は素直にランクマッチで1戦してください(その場合、Nintendo Onlineへの加入が必要です)。

  1. YY通信で「通信対戦」を行う(ローカル通信)
  2. もう一台のSwitch・剣盾を使ってその通信対戦に応じる
  3. 戦闘が始まったらHomeボタン長押し→「機内モード」をONに変更する
  4. エラーが発生するのでそれを閉じて、「にげる」選択
  5. フィールド画面に戻ってくる(ランクマバグ状態に移行完了)

ちなみに、しっかりとランクマバグ状態に移行できているかを確認するためには、一度Home画面に戻り、もう一度ゲーム画面に戻ると分かります。ゲーム画面に復帰した際に、一瞬だけ画面全体がチラッと暗転すればOKです。

なお、【Arduino自動化05】乱数調整レイドで色違いパッチルドン探し【冠の雪原】の記事で紹介した時とは違い、今回は一度に大量の日付変更を行わない前提なので、必ずしもポケモンセンターに移動する必要はありません(画像は使いまわしです)。

f:id:tangential_star:20201122213022g:plain
f:id:tangential_star:20201122213037g:plain
YY通信でローカル対戦を募る。バトルが始まったらHomeボタン長押しで機内モード
f:id:tangential_star:20201122213148g:plain
f:id:tangential_star:20201122213205g:plain
エラーが出たら「にげる」で対戦終了。この状態で時刻変更すると一瞬画面が暗転する

 

プログラムのソースコード

「ランクマバグ状態」で、揺らしたい木の目の前でArduinoを挿すだけの簡単仕様です。

/* 
 *  きのみ・ぼんぐり回収c⌒っ.ω.)っ 
 *  【ランクマバグ状態必須!!】
 *  
 *  (c) 2021 ますたーの忘備録
 *  https://tangential-star.hatenablog.jp/
*/

#include <SwitchControlLibrary.h>

#define HOLDTIME (95) // 1回のキー入力の長押し時間

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);

void setup() {
  // コントローラーとして認識されるためにRLを7回ほどカチャカチャする
  for(int i=0;i<7;i++)PushRL(300);
  delay(1000);
}

void loop() {

  // ★きのみ回収
  PushKey("A", HOLDTIME, 600); // きのみが とれる 木だ! 揺らしてみますか?
  PushKey("A", HOLDTIME, 900); // ▼ or ……いまは きのみが ないみたい 
  PushKey("A", HOLDTIME, 3200);// はい
  PushKey("B", HOLDTIME, 600); // ○○が ○個 落ちてきた!▼
  PushKey("B", HOLDTIME, 600); // きのみが ○個 落ちている 「やめる」※以降はB連打
  PushKey("B", HOLDTIME, 600); // ○○は 木から落とした きのみを 拾いあげた! 
  PushKey("B", HOLDTIME, 600); // ○○を ○個 手に入れた!
  PushKey("B", HOLDTIME, 600);
  PushKey("B", HOLDTIME, 600);
  PushKey("B", HOLDTIME, 600);
  PushKey("B", HOLDTIME, 600); // 2種類までならここまででOK(ボールレイクの湖畔 C のネコブのみ・イバンのみ など)

  PushKey("B", HOLDTIME, 600);
  PushKey("B", HOLDTIME, 600);
  PushKey("B", HOLDTIME, 600);
  PushKey("B", HOLDTIME, 600);
  PushKey("B", HOLDTIME, 600); // 1試行で3種落ちる木の場合はここまで処理必要


  // ★日付変更

  // Homeボタンを押して設定の画面へ移動
  PushKey("Home", HOLDTIME, 1000);

  // Home画面で「設定」を選ぶ
  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); // 1.5秒「↓」長押し
  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);
  
}

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){
  // ホームボタン・方向キーはRight, Left, Up, Down, Homeなど2文字以上で入力。
  // その他ボタン入力は1文字(A,B,X,Y,R,L,+,-)ZR・ZLにも対応
  // 同時押しは非対応
  
  if(strlen(keyname)==1){
    switch(keyname[0]){
      case 'A': case 'a': // A
        SwitchControlLibrary().PressButtonA(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonA(); delay(delaytime);
      break;
      case 'B': case 'b': // B
        SwitchControlLibrary().PressButtonB(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonB(); delay(delaytime);
      break;
      case 'X': case 'x': // X
        SwitchControlLibrary().PressButtonX(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonX(); delay(delaytime);
      break;
      case 'Y': case 'y': // Y
        SwitchControlLibrary().PressButtonY(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonY(); delay(delaytime);
      break;
      case 'L': case 'l': // L
        SwitchControlLibrary().PressButtonL(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonL(); delay(delaytime);
      break;
      case 'R': case 'r': // R
        SwitchControlLibrary().PressButtonR(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonR(); delay(delaytime);
      break;
      case 'H': case 'h': // Home
        SwitchControlLibrary().PressButtonHome(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonHome(); delay(delaytime);
      break;
      case '+': case 'p': case 'P': // Plus
        SwitchControlLibrary().PressButtonPlus(); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().ReleaseButtonPlus(); delay(delaytime);
      break;
      case '-': case 'm': case 'M': // Minus
        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': // ZR/ZL
        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': // right
        SwitchControlLibrary().MoveHat(2); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().MoveHat(8); delay(delaytime);
      break;
      case 'l': case 'L': // left
        SwitchControlLibrary().MoveHat(6); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().MoveHat(8); delay(delaytime);
      break;
      case 'u': case 'U': // up
        SwitchControlLibrary().MoveHat(0); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().MoveHat(8); delay(delaytime);
      break;
      case 'd': case 'D': // down
        SwitchControlLibrary().MoveHat(4); delay(holdtime);
        if(holdtime>0)SwitchControlLibrary().MoveHat(8); delay(delaytime);
      break;
      case 'H': case 'h': // Home
        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){ // holdtime=0のときは押しっぱなし。
    delay(holdtime);
    SwitchControlLibrary().MoveLeftStick(128,128); // 傾きを直す
  }
  if(delaytime>0) delay(delaytime);
  return;
}

 結構お手軽ですね。

 

あとがき 

今回は、いわゆる「ランクマバグ」を利用した、全自動「きのみ」「ぼんぐり」高速回収のプログラムを紹介しました。

紹介していて言うのも変な話なのですが、個人的には「ランクマバグ」という表現があまり好きではありません。というのも、一昔前まで、ランクマッチでの初手降参や、見ず知らずの人とのYY通信から機内モード(=切断)を通して「ランクマバグ」に移行することを紹介するWebサイトや動画が散見され、各人のリテラシーが問われるようなバグの活用と周知が成されていたからです。

当時の煽りを目の当たりにしていた私からすると、ランクマバグ自体が敬遠されるべきものとしてイメージされていました(ゆえに、自動化記事2つ目「【Arduino自動化02】雪中渓谷のワットショップの目玉商品を全自動回収」についても、ランクマバグを利用しない方法で作成していました)。

今でこそ、マナーアップ啓蒙も含めた紹介動画やWeb攻略情報の掲載がされており、真摯にランクマッチ1戦で紹介されたり、各人でSwitchを2台用意した上での機内モード活用が紹介されたりしているため、一昔前ほどの心配は杞憂なのでしょうが、そういった背景心情があった上での記事執筆だとご理解いただければと思います。

つきましては、釈迦に説法かとは思いますが、読者の皆様においても、上述のような「他人に迷惑がかかる方法」でのバグ活用はお控えいただくよう、お願いします。

ところで、2020年11月7日 に本ブログを始めてから、明日で3ヶ月を迎えます。PV数・検索流入数も少しずつですが増えてきており、お陰様でモチベーションも保ちながら更新ができています。まだまだ駆け出しの当ブログですが、もし皆様のお役に立てるところがあれば幸いです。

もし、「いつも見てます!」とか「応援してます!」と言ったハートウォーミングな御仁がいらっしゃれば、「コメント」や「はてなスター」を残していただけますと幸いです。

 

どうぞ、引き続きよろしくお願いいたします。

ではではc⌒っ.ω.)っ

 

導入記事:【Arduino自動化01】Arduino開発環境の導入

前回記事:【Arduino自動化09】ポケモンキャンプでカレー自動調理【カレーのあかし】

次の記事:【Arduino自動化11】5番道路で全自動羽集め【DLC不要】