【ポケモンBDSP】面倒な孵化作業をArduinoで完全自動化する

::: note warn
この記事に記載しているスケッチ例は絶賛調整中のため、途中で動作しなくなる場合があります。
:::

# はじめに
みなさんポケットモンスター、ブリリアントダイヤモンド・シャイニングパールをプレイしておりますでしょうか！
原作のダイヤモンド・パールが発売されて15年という月日が経過した今リメイクが発表されましたが
前作の剣盾環境と比べて融通が効かない箇所が多く、厳選等の作業が煩雑なように感じています。

そこで、今作でも大変面倒くさい孵化作業を全自動化するための方法をこちらに記載いたします。

この記事が「参考になったよ！」という方はLGTM、フォロー、記事ツイート、記事の埋め込み等々、ぜひよろしくお願いします！

# 必要なもの
すべてAmazonで購入が出来ます。Amazonのリンクも載せておきます。

**Arduino Leonardo (ATMega32U4)**
[Arduino Leonardo (3,200円前後)](https://amzn.to/3czfOFK)
<details><summary>Arduinoボードを安く購入したい方向け (2021/11/21記載)</summary>
[互換ボード / Rasbee ATmega32U4 (1,000円前後)](https://amzn.to/30ETrvQ)
[コード不要の小型互換ボード / DM Micro-AU (1,500円前後)](https://amzn.to/3CvTk2U)
※複数個同封の物のほうが1個あたりの値段は落ちます。４，５個購入しておき、用途ごとに差し替えるようにするとめちゃくちゃ便利です。</details>

**ポケットモンスターBDSP**
[ポケットモンスター・ブリリアントダイヤモンド](https://amzn.to/3qWA3FJ)
[ポケットモンスター・シャイニングパール](https://amzn.to/3FqWnLI)

**Nintendo Switch (Liteでも可)**

オプション
[USB Type-A -> Type-C変換ケーブル (750円前後)](https://amzn.to/3nAMlS3)


# 導入方法
### Arduino IDEのインストール
Arduino IDEを[公式のページ](https://www.arduino.cc/en/software)からダウンロードしてインストールします。

### ライブラリのインストール
Arduinoライブラリフォルダ(/Users/ユーザー名/Documents/Arduino/libraries)にライブラリをCloneかダウンロードして展開しておきます。
[NintendoSwitchController](https://github.com/interimadd/NintendoSwitchControll)

### デバイスIDを書き換える
1. Arduinoアプリを起動
2. [ファイル] -> [スケッチ例] から適当に一つ選択して開く
3. [スケッチ] -> [スケッチの場所を開く]をクリック
4. ***board.txt***を適当なメモ帳で開く
5. 画像の箇所を以下のように書き換える

```
leonardo.vid=0x0f0d
leonardo.pid=0x0092
```

(左: 初期設定 / 右: 書き換え後)
![書き換える場所](https://user-images.githubusercontent.com/14195687/112593510-f4293f00-8e4a-11eb-82e2-ae1317b5a618.jpg)

:::note alert
画像の箇所をすべて書き換え終わらないまま、スケッチを書き込んで動作しないことがよくあるので注意が必要です。
:::

### 新しいスケッチを作成し、以下のスケッチを書き込む
自動孵化スケッチをコピー＆ペーストして、ボードに書き込むだけで完了です。

```auto_hatching.ino
/**
* 育て屋から卵を回収→孵化→ボックスに預けるを繰り返すスケッチ
* ボックスに空きがある限り、ポケモンを孵化し続ける
*
* 初期条件は以下の通り
* 1.手持ちが1体のみのこと
* 2.Xボタンを押したときに「タウンマップ」が左下、「ポケモン」が左上から２個目にあること（デフォルト）
* 3.選択中のボックスが空のこと
* 4.Xでメニューを開くと図鑑が選択されていること
* 5.廃人ロードのトレーナーはすべて倒しておくこと
* 6.外にいること
*/
#include <auto_command_util.h>

const int ZUI_TOWN_MAP_POSITION_X = 19;
const int ZUI_TOWN_MAP_POSITION_Y = 17;
const int TIME_TO_HATCHING_SEC = 120;

// そらとぶでズイタウンに移動
void moveToInitialPlayerPosition() {
    // マップを開く
    pushButton(Button::X, 1500);
    pushHatButtonContinuous(Hat::LEFT_UP, 2000);
    pushHatButton(Hat::DOWN, 200);
    pushButton(Button::A, 1500);

    // マップのカーソルを左上に設定
    pushHatButtonContinuous(Hat::LEFT_UP, 4000);

    // ズイタウンまでカーソルを移動
    for (int x = 0; x < ZUI_TOWN_MAP_POSITION_X; x++)
    {
        pushHatButton(Hat::RIGHT, 0);
    }
    for (int y = 0; y < ZUI_TOWN_MAP_POSITION_Y; y++)
    {
        pushHatButton(Hat::DOWN, 0);
    }
    
    // 空を飛ぶ実行
    pushButton(Button::A, 1000);
    pushButton(Button::A, 7500);
}

// 廃人ロード移動初期位置に設定
void initHaijinRoad() {
    pushHatButtonContinuous(Hat::LEFT, 1000);
    pushButton(Button::PLUS, 700);
    pushHatButton(Hat::RIGHT, 100);
    pushHatButtonContinuous(Hat::UP, 6000);
}

// 初期位置から育て屋さんに移動し卵を受け取る
void getEggFromBreeder() {
  pushHatButtonContinuous(Hat::LEFT, 2300);
  pushHatButtonContinuous(Hat::UP, 2600);
  pushHatButtonContinuous(Hat::LEFT, 500);
  pushButton(Button::A, 500, 8);
  pushButton(Button::B, 500, 10);
}

// 廃人ロードを往復
// 往復しつつ、B連打を繰り返す
void loopHaijinRoad(int loopCount = 20) {
  for (int i = 0; i < loopCount; i++) {
    // 移動開始sp
    for (int t =0; t < 2; t++)
    {
      if (t == 0) {
        SwitchController().pressHatButton(Hat::DOWN);
      }
      else {
        SwitchController().pressHatButton(Hat::UP);
      }

      // B連打しつつ移動完了を待つ
      // cのカウントは0.1秒ごとにカウントアップする仕様
      for (int c = 0; c < 70; c++) {
        pushButton(Button::B, 0);
      }
    
      SwitchController().releaseHatButton();
    }
  }
}

// 孵化した手持ちのポケモンをボックスに預ける
// box_line : 何列目にポケモンを預けるか
void sendHatchedPoemonToBox(int box_line){
    // ボックスを開く
    pushButton(Button::X, 1500);
    pushHatButtonContinuous(Hat::LEFT_UP, 2000);
    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, 2000);
    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);
}

void setup() {
    pushButton(Button::B, 500, 5);

    // 初回は廃人ロードで卵が見つかるのを待つ
    moveToInitialPlayerPosition();
    initHaijinRoad();
    loopHaijinRoad(2);
}

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

// 孵化シーケンスを実行
void execHatchingSequence(){
    for(int box_line=0; box_line<6; box_line++){
        reciveAndHatchEggs();
        sendHatchedPoemonToBox(box_line);
    }
    moveToNextBox();
}

void loop() {
  execHatchingSequence();
}
```

# 使い方
**事前準備**
 1. 手持ちが1体のみのこと
 2. Xボタンを押したときに「タウンマップ」が左下、「ポケモン」が左上から２個目にあること（デフォルト）
 3. 選択中のボックスが空のこと
 4. Xでメニューを開くと図鑑が選択されていること
 5. 廃人ロードのトレーナーはすべて倒しておくこと
 6. 外にいること

**自動操作を開始する**
`Nintendo Switch`に`Arduino Leonardo`を接続すると操作が開始されます。

::: note info
Arduinoと接続する時に、ジョイコンがワイヤレスモードになっていたり、外部コントローラーを接続したりしないでください。
ポケモンはコントローラーが１台だけ認識される仕様のため、Arduinoを接続しても操作がされないことがあります。
:::


# 参考記事

https://qiita.com/interimadd/items/05a81677d177d5a0af70

https://github.com/interimadd/NintendoSwitchControll#nintendoswitchcontroller