【Arduino自動化08】バトルタワー自動周回【BP稼ぎ】

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

今回は、「バトルタワー」自動周回で、BPを稼ぐArduino Leonardo自動化の記事です。

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

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

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

 

概要

本記事では、シュートシティの「バトルタワー」自動周回を実装します。

f:id:tangential_star:20201229104124p:plain
f:id:tangential_star:20201229144836g:plain
バトルタワーを自動周回(1周あたり3分~6分ほど)。1周ごとに2BP貰える

目次です。

 

経緯

バトルタワー周回という「いまさら」感が強いタイミングでの記事化となりますが、実は、当ブログのアクセス状況を確認すると、ダイマックスアドベンチャーの自動周回記事が(アクセス数も検索流入も)大変盛況なのです。

どうやら、従来のマクロ対応コントローラーでできることをマイコン(本ブログだとArduino Leonardo)に対応させたいという需要は一定数あるらしく、それならこのブログでも取り上げる価値があるかな?と思ったので記事化しました。

BP(バトルポイント)は、お金やワットとは引き換えられないもの(いじっぱりミントなど)も多いため、そういう意味でも需要はあると思います。

 

この記事のスタンス

本記事では、下記の前々回・前回(第6回・第7回)の記事に引き続き、マクロ対応コントローラーの代替用の雛形をベースにArduino Leonardo用にプログラムを書いていきます。例のごとく、入力するコマンド列が予め分かっているものを活用します。

なお、本ブログで扱うコマンド列は、私が考案したものではありません。ダイマックスアドベンチャーの記事でも紹介した、あずき氏によるものです。当該ブログ記事を下記にて引用します。合わせてご参考ください。

azukiss.hatenablog.com

 

バトルタワー自動周回のやりかた

バトルタワーの自動周回には、以下の条件を満たしている必要があります。

  1. バトルタワー「シングルバトル」ランクが「マスターボール級」であること
  2. 自動周回用のパーティーを準備していること

1のマスターランク到達は前提ですが、今作では禁止伝説級のポケモンが使用できるのでゴリ押しで攻略可能です。1時間ほどでマスターランクに到達できると思います。

2の自動周回用パーティーですが、

の3匹を準備してください。なお、それぞれのPPは最大値まで増やしておきましょう。

自前で準備が難しければレンタルチーム(要Nintendo Online加入)でも可能です(参考チームID:0000 0000 5MJ4 0K)。

f:id:tangential_star:20201229133747p:plain
f:id:tangential_star:20201229133714p:plain
自動周回は、シングルバトルが「マスターボール級」前提。まだの人はまず攻略しよう
f:id:tangential_star:20201229130956p:plain
f:id:tangential_star:20201229130109p:plain
メニューの「VS」からチームのレンタルが可能(要Nintendo Online加入)。

コマンド列ですが、原則としてAボタン連打で攻略していき、ポケモン入れ替えなどのために「↑」や「B」を押す、というイメージです。下記を御覧ください。

 

自動周回のコマンド入力列(あずき氏のブログ記事より引用・一部改変)

1 2 3 4 5 6 7 8
9 10 11 12 13 14 15,16
R,R(連打)

※指定が無い部分のキー入力間隔は約1.5秒

要するに、この16コマンドをひたすら繰り返すことで、バトルタワーを延々と攻略してBPを稼いでくれるということです。すごいですね。

 

自動化コマンドの実装

前回の記事で紹介した雛形・関数の要領で、前述の16コマンドを入力していきます。雛形は下記テキストボックスからコピペも可能です。今回はスティック入力が無いので、「PushKey("入力キー", 長押しミリ秒, 待機ミリ秒 ); 」を16行並べるだけです。

 

Arduino Leonardo自動化用 プログラム雛形

 

前述のコマンド列を関数に当てはめると下記の様になります。この雛形の中に下記「loop(){」と「}」の間16行を埋め込むだけでプログラムになります。

こちらは説明用に抜粋したプログラムの一部です。プログラム全文は⇒コチラをクリックして読み飛ばす

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

void loop() {
  // ↓ここに繰り返すコマンド列を入れよう
  PushKey("A",    HOLDTIME, 1500); // 1: A HOLTIMEミリ秒長押し 1.5秒待機
  PushKey("A",    HOLDTIME, 1500); // 2: A HOLTIMEミリ秒長押し 1.5秒待機
  PushKey("A",    HOLDTIME, 1500); // 3: A HOLTIMEミリ秒長押し 1.5秒待機
  PushKey("A",    HOLDTIME, 1500); // 4: A HOLTIMEミリ秒長押し 1.5秒待機
  PushKey("UP",   HOLDTIME, 1500); // 5: ↑HOLTIMEミリ秒長押し 1.5秒待機
  PushKey("A",    HOLDTIME, 1500); // 6: A HOLTIMEミリ秒長押し 1.5秒待機
  PushKey("A",    HOLDTIME, 1500); // 7: A HOLTIMEミリ秒長押し 1.5秒待機
  PushKey("UP",   HOLDTIME, 1500); // 8: ↑HOLTIMEミリ秒長押し 1.5秒待機
  PushKey("A",    HOLDTIME, 1500); // 9: A HOLTIMEミリ秒長押し 1.5秒待機
  PushKey("A",    HOLDTIME, 1500); // 10:A HOLTIMEミリ秒長押し 1.5秒待機
  PushKey("A",    HOLDTIME, 1500); // 11:A HOLTIMEミリ秒長押し 1.5秒待機
  PushKey("A",    HOLDTIME, 1500); // 12:A HOLTIMEミリ秒長押し 1.5秒待機
  PushKey("B",    HOLDTIME, 1500); // 13:B HOLTIMEミリ秒長押し 1.5秒待機
  PushKey("UP",   HOLDTIME, 1500); // 14:↑HOLTIMEミリ秒長押し 1.5秒待機
  PushKey("R",    HOLDTIME, 150 ); // 15:R HOLTIMEミリ秒長押し ほぼ待機なし
  PushKey("R",    HOLDTIME, 150 ); // 16:R HOLTIMEミリ秒長押し ほぼ待機なし
  // ↑ここに繰り返すコマンド列を入れよう
}

なお、今回は15個目のRと16個目のRの間を連打するので、それぞれの待機時間を150ミリ秒(充分短い時間)にしています。なお、この間が極端に短すぎる(例えば10ミリ秒などに設定する)と、Switch側がボタンの押下状態を認識できずコマンドミスすることがあるようです。注意しましょう。

 

プログラムの使い方

前述の通り、攻略用3匹のみのパーティーを準備の上、シュートシティの上側にあるバトルタワーへ行き、受付の前でArduino Leonardoを差し込むだけです。
なお、初回は「手持ち」にカーソルが合っていると思いますので、上記のレンタルチームを活用している人は、手動でそちらにカーソルをあわせておいてください。

f:id:tangential_star:20201229143158p:plain
f:id:tangential_star:20201229133747p:plain
f:id:tangential_star:20201229133714p:plain
f:id:tangential_star:20201229143220p:plain
攻略用チームが選ばれるように準備の上、バトルタワーの受付前でArduinoを差し込む。

 

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

雛形は前回の記事と同一です。

/* 
 *  バトルタワー自動周回
*/

/* マクロコンのコマンドをソースコード化するための雛形【Newバージョン】

loop(){」のすぐ下に繰り返したいコマンドを書いていくだけで、
Arduino Leonardo用のソースコードができあがる簡単設計。

書き方としては1行ずつ↓を書いていく。
・ボタン入力:「PushKey("入力キー", 長押しミリ秒, 待機ミリ秒 ); 」または
・スティック:「TiltLeftStick(真上を基準に傾ける角度(0-360),傾きの強さ(0.0-1.0),長押しミリ秒, 待機ミリ秒)」
 ※スティック入力に関しては、長押しミリ秒を「0」にすると傾けっぱなしになります。

たとえば、「Aボタンを1.5秒間押してその後3秒待機する」場合には、
  PushKey( "A" , 1500 , 3000 );
と書けばオーケー。
たとえば、「スティックを3秒間右に傾けてその後2秒待機する」場合には、
  TiltLeftStick(90, 1.0, 3000, 2000);
と書けばオーケー。

なお、プログラムはすべて「半角英数字」で書くこと!

ちなみに「//」の右側の文字はプログラム上で無視されるので開発用コメントを残せる。
*/
#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, 1500); // 1: A HOLTIMEミリ秒長押し 1.5秒待機
  PushKey("A",    HOLDTIME, 1500); // 2: A HOLTIMEミリ秒長押し 1.5秒待機
  PushKey("A",    HOLDTIME, 1500); // 3: A HOLTIMEミリ秒長押し 1.5秒待機
  PushKey("A",    HOLDTIME, 1500); // 4: A HOLTIMEミリ秒長押し 1.5秒待機
  PushKey("UP",   HOLDTIME, 1500); // 5: ↑HOLTIMEミリ秒長押し 1.5秒待機
  PushKey("A",    HOLDTIME, 1500); // 6: A HOLTIMEミリ秒長押し 1.5秒待機
  PushKey("A",    HOLDTIME, 1500); // 7: A HOLTIMEミリ秒長押し 1.5秒待機
  PushKey("UP",   HOLDTIME, 1500); // 8: ↑HOLTIMEミリ秒長押し 1.5秒待機
  PushKey("A",    HOLDTIME, 1500); // 9: A HOLTIMEミリ秒長押し 1.5秒待機
  PushKey("A",    HOLDTIME, 1500); // 10:A HOLTIMEミリ秒長押し 1.5秒待機
  PushKey("A",    HOLDTIME, 1500); // 11:A HOLTIMEミリ秒長押し 1.5秒待機
  PushKey("A",    HOLDTIME, 1500); // 12:A HOLTIMEミリ秒長押し 1.5秒待機
  PushKey("B",    HOLDTIME, 1500); // 13:B HOLTIMEミリ秒長押し 1.5秒待機
  PushKey("UP",   HOLDTIME, 1500); // 14:↑HOLTIMEミリ秒長押し 1.5秒待機
  PushKey("R",    HOLDTIME, 150 ); // 15:R HOLTIMEミリ秒長押し ほぼ待機なし
  PushKey("R",    HOLDTIME, 150 ); // 16:R HOLTIMEミリ秒長押し ほぼ待機なし
  // ↑ここに繰り返すコマンド列を入れよう
}

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

 

f:id:tangential_star:20201229144836g:plain
f:id:tangential_star:20201229145153p:plain
自動周回で手に入れたBPで「ようきミント」「こだわりスカーフ」などを交換しよう


あとがき

本記事では、Arduino Leonardoを使ったバトルタワー自動周回(BP稼ぎ)について紹介をしました。

パーティーをいくつも作っていると「こだわりメガネ」「こだわりスカーフ」が足りなくなったり、不意の事故で「きあいのタスキ」「ふうせん」などを野生戦で消費してしまったりしますよね。ほかにも、育成用に欠かせない「ようきミント」「いじっぱりミント」なども不足しがちです。このあたりの道具が引き換えられるのはありがたいですね。

 

今回のバトルタワー然り、前回のトーナメント然り、前々回のダイマックスアドベンチャー然り、ポケモンには対人戦の準備に関わる「周回」が切っても切り離せないですね。無論、こういったエンドレスに遊べるコンテンツを通してやりこみや対人戦前の調整をするのが目的になるのでしょうが、アイテム回収を目的にすると少しツライものがあります。この観点において、マクロ対応コントローラーやマイコンは、各人のゲームとの関わり方の選択肢を増やしてくれるので、うまく活用していきたいものですね。

 

本記事が皆様のポケモン自動化ライフの一助となることを願っています。

 

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

 

前回記事:

【Arduino自動化07】トーナメント自動周回【マクロコン代替】

前々回記事:

【Arduino自動化06】完全放置「マックスこうせき」集め【ダイマックスアドベンチャー】

次の記事:

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

導入記事:

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