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

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

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

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

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

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

概要

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

NintendoSwitch Ver.11.0アップデート対応済です。
★2021/9/20追記:NintendoSwitch Ver.13.0アップデート対応済です。
★2022/10/23追記:NintendoSwitch Ver.15.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を挿すだけの簡単仕様です。

2021/9/20追記:NintendoSwitch Ver.13.0アップデート対応済(詳細はこちら
2022/10/23追記:NintendoSwitch Ver.15.0アップデート対応済
※Switch Liteをご利用の方は「#define SWITCH_VER (15)」を「#define SWITCH_VER (12)」に書き換えてご利用ください。

/* ★NintendoSwitchのファームウェアVer13に対応版(2021/9/20)★
 * ★NintendoSwitchのファームウェアVer15に対応版(2022/10/23)★ 
 *   
 *  きのみ・ぼんぐり回収c⌒っ.ω.)っ 
 *  【ランクマバグ状態必須!!】
 *  
 *  (c) 2021 ますたーの忘備録
 *  https://tangential-star.hatenablog.jp/
*/

#include <SwitchControlLibrary.h>

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

#define SWITCH_VER (15)
// 2021/9/20、SwitchのVer13アップデートに伴う追記
// Switchのバージョンを整数で入力(例:12.2.1⇒12, 13.0.0⇒13, 15.0.0→15)

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

#if (SWITCH_VER >= 15)
  // Switchのver15アプデでBluetoothオーディオの設定が変わった?ので、微修正。
  // ※本体のバッテリー残量(%)にずれるようになったので。
  PushKey("down", 750, 105);
#elif (SWITCH_VER >= 13)
  // Switchのアプデで「ドックの更新」項目が追加されたので
  // 日付変更をする場合には「↓」キー長押しが必要に。
  PushKey("down", 780, 105);
#else
  // ver12まで(13アプデ前)は、1ページに収まるのでそのままでOK
  for(int i=0; i<4; i++){
    PushKey("down", (int)HOLDTIME/2, 105);
  }
#endif
  
  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不要】