Arduino使ってポケモン剣盾で金稼ぎとBP稼ぎも自動化（+α）

## 背景
[ポケモン剣盾の孵化作業をArduinoで完全自動化する][1]という記事で孵化厳選の自動化、ポイントアップ（ポイントマックス）量産、レイドバトル自動化はできたが、BP稼ぎと金稼ぎがなかったので自動化した

## 環境
基本的には上記の記事と同じ環境なので、細かいのは上の記事を見たほうがいいです
本体：Arduino Leonard（1個1000円くらい）
ライブラリ：[NintendoSwitchControll][2]

## ソースコード
論より証拠なので2つとも載せてみる

### 金稼ぎ
自動化で稼いだWでゴージャスボールを買って、ポケセン（フレンドリィショップ）に売る
細かい条件とかはソースコードに記載

```cpp:SellLuxuryBall.ino
/**
* Wショップからゴージャスボールを999個買い、ポケセンに全部売るスケッチ
*
* 初期条件は以下の通り
*
* 1. 日付バグでハシノマ原っぱのWショップでゴージャスボールを出すこと
* 2. Wショップの店員の前に待機すること
* 3. ゴージャスボールを持っていないこと
* 4. 十分なWがあること
* 5. Xボタンを押したときに「タウンマップ」が左上にあること
*/

#include <auto_command_util.h>
#include <switch_controller.h>

// 1回売却すると1498500円稼げるので最大7回(max 9999999円)
const int COUNT_LOOP = 7;

void setup(){
    pushButton(Button::B, 100, 10);

    for(int i = 0; i < COUNT_LOOP;i++){
        buyBall();
        goShop();
        sellBall();
        goWattShop();
    }
}

void loop(){}

void buyBall(){
    // 話しかける
    pushButton(Button::A, 1000, 3);
    // ゴージャスボールを買う
    pushHatButton(Hat::LEFT, 1000);
    pushButton(Button::A, 1000, 2);
    pushButton(Button::B, 1000, 4);
    delay(1000);
}

void goShop(){
    // マップを開いてエンジンシティに行く
    pushButton(Button::X, 1000, 1);
    pushHatButtonContinuous(Hat::LEFT_UP, 1000);
    pushButton(Button::A, 2000, 1);
    pushButton(Button::R, 1000, 2);
    pushButton(Button::A, 1000, 2);
    delay(15000);
    // ポケセンに入る
    tiltJoystick(0, -100, 0 , 0, 5000);
    delay(3000);
    tiltJoystick(100, 0, 0, 0, 1000);
    delay(1000);
}

void sellBall(){
    // 話しかける
    pushButton(Button::A, 1000, 2);
    pushHatButton(Hat::DOWN, 1000);
    pushButton(Button::A, 2000);
    // メニュー画面
    pushHatButtonContinuous(Hat::LEFT, 3000);
    pushHatButton(Hat::RIGHT, 1000);
    pushButtonContinuous(Button::R, 3000);
    // ゴージャスボールを選択
    pushButton(Button::A, 1000);
    pushHatButton(Hat::LEFT, 1000);
    pushButton(Button::A, 1000, 4);
    pushButton(Button::B, 1000, 3);
}

void goWattShop(){
    // ポケセンから出る
    tiltJoystick(-100, 0, 0, 0, 800);
    delay(1000);
    tiltJoystick(0, 100, 0 , 0, 5000);
    delay(3000);

    // マップを開いてハシノマ原っぱに行く
    pushButton(Button::X, 1000, 1);
    pushHatButtonContinuous(Hat::LEFT_UP, 1000);
    pushButton(Button::A, 2000, 1);
    pushHatButton(Hat::RIGHT, 100);
    pushHatButton(Hat::UP, 100);
    delay(1000);
    pushButton(Button::A, 1000, 2);
    delay(15000);
    // Wショップに移動する
    tiltJoystick(0, 0, 100, 0, 2000);
    tiltJoystick(0, -100, 0, 0, 800);
    tiltJoystick(-100, 0, 0, 0, 800);
}

void pushButtonContinuous(Button button, int pushing_time_msec){
    SwitchController().pressButton(button);
    delay(pushing_time_msec);
    SwitchController().releaseButton(button);  
    delay(100);
}
```

### BP稼ぎ
バトルタワーの戦闘を自動化
[マクロコントローラで全自動化した記事][3]を見つけたのでこれを~~パクリ~~参考にしました
初期条件が少ないのが魅力的、ついでにおうかんも増やせる

```cpp:BattleTower.ino
/**
* バトルタワー自動周回用スケッチ
*
* 初期条件は以下の通り
*
* 1. バトルタワーの受付の前に待機すること
* 2. レンタルチームに 0000 0000 NBN6 HL を追加すること
* 3. 上記のチームがバトルチーム選択で最初に来るようにすること
*/

#include <auto_command_util.h>

void setup(){
    pushButton(Button::B, 100, 10);
}

void loop(){
    pushButton(Button::ZL, 3000);
    pushButton(Button::A, 1000, 2);
    pushButton(Button::B, 1000);
    pushHatButton(Hat::UP, 1000);
    pushButton(Button::ZL, 3000);
    pushButton(Button::A, 1000);
    pushButton(Button::ZL, 3000);
    pushButton(Button::A, 1000);
    pushHatButton(Hat::UP, 1000);
    pushButton(Button::ZL, 3000);
    pushButton(Button::A, 1000);
    pushHatButton(Hat::UP, 1000);
    pushButton(Button::ZL, 3000);
    pushButton(Button::L,100, 2);
    delay(100);
}
```

## おわりに
いかがでしたか？（迫真）
孵化厳選自動化・ドーピングアイテム量産・アメ量産・BP量産（戦闘アイテムと交換）が可能になったのでこれだけあれば育成も相当楽になるはず
良いポケモン対戦ライフを！！

## 補足
[NintendoSwitchControll][2]ライブラリに付属しているスケッチの修正とか
普通に使ってれば不具合に気づくし自分で修正した人も多いと思うのでおまけみたいなものです

### 孵化厳選
変更点
・卵のサイクル数*7/2[sec] 走り回ると孵化する
・卵未発見時に預けたポケモンが回収されるのを修正
・あずかりやに話しかけられない場合があるのを修正

```cpp:Hatching.ino
/**
* 育て屋から卵を回収→孵化→ボックスに預けるを繰り返すスケッチ
* ボックスに空きがある限り、ポケモンを孵化し続ける
*
* 初期条件は以下の通り
* 1.ハシノマはらっぱにいること
* 2.自転車に乗っていること
* 3.手持ちが1体のみのこと
* 4.Xボタンを押したときに「タウンマップ」が左上、「ポケモン」がその右にあること
* 5.ボックスが空のこと
*/
#include <auto_command_util.h>

const int EGG_CYCLE = 20;
const int TIME_TO_HATCHING_SEC = EGG_CYCLE * 7/2;

void setup(){
    pushButton(Button::B, 500, 5);
    // 初めの卵が出現するまで走り回る
    moveToInitialPlayerPosition();
    runAround(60);
}

void loop(){
    for(int box_line=0; box_line<6; box_line++){
        reciveAndHatchEggs();
        sendHatchedPoemonToBox(box_line);
    }
    moveToNextBox();
}

// 空飛ぶタクシーでハシノマはらっぱに移動
void moveToInitialPlayerPosition(){
    pushButton(Button::X, 1000);
    pushHatButtonContinuous(Hat::LEFT_UP, 1000);
    pushButton(Button::A, 2500);
    pushButton(Button::A, 1500, 2);
    delay(2000);
}

// 初期位置から育て屋さんに移動し卵を受け取る
void getEggFromBreeder(){
    // 初期位置(ハシノマはらっぱ)から育て屋さんのところまで移動
    pushButton(Button::PLUS, 1000);
    tiltJoystick(0, 0, 100, 0, 2000);
    tiltJoystick(30, -100, 0, 0, 800); // 2000(ini) -> 800(chg)
    pushButton(Button::PLUS, 1000);
    // 育て屋さんから卵をもらう
    pushButton(Button::A, 1000, 3); // 4(回) -> 3(回), 卵未発見で詰む場合がある
    pushButton(Button::B, 500, 10);
}

// 初期位置(ハシノマはらっぱ)からぐるぐる走り回る
void runAround(int run_time_sec){
    // 野生ポケモンとのエンカウントを避けるため初期位置から少し移動する
    tiltJoystick(100, 0, 0, 0, 600);
    // delayの秒数がintの最大値を越えないように30秒ごとに実行する
    for(int i=0; i<run_time_sec/30; i++){
        tiltJoystick(100, 100, -100, -100, 30000);
    }
    tiltJoystick(100, 100, -100, -100, (run_time_sec%30)*1000);
}

// 卵が孵化するのを待つ
void waitEggHatching(){
    pushButton(Button::B, 500, 40);
}

// 孵化した手持ちのポケモンをボックスに預ける
// box_line : 何列目にポケモンを預けるか
void sendHatchedPoemonToBox(int box_line){
    // ボックスを開く
    pushButton(Button::X, 1000);
    pushHatButtonContinuous(Hat::LEFT_UP, 1000);
    pushHatButton(Hat::RIGHT, 500);
    pushButton(Button::A, 2000);
    pushButton(Button::R, 2000);
    // 手持ちの孵化したポケモンを範囲選択
    pushHatButton(Hat::LEFT, 500);
    pushHatButton(Hat::DOWN, 500);
    pushButton(Button::Y, 500, 2);
    pushButton(Button::A, 500);
    pushHatButtonContinuous(Hat::DOWN, 2000);
    pushButton(Button::A, 500);
    // ボックスに移動させる
    pushHatButton(Hat::RIGHT, 500, box_line+1);
    pushHatButton(Hat::UP, 500);
    pushButton(Button::A, 500);
    // ボックスを閉じる
    pushButton(Button::B, 1500, 3);
}

// ボックスを次のボックスに移動させる
void moveToNextBox(){
    // ボックスを開く
    pushButton(Button::X, 1000);
    pushHatButtonContinuous(Hat::LEFT_UP, 1000);
    pushHatButton(Hat::RIGHT, 500);
    pushButton(Button::A, 2000);
    pushButton(Button::R, 2000);
    // ボックスを移動
    pushHatButton(Hat::UP, 500);
    pushHatButton(Hat::RIGHT, 500);
    // ボックスを閉じる
    pushButton(Button::B, 1500, 3);
}

// 手持ちが1体の状態から、卵受け取り→孵化を繰り返す
void reciveAndHatchEggs(){
    for(int egg_num=0; egg_num<5; egg_num++){
        moveToInitialPlayerPosition();
        getEggFromBreeder();
        moveToInitialPlayerPosition();
        runAround(TIME_TO_HATCHING_SEC);
        waitEggHatching();
    }
}
```

### ワット稼ぎ
変更点
・ランクバグしたかどうかで切り替えられるようにした
・秒数早めた
・まれに地域設定が変更されてしまいスイッチが再起動するのを修正

（一定回数ごとに自動レポートするコードを追加した方がいいのかもしれない）

```cpp:FarmingWatt.ino
/**
* 巣穴からワットを回収し続けるスケッチ
*
* 初期条件は以下の通り
* 1. 願いのかたまりを投げ入れた巣穴の前にいること
* 2. 巣穴のワットは回収済みであること
*/
#include <auto_command_util.h>

const bool WAS_RANKMATCH_PLAYED = true;

// ワット回収済みのねがいのかたまりを投げ入れた巣穴の前からスタート
void setup(){
    pushButton(Button::B, 100, 10);
}

void loop(){
    if(WAS_RANKMATCH_PLAYED){
        abortRecruiting();
        changeDate();
        gainWatt();
    }else{
        startRecruiting();
        changeDate();
        abortRecruiting();
        gainWatt();
    }
}

void changeDate(){
    // ホーム画面 > 設定
    pushButton(Button::HOME, 500);
    pushHatButton(Hat::RIGHT, 100, 2);
    pushHatButton(Hat::DOWN, 20);
    pushHatButton(Hat::RIGHT, 20);
    pushButton(Button::A, 100);
    // 設定 > 本体 > 日付と時刻
    pushHatButtonContinuous(Hat::DOWN, 2000);
    pushButton(Button::A, 1000);
    pushHatButton(Hat::DOWN, 100, 4);
    pushButton(Button::A, 200);
    // 日付と時刻 > 現在の日付と時刻
    pushHatButton(Hat::DOWN, 20, 4); // 回数多め
    pushButton(Button::A, 500);
    pushHatButton(Hat::RIGHT, 20, 2);
    pushHatButton(Hat::UP, 20);
    pushHatButton(Hat::RIGHT, 20, 5); // 回数多め
    pushButton(Button::A, 50);
    // ホーム画面 > ゲーム画面
    pushButton(Button::HOME, 500);
    pushButton(Button::A, 500);
}

void gainWatt(){
    // ワット回収
    pushButton(Button::A, 1000);
    pushButton(Button::B, 1000);
    pushButton(Button::A, 1000);
}

void startRecruiting(){
    pushButton(Button::A, 3000);
}

void abortRecruiting(){
    pushButton(Button::B, 20, 15);
}
```

### 自動逃し
変更点
・ボックスの数を指定できるように変更
・終わったらスリープモードに入る（時間がかかるのでArduinoのランプで終わったか判別できる）

<font color="Red">**※ポケモンホームでまとめて逃せるため、剣盾内でのみ自動逃しをしたい人向け**</font>

```cpp:ReleasingPokemons.ino
/**
* ボックスのポケモンを全て逃がす
* 
* 初期条件は以下の通り
* 1.メニューを開いていないこと
* 2.Xボタンを押したときに「ポケモン」が左上から1つ右の場所にあること
*/
#include <auto_command_util.h>
#include <switch_controller.h>

const int NUM_BOX = 10;

void setup(){
    // コントローラの初期化
    pushButton(Button::B, 500, 5);

    open_box();

    for(int i = 0; i < NUM_BOX; i++){
        release_all_pokemons_in_a_box();
        // カーソルは「いちらん」を選択中
        move_to_next_box();
    }

    put_the_switch_in_sleep_mode();
}

void loop(){
}

// ボックスを開く
void open_box(){
    pushButton(Button::X, 1000);
    pushHatButtonContinuous(Hat::LEFT_UP, 1000);
    pushHatButton(Hat::RIGHT, 500);
    pushButton(Button::A, 2000);
    pushButton(Button::R, 2000);
}

// ボックス内の今いるカーソルにいるポケモンを逃がす
void release_pokemon(){
    pushButton(Button::A, 500);
    pushHatButton(Hat::UP, 100, 2);
    pushButton(Button::A, 1000);
    pushHatButton(Hat::UP, 100);
    pushButton(Button::A, 1000);
    pushButton(Button::A, 300, 4);
}

void release_all_pokemons_in_a_box(){
    for(int release_poke_num = 0; release_poke_num < 30; release_poke_num++){
        // ポケモンを逃がす
        release_pokemon();
        // カーソルを次のポケモンの場所に移動させる
        pushHatButton(Hat::RIGHT, 100);
        if(release_poke_num%6 == 5){
            pushHatButton(Hat::DOWN, 100);
            pushHatButton(Hat::RIGHT, 100);
        }
    }
}

void move_to_next_box(){
    pushButton(Button::R, 1000);
    pushHatButton(Hat::DOWN, 100, 2);
}

void put_the_switch_in_sleep_mode(){
    SwitchController().pressButton(Button::HOME);
    delay(4000);
    SwitchController().releaseButton(Button::HOME);
    delay(1000);
    pushButton(Button::A, 100);
}
```

[1]:https://qiita.com/interimadd/items/05a81677d177d5a0af70
[2]:https://github.com/interimadd/NintendoSwitchControll
[3]:https://hyperts.net/pokemonswsh-bpmacro/