【BDSP自動化01】バトルサーチャーで自動お金稼ぎ【ダイパリメイク】

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

今回は、ポケモンBDSP(ダイパリメイク)の「お金稼ぎ」を自動化しました。

 

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

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

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

 

概要

「バトルサーチャー」を使って、212番道路にいるジェントルマンのサダミツと再戦し続けて、賞金をいただく自動化です。約30分完全放置で244,480円*1稼げました(時給換算459,000円)

f:id:tangential_star:20211123222346g:plain
f:id:tangential_star:20211123222414p:plain
バトルサーチャーで212番道路のジェントルマンと再戦し続ける
※金額はヒードラン撃破/捕獲の前。撃破すれば1試合24480円まで跳ね上がるぞ

なお、上記の金額は、殿堂入り後・ヒードラン捕獲前のROM*2で、先頭ポケモンには「おまもりこばん」*3を持たせた1ターン周回です。ヒードラン撃破後であれば、更に効率よく稼げると思われます。

f:id:tangential_star:20211123213633g:plain

約30分の完全放置で、おこづかいが244,800円増えた!

 

もくじです。本ブログでは、ポケモンBDSPの自動化記事は初めてですね。

例のごとく「ソースコードだけ欲しい」という方はどうぞ→ソースコード
自動化の方法が知りたいという方はどうぞ→「自動化の準備」
動いている様子ももう少し詳しく見たいという方はどうぞ→「動作確認(自動化の様子)」

 

ポケモンBDSPのお金稼ぎには「バトルサーチャー」

ダイパリメイクのお金稼ぎの方法として、もっとも有力なのはトレーナーとの戦闘です。

ポケモン剣盾とは違い、今作では一部の例外を除いてお金稼ぎの方法がほとんど無いのです。一応、ポケモン剣盾のウッウロボ*4のように、「地下大洞窟」の道具交換で「台座」→「べにたま」→「わざマシン」の購入/交換/売却時の差額*5で稼ぐ方法もあるのですが、そもそも元手となるお金が必要という点や、しっかり差額を計算しながら購入しないと赤字になるリスクがあるなどの欠点があります。代わりに、今作では久しぶりに「バトルサーチャー」が登場しました。使うと、近くにいる再戦可能なNPCと確率*6で再戦ができるようになるアイテムです。

f:id:tangential_star:20211123233700p:plain
f:id:tangential_star:20211123233704p:plain
バトルサーチャーも「たいせつなもの」なので+ボタンに「とうろく」できる

今作でも、バトルサーチャーを使ってくださいと言わんばかりにジェントルマンとマダム、おぼっちゃまとおじょうさま、のように高額の賞金をくれる2人が1組で同じところにいるため、効率良くおこづかいを稼ぐこともできます。

 

今作の所持金は100万円に届かない(999999円でカンスト

ダイパリメイクの最大の特徴は、その呼び名が冠する通り、リメイクであること。これはゲームシステムなども同様です。2Dマップ、SDキャラなど端々に特徴が現れていますが、中でも、使い勝手という意味で決定的に違うのが、第4世代までと同様におこづかいが999,999円でカンストしてしまうこと。第5世代以降は9,999,999円と1桁違うため、剣盾ぶりの新作としてガッツリ(対戦・ポケモン収集などで)遊ぼうにも、この上限が意外とネックになります。

f:id:tangential_star:20211124002500p:plain
f:id:tangential_star:20211124002507p:plain
第4世代までと同様に999999円が所持上限。1000円のボールでもカツカツだ

タウリンなどの努力値振り*7アイテムも、BP交換以外では、トバリシティで定価(9,800円)で買わねばならず、地下大洞窟での捕獲用ボールの購入も費用がかさみます。

特に今作は剣盾と同様に野生で出現するポケモンのレベルが非常に高い反面、「クイックボール」や「ダークボール」などのもっぱら捕獲用のボールを大量に手に入れる方法が無いことから、捕獲するためにもお金を稼がなければならないストレス(1個1000円)が生まれます。

f:id:tangential_star:20211124113848p:plain

所持金の上限が厳しく、高額なドーピングアイテムの買いだめは一苦労

上述の通り、今作では剣盾と比しても、おこづかいの上限がシビアで「買いだめ」が難しいです。特に、単価の高いドーピングアイテムはその影響が顕著で、1匹分の努力値510を賄うだけで499,980円、すなわち、おこづかい上限額の半分を要します

従って、効率良くお金を稼ぎつつ、アイテムを集めつつ、捕獲・育成しつつ、という「やりくり」が必要ですから、今回のような「片手間に放置できる自動化」は大いに価値があると考えます(突然の持論)。

 

自動化の準備

さて、おまたせしました。ここからは自動化に向けての準備です。

なお、筆者の検証環境はあくまでも「殿堂入り後」「ヒードラン捕獲前」のタイミングとなりますのでその点、ご了承くださいませ。

バトルサーチャーを「下」、じてんしゃを「左」に「とうろく」する

まずは、今回の肝となる「バトルサーチャー」「じてんしゃ」についてです。これらは「たいせつなもの」ポケットにあるアイテムで、212番道路までシナリオが進んでいればすでに所持しているはずです。

今回の自動化では、バトルサーチャーを「下」、じてんしゃを「左」にそれぞれ割り当てておいてください。やり方としては、たいせつなものポケットを開き、「とうろく」したいアイテムを選択することで、+ボタンを押した際のショートカットを4つまで登録することができます。

f:id:tangential_star:20211123233704p:plain
f:id:tangential_star:20211123233700p:plain
「じてんしゃ」を左、「バトルサーチャー」を下、にそれぞれ登録しておこう

ちなみに余談ですが、アイテムを1つだけ「登録」した場合は、「+」ボタンを押した際にダイアログが出ずに即座に使用できます。ポケトレなど即座に使いたいアイテムがある場合は重宝するので、覚えておくと良いと思います。

ペラップを1ターンで倒せるポケモンを用意する

手持ちの先頭ポケモンで1撃でペラップを倒せるようにします。具体的には、1番目の技を今回の周回で使いたい技に並び替えておきます。

この際に注意すべきは、確実に1ターンで周回させるため、命中が100または必中技を使うようにしましょう。命中率95*8や90ではダメですし、命中率に下降補正がかかる特性「はりきり」などは注意しましょう。ペラップ自身はそれほど耐久も高く無いので十分なレベル差があれば、旅パでもタイプ一致技で確1にできます。

なお、もらえる賞金に直結するので、もし可能なら「おまもりこばん」をもたせることも忘れずにしておきましょう。もちろん、1ターンで周回できることが前提なので、火力補正アイテムや命中率の補正アイテムをもたせることの方が重要です。

f:id:tangential_star:20211124011042p:plain
f:id:tangential_star:20211124011049p:plain
安定した1ターン周回のためにも、命中率は100%の技を使おう。「おまもりこばん」も忘れずに
f:id:tangential_star:20211124010140p:plain
f:id:tangential_star:20211124010132p:plain
サダミツが繰り出すのはペラップ1匹だけ。十分なレベル差があれば抜群じゃなくてもOK
ヒードラン撃破前なら、ペラップはLv.40しかないぞ。撃破後でもLv.60だ。

212番道路でいろいろ準備

戦闘用のポケモン・バトルサーチャーの登録が終わったら、まずは、今回お世話になるジェントルマンのサダミツの元まで向かいます。

最寄りはヨスガシティです。ひたすら南へ木々の合間を進み、ジェントルマンとマダムが立っているところまで向かいます。2本の木がある角に着けばOKです。なお、自転車は4速ではなく3速(=早い方)にしておきます。他作品でいう「マッハ自転車」の方です。

着いたら、自転車から必ず降りてください。また、左上の角に主人公が立つように調整してください。

f:id:tangential_star:20211124111158g:plain
f:id:tangential_star:20211124111316p:plain
ヨスガシティから南に行き、ジェントルマンのサダミツの元へ。2本の木が目印だ

その後、「設定」画面から「はなしのはやさ」「せんとうアニメ」の項目をそれぞれ「はやい」「みない」に設定しておきます。ここまで終われば、ゲーム側の準備は完了です。

f:id:tangential_star:20211124101505p:plain
f:id:tangential_star:20211124101501p:plain
「はなしのはやさ」は「はやい」、「せんとうアニメ」は「みない」にしておく

 

ソースコードの「PP」部分、「戦闘時間(A連打時間)」などを修正する

ソースコード後述の通りですが、皆様の環境に応じてあらかじめ2箇所ほど修正をする必要があります。とは言っても、基本的にはPP部分だけ。ご自身で準備したポケモンのPP上限数を「PP」に入れるだけです。

なお、筆者の体感によればバトルサーチャーでの再戦に応じてくれる確率はおおよそ50%のため、実際にはPP数×1.3~1.5倍程度の数字をいれておくと、無駄なく周回してくれる可能性が高くなります(自己責任でお願いします)。筆者は1.4倍で周回しています。

// ★何試行するか?=先頭ポケモンの1つ目の技のPPを入れてください。
volatile int PP = (20*1.4); 
// 先頭ポケモン(1番目の技)の残りPP。このPPが0になったらループを終了します。
// ※一撃で倒す想定なので、必中or命中100の技を使ってください。また、「はりきり」などの命中が下がる特性には要注意です。
// ※バトルサーチャーに応じる確率が約1/2(筆者検証)なので、
//   実際のPP×1.4倍程度で指定しておくのも良いかもしれません(もちろん自己責任)

また、バトルサーチャーとじてんしゃについて、「+」ボタンのとうろく先の向きが違う場合は、それぞれ「BATTLE_SEARCHER_KEY」「BICYCLE_KEY」に"Right", "Left", "Up", "Down"いずれかを必要に応じて更新してください。

なお、戦闘中は常にA連打をするプログラムになっていますので、何秒間Aを連打し続けるかは、お使いの環境に合わせて「BATTLE_ELAPSED_TIME」を修正してください。

// ★基本設定
#define BATTLE_SEARCHER_KEY "Down" // 「バトルサーチャー」の登録位置
#define BICYCLE_KEY "Left"         // 「じてんしゃ」の登録位置
#define BATTLE_ELAPSED_TIME (45)   // [s] 何秒間、A連打で戦い続けるか?

以上で、プログラム側の準備も完了です。お疲れさまでした。あとは、このプログラムをArduino Leonardoに書き込み、Switchと接続するだけです。

 

動作確認(自動化の様子)

本章では、ArduinoをSwitchに差し込んだ際の画面の流れを紹介します。ソースコードが早く欲しい!という方はどうぞ→ソースコード

 

Arduinoを差し込むと、まずRボタンが何回か押される挙動をします。すなわち、ポケッチが開いたり閉じたりしますが、慌てずそのまま放置してください(正常な動作です)。これは、Nintendo Switchの仕様で、新たに挿したコントローラーを認識するためにキー入力が必要だからです。

プログラムではRキーとLキーをカチャカチャと7回ほど入力するように作っています。

f:id:tangential_star:20211124134605g:plain

SwitchにArduinoを挿すと、ポケッチが開いたり閉じたりする
歩数計」を開いておくと、動きがわかりやすいぞ

その後は、プログラム内に書き込んだ「PP」の数だけ、バトルサーチャーによるジェントルマンのサダミツとの再戦を自動で繰り返してくれます。

f:id:tangential_star:20211124143229g:plain

「バトルサーチャー」を使ってジェントルマンのサダミツに再戦の申し込み

f:id:tangential_star:20211124143244g:plain

ジェントルマンのサダミツを撃破&賞金をいただく(A連打で1ターン周回)

f:id:tangential_star:20211124143257g:plain

対戦後は「じてんしゃ」に乗って歩数カウントを高速チャージ!

 

ソースコード

おまたせしました。ソースコードです。必要に応じて「★修正ここから★」から「★修正ここまで★」まで修正をしてお使いください。

/* 
 *  ★ポケモンBDSP Arduino Leonardoによる自動お金稼ぎ★
 *  
 *  ポケモンやしき(じまんのうらにわ)横にいるジェントルマン(サダミツ氏)と
 *  バトルサーチャーで再戦し続けて、賞金を稼ぎ続けるプログラムです。
 *  実績として、48試行中、24試行で再戦に応じてくれました。
 *  ※ヒードラン撃破前の参考数値:賞金額は244800円稼ぎ=時給換算で459000円)
 *  ※今作は、おこづかいの所持上限が999999円なので、2時間程度でカンストできます。
 *    
 *  
 *  先頭ポケモンのPPを上限としてバトルサーチャーを使い続けます(当該部のプログラムを修正してお使いください)。
 *  ポケッチを歩数カウンタ画面にセットしておくと、バトルサーチャーのチャージの様子が面白いです。
 *  
 *  事前準備
 *  ・じまんのうらにわの側道(212ばんどうろ)にいるジェントルマンとマダムを倒しておく
 *  ・バトルサーチャーを、+ボタンの「下」に登録しておく
 *  ・じてんしゃを、+ボタンの「左」に登録しておく
 *  ・ジェントルマンすぐ左の2本の木の真下でセーブしておく。
 *  ・手持ちの先頭を十分に強いポケモンにしておく(1つ目の技でワンパンできること)
 *  ※レベルアップが起こり得るので、可能なら手持ちは1匹だけ、Lv100にしておくのが望ましいです  
 *  
 *  注意事項
 *  ・自転車からは降りておくこと&自転車は3速(早い方)にしておくこと。
 *  ・バトルサーチャーの仕様上、確率で再戦できません。
 *  ・このプログラムでは、マダムは無視しています(安定性向上のため)
 *  
 *  設定
 *  ・はなしのはやさ:はやい
 *  ・もじモード:ひらがな
 *  ・せんとうアニメ:みない
 *  
 *  (c) 2021 ますたーの忘備録
 *  https://tangential-star.hatenablog.jp/
*/

#include <SwitchControlLibrary.h>

#define HOLDTIME (95)     // 1回のキー入力の長押し時間
#define RENDA_CYCLE (150) // A連打の間隔(ミリ秒)

// ★修正ここから★-----------------

// ★何試行するか?=先頭ポケモンの1つ目の技のPPを入れてください。
volatile int PP = (20*1.4); 
// 先頭ポケモン(1番目の技)の残りPP。このPPが0になったらループを終了します。
// ※一撃で倒す想定なので、必中or命中100の技を使ってください。また、「はりきり」などの命中が下がる特性には要注意です。
// ※バトルサーチャーに応じる確率が約1/2(筆者検証)なので、
//   実際のPP×1.4倍程度で指定しておくのも良いかもしれません(もちろん自己責任)


// ★基本設定
#define BATTLE_SEARCHER_KEY "Down" // 「バトルサーチャー」の登録位置。「+」ボタンで呼び出した時の登録先
#define BICYCLE_KEY "Left"         // 「じてんしゃ」の登録位置。「+」ボタンで呼び出した時の登録先
#define BATTLE_ELAPSED_TIME (45)   // [s] 何秒間、A連打で戦い続けるか?(話しかけ~戦闘~フィールドに戻るまでの時間)
// ※登場時のジャンプ(なつき)、「こちらを向いてうなずいた」「きゅうしょにあたった!」「褒めてもらおうときゅうしょに当てた!」「こうかはばつぐんだ!」など
//  あらゆる遅延を考慮して十分に長い数字を入力してください。
//   ★特に、控えポケモンを含む【レベルアップ】には十分に注意してください。


// ★下記、自転車でのチャージ部分の設定 楽しい!!✌('ω'✌ )三✌('ω')✌三( ✌'ω')✌
#define ROTATE_CYCLE (165)        // [ms] 1回転の必要ミリ秒を指定。
#define CHARGE_ELAPSED_TIME (7.2) // [s] 何秒間、ぐるぐるチャージするか? (最短:7秒)

// ★上級設定
#define BEGIN_TRAINER_CARD (false) // トレーナーカード画面から始めた場合の設定。開始時にBを押してくれるだけです


// ★修正ここまで★-----------------


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 die(void);


void setup() {

  if(BEGIN_TRAINER_CARD){
    // コントローラーとして認識されるためにRLを7回ほどカチャカチャする
    for(int i=0;i<7;i++)PushRL(300);
    delay(1200);
    PushKey("B", HOLDTIME, 1000);
    PushKey("B", HOLDTIME, 1300);
    
  }else{
    // コントローラーとして認識されるためにRLを7回ほどカチャカチャする
    for(int i=0;i<7;i++)PushRL(300);
    delay(1200);
  }

  // Rを長押しして、ポケッチを収納
  PushKey("R", 1300, 1000);

  // Rを押してポケッチ出現
  PushKey("R", HOLDTIME, 1000);

}

void loop() {
  int i=0;
  unsigned long int current_time=0;
  unsigned long int start_time=0;
  unsigned long int temp_time=0;
  int isholding = 0;
  float holdA = 0;
  float temp_deg = 0;

  // ★バトルサーチャー使用
  PushKey("+", HOLDTIME, 1000);
  if(strcmp(BATTLE_SEARCHER_KEY,"+")!=0){
    PushKey(BATTLE_SEARCHER_KEY, HOLDTIME, 1200); // バトルサーチャーを使用。
  }
  delay(300); // 予備待機。
  PushKey("A",    HOLDTIME, 400); // 「しょうぶの じゅんびが できていない みたい」対策
  // PushKey("A",    HOLDTIME, 400); // ▼ 対策(心配な人は入れよう)
  PushKey("A",    HOLDTIME, 400); // 「また あとに しよう!」対策

  // ★ジェントルマンに話しかける
  PushKey("Right",1150, 400); // 4歩右へ。
  PushKey("Down", HOLDTIME, 400); // 下を向いて
  
  // ★ジェントルマンと戦闘(指定時間だけA連打)
  for( start_time=millis(), current_time=start_time, isholding=0 ; current_time - start_time < (unsigned long)BATTLE_ELAPSED_TIME*1000UL ; current_time=millis() ){

     // RENDA_CYCLEミリ秒ごとにAを押す
     temp_time = (current_time - start_time) % (RENDA_CYCLE+1); // 経過ミリ秒のRENDA_CYCLE剰余を計算
     holdA = (float)temp_time / (float)RENDA_CYCLE * 100.0; // 剰余から100%に変換
     if( holdA <= 35.0 ){ // RENDA_CYCLEの3.5割の時間はA押し、それ以外はAを離す
       if(!isholding){
         SwitchControlLibrary().PressButtonA();
         isholding = 1;
       }
     }else{
       if(isholding){
         SwitchControlLibrary().ReleaseButtonA();
         isholding = 0;
       }
     }
  }
  if(isholding) SwitchControlLibrary().ReleaseButtonA();

  // ★戦闘終了。PPを1減らす。
  PP--;
  PushKey("B",HOLDTIME, 400); // 会話していたらキャンセル
  PushKey("B",HOLDTIME, 400); // 会話していたらキャンセル
  PushKey("B",HOLDTIME, 400); // 会話していたらキャンセル


  // ★所定の位置まで戻る
  PushKey("Left",1800, 400); // 左へ。
  PushKey("Up", 400, 400);   // 上へ(ズレ防止用)

  // ★試行回数終了時は待機。
  if(PP <= 0) die(); // 残PPが0になったら処理終了

  // ★チャージ(100歩あるくよ)

  // ★自転車に乗る
  PushKey("+", HOLDTIME, 1000);
  PushKey(BICYCLE_KEY, HOLDTIME, 400);

  // ★ぐるぐる
  for( start_time=millis(), current_time=start_time ; current_time - start_time < (unsigned long)CHARGE_ELAPSED_TIME*1000UL ; current_time=millis() ){
     // ROTATE_CYCLEミリ秒ごとに1周スティック回転
     temp_time = (current_time - start_time) % (ROTATE_CYCLE+1); // 経過ミリ秒のROTATE_CYCLE剰余を計算
     temp_deg = (float)temp_time / (float)ROTATE_CYCLE * 360.0; // 剰余から360°角度に変換
     TiltLeftStick( (int)temp_deg, 1.0, 0, 0); // 経過時間とROTATE_CYCLEの周期に応じた角度に倒す
  }
  TiltLeftStick( 330, 1.0, 1000, 65); // 所定の位置の方向にスティック入力(左上30度=330度)
  TiltLeftStick( 0, 0.0, 0, 0); // スティックを初期位置に戻す

  // ★自転車を降りる
  PushKey("+", HOLDTIME, 1000);
  PushKey(BICYCLE_KEY, HOLDTIME, 1200);

  // ★所定の位置に戻る
  PushKey("Left", 500, 250);
  PushKey("Up", 500, 250);

}

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 die(void){
  if(BEGIN_TRAINER_CARD){
    PushKey("X", HOLDTIME, 550);
    PushKey("A", HOLDTIME, 350);
  }
  for(;;)delay(1000);
}

 

あとがき

今回は、ポケモンBDSPの「お金稼ぎ」の自動化を紹介しました。

所持金の上限まで4世代に準拠するとは…恐るべし「リメイク」ですね(ピカブイですら100万を超えて所持できたのに…)。ちょっとだけ時流を遡っている感じが否めないですね。なお、本ブログで今後、BDSPの自動化を実装・紹介していくかは、殿堂入り後の諸々をクリアしてから、ゆっくりと検討していきます。

 

 

いやー!!2021年11月19日(金)、ついにポケモンBDSPが販売されましたね!(いまさら)。

皆様、ダイパリメイクは遊んでらっしゃいますでしょうか?私はもちろん、ブリリアントダイヤモンド・シャイニングパール共に、「ダブルパック」で買いました。発売日の11月19日には有給を取り、しっかりと遊びました!遊びごたえもバッチリで、シナリオクリアだけで3日間も使ってしまいました。

まだの人、見送っている人は、ぜひ買いましょう!笑→Amazonで買う!

f:id:tangential_star:20211124162043j:plain

筆者はもちろんBDSPの両方を「ダブルパック」で購入したぞ

昨今の噂の通り、グラフィックやらUIやら上記のシステム面やら、かなり難があるところもありますが、今作はジムリーダーも四天王も強く、やりごたえのある「古き良きポケモン」が文字通りリメイクとして帰ってきています。本記事では紹介しませんが、当時の「なぞのばしょ」に迫るような大規模な「バグ」も多く、そういう意味でも当時を懐かしむことができます。

バグのみならず、もちろんゲーム面も改良されていて、「ヒコザルの『グロウパンチ』修得」「ひでんわざの廃止」など、旅パの編成自由度が増したこともあり、ビーダル*9を連れて行かずに旅パを作れるなど、6匹をフルで使って楽しく遊ぶことができました。その分、四天王・チャンピオンは本当に強かったですが…。

f:id:tangential_star:20211124162354j:plain
f:id:tangential_star:20211124162351j:plain
筆者がBDSPクリアにかかった時間は30時間超!(エンジョイ勢)
クリア時のパーティメンバーのレベルから、チャンピオン戦の難しさがにじみ出る

 

リメイクの目玉である「地下大洞窟」も試みとしては非常に面白く、オンラインプレイもできるので、思わず何十分も潜っていたくなるような仕掛けとして仕上がっています。

剣盾からポケモンを始めた方にとっては、おそらくライバルが主人公の弱点属性のポケモンを使うことにも(逆に)新鮮さを覚えるかもしれません。

そういう意味でも、DP時代のポケモンを知っている方には(ゲーム性はともかく)、満足できる内容になっていると思いますし、オススメです。今は発売まもなくですから、攻略Webサイトも増えてきていますし、私もこうして記事執筆できるまではシナリオも進められたわけですから、今がチャンスですね。

 

もう一度言います。買わずに見送っている人は、ぜひ買いましょう!!→Amazonで買う!

むしろ、「ポケモンLegendsアルセウス」の発売まであと3ヶ月しか無いですから、今を逃すと遊ばずに終わってしまうかもしれませんよ!(もったいない!)

 

 

最後はBDSPの宣伝みたいになりましたが、いちポケモンファンとしては楽しく遊べたので良かったです。もちろん、ポケモン剣盾もBDSPも遊んでいけたらと思いますので、これからも本ブログをよろしくお願いします!

もし、BDSP・剣盾で自動化のリクエストや、応援メッセージなどがあれば、コメントをいただければと思います(マイペース更新なのでリクエストについては仕上げられるか分からないですが…)

 

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

 

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

ポケモン「剣盾」の自動化:ポケモン剣盾Arduino自動化 カテゴリーの記事一覧

YouTubeチャンネル:ますたーの忘備録 - YouTube

---

*1:殿堂入り後・ヒードラン撃破前の参考金額です

*2:ヒードランを捕獲または撃破することで、NPCからもらえる賞金額がさらに上がるという報告があります

*3:「おまもりこばん」は、もらえる賞金額が2倍になるアイテムで、今作BDSPではヨスガシティ北にある「ふれあいひろば」に落ちています

*4:クラボ4つ(=320円)をウッウロボに入れて「わざレコード88(=3000円)」を作って売れば1個あたり2680円の差益が発生、みたいな方法

*5:ダイパリメイクでは、地下大洞窟で「べにたま」と交換できる「台座」を、トバリデパート4階で1000円で買える仕様があります

*6:確率は公表されていませんが、筆者の体感では今作は50%程度と思われます

*7:今作の努力値振りは第4世代の頃のように「ポケトレ」やら「バトルサーチャー」やらの方が効率が良さそうです

*8:「エアカッター」や「ほのおのキバ」「エアスラッシュ」などが命中率95の代表的な技です

*9:DP・Pt当時はフィールドで8種類の「ひでんわざ」を使うためにポケモンの技スペースを割く必要があり、ビーダルなどの4つ以上のひでんわざを修得できるポケモンは実質マストでした。特にビーダルは6種類のひでんわざを修得できるため、旅パの穴埋めには最適でした

【Arduino自動化22】ウッウロボ自動合成【アメざいく・わざレコード・オシャボ集め】

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

今回は、ウッウロボにレシピ通りに道具を入れ続ける自動化の記事です。

本ブログを2020年11月7日に公開してから、本日、おかげさまで1周年を迎えました!!。これからもよろしくお願いいたします

 

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

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

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

 

概要

ウッウロボにアイテムを入れて、道具を作り続けます。もちろん、アイテムを入れる順番はプログラムで指定可能です。
※ウッウロボの特性上「セーブ」されます。くれぐれも自己責任でご利用ください

f:id:tangential_star:20211031182345p:plain
f:id:tangential_star:20211031181836g:plain
複雑なレシピでもしっかりとウッウロボに自動投入してくれる。アメざいく厳選もお手の物だ

もくじです。

今回も余談が多め、というかウッウロボの解説記事っぽくなりました。1周年にふさわしいボリューム感です。私のブログって、実は「自動化ブログ」じゃなくて「攻略記事ブログ」なのかもしれませんね。

例のごとく、ソースコードだけ欲しいという方はどうぞ→読み飛ばす

 

ウッウロボにアイテムを4つ入れると別のアイテムに変換できる

まずは、皆様も世話になっている「ウッウロボ」について紹介です。もちろん読み飛ばしOK→「ウッウロボのレシピの仕組み」まで読み飛ばす

ウッウロボとは、DLC第1弾「ヨロイの孤島」にて登場した、アイテム4つを入れると、別のアイテムに変換してくれるロボットです。その名が冠する通り、ウッウをモチーフにしたロボットで、獲物を丸呑みした状態から吐き出すまでの一連の動作を体現してくれます。

さて、このウッウロボですが、奇怪な見た目も然ることながら、本編(ガラル本土)だけでは入手方法が限られていたアイテム(特に「かえんだま」「どくどくだま」など)や、使い捨て故に個数が必要な「わざレコード」が全種類入手ができることも判明し、DLC公開当時は話題になりました。そして、DLC第1弾の目玉はなんと言っても「ぼんぐり」の登場。ウッウロボに入れる4つの道具をすべて「ぼんぐり」にすることで極低確率(0.1~1%)ながら、いわゆる「オシャボ」も手に入れることができるようになりました。

こういった経緯もあり、DLC公開が始まるや否や、有志によるウッウロボの排出アイテムの法則性探し、いわゆる「レシピ」の開発・調査が始まりました。

f:id:tangential_star:20211031214748p:plain
f:id:tangential_star:20211031182345p:plain
ガラル本土では入手できない&入手方法が限られていたアイテムも、ウッウロボならお手の物

ウッウロボのレシピの仕組み

ここで、レシピを紹介する前に、ウッウロボのレシピの仕組みについて紹介します(仕組みは分かっている、という方は→「ウッウロボで生成できるアイテムの一覧」まで読み飛ばす)。早く作り方が知りたい、という方は→「ウッウロボのレシピの作り方」まで読み飛ばす

アイテムを4つ入れることで新しいアイテムを出してくれるウッウロボですが、排出されるアイテムの種類はモンスターボール22種類を除く、純粋なアイテム「だけ」で実に180種類以上あります*1。このウッウロボから排出されるアイテムですが、実は180種からのランダムではなく、入れたアイテムの順番・レア度によって決定されています(=レシピがあります)。

実は、ポケモン剣盾の「アイテム」には、ポケモンのタイプのような18種類の「タイプ」と「ポイント」が、隠しパラメータとして設定されています。これらの組み合わせ(=レシピ)によって、出てくるアイテムが決まる、という仕組みです。

より厳密には「1番目に入れたアイテムに応じて15パターンに絞られ、アイテム4つのレア度の合計によって決定」される、という仕組みです*2。詳しくはレシピ例を交えて次節で説明します。

f:id:tangential_star:20211106151128p:plain

f:id:tangential_star:20211104000246p:plain

ウッウロボの仕組み。1番目の「タイプ」でラインナップが決まり、4つの合計ポイントでアイテムが決定する

ウッウロボで生成できるアイテムの一覧

以上に説明した通り、ウッウロボでは、道具ごとに決められた「タイプ」と「ポイント」の組み合わせによって狙ったアイテムが生成できることが分かります。

この「タイプ」ごとに載せたのが下記の表です。何が作れるのかが一覧で分かります*3

目当てのアイテムが探しやすいように、極力、同じアイテム同士が横並びになるよう、工夫して並び替えてありますので、是非ともご参考ください。

ウッウロボ排出アイテム(完成品)早見表(ポイント別;例外レシピ・ぼんぐり4つによるボール類を除く)
合計pt ノーマル エスパー みず でんき じめん くさ むし こおり ひこう ゴースト ドラゴン あく ほのお どく はがね かくとう いわ フェアリー
1~10pt わざレコード「ノーマル」
わざレコード85
(ふるいたてる)
わざレコード「エスパー」
わざレコード12
(こうそくいどう)

f:id:tangential_star:20211103215559p:plain

うしおのおこう

f:id:tangential_star:20211103215638p:plain

エレキシード

f:id:tangential_star:20211103215649p:plain

ほしのすな

f:id:tangential_star:20211103215704p:plain

グラスシード
わざレコード「むし」
わざレコード60
(シザークロス)

f:id:tangential_star:20211103215715p:plain

ゆきだま

f:id:tangential_star:20211103215827p:plain

きれいなハネ

f:id:tangential_star:20211103215836p:plain

あやしいおこう
わざレコード「ドラゴン」
わざレコード47
(ドラゴンクロー)
わざレコード「あく」
わざレコード37
(ちょうはつ)
わざレコード「ほのお」
わざレコード88
(ヒートスタンプ)

f:id:tangential_star:20211103215849p:plain

くろいヘドロ

わざレコード「はがね」
わざレコード31
(アイアンテール)
わざレコード「かくとう」
わざレコード07
(けたぐり)

f:id:tangential_star:20211103215905p:plain

かるいし

f:id:tangential_star:20211103215649p:plain

ほしのすな
11~15pt わざレコード「ノーマル」
わざレコード14
(ゆびをふる)
わざレコード「エスパー」
わざレコード34
(みらいよち)
わざレコード「みず」
わざレコード04
(なみのり)
わざレコード「でんき」
わざレコード80
(エレキボール)
わざレコード「じめん」
わざレコード23
(まきびし)
わざレコード「くさ」
わざレコード59
(タネばくだん)
わざレコード「むし」
わざレコード18
(きゅうけつ)

f:id:tangential_star:20211103215931p:plain

つめたいいわ

f:id:tangential_star:20211103215938p:plain

するどいくちばし

f:id:tangential_star:20211103215946p:plain

ビビリだま

f:id:tangential_star:20211103215953p:plain

りゅうのキバ

f:id:tangential_star:20211103220003p:plain

こうかくレンズ

f:id:tangential_star:20211103220010p:plain

かえんだま

f:id:tangential_star:20211103220018p:plain

どくどくだま
わざレコード「はがね」
わざレコード46
(てっぺき)
わざレコード「かくとう」
わざレコード56
(はどうだん)

f:id:tangential_star:20211103220028p:plain

まんまるいし

f:id:tangential_star:20211103220037p:plain

ミストシード
16~20pt わざレコード「ノーマル」
わざレコード26
(こらえる)
わざレコード「エスパー」
わざレコード40
(スキルスワップ)

f:id:tangential_star:20211103220051p:plain

かいがらのすず

f:id:tangential_star:20211103220059p:plain

じゅうでんち

f:id:tangential_star:20211103213410p:plain

おおきなキノコ

f:id:tangential_star:20211103220143p:plain

しろいハーブ

f:id:tangential_star:20211103220151p:plain

ひかりのこな

f:id:tangential_star:20211103220202p:plain

とけないこおり

f:id:tangential_star:20211103213410p:plain

おおきなキノコ

f:id:tangential_star:20211103220210p:plain

ねらいのまと

f:id:tangential_star:20211103213410p:plain

おおきなキノコ
わざレコード「あく」
わざレコード68
(わるだくみ)
わざレコード「ほのお」
わざレコード41
(ブレイズキック)
わざレコード「どく」
わざレコード91
(ベノムトラップ)

f:id:tangential_star:20211103220230p:plain

メタルパウダー

f:id:tangential_star:20211103220236p:plain

ちからのハチマキ

f:id:tangential_star:20211103220245p:plain

かたいいし

f:id:tangential_star:20211103213410p:plain

おおきなキノコ
21~25pt わざレコード「ノーマル」
わざレコード13
(きあいだめ)
わざレコード「エスパー」
わざレコード82
(アシストパワー)
ねがいのかたまり
ねがいのかたまり
ねがいのかたまり
ねがいのかたまり
ねがいのかたまり
ねがいのかたまり
ねがいのかたまり
ねがいのかたまり
ねがいのかたまり
ねがいのかたまり
ねがいのかたまり
ねがいのかたまり
ねがいのかたまり
ねがいのかたまり
ねがいのかたまり
ねがいのかたまり
ねがいのかたまり
ねがいのかたまり
ねがいのかたまり
ねがいのかたまり
ねがいのかたまり
ねがいのかたまり
ねがいのかたまり
ねがいのかたまり
ねがいのかたまり
ねがいのかたまり
ねがいのかたまり
ねがいのかたまり
ねがいのかたまり
ねがいのかたまり
ねがいのかたまり
ねがいのかたまり
26~30pt わざレコード「ノーマル」
わざレコード27
(ねごと)
わざレコード「エスパー」
わざレコード44
(コスモパワー)
わざレコード「みず」
わざレコード16
(たきのぼり)

f:id:tangential_star:20211103220302p:plain

じしゃく

f:id:tangential_star:20211103220310p:plain

ひかりのねんど
わざレコード「くさ」
わざレコード77
(くさむすび)

f:id:tangential_star:20211103220321p:plain

ぎんのこな
ほしのかけら
ほしのかけら

f:id:tangential_star:20211103215733p:plain

ふうせん
ほしのかけら
ほしのかけら
ほしのかけら
ほしのかけら
ほしのかけら
ほしのかけら
わざレコード「ほのお」
わざレコード02
(かえんほうしゃ)
わざレコード「どく」
わざレコード54
(どくびし)

f:id:tangential_star:20211103220346p:plain

ばんのうがさ
わざレコード「かくとう」
わざレコード48
(ビルドアップ)

f:id:tangential_star:20211103220354p:plain

かわらずのいし

f:id:tangential_star:20211103220402p:plain

においぶくろ
31~35pt わざレコード「ノーマル」
わざレコード35
(さわぐ)
わざレコード「エスパー」
わざレコード83
(サイドチェンジ)
わざレコード「みず」
わざレコード98
(アクアブレイク)
わざレコード「でんき」
わざレコード86
(ワイルドボルト)
わざレコード「じめん」
わざレコード87
(ドリルライナー)
わざレコード「くさ」
わざレコード50
(リーフブレード)
かおるキノコ
かおるキノコ
かおるキノコ
かおるキノコ

f:id:tangential_star:20211103220332p:plain

からぶりほけん
かおるキノコ
かおるキノコ
かおるキノコ
かおるキノコ
かおるキノコ
かおるキノコ
かおるキノコ
かおるキノコ

f:id:tangential_star:20211103220537p:plain

けむりだま

f:id:tangential_star:20211103220529p:plain

メタルコート
わざレコード「かくとう」
わざレコード21
(きしかいせい)

f:id:tangential_star:20211103220442p:plain

プロテクター

f:id:tangential_star:20211103220410p:plain

ルームサービス
36~40pt わざレコード「ノーマル」
わざレコード01
(のしかかり)
わざレコード「エスパー」
わざレコード25
(サイコショック)

f:id:tangential_star:20211103221031p:plain

きれいなウロコ

f:id:tangential_star:20211103221023p:plain

アップグレード
わざレコード「じめん」
わざレコード67
(だいちのちから)
わざレコード「くさ」
わざレコード65
(エナジーボール)
わざレコード「むし」
わざレコード61
(むしのさざめき)

f:id:tangential_star:20211103220941p:plain

するどいツメ

f:id:tangential_star:20211103220935p:plain

ねばりのかぎづめ

f:id:tangential_star:20211103220910p:plain

きよめのおふだ

f:id:tangential_star:20211103220904p:plain

りゅうのウロコ
わざレコード「あく」
わざレコード81
(イカサマ)
わざレコード「ほのお」
わざレコード36
(ねっぷう)
わざレコード「どく」
わざレコード57
(どくづき)
わざレコード「はがね」
わざレコード52
(ジャイロボール)

f:id:tangential_star:20211103220433p:plain

きょうせいギプス

f:id:tangential_star:20211103220426p:plain

ゴツゴツメット

f:id:tangential_star:20211103220417p:plain

ホイップポップ
41~45pt わざレコード「ノーマル」
わざレコード19
(トライアタック)
わざレコード「エスパー」
わざレコード69
(しねんのずつき)

f:id:tangential_star:20211103221037p:plain

しんぴのしずく

f:id:tangential_star:20211103221010p:plain

でんきだま

f:id:tangential_star:20211103221004p:plain

グランドコート

f:id:tangential_star:20211103220958p:plain

きゅうこん

f:id:tangential_star:20211103220949p:plain

きれいなぬけがら

f:id:tangential_star:20211103215724p:plain

ゆきだま

f:id:tangential_star:20211103215733p:plain

ふうせん

f:id:tangential_star:20211103220918p:plain

のろいのおふだ

f:id:tangential_star:20211103220857p:plain

いのちのたま

f:id:tangential_star:20211103220823p:plain

ピントレンズ

f:id:tangential_star:20211103220815p:plain

レッドカード

f:id:tangential_star:20211103220516p:plain

スピードパウダー

f:id:tangential_star:20211103220523p:plain

とつげきチョッキ
わざレコード「かくとう」
わざレコード99
(ボディプレス)
わざレコード「いわ」
わざレコード63
(パワージェム)

f:id:tangential_star:20211103220449p:plain

あかいいと
46~50pt わざレコード「ノーマル」
わざレコード29
(バトンタッチ)
わざレコード「エスパー」
わざレコード17
(ドわすれ)
おだんごしんじゅ
おだんごしんじゅ
おだんごしんじゅ
おだんごしんじゅ
おだんごしんじゅ
おだんごしんじゅ
おだんごしんじゅ
おだんごしんじゅ
おだんごしんじゅ
おだんごしんじゅ
おだんごしんじゅ
おだんごしんじゅ

f:id:tangential_star:20211103220927p:plain

じゃくてんほけん

f:id:tangential_star:20211103220851p:plain

われたポット
わざレコード「ドラゴン」
わざレコード62
(りゅうのはどう)
わざレコード「あく」
わざレコード95
(じごくづき)
わざレコード「ほのお」
わざレコード15
(だいもんじ)

f:id:tangential_star:20211103220508p:plain

どくバリ
わざレコード「はがね」
わざレコード79
(ヘビーボンバー)

f:id:tangential_star:20211103220500p:plain

たつじんのおび
ねがいのかたまり
ねがいのかたまり

f:id:tangential_star:20211103220700p:plain

アメざいく4種
(ランダム)
51~55pt わざレコード「ノーマル」
わざレコード30
(アンコール)
わざレコード「エスパー」
わざレコード38
(トリック)
わざレコード「みず」
わざレコード45
(だくりゅう)

f:id:tangential_star:20211103221015p:plain

あやしいパッチ
わざレコード「じめん」
わざレコード94
(10まんばりき)
わざレコード「くさ」
わざレコード72
(パワーウィップ)
わざレコード「むし」
わざレコード96
(かふんだんご)
わざレコード「こおり」
わざレコード05
(れいとうビーム)
わざレコード「ひこう」
わざレコード89
(ぼうふう)

f:id:tangential_star:20211103220843p:plain

れいかいのぬの

f:id:tangential_star:20211103220836p:plain

おうじゃのしるし
わざレコード「あく」
わざレコード58
(あくのはどう)

f:id:tangential_star:20211103220829p:plain

もくたん
わざレコード「どく」
わざレコード22
(ヘドロばくだん)

f:id:tangential_star:20211103220546p:plain

おまもりこばん
わざレコード「かくとう」
わざレコード64
(きあいだま)

f:id:tangential_star:20211103220554p:plain

しんかのきせき
わざレコード「フェアリー」
わざレコード92
(マジカルシャイン)
56~60pt わざレコード「ノーマル」
わざレコード20
(みがわり)
わざレコード「エスパー」
わざレコード49
(めいそう)
わざレコード「みず」
わざレコード84
(ねっとう)
わざレコード「でんき」
わざレコード08
(10まんボルト)
すいせいのかけら
すいせいのかけら
すいせいのかけら
すいせいのかけら
すいせいのかけら
すいせいのかけら
すいせいのかけら
すいせいのかけら
すいせいのかけら
すいせいのかけら
すいせいのかけら
すいせいのかけら
わざレコード「ドラゴン」
わざレコード51
(りゅうのまい)
わざレコード「あく」
わざレコード32
(かみくだく)
わざレコード「ほのお」
わざレコード55
(フレアドライブ)
わざレコード「どく」
わざレコード78
(ヘドロウェーブ)
わざレコード「はがね」
わざレコード70
(ラスターカノン)
わざレコード「かくとう」
わざレコード39
(ばかぢから)
わざレコード「いわ」
わざレコード76
(ステルスロック)

f:id:tangential_star:20211104002846p:plain

アメざいく3種
(ランダム)

61~65pt わざレコード「ノーマル」
わざレコード00
(つるぎのまい)
わざレコード「エスパー」
わざレコード97
(サイコファング)
わざレコード「みず」
わざレコード03
(ハイドロポンプ)
わざレコード「でんき」
わざレコード09
(かみなり)
わざレコード「じめん」
わざレコード10
(じしん)
わざレコード「くさ」
わざレコード71
(リーフストーム)
わざレコード「むし」
わざレコード28
(メガホーン)
わざレコード「こおり」
わざレコード06
(ふぶき)
わざレコード「ひこう」
わざレコード66
(ブレイブバード)
わざレコード「ゴースト」
わざレコード33
(シャドーボール)
わざレコード「ドラゴン」
わざレコード24
(げきりん)
わざレコード「あく」
わざレコード93
(DDラリアット)
わざレコード「ほのお」
わざレコード43
(オーバーヒート)
わざレコード「どく」
わざレコード73
(ダストシュート)
わざレコード「はがね」
わざレコード74
(アイアンヘッド)
わざレコード「かくとう」
わざレコード53
(インファイト)
わざレコード「いわ」
わざレコード75
(ストーンエッジ)
わざレコード「フェアリー」
わざレコード90
(じゃれつく)
66~70pt わざレコード「ノーマル」
わざレコード42
(ハイパーボイス)
わざレコード「エスパー」
わざレコード11
(サイコキネシス)
ふしぎなアメ
ふしぎなアメ
ふしぎなアメ
ふしぎなアメ
ふしぎなアメ
ふしぎなアメ
ふしぎなアメ
ふしぎなアメ
ふしぎなアメ
ふしぎなアメ
ふしぎなアメ
ふしぎなアメ
ふしぎなアメ
ふしぎなアメ
ふしぎなアメ
ふしぎなアメ
ふしぎなアメ
ふしぎなアメ
ふしぎなアメ
ふしぎなアメ
ふしぎなアメ
ふしぎなアメ
ふしぎなアメ
ふしぎなアメ
ふしぎなアメ
ふしぎなアメ
ふしぎなアメ
ふしぎなアメ
ふしぎなアメ
ふしぎなアメ
ふしぎなアメ
ふしぎなアメ
71~75pt ぎんのおうかん
ぎんのおうかん
ぎんのおうかん
ぎんのおうかん
ぎんのおうかん
ぎんのおうかん
ぎんのおうかん
ぎんのおうかん
ぎんのおうかん
ぎんのおうかん
ぎんのおうかん
ぎんのおうかん
ぎんのおうかん
ぎんのおうかん
ぎんのおうかん
ぎんのおうかん
ぎんのおうかん
ぎんのおうかん
ぎんのおうかん
ぎんのおうかん
ぎんのおうかん
ぎんのおうかん
ぎんのおうかん
ぎんのおうかん
ぎんのおうかん
ぎんのおうかん
ぎんのおうかん
ぎんのおうかん
ぎんのおうかん
ぎんのおうかん
ぎんのおうかん
ぎんのおうかん
ぎんのおうかん
ぎんのおうかん
ぎんのおうかん
ぎんのおうかん
76~80pt ポイントアップ
ポイントアップ
ポイントアップ
ポイントアップ
ポイントアップ
ポイントアップ
ポイントアップ
ポイントアップ
ポイントアップ
ポイントアップ
ポイントアップ
ポイントアップ
ポイントアップ
ポイントアップ
ポイントアップ
ポイントアップ
ポイントアップ
ポイントアップ
ポイントアップ
ポイントアップ
ポイントアップ
ポイントアップ
ポイントアップ
ポイントアップ
ポイントアップ
ポイントアップ
ポイントアップ
ポイントアップ
ポイントアップ
ポイントアップ
ポイントアップ
ポイントアップ
ポイントアップ
ポイントアップ
ポイントアップ
ポイントアップ

※タイプ・数値ポイントの情報はポケモンWikiウッウロボ」より引用。

この表から分かる通り、ウッウロボからはすべての「わざレコード」、入手困難な「進化アイテム」、BP交換で入手可能な「戦闘用アイテム」など、様々なものが「狙って出せる」ということがわかります。また、表の行には1つ目に入れるアイテムの「タイプ」が、列には必要なポイント合計が逆引きできるので、ウッウロボに入れるアイテムのレシピを考えることができます。実際の作り方は次節にて説明します。

 

ウッウロボのレシピの作り方

前述の通り、ウッウロボでは、1番目の投入アイテムの「タイプ」と、投入アイテム4つの「素材ポイント」の合計によって排出されるアイテムが決まります。

f:id:tangential_star:20211106195859p:plain
f:id:tangential_star:20211106195909p:plain
ウッウロボで作れるアイテム一覧(左)と、そのレシピの作り方のイメージ(右)
これらは画像保存しておくとすごく便利だ。

この「素材ポイント」「タイプ」についてですが、こちらも一定の法則性(例えば「きのみ」の素材ptが6,7世代の「しぜんのめぐみ」の威力に連動する*4、「メモリ」は一律10ptでタイプはそれぞれに依存、「カセキ」は一律5ptでタイプは対応するポケモンに複合されるもの、など)があります。そして、アイテムごとの「ポイント」についてはポケモンWikiウッウロボ」のページに纏まっています。

しかし、せっかくポイントが分かっても、アイテムをウッウロボに入れる(=消費する)ので、よほどブルジョワでない限り、複数入手が容易なアイテムを選ぶべきです*5

そこで、筆者は下記画像のように、よく使うアイテム(=複数入手が比較的容易なアイテム)について「タイプ」と「ポイント」をまとめました。まとめるのめちゃくちゃ頑張りました

f:id:tangential_star:20211106203346p:plain

ウッウロボで使うのに適した「素材」ガイド
 周回作業などで溜まっていることが多いものを筆者でチョイスした。画像保存推奨

ウッウロボのレシピ例

さて、レシピの仕組み・作り方については前述の通りですが、これらを踏まえて、いくつか、具体的な例を挙げます。もちろん、ただの例なので読み飛ばしOK→「例外のレシピ(上位アイテムへの変換)」まで読み飛ばす

下記の表をご覧ください。

ウッウロボの合成レシピ例(「1つ目のタイプと4つの合計pt」に対応するアイテムが排出)
完成品 1番目のアイテム 2番目のアイテム 3番目のアイテム 4番目のアイテム

f:id:tangential_star:20211103220857p:plain

どくどくだま
どく/11~15pt)

f:id:tangential_star:20211105205207p:plain

オレンのみ
どく・1pt

f:id:tangential_star:20211105205207p:plain

オレンのみ

どく・1pt

f:id:tangential_star:20211105205207p:plain

オレンのみ
どく・1pt

f:id:tangential_star:20211103213342p:plain

ほしのかけら
いわ・10pt

f:id:tangential_star:20211103221010p:plain

でんきだま
でんき/41~45pt)

f:id:tangential_star:20211105205323p:plain

かみなりのいし
でんき・3pt

f:id:tangential_star:20211103213318p:plain

ねがいのかたまり
どく・14pt

f:id:tangential_star:20211103213318p:plain

ねがいのかたまり
どく・14pt

f:id:tangential_star:20211103213318p:plain

ねがいのかたまり
どく・14pt

f:id:tangential_star:20211103220426p:plain

ゴツゴツメット
いわ/36~40pt)

f:id:tangential_star:20211103220354p:plain

かわらずのいし
いわ・10pt

f:id:tangential_star:20211103220354p:plain

かわらずのいし
いわ・10pt

f:id:tangential_star:20211103220354p:plain

かわらずのいし
いわ・10pt

f:id:tangential_star:20211103220354p:plain

かわらずのいし
いわ・10pt

f:id:tangential_star:20211103220426p:plain

ゴツゴツメット
いわ/36~40pt)

f:id:tangential_star:20211103220354p:plain

かわらずのいし
いわ・10pt

f:id:tangential_star:20211103213342p:plain

ほしのかけら
いわ・10pt

f:id:tangential_star:20211103213342p:plain

ほしのかけら
いわ・10pt

f:id:tangential_star:20211103213342p:plain

ほしのかけら
いわ・10pt

f:id:tangential_star:20211103220346p:plain

ばんのうがさ
はがね/26~30pt)

f:id:tangential_star:20211103220529p:plain

メタルコート
はがね・12pt

f:id:tangential_star:20211103220529p:plain

メタルコート
はがね・12pt

f:id:tangential_star:20211105205207p:plain

オレンのみ
どく・1pt

f:id:tangential_star:20211105205207p:plain

オレンのみ
どく・1pt

f:id:tangential_star:20211103220529p:plain

メタルコート

はがね/31~35pt)

f:id:tangential_star:20211103213429p:plain

ぎんのおうかん

はがね・20pt

f:id:tangential_star:20211103213342p:plain

ほしのかけら
いわ・10pt

f:id:tangential_star:20211105205207p:plain

オレンのみ
どく・1pt

f:id:tangential_star:20211105205207p:plain

オレンのみ
どく・1pt

f:id:tangential_star:20211103215733p:plain

ふうせん
ひこう/26~30pt)

f:id:tangential_star:20211103215827p:plain

ハネ系アイテム
ひこう・3pt

f:id:tangential_star:20211103215827p:plain

ハネ系アイテム
ひこう・3pt

f:id:tangential_star:20211103213342p:plain

ほしのかけら
いわ・10pt

f:id:tangential_star:20211103213342p:plain

ほしのかけら
いわ・10pt

f:id:tangential_star:20211103220332p:plain
からぶりほけん

ひこう/31~35pt)

f:id:tangential_star:20211103215827p:plain

ハネ系アイテム
ひこう・3pt

f:id:tangential_star:20211103213342p:plain

ほしのかけら

いわ・10pt

f:id:tangential_star:20211103213342p:plain

ほしのかけら
いわ・10pt

f:id:tangential_star:20211103213342p:plain

ほしのかけら
いわ・10pt

※タイプ・数値ポイントの情報はポケモンWikiウッウロボ」より引用。

まず前提として、ウッウロボからの排出アイテム・入れるアイテムには、それぞれ「タイプ」と「ポイント」が割り振られています。そして、前述の通り、ウッウロボから排出されるアイテムは「1つ目のタイプ」と「4つのポイント合計値」で決まります

例えば、「ばんのうがさ」を作るためには、1つ目に「はがね」タイプのアイテムを入れて、かつ、4つのアイテムのポイント合計が「26~30」の範囲内に収まれば良いので、「メタルコート」「メタルコート」「オレンのみ」「オレンのみ」で作ることができます。また、順番を入れ替えて、あるいは「メタルコート」「オレンのみ」「オレンのみ」「メタルコート」でも作ることができます*6

また、ウッウロボでは、計算でアイテムが決まる仕様のため、1つのアイテムに対して複数のレシピが成り立ちます。例えば「ゴツゴツメット」を作るためには「1つ目が『いわ』タイプで合計ポイントが『36~40pt』」である必要がありますが、最初に「かわらずのいし(いわ・10pt)」を入れ、以降2~4番目には「かわらずのいし」もしくは「ほしのかけら(いわ・10pt)」を任意の組み合わせで投入すれば良いと分かります*7。なお、言わずもがな「完成品に必要なポイント」と「入れたときに計算されるポイント」が異なるということには注意しなければなりません。例えば、「メタルコート」を作るためには「はがね/31~35pt」が必要ですが、メタルコートを素材にするときには「はがね・12pt」として計算されます。

以上のようにウッウロボは、要領が分かれば大して難しくないので、先程の一覧画像や素材の表を頼りに、自ら所持しているアイテムを使えるレシピを考えると良いかと思います。

例外のレシピ(上位アイテムへの変換)

ウッウロボから排出されるアイテムには、前述のレシピに則らない例外もあります。具体的には、下記11レシピですが、特筆すべきは「とくせいカプセル」でしょう。それ以外は、基本的に上位アイテムへの交換になるためある意味、想像通りの変換になります。

ウッウロボの例外レシピ(「1つ目・3つ目・4つ目」に対応するアイテムが排出)
完成品 1番目のアイテム 2番目のアイテム 3番目のアイテム 4番目のアイテム

f:id:tangential_star:20211106204440p:plain

でかいきんのたま

f:id:tangential_star:20211106204458p:plain

きんのたま
任意

f:id:tangential_star:20211106204458p:plain

きんのたま

f:id:tangential_star:20211106204458p:plain

きんのたま

f:id:tangential_star:20211106204523p:plain

おおきなキノコ

f:id:tangential_star:20211106204531p:plain

ちいさなキノコ
任意

f:id:tangential_star:20211106204531p:plain

ちいさなキノコ

f:id:tangential_star:20211106204531p:plain

ちいさなキノコ

f:id:tangential_star:20211106204552p:plain

かおるキノコ

f:id:tangential_star:20211106204523p:plain

おおきなキノコ
任意

f:id:tangential_star:20211106204523p:plain

おおきなキノコ

f:id:tangential_star:20211106204523p:plain

おおきなキノコ

f:id:tangential_star:20211106204618p:plain

ほしのかけら

f:id:tangential_star:20211106204626p:plain

ほしのすな
任意

f:id:tangential_star:20211106204626p:plain

ほしのすな

f:id:tangential_star:20211106204626p:plain

ほしのすな

f:id:tangential_star:20211106204644p:plain

すいせいのかけら

f:id:tangential_star:20211106204618p:plain

ほしのかけら
任意

f:id:tangential_star:20211106204618p:plain

ほしのかけら

f:id:tangential_star:20211106204618p:plain

ほしのかけら

f:id:tangential_star:20211106204708p:plain

おおきなしんじゅ

f:id:tangential_star:20211106204718p:plain

しんじゅ
任意

f:id:tangential_star:20211106204718p:plain

しんじゅ

f:id:tangential_star:20211106204718p:plain

しんじゅ

f:id:tangential_star:20211106204736p:plain

おだんごしんじゅ

f:id:tangential_star:20211106204708p:plain

おおきなしんじゅ
任意

f:id:tangential_star:20211106204708p:plain

おおきなしんじゅ

f:id:tangential_star:20211106204708p:plain

おおきなしんじゅ

f:id:tangential_star:20211106204756p:plain

きんのおうかん

f:id:tangential_star:20211106204804p:plain

ぎんのおうかん
任意

f:id:tangential_star:20211106204804p:plain

ぎんのおうかん

f:id:tangential_star:20211106204804p:plain

ぎんのおうかん

f:id:tangential_star:20211106204825p:plain

とくせいカプセル

f:id:tangential_star:20211106204834p:plain

ふしぎなアメ
任意

f:id:tangential_star:20211106204834p:plain

ふしぎなアメ

f:id:tangential_star:20211106204834p:plain

ふしぎなアメ

f:id:tangential_star:20211106204856p:plain

ポイントマックス

f:id:tangential_star:20211106204911p:plain

ヨロイこうせき
任意

f:id:tangential_star:20211106204911p:plain

ヨロイこうせき

f:id:tangential_star:20211106204911p:plain

ヨロイこうせき

f:id:tangential_star:20211106204856p:plain

ポイントマックス

f:id:tangential_star:20211106204938p:plain

マックスこうせき
任意

f:id:tangential_star:20211106204938p:plain

マックスこうせき

f:id:tangential_star:20211106204938p:plain

マックスこうせき

※タイプ・数値ポイントの情報はポケモンWikiウッウロボ」より引用。

重要なのは、きんのたま」「ちいさなキノコ」「おおきなキノコ」「ほしのかけら」「すいせいのかけら」「しんじゅ」「おおきなしんじゅ」「ぎんのおうかん」「ふしぎなアメ」「ヨロイこうせき」「マックスこうせき」のいずれかを1アイテム目に入れた場合に、意図せずこちらの例外レシピになってしまう可能性がある、という点です(裏返せば、これ以外のアイテムを1つ目に入れれば、前述のレシピの作り方に則る、ということです)。自らレシピを検討するときは気をつけましょう。

ガンテツボールレシピ(ぼんぐり4つ)

ウッウロボでは、投入したアイテム4つの「ポイント」合計が0pt、すなわち「ぼんぐり」を4つ入れた場合に、ボール(稀にガンテツボール)が生成されます。この時に生成されるボールの種類は、入れたぼんぐりの色に応じて下記の表のようになります。

ガンテツボールレシピ(同じ「ぼんぐり」4つを入れた場合の排出確率)
確率

f:id:tangential_star:20211106212843p:plain

くろぼんぐり

f:id:tangential_star:20211106212852p:plain

あおぼんぐり

f:id:tangential_star:20211106212930p:plain

しろぼんぐり

f:id:tangential_star:20211106212912p:plain

みどぼんぐり

f:id:tangential_star:20211106212939p:plain

ももぼんぐり

f:id:tangential_star:20211106212949p:plain

あかぼんぐり

f:id:tangential_star:20211106213000p:plain

きぼんぐり
約1%

f:id:tangential_star:20211106213024p:plain

ヘビーボール

f:id:tangential_star:20211106213034p:plain

ルアーボール

f:id:tangential_star:20211106213051p:plain

スピードボール

f:id:tangential_star:20211106213103p:plain

フレンドボール

f:id:tangential_star:20211106213114p:plain

ラブラブボール

f:id:tangential_star:20211106213125p:plain

レベルボール

f:id:tangential_star:20211106213137p:plain

ムーンボール
約25%

f:id:tangential_star:20211106213149p:plain

ゴージャスボール

f:id:tangential_star:20211106213157p:plain

ネットボール

f:id:tangential_star:20211106213205p:plain

プレミアボール

f:id:tangential_star:20211106213218p:plain

ネストボール

f:id:tangential_star:20211106213230p:plain

ヒールボール

f:id:tangential_star:20211106213239p:plain

リピートボール

f:id:tangential_star:20211106213246p:plain

クイックボール
約25%

f:id:tangential_star:20211106213257p:plain

ダークボール

f:id:tangential_star:20211106213305p:plain

ダイブボール

f:id:tangential_star:20211106213316p:plain

タイマーボール

f:id:tangential_star:20211106213325p:plain

ハイパーボール

f:id:tangential_star:20211106213325p:plain

ハイパーボール

f:id:tangential_star:20211106213325p:plain

ハイパーボール

f:id:tangential_star:20211106213325p:plain

ハイパーボール
約25%

f:id:tangential_star:20211106213345p:plain

スーパーボール
約25%

f:id:tangential_star:20211106213355p:plain

モンスターボール
約0.1%

f:id:tangential_star:20211106213411p:plain

サファリボール

※ボールの種類の情報はポケモンWikiウッウロボ」より引用。

なお、上記の表は4つとも同じ「ぼんぐり」を入れた場合の確率ですが、2種類以上のぼんぐりを混ぜると、その個数の比に応じた確率が適用されます。例えば、「くろぼんぐり」3つと「あおぼんぐり」1つを入れた場合、「ヘビーボールが0.75%」「ルアーボールが0.25%」になります。また、2個以上のぼんぐりを混ぜた場合には「サファリボール」が排出しなくなり、代わりに「コンペボール」が排出されるようになります。

 

ウッウロボ自動化の事前準備

さて、余談が長くなりました。ここからは、プログラムを使う上での事前準備についてです。とは言っても自分で決めたレシピを設定するだけ、ですが。

ウッウロボのレシピの例・作り方は前述しているので、もし、「何を作るか」が決まっていなければ、適宜上記を振り返っていただければと思います。

プログラム冒頭のレシピ部分を修正する

プログラムで修正するのは、冒頭の部分のみです。

ウッウロボでは、リュックのうち「きのみ」「どうぐ」「おたから」の3ポケットの中から投入アイテムを選べます。プログラムでは使うアイテムをそれぞれ何個ずつ持っているかをnum1, num2, num3, num4に、それぞれがどのポケットの上から何番目にあるのかをrecipe1, recipe2, recipe3, recipe4に書き換えます例えば、「きのみ」ポケットの2番目のアイテムを指定するには「"B2"」と記載します

なお、このプログラムは、原則として「ぼんぐり4つ」以外のレシピ合成用として作成しておりますが、ぼんぐりを4つ入れる場合はALL_APLICORNの「false」を「true」に変更してください*8

// ★ここに、レシピを記入してください!★
// リュックの何番目に使いたいアイテムが入っているかを、下記凡例に沿って記入。
// きのみ=B(Berries) / どうぐ=I(other Items) / おたから=T(Treasure)
// 例:「きのみ」欄の一番上→ B1 , 「どうぐ」欄の上から15番目→ I15 , 「おたから」欄の上から5番目→ T5
char* recipe1 = "I2"; int num1= 817; // 1番目に入れる道具とその所持数
char* recipe2 = "T1"; int num2= 926; // 2番目に入れる道具とその所持数
char* recipe3 = "T1"; int num3= 705; // 3番目に入れる道具とその所持数
char* recipe4 = "I4"; int num4= 498; // 4番目に入れる道具とその所持数
const bool ALL_APLICORN = (false);   // アイテム4つがすべて「ぼんぐり」ならtrueに。

一応、プログラム上は上から何番目かが正しく指定されていれば、20番目でも30番目でも参照できるように作っていますが、ご自身が数え間違えるリスクもあるので、極力「お気に入り」「並び替え」機能を使ってリュックの上の方に置いておくと便利&安心です。

また、上記の例にもありますが、同じアイテムを複数回入れることも可能です。例えば、recipe1, recipe2で同じアイテムを入れる場合は、それぞれ同じ記号・同じ数量を指定してください。

f:id:tangential_star:20211106230950p:plain
f:id:tangential_star:20211106230956p:plain
プログラムソースの変更例(レシピは「レッドカード」のもの)
「きのみ1番目」「どうぐ1番目」それぞれを"B1"と"I1"でプログラムに書き込む


リュックのカーソルをすべて一番上にして「かいふく」ポケットを開く【重要】

続いて、ポケモン剣盾側の準備ですが、こちらはいたってシンプル。

道具ポケットのすべてのカーソル(選択しているアイテム)を一番上」になるようにして『かいふく』ポケットを開いた状態」にする(両方とも重要)にするだけです。

この手順をうっかり忘れると、自動化した際に道具選択がずれるため、意図しないアイテムを消費してしまい、セーブまでされてしまいます。取り返しがつかないので、必ず確認してください。

f:id:tangential_star:20211106232207g:plain

リュックですべてのカーソルを「一番上」にして「かいふく」ポケットを開いておく
これを忘れると、誤ったアイテムが選択され、あわや大惨事に(セーブされてしまう)。

 

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

ウッウロボの目の前でArduinoを差し込むだけで使用できるカンタン仕様です。事前にソースコード中の「recipe1」~「recipe4」および「num1」~「num4」を書き換えてお使いください。また、リュックのカーソルはすべてのポケットで先頭にした上で、「かいふく」ポケットを開いた状態にしてからご利用ください(重要)。

/*  ★★★★★★★★★★★★★★★★
 *  ★ウッウロボ自動投入プログラム★
 *  ★★★★★★★★★★★★★★★★
 *  
 * ウッウロボにアイテムを自動で入れてくれます。
 * たとえば「アメざいく」「わざレコード」など、複数欲しいものや厳選が必要なものをウッウロボで大量回収する際にお役立てください。
 * ※いちおう、ぼんぐり4つでのガンテツボール厳選にも使えます(素直にA連打のほうが効率が良い気もしますが)。
 *  
 *  【事前準備】
 *  リュックのカーソルをすべて1番目に戻しておき、きのみを開いた状態で閉じておきます(重要)。
 *  プログラム内の「recipe1」~「recipe4」に、ウッウロボに入れる道具の位置を入れておきます。
 *  あわせて「num1」~「num4」に、それぞれの所持数(=使用上限数)を入れます。
 *  ※「1個」しか所持していないアイテムは利用できません(手動でやってください)
 *  ※プログラム上は、1個以上残るようにアイテムを消費する設計(途中で止まる設計)になっています。
 *  ※万が一、貴重なアイテムが消費されても責任は持てません。「自己責任」でお願いします。
 *  
 *  【利用方法】
 *  ウッウロボの目の前でArduinoを差し込むだけです。
 *  ※セーブされるので、自己責任でお願いします。
 *  
 *  (c) 2021 ますたーの忘備録
 *  https://tangential-star.hatenablog.jp/
*/

#include <SwitchControlLibrary.h>

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

// ◆レシピの簡易説明◆
// ウッウロボから出てくるアイテムは「1番目に入れたアイテムに割り当てられた属性テーブル」から排出されるようになります。
// (例えば「あかぼんぐり」は「ほのおタイプ」、「かわらずのいし」は「いわタイプ」など)
// また、隠しパラメータとしてアイテムごとにポイント(レア度みたいな)があり、これの合計で出てくるアイテムが決定されます。
// ぼんぐりはポイントが「0」で設定されており、合計0だとボールになる、みたいな仕組みです。
// 下記は、レシピの例です。※カッコ書きのパラメータ[]は、ポケモンWiki様よりお借りしました
// 【ポケモンWiki】ウッウロボ https://wiki.xn--rckteqa2e.com/wiki/%E3%82%A6%E3%83%83%E3%82%A6%E3%83%AD%E3%83%9C
// 
// ●アメざいく(スター・リボン)[57]
//    =[ひかり/つきのいし(3)]+[きん/ぎんのおうかんorふしぎなアメ(20)]+[きん/ぎんのおうかんorふしぎなアメ(20)]+[マックス/ヨロイこうせき(14)]
// ●アメざいく(その他) [46]
//    =[ひかり/つきのいし(3)]+[ひかり/つきのいし(3)]+[きん/ぎんのおうかんorふしぎなアメ(20)]+[きん/ぎんのおうかんorふしぎなアメ(20)]
// ●においぶくろ [29]
//    =[ひかり/つきのいし(3)]+[ひかり/つきのいし(3)]+[ひかり/つきのいし(3)]+[きん/ぎんのおうかんorふしぎなアメ(20)]
// ●ホイップポップ [38]
//    =[ひかり/つきのいし(3)]+[クラボ/カゴ/モモン/オレンのみ(1)]+[きん/ぎんのおうかんorふしぎなアメ(20)]+[マックス/ヨロイこうせき(14)]

// ★ここに、レシピを記入してください!★
// リュックの何番目に使いたいアイテムが入っているかを、下記凡例に沿って記入。
// きのみ=B(Berries) / どうぐ=I(other Items) / おたから=T(Treasure)
// 例:「きのみ」欄の一番上→ B1 , 「どうぐ」欄の上から15番目→ I15 , 「おたから」欄の上から5番目→ T5
char* recipe1 = "I2"; int num1= 817; // 1番目に入れる道具とその所持数
char* recipe2 = "T1"; int num2= 926; // 2番目に入れる道具とその所持数
char* recipe3 = "T1"; int num3= 705; // 3番目に入れる道具とその所持数
char* recipe4 = "I4"; int num4= 498; // 4番目に入れる道具とその所持数
const bool ALL_APLICORN = (false);   // 【非推奨!】アイテム4つがすべて「ぼんぐり」ならtrueに。(ぼんぐりを入れる場合にはA連打をご検討ください)


// ★上級者向け設定★
// Arduinoがメモリアロケーションに失敗した時、動作を継続するかどうか。
// このプログラムでは、上記レシピの道具の残数を、動的メモリ確保でリスト化して管理します。
// この動的メモリ確保に失敗した場合に、このプログラムの動作を止めるかどうかです。
// ※Arduinoを挿したらすぐにHome画面に入ってしまう方は、Arduinoがメモリ確保に失敗しています。
// アイテムの残数は自分で適宜画面で確認する、という方はfalseにしてください(自己責任)
const bool STOP_PROGRAM_WHEN_ERROR = (true); 


// アイテム重複確認用のリスト構造体
struct t_item_list{
  char keyname[5]; // "I1"など、任意のなまえ
  int num; // 残り個数
  int use; // 1回の試行で使う数(重複がなければ「1」)
  struct t_item_list* next;
};
// 便宜的な配列添字(リュックの中身)
const int B =0;
const int I =1;
const int T =2;
// リュックのカーソル位置
int target = B; // 初期オープン位置→「きのみ」
int cur[3] = {1, 1, 1}; // 「きのみ」「どうぐ」「おたから」それぞれの初期カーソルの位置。

// アイテム残り数を保存するリスト
struct t_item_list* restitem = NULL;
bool BAD_ALLOCATION = false;

// 作った関数たち
void PushRL(int delay_time_ms);
int PushKey(char* keyname, int holdtime, int delaytime);
struct t_item_list* search_sameitem(char *recipe, struct t_item_list* searchindex);
struct t_item_list* search_lastitem(struct t_item_list* searchindex);
bool can_use_item(struct t_item_list* searchindex);
int update_itemlist(struct t_item_list* searchindex);

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

  struct t_item_list* temp;


  // 道具の正式な量を把握 -----------------------------------------

  // recipe1を登録
  restitem = malloc( sizeof(struct t_item_list) );
  if(restitem == NULL){
    BAD_ALLOCATION = true;
    if(STOP_PROGRAM_WHEN_ERROR){
      PushKey("Home", HOLDTIME, 1200);
      for(;;); // 動作終了。
    }
  }else{
    strcpy( restitem->keyname, recipe1);
    restitem->num = num1;  // 残り個数を保存
    restitem->use = 1;     // 消費数を保存
    restitem->next = NULL; // 次のキーはnull
  }

  // recipe2を登録
  if(search_sameitem(recipe2, restitem) == NULL){ // 同一アイテムが無い場合は末尾に追記
    temp = search_lastitem(restitem); // 2個目以降は、リスト最後のnextにmallocする
    temp->next = malloc( sizeof(struct t_item_list) );
    if(temp->next == NULL){
      BAD_ALLOCATION = true; 
    }else{
      strcpy( temp->next->keyname, recipe2);
      temp->next->num = num2;  // 残り個数を保存
      temp->next->use = 1;     // 消費数を保存
      temp->next->next = NULL; // 次のキーはnull
    }
  }else{ // 重複アイテム発見時は…
    temp = search_sameitem(recipe2, restitem);
    temp->use = temp->use + 1; // アイテム消費数を追加
  }

  // recipe3を登録
  if(search_sameitem(recipe3, restitem) == NULL){ // 同一アイテムが無い場合は末尾に追記
    temp = search_lastitem(restitem); // 2個目以降は、リスト最後のnextにmallocする
    temp->next = malloc( sizeof(struct t_item_list) );
    if(temp->next == NULL){
      BAD_ALLOCATION = true; 
    }else{
      strcpy( temp->next->keyname, recipe3);
      temp->next->num = num3;  // 残り個数を保存
      temp->next->use = 1;     // 消費数を保存
      temp->next->next = NULL; // 次のキーはnull
    }
  }else{ // 重複アイテム発見時は…
    temp = search_sameitem(recipe3, restitem);
    temp->use = temp->use + 1; // アイテム消費数を追加
  }

  // recipe4を登録
  if(search_sameitem(recipe4, restitem) == NULL){ // 同一アイテムが無い場合は末尾に追記
    temp = search_lastitem(restitem); // 2個目以降は、リスト最後のnextにmallocする
    temp->next = malloc( sizeof(struct t_item_list) );
    if(temp->next == NULL){
      BAD_ALLOCATION = true; 
    }else{
      strcpy( temp->next->keyname, recipe4);
      temp->next->num = num4;  // 残り個数を保存
      temp->next->use = 1;     // 消費数を保存
      temp->next->next = NULL; // 次のキーはnull
    }
  }else{ // 重複アイテム発見時は…
    temp = search_sameitem(recipe4, restitem);
    temp->use = temp->use + 1; // アイテム消費数を追加
  }

  delay(1000);

  // mallocに失敗していたらプログラムを中断。
  if(STOP_PROGRAM_WHEN_ERROR && BAD_ALLOCATION){
    PushKey("Home", HOLDTIME, 1200);
    for(;;); // 動作終了。
  }
  
}


void loop() {

  // ★ウッウロボに話しかける
  PushKey("A", HOLDTIME, 800);  //  
  PushKey("A", HOLDTIME, 800);  // メニューを 選んでください ▼
  PushKey("A", HOLDTIME, 1300); // →合成する
  PushKey("A", HOLDTIME, 1500); // 合成に 使う 道具を 選んでください ▼

  // ★道具選択画面

  // 投入アイテムの残数チェック
  if(!BAD_ALLOCATION){ // ただしくmallocできていれば有効。
    if(can_use_item(restitem)){
      // 所持アイテムが十分な数であればアイテム数をへらす
      update_itemlist(restitem);
    }else{
      // 不十分の場合は、ゲーム中断
      PushKey("Home", HOLDTIME, 1200);
      for(;;); // 動作終了。
    }
  }


  // 投入するアイテム1つ目----------------------------------------

  // リュックのカーソル合わせ
  switch( tolower(recipe1[0]) ){ // 「きのみ」「どうぐ」「おたから」カーソル合わせ
    case 'b': // きのみ
      if(target == I) PushKey("left", HOLDTIME, 800);
      if(target == T) PushKey("right", HOLDTIME, 800);
      target = B;
      break;
    case 'i': // どうぐ
      if(target == T) PushKey("left", HOLDTIME, 800);
      if(target == B) PushKey("right", HOLDTIME, 800);
      target = I;
      break;
    case 't': // おたから
      if(target == B) PushKey("left", HOLDTIME, 800);
      if(target == I) PushKey("right", HOLDTIME, 800);
      target = T;
      break;
    default: break;
  }

  // どうぐへのカーソル合わせ
  while( cur[target] != atoi(&recipe1[1]) ){ // 数字部分と比較(0文字目は符号B,T,Iいずれかなのでatoiには[1]から渡している)
    if( cur[target] < atoi(&recipe1[1]) ){
      PushKey("down", HOLDTIME, 280);
      cur[target]++;
    }
    if( cur[target] > atoi(&recipe1[1]) ){
      PushKey("up", HOLDTIME, 280);
      cur[target]--;
    }
  }
  PushKey("A", HOLDTIME, 400);


  // 投入するアイテム2つ目----------------------------------------
  // リュックのカーソル合わせ
  switch( tolower(recipe2[0]) ){ // 「きのみ」「どうぐ」「おたから」カーソル合わせ
    case 'b': // きのみ
      if(target == I) PushKey("left", HOLDTIME, 800);
      if(target == T) PushKey("right", HOLDTIME, 800);
      target = B;
      break;
    case 'i': // どうぐ
      if(target == T) PushKey("left", HOLDTIME, 800);
      if(target == B) PushKey("right", HOLDTIME, 800);
      target = I;
      break;
    case 't': // おたから
      if(target == B) PushKey("left", HOLDTIME, 800);
      if(target == I) PushKey("right", HOLDTIME, 800);
      target = T;
      break;
    default: break;
  }

  // どうぐへのカーソル合わせ
  while( cur[target] != atoi(&recipe2[1]) ){ // 数字部分と比較(0文字目は符号B,T,Iいずれかなのでatoiには[1]から渡している)
    if( cur[target] < atoi(&recipe2[1]) ){
      PushKey("down", HOLDTIME, 280);
      cur[target]++;
    }
    if( cur[target] > atoi(&recipe2[1]) ){
      PushKey("up", HOLDTIME, 280);
      cur[target]--;
    }
  }
  PushKey("A", HOLDTIME, 400);

  // 投入するアイテム3つ目----------------------------------------
  // リュックのカーソル合わせ
  switch( tolower(recipe3[0]) ){ // 「きのみ」「どうぐ」「おたから」カーソル合わせ
    case 'b': // きのみ
      if(target == I) PushKey("left", HOLDTIME, 800);
      if(target == T) PushKey("right", HOLDTIME, 800);
      target = B;
      break;
    case 'i': // どうぐ
      if(target == T) PushKey("left", HOLDTIME, 800);
      if(target == B) PushKey("right", HOLDTIME, 800);
      target = I;
      break;
    case 't': // おたから
      if(target == B) PushKey("left", HOLDTIME, 800);
      if(target == I) PushKey("right", HOLDTIME, 800);
      target = T;
      break;
    default: break;
  }

  // どうぐへのカーソル合わせ
  while( cur[target] != atoi(&recipe3[1]) ){ // 数字部分と比較(0文字目は符号B,T,Iいずれかなのでatoiには[1]から渡している)
    if( cur[target] < atoi(&recipe3[1]) ){
      PushKey("down", HOLDTIME, 280);
      cur[target]++;
    }
    if( cur[target] > atoi(&recipe3[1]) ){
      PushKey("up", HOLDTIME, 280);
      cur[target]--;
    }
  }
  PushKey("A", HOLDTIME, 400);

  // 投入するアイテム4つ目----------------------------------------

  // リュックのカーソル合わせ
  switch( tolower(recipe4[0]) ){ // 「きのみ」「どうぐ」「おたから」カーソル合わせ
    case 'b': // きのみ
      if(target == I) PushKey("left", HOLDTIME, 800);
      if(target == T) PushKey("right", HOLDTIME, 800);
      target = B;
      break;
    case 'i': // どうぐ
      if(target == T) PushKey("left", HOLDTIME, 800);
      if(target == B) PushKey("right", HOLDTIME, 800);
      target = I;
      break;
    case 't': // おたから
      if(target == B) PushKey("left", HOLDTIME, 800);
      if(target == I) PushKey("right", HOLDTIME, 800);
      target = T;
      break;
    default: break;
  }

  // どうぐへのカーソル合わせ
  while( cur[target] != atoi(&recipe4[1]) ){ // 数字部分と比較(0文字目は符号B,T,Iいずれかなのでatoiには[1]から渡している)
    if( cur[target] < atoi(&recipe4[1]) ){
      PushKey("down", HOLDTIME, 280);
      cur[target]++;
    }
    if( cur[target] > atoi(&recipe4[1]) ){
      PushKey("up", HOLDTIME, 280);
      cur[target]--;
    }
  }
  PushKey("A", HOLDTIME, 900); // えらんだ 道具を 合成します よろしいですか?

  PushKey("A", HOLDTIME, 1500); // →はい
  PushKey("A", HOLDTIME, 1600); // 合成を 始めるため レポートを 書きますか? →はい
  PushKey("A", HOLDTIME, 4200); // ●●は レポートを しっかり 書き残した ▼
  PushKey("A", HOLDTIME, 7000); // ゴウセイ カンリョウ ▼
  PushKey("A", HOLDTIME, 1200); // ●●は ●● を 手に入れた!
  PushKey("A", HOLDTIME, 1800); // ●●は ●● を ●●ポケットに しまった ▼

  while(ALL_APLICORN){ // 「ぼんぐり」でオシャボ回収の場合はA連打(ループ)
    PushKey("A", HOLDTIME, 1800); // 同じ 組み合わせで 続けて 合成しますか? →はい
    PushKey("A", HOLDTIME, 4200); // ●●は レポートを しっかり 書き残した ▼
    PushKey("A", HOLDTIME, 1200); // ●●は ●● を 手に入れた!
    PushKey("A", HOLDTIME, 1800); // ●●は ●● を ●●ポケットに しまった ▼
    // 投入アイテムの残数チェック
    if(!BAD_ALLOCATION){ // ただしくmallocできていれば有効。
      if(can_use_item(restitem)){
        // 所持アイテムが十分な数であればアイテム数をへらす
        update_itemlist(restitem);
      }else{
        // 不十分の場合は、ゲーム中断
        PushKey("Home", HOLDTIME, 1200);
        for(;;); // 動作終了。
      }
    }
  }


}

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

// ★同じアイテムがすでに登録されているかを検索する関数
// input1: keyname(レシピ"I2"など), input2: 対象のリスト
// 返り値: 見つかった場合、そのリストのポインタ、見つからなかった場合はNULL
struct t_item_list* search_sameitem(char *recipe, struct t_item_list* searchindex){
  for(;;){
    if( searchindex == NULL ) return NULL;
    if( strcmp(  searchindex->keyname, recipe) == 0) return searchindex;
    else searchindex = searchindex->next;
  }  
  return NULL;
}

// ★リストの最後のポインタを返す関数
// input: 対象のリスト
// 返り値:リストの最後のポインタ ※NULLが渡されていればNULLを返す。
struct t_item_list* search_lastitem(struct t_item_list* searchindex){
  if( searchindex == NULL ) return NULL;

  while( searchindex->next != NULL){
    searchindex = searchindex->next;
  }  
  return searchindex;
}

// ★リストのキーごとのuseでいずれかが0を下回らないかを確認する関数
// input: 対象のリスト
// 返り値:false:下回る(=使ってはいけない)/true:問題なし。
bool can_use_item(struct t_item_list* searchindex){
  if( searchindex == NULL ) return false;
  do{
    if( searchindex->num - searchindex->use  <= 0 ) return false; 
    searchindex = searchindex->next; // 次のキーに対象を移す
  }while(searchindex!=NULL); // 最後のキーになったら終了
  return true;
}

// ★リストのキーごとのuseを適用する関数
// input: 対象のリスト
// 返り値:成功=0, 失敗=-1
int update_itemlist(struct t_item_list* searchindex){
  if( searchindex == NULL ) return -1;
  do{
    searchindex->num = searchindex->num - searchindex->use; // 減算
    searchindex = searchindex->next; // 次のキーに対象を移す
  }while(searchindex!=NULL); // 最後のキーになったら終了
  return 0;
}

 

本記事についてのあとがき

いかがでしたでしょうか。今回は、ウッウロボに道具を入れ続け、ひたすら道具を生成する自動化プログラムについて紹介しました。

このプログラムを使えば、不要なアイテムを処分しつつ、有用な「わざレコード」や「進化系のアイテム」などに変換し続けることができます。特にマホイップの進化に必要な「アメざいく」はウッウロボで全種類がランダムながらも手に入りますので、コレクターの方にもご利用いただけますね!

ちなみに、筆者は

を回して活用しました。

 

今回の記事では、ブラックボックスだと思われがちな「ウッウロボ」のレシピの仕組みや作り方などを、我ながら上手にまとめられたなと思います。特に、HTMLの「表」と挿入する「絵」をとにかく分かりやすくなるように一つひとつ丁寧に作れたので、きっとご参考いただけるのではないかな、と考えます(めっちゃくちゃ時間かかりましたけどね…プログラミングよりもブログの「レシピ表」作りに時間がかかりました)。

はてなブログを1年間使ってきて、今日初めてstyleタグを記事中に直打ちしてcssが書けることを知りました。スマホユーザーの方が多いので、肥大化しがちな表をスクロール表示できるように工夫してみました。慣れないHTMLとCSSで大変でしたが、それっぽくなったので良かったです。

また、プログラムにおいては、C言語でよく使われる「構造体」と「リスト」を活用し、持っている道具の個数を管理する工夫を加えており、プログラミングスキルという意味でも着実に伸びているような気がしています(本職じゃないのでアレですが…)。Arduinoでも動的メモリ確保用の関数「malloc」が使える、というのが今回の発見で一番大きい学びでした(ついでにnullじゃなくてNULL(大文字)じゃないとダメとかも学びました)。

 

これからも、のんびりと学びながら、のんびりと更新していけたらと思います。引き続きよろしくお願いいたします。

 

あとがき(1周年記念!ありがとうございます!!)

さて、改めてになりますが、本日2021年11月7日(日)は、本ブログを始めた2020年11月7日からちょうど1年目となる節目の日となります。

当初は自らの趣味で、文字通り「忘備録」として始めた本ブログでしたが、1年が経過した今では、総PV数は50,000を超え、先月には初めて「単月PV」が10,000PVを突破するなど、お陰様でたくさんの方々にご覧いただけていて非常に嬉しく思います

f:id:tangential_star:20211107000527p:plain

2021年11月7日午前0時00分時点での総PV数は「53,832PV」だった

この記事を含めれば、「ポケモン剣盾Arduino自動化」カテゴリの記事は22記事目、リライト記事(ex)を含めれば25記事目となります。さらには、「ポケモン自動化番外編」の記事・レジンキーキャップの記事を含めれば、実に31記事も更新していることになりますこの1年間でこれほどブログ更新ができていることに我ながら感動を覚えます

そして何よりも温かいコメントもたくさん頂戴しており一重に皆様の支えがあったからこそこうして1年を迎えられたんだろうなぁと痛感しております。この場を借りて御礼申し上げます。

これからも本ブログを温かく見守っていただければ幸いですし、よければお知り合いにも紹介いただいたり、コメントをいただけますと励みになりますもちろん、なにかリクエストがあれば(多忙ゆえできるかはわかりませんが)検討させていただきたく思いますので、そういったものもコメントでいただければと思います。

 

それでは、改めて、本ブログが本日11/7に1年を迎えられたことを記念して、感謝を込めて本記事を結ばせていただきます。

 

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

 

#p.s.

今月の19日にはポケモンBDSPの発売日が控えていますね。私としては、まだまだ剣盾も自動化ニーズは高まるばかりですし、これからもぼちぼち剣盾を遊びながら、BDSPを楽しみたいなと考えます(それにしても有機ELモデルの新型Switch、何回もニンテンドーストアで予約抽選していますが、1度たりとも購入できません…転売ヤー勘弁してくれ…)。

 

前回記事:【Arduino自動化21】自動ドロップアイテム回収【ミント島でミント集め】

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

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

他のArduino自動化:ポケモン剣盾Arduino自動化 カテゴリーの記事一覧

YouTubeチャンネル:ますたーの忘備録 - YouTube 【NEW!!】

---

*1:ウッウロボから出るアイテム180種類の内訳は、「わざレコード」の100種類すべて(00番~99番)、「じゃくてんほけん」などの汎用アイテム、「アメざいく」などの進化アイテムなど

*2:ウッウロボの仕組みには例外もあります。ぼんぐりを4つ入れた場合や、1・3・4番目に所定のアイテムを入れた場合など、この仕組みに則さない場合があります

*3:ウッウロボの生成アイテムは、タイプ18種×ポイント15段階=270パターンになるが、うち100種類は「わざレコード」で、同じアイテムができるパターンも含まれるため、アイテムの種類としては180種類程度になる

*4:ウッウロボに入れた時の換算ptには規則性があり、6,7世代の「しぜんのめぐみ」での威力の決まり方と連動します。具体的には「威力80になるきのみ=1pt」、「威力80になる『混乱きのみ』『半減きのみ』=2pt」「威力90になるきのみ=3pt」「威力100になるきのみ=5pt」

*5:例えば、入手困難なアイテム「くちたたて」は「はがね・1pt」だが、それを使うくらいなら「リリバのみ(はがね・2pt)」を使ったほうが望ましい

*6:原則としてウッウロボに入れるアイテムの2~4番目は順不同です。

*7:「ほしのかけら」は例外レシピがあり、1番目・3番目・4番目に入れると「すいせいのかけら」になってしまいます

*8:ぼんぐりを合成し続ける場合は、単純にA連打のほうが効率が良いです

【Arduino自動化21】自動ドロップアイテム回収【ミント島でミント集め】

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

今回は、ドロップアイテムの無限回収の記事です。自動でミント回収ができます。

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

 

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

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

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

 

概要

目の前のドロップアイテム(キラキラ光るアイテム)を延々と回収し続けます。

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

f:id:tangential_star:20211018001718p:plain
f:id:tangential_star:20211016184833g:plain
キャンプを2回挟むことでアイテムが復活。自動で延々とミントを拾い続けるぞ

 

もくじです。

折角なのでArduinoを持っていない人も楽しく読めるように、手動ミント周回の方法や説明を交えつつ、自動化したポイントについても紹介します。ミントの収集おすすめ場所もブログ最後に載せました。

ソースコードだけ欲しいという方はどうぞ→ソースコード

 

ミントの自動回収ができました(結論)

結論ファーストです。

わずか50分間の放置で18個の性格補正ミントが、全自動で回収できました(この数字には「まじめミント」を含みません!)。効率にして「0.36個/分」なので、3~4分に1個の性格補正ミントが手に入ることになります*1

f:id:tangential_star:20211016184755g:plain

わずか50分(1時間足らず!)放置するだけで、18個のミントが自動的に拾い上げられた

本記事では、ドロップアイテムについての基礎知識と、「ミント」の取れるポイントをまとめ、自動化プログラムのソースコードを紹介します!

※自動化のやり方はこちら→プログラムの使い方ソースコードはこちら→ソースコード

 

日付更新と画面更新でドロップアイテムが復活する

さて、まずは前提知識から紹介します。もちろん、読み飛ばしOKです。→「ミント島の説明」まで読み飛ばす

ポケモン剣盾には、ワイルドエリアの地上や5番道路の橋の上、海の上、木々の根本など、様々な場所に「キラキラと光るアイテム」が落ちていることがあります。これは便宜的に「ドロップアイテム」と呼ばれます*2。このドロップアイテムですが、実は日付更新&画面更新によって同じ場所に復活します。

f:id:tangential_star:20211016221004p:plain
f:id:tangential_star:20211016192540p:plain
ドロップアイテムは、ガラル本土やDLCの水上のいたるところに落ちている(浮いている?)

より詳しく拾ったアイテムの復活の条件を書くとすれば、Switchの時刻が24時を超えた後に画面更新、すなわち、日付が回った後に「そらとぶタクシーによる移動」「ポケモンキャンプ」「野生ポケモンとのエンカウント」などを行うこと、です。

この特性を利用すれば、日付更新バグを活用することで、高速にドロップアイテムの回収ができます。

例えば、本ブログのArduino自動化第11回の記事では、5番道路での羽系アイテム集めを自動化しています。この際には、任意の場所で日付を変更できる裏技、いわゆる「ランクマバグ*3を活用し、「そらとぶタクシー」で画面更新を行うことでドロップアイテムの復活をしています。

最初の位置から周回するためにも、一般的には「そらとぶタクシー」を使って「画面更新」を兼ねるのが一般的です。

f:id:tangential_star:20210111121951g:plain
f:id:tangential_star:20210111151537g:plain
「日付変更」の後の「画面更新(そらとぶタクシー)」でドロップアイテムが復活する
(左:ランクマバグで日付変更/右:巣穴時渡りで日付変更)

なお、ドロップアイテムで手に入るもののうち、特筆すべきは下記2つでしょうか。

  • 端数の努力値振りアイテムとして有用な(5番道路)
  • 性格補正が変えられる「ミント」系アイテム(離れ島海域)

ハネ系アイテムの説明は過去記事(Arduino自動化11)に譲りますが、これらは対戦ガチ勢はもちろんのこと、これから育成・対戦を始めてみたい初心者の方には特に有用です。ミントについては後述します。

 

ミントを使えば、性格の能力補正を上書きできる

「ミント」とは、ポケモンが持つ「性格ごとに定められた能力補正値*4」を、性格とは別に設定させるアイテムです。

主に、旅で使った仲間のポケモンや、厳選難度が高いポケモン*5を、対戦用に育成する際に使われます。他にも、「性格はそのまま」という仕様を活かして、投げつける・トリック対策で「混乱きのみの嫌いな味」を性格補正値と変えて厳選したり、「すがた」で性格が分かってしまう「ストリンダー」の補正値を変えたりなど、初心者~対戦ガチ勢まで幅広く利用されるアイテムです。

f:id:tangential_star:20211017164335p:plain
f:id:tangential_star:20211017164338p:plain
一緒に旅をした仲間のポケモンも、ミントがあれば対人戦向けに育て上げられる

鎧の孤島に「ミント」が効率的に拾える島(通称:ミント島)がある

さて、前述の「ミント」ですが、従来、入手方法はバトルタワーで50BPとの交換だけでした。ところが、DLC「鎧の孤島」「冠の雪原」では、ドロップアイテムとして日替わり回収ができるようになりました。特に有名な回収ポイントとして、DLC第1弾「鎧の孤島」海上の北西に位置する島(離れ島海域;通称「ミント島」)が挙げられます。

「ミント島」への行き方

この「ミント島」ですが、アクセスも良好で、そらとぶタクシーで「チャレンジビーチ」に降りたら、そのまま反対側の海に向かってまっすぐ進むだけです。「みずの塔」のちょうど反対側に位置し、目印となる大きな木があるので、迷うことなくたどり着けます。

f:id:tangential_star:20211017192121p:plain
f:id:tangential_star:20211016220832g:plain
そらとぶタクシーで「チャレンジビーチ」に行けば、ミントが取れる島(いわゆる「ミント島」)は目と鼻の先だ

「ミント島」はミント周回に最適

誰が呼んだか「ミント島」と異名が付くこの島の、唯一無二にして最大の特徴が、島内5箇所にあるドロップアイテムで全種類のミントが手に入ることです。

その異名が語るとおり、他のドロップアイテムポイントよりもミントの出現率も高く、島をぐるっと1周拾い集めるだけで、2,3個のミントを回収することができます。さらに、誂え向きにポケモンの巣穴が島の中央にあるため、日付変更バグも島内で行うことができます

アクセスが好立地であることもあり、まさに、ミント周回してくださいと言わんばかりの好条件が整っています。

f:id:tangential_star:20211017170909p:plain

離れ島海域(いわゆるミント島)で拾えるミント位置 逆引きマップ(筆者作成)
島内をぐるっと一周すれば、各補正ミントが拾い集められるぞ!

ミント周回のやり方(手動)@ミント島

前述の通り、ドロップアイテムは「日付変更」&「画面更新」で復活します。

「日付変更」については、島の中央にある巣穴を使えばペナルティ無しで行うことができます。さらに、「画面更新」についても「野生ポケモンとの戦闘」か「キャンプを開く」ことで実施できます。

これを手順としてまとめると、下記のようになります。

  1. ミント島に行き、島の中央の巣穴に「ねがいのかたまり」を投げ入れる
  2. 島を1周して、落ちているドロップアイテムを拾い集める
  3. 巣穴で「みんなで挑戦!」から「日付変更」を行う
  4. ポケモンキャンプを開くor「野生ポケモンと戦闘する(すぐ逃げてもOK)」
  5. ミントが復活するので、好きなだけ2~4を繰り返す

実は、上記4の画面更新で、「ポケモンキャンプ」を開くのはあまり知られておらず、一般的なミント周回作業では、「野生ポケモンとの戦闘」の利用が多いようです。

もっとも、時間効率という点ではどっちもどっちですが、草むらに入って野生ポケモンを探す手間が省けるので、キャンプのほうが楽だと思います。

f:id:tangential_star:20211017191054g:plain

ミント集め周回作業(手動)の様子。ミスって1箇所飛ばしている…
巣穴で日付変更をした後、キャンプで画面更新をしているのがミソだ

 

長時間の自動化のためには、シンプルかつ短い動作の繰り返しが必要条件

さて、おまたせしました。ここからは、Arduino自動化のお話です。まずは、今回のミント島での自動化プログラムが作りにくい理由・自動化の設計指針について解説です。

テクニカルな話なので、興味がない人は読み飛ばしOKです読み飛ばす

 

Arduinoを使ってコントローラー操作の自動化を実装する際に、もっとも気を配るのが「どんな状況でもほぼ確実に同じ動作をすること(=動作に再現性があること)」です。言い換えると「ループが破綻しないこと」です。そのためには、野生ポケモンとのエンカウントなどの「予想外」の動きが発生することや、だんだんとミリ秒単位での動作誤差(オフセット)が溜まっていくことに留意せねばなりません。

したがって、可能な限り、不定な動きをする可能性がある要素は排除し、可能な限り、プレイヤー(キャラクター)が動かないで済む操作で済ませることが、自動化の定石と言えます。やむを得ずプレイヤーを操作させる場合にも、その動きによって発生しうる誤差をリセットあるいは吸収する手段を講じます。代表的な例としては「そらとぶタクシー」による座標リセット、壁にぶつかることで擬似的に動きを局所的に制限するなどです。

f:id:tangential_star:20210116212540g:plain
f:id:tangential_star:20210921201235g:plain
プレイヤー操作の自動化には「そらとぶタクシー」での誤差リセットが定番。
特に、誤差の溜まりやすいワイルドエリア(オープンワールド)ではマストだ

さて、こういった技術的な制約もとい設計指針に立って、今回のミント島での回収作業の自動化を考えると、かなり自動化の実現ハードルが高いことが分かります

  • 円形のルートを通らなければならない→動作が複雑で、そもそも自動化に向かない
  • 周りに壁などが無い→冗長なコマンド入力でも動作誤差をリセットできない
  • そらとぶタクシー発着点が無い→最寄りの「チャレンジビーチ」で座標をリセットしても、海を越えなければならない
  • 草むらが近くにあり、野生シンボルが多く出現→予期しないエンカウントの恐れがある
  • オープンワールドで日付変更を行う→天候などによる動作のカクつき・操作誤差の発生の恐れがある

そこで、抜本的に考え方を改めるため、初心に立ち返り、ミント集めの動機を考えました。我々がミント周回をするのは「ミント」が必要だからですが、別に21種類すべてのミントが必要なわけではありません。つまりは、よく使う特定のミントさえあれば、実用には困らないはずです。

例えば「いじっぱり」「ようき」「ひかえめ」「おくびょう」だけが必要であれば、ミント島のアイテムドロップ回収ポイントの5箇所すべてではなく、「こうげき」「とくこう」「すばやさ」の3箇所だけ回れば良いことになります。突き詰めれば、いずれか1箇所を3回に分けて自動化すれば、総じて無駄の少ない自動化が可能です。

漫然とミントを集めるのではなく、特定の種類のミント1種類「だけ」を周回するように設計すれば、プレイヤーの操作も必要なく、上記の種々の問題はクリアできます。前述の通り、ミント島で拾えるミントは場所によって拾えるミントの種類(こうげき、ぼうぎょ、など)に偏りがあるので、任意の1箇所でドロップアイテムを拾い続けることで、上記要件を満たすことができます。

したがって、今回のミント回収自動化プログラムでは、任意の1箇所でドロップアイテムを拾い続ける仕様としました。

 

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

おまたせしました!Arduinoプログラムの使い方についてです。と言っても、準備は非常にシンプルです。剣盾側の準備はわずか2つ、ソースコードの修正はわずか1箇所です。

自動化プログラムを使うための準備(ポケモン剣盾)

剣盾側の準備としては、下記2つです。

  1. Xでメニューを開いたときに「キャンプ」にカーソルが合う状態にする
  2. 狙いのミントがAで拾える場所の目の前でセーブする

わずか2つの準備で、ひたすら目の前のキラキラ(ドロップアイテム)を拾い続けてくれます。「ねがいのかたまり」や「ランクマバグ」は一切使いません!便利ですね。

f:id:tangential_star:20211017223716p:plain
f:id:tangential_star:20211017223721p:plain
メニューで「キャンプ」にカーソルを合わせて、拾いたいアイテムの前でセーブすれば準備完了

自動プログラムのソースコードの修正

お使いの環境に合わせて(true)を(false)に変えるだけです。具体的には、IGNORE_NEW_ITEMを、そのROMで入手したことがないアイテムを拾う可能性がある場合に(false)に書き換えます。

要するに、その場所で拾える特定の能力補正がかかるミント4種類とまじめミントの5種類のうち1個でも所持していないものがあれば(false)です。サブロムなどでは注意しましょう。

#define IGNORE_NEW_ITEM (true) // ★その場所で拾えるアイテムのうち、「まだ拾っていないもの」がある場合は「false」にしてください。
f:id:tangential_star:20211016173732p:plain
f:id:tangential_star:20211017233939p:plain
入手したことがないアイテムを拾うと動作がズレる。リュックをしっかり確認しよう
(アイテムを並び替えるとわかりやすい。右図だと「わんぱくミント」が1つもない)

 

ソースコード

アイテムドロップの場所の目の前でArduinoを挿すだけのカンタン仕様です。メニューを開いたときに「キャンプ」にカーソルをあわせることをお忘れなく。

NintendoSwitch Ver.13.0アップデート対応済(詳細はこちら
※Switch Liteをご利用の方は「#define SWITCH_VER (13)」を「#define SWITCH_VER (12)」に書き換えてご利用ください。

/*  ★★★★★★★★★★★★★★★★★★★★★★★★★★★★
 *  ★目の前のドロップアイテム(ミントや羽)を拾い続けるやつ★
 *  ★★★★★★★★★★★★★★★★★★★★★★★★★★★★
 *  
 *  「●●ミント」や「●●のハネ」など、地上のドロップアイテムを永遠に拾ってくれます。
 *  
 *  ★目の前にドロップアイテムが落ちているところで使います。
 *  ★Xボタンを押したときに「キャンプ」にカーソルが合う状態で使うこと【重要】
 *  ★野生ポケモンが突っ込んでこないところでやりましょう。特に雨下のオニシズクモのような、
 *   プレイヤー側に走って向かってくるポケモンが出る場所は要注意。
 *  
 *  【利用方法】
 *  地上で使ってください。水上のドロップアイテム回収は無理です。
 *  ワイルドエリアや5版道路の橋など、日付経過でドロップアイテムが復活する場所で使います。
 *  目の前にキラキラ光るアイテムが落ちていて、Aを押したら拾える場所に立ってください。
 *  Xボタンでメニューを開いたときに「キャンプ」にカーソルを合わせてメニューを閉じてください。
 *  あとは、Arduinoを挿すだけでドロップアイテムを永遠に回収してくれます。c⌒っ.ω.)っ
 *  
 *  (c) 2021 ますたーの忘備録
 *  https://tangential-star.hatenablog.jp/
*/

#include <SwitchControlLibrary.h>

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

#define SWITCH_VER (13)
// ★Lite利用者は「12」にしてください★
// SwitchのVer13アップデートに伴う記述。

#define IGNORE_NEW_ITEM (true)
// ★その場所で拾えるアイテムのうち、「まだ拾っていないもの」がある場合は「false」にしてください。
// ※筆者は未検証です。

#define SHOW_PICKUP (false)
// ワイルドエリアなどで、プレイヤーが拾っている様子を見たい人は「true」にしてください。
// 大きなポケモンを連れ歩きしているときなど、プレイヤーの様子が見えなくて悲しい人向け。
// RやLでカチャカチャした後に、右スティックでカメラを回転させて、筆者好みの角度にしてくれます。
// ※自動化には何の影響もありません。単純に好みです。

void PushRL(int delay_time_ms);
int PushKey(char* keyname, int holdtime, int delaytime);
void NextDayInCheatMode(void);
void TiltRightStick(int direction_deg, double power, int holdtime, int delaytime);

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

  if(SHOW_PICKUP){
    // TiltRightStick(0,  1.0,  300, 100);
    TiltRightStick(90, 1.0, 1480, 100); // カメラ チルト回転
    TiltRightStick(180,1.0,  300, 100); // カメラ パン回転
  }
}


void loop() {

  // ★アイテムを拾う
  PushKey("A", HOLDTIME, 800);
  PushKey("B", HOLDTIME, 800); // ○○は ○○を みつけた! ▼ 

  if(IGNORE_NEW_ITEM){
    // [NEW] むじゃきミント
    // ポケモンに 香りを かがせると 
    // 素早さが 上がりやすく なるが 
    // 特防は 上がりにくくなる ミント。
    PushKey("A", HOLDTIME, 800); // 説明文おくり(2行パターン)
    PushKey("B", HOLDTIME, 800); // 説明文おしまい
  }

  // ★キャンプを開く(日付変更)
  PushKey("X", HOLDTIME, 500);
  PushKey("A", HOLDTIME, 10200); // 顔アップから始まるとタイムロス(悪天候でなければ9800でOK)

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

  // ★キャンプをとじる
  PushKey("B", HOLDTIME, 800);  // キャンプを 終了 しますか?
  PushKey("A", HOLDTIME, 5300); //  ⇒ はい
  
  // ★キャンプを開く(画面更新)
  PushKey("X", HOLDTIME, 500);
  PushKey("A", HOLDTIME, 10200); // 顔アップから始まるとタイムロス(悪天候でなければ9800でOK)

  // ★キャンプをとじる
  PushKey("B", HOLDTIME, 800);  // キャンプを 終了 しますか?
  PushKey("A", HOLDTIME, 5300); //  ⇒ はい


}

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

#if (SWITCH_VER >= 13)
  // Switchのアプデで「ドックの更新」項目が追加されたので
  // 日付変更をする場合には「↓」キー長押しが必要に。
  PushKey("down", 735, 55); // 2021/10/16修正案。780→735 
#else
  // ver12まで(13アプデ前)は、1ページに収まるのでそのままでOK
  for(int i=0; i<4; i++){
    PushKey("down", HOLDTIME, 55);
  }
#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);
  return;
}

 

あとがき

以上が本記事の内容となります!いやぁ~今回も盛りだくさんでしたね(←書いた本人)

ところで、「『ドロップアイテム回収の自動化』なんて何を今更」と思われた方もいらっしゃることかと思います。実際、私がハネ系アイテムの自動化をしたのも、2021年1月(10ヶ月前!)ですし、たしかに今更感は否めません。

ところが、剣盾発売から2年近く経った今でさえ、賑わう多くの自動化界隈で未だに「ミント回収自動化プログラム」として公開されたものは無く*6、実は、日本初の「ミント」回収自動化の成功例だったのではないかな?と思います(違ったらごめんなさい)。

そういう意味でも、今回の記事は、そういったニッチだけどパワフルな需要を兼ねるものになります。もし、皆様にご参考頂けたようであれば嬉しく思います。

もし、参考になったよ、という方がいらっしゃれば、是非ともコメントをいただければと思います。励みになります!

 

-- 

【おまけ】狙う性格補正ごとの「ミント」おすすめ入手場所

オススメのミント採取場所(ドロップしている場所)を表にまとめました!自動化する際はもちろん、手作業の周回でもご参考いただけると思います。本記事の真ん中に入れると読みにくそうだったのでここに入れました

 

狙う性格補正の「ミント」が拾える場所の一覧 ※ヨロイ島はすべて「ミント島」です

名前 効果 おすすめの場所

f:id:tangential_star:20211017125416p:plain「こうげき」が上がるミント

さみしがりミント 攻撃・防御

f:id:tangential_star:20211017151221g:plain

ミント島の最奥部に生えている木の根本
いじっぱりミント 攻撃・特攻
やんちゃミント 攻撃・特防
ゆうかんミント 攻撃・素早

f:id:tangential_star:20211017125418p:plain「ぼうぎょ」が上がるミント

ずぶといミント 防御・攻撃

f:id:tangential_star:20211017151247g:plain

ミント島できのみが採れる木のすぐ奥。
わんぱくミント 防御・特攻
のうてんきミント 防御・特防
のんきミント 防御・素早

f:id:tangential_star:20211017125420p:plain「とくこう」が上がるミント

ひかえめミント 特攻・攻撃

f:id:tangential_star:20211018000724g:plain

巨人の寝床(東)から道なり右手にある木の陰
(カンムリ雪原)【オススメ!】

f:id:tangential_star:20211017151320g:plain

ミント島中央の草むら奥側にある赤い草花
(ヨロイ島)
おっとりミント 特攻・防御
うっかりやミント 特攻・特防
れいせいミント 特攻・素早

f:id:tangential_star:20211017125422p:plain「とくぼう」が上がるミント

おだやかミント 特防・攻撃

f:id:tangential_star:20211017151352g:plain

ミント島中央の巣穴より右手側の原っぱ
おとなしいミント 特防・防御
しんちょうミント 特防・特攻
なまいきミント 特防・素早

f:id:tangential_star:20211017125425p:plain「すばやさ」が上がるミント

おくびょうミント 素早・攻撃

f:id:tangential_star:20211018114817g:plain

巨人の寝床(東)から道なりの丘を登って左手の藪
(カンムリ雪原)【オススメ!】

f:id:tangential_star:20211017151421g:plain

ミント島に上陸してすぐの原っぱ
せっかちミント 素早・防御
ようきミント 素早・特攻
むじゃきミント 素早・特防

f:id:tangential_star:20211017125414p:plain「能力補正」をなくすミント

まじめミント 性格補正無し 各ミント採取場所でランダムに入手可能

 

本当のあとがき

この自動化プログラムでは、「キャンプ」を開いて、その状態でしれっと日付変更をしています。これ、冷静に考えれば、もし時間変更ペナルティを受けていれば成り立たない自動化です。

言い換えると、いわゆる「ランクマバグ状態」や、「巣穴で『みんなで挑戦』を押している状態」と同様に、ポケモンキャンプを開いている間は一時的に日付変更のペナルティが解除されていることになります

実は「ポケモンキャンプ」を使って時間変更バグができた?

このプログラムを組む前の経験談なのですが、ミントの復活条件を検証中に偶然、キャンプを開いて日付を更新してしまったことがありました。

その時、特に何事も無く(=いわゆるペナルティが発生せず)ミントが復活したことを受けて、「あれ?巣穴バグを使わなくても日付変更できるのでは?」と気づき、色々と検証をして、今回の記事執筆に漕ぎ着けました。つまり、この発見により、従来、ミントの自動化でネックになっていた「日付変更」を、巣穴を使わず=場所を選ばず実現できるようになったのです。

そういう意味では、もしかしたら、「ミントの自動化ができたこと」よりも、こっち(=巣穴で「みんなで挑戦」をせずとも、キャンプでを開けばどこでも日付変更できる)のほうが大発見なのかもしれません。これ、発見したときはめちゃくちゃテンション上がりました。

全然見たことも聞いたこともない日付変更の方法だったので「このバグ自体、初出?もしそうだったら、すごいことなのでは?」と思いつつ、この気持ちを共感できる人もいないので、こういうカタチであとがきとしてまとめさせていただきした。

(このあたり、初出そうだったら、後日、記事としてまとめるかもです)【2021/10/18 11:52追記:初出ではなかったです…】

 

 

改めて、本ブログを引き続きよろしくお願いいたします。

もし、このブログが参考になったよ!いつも見ているよ!という方がいらっしゃれば、是非ともコメントをいただけると励みになります
(リクエストがあればコメントにいただければと思いますが、多忙&マイペース更新なのであしからず。ぜひ検討したいとは思います)

 

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

 

前回記事:【Arduino自動化20】Switch2台で「完全」自動!レイドバトル自動周回

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

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

他のArduino自動化:ポケモン剣盾Arduino自動化 カテゴリーの記事一覧

YouTubeチャンネル:ますたーの忘備録 - YouTube 【NEW!!】

---

*1:いわゆるミント島で、1時間足らずでの検証結果です。場所によって入手効率は変わります

*2:剣盾で「ドロップアイテム」と言えば文字通り落ちているアイテムのことを指しますが、文字を入れ替えて「アイテムドロップ」だとレイドバトル報酬を指す場合が多いようです

*3:ランクマバグ:ランクマッチの直後にSwitchの日付変更を行ってもペナルティが発生しない状態のこと。Switchが2台あればローカル通信で対戦→機内モードで同様の状態にできる

*4:性格による能力補正:例えば「ようき」なら「すばやさ」が1.1倍・「とくこう」が0.9倍になります

*5:配布ポケモン、偶発的に出会った色違いのポケモン、孵化厳選ができないタマゴ未発見ポケモン・準伝説ポケモン、強制捕獲イベントがあるポケモン(ムゲンダイナ・ザシアン・ザマゼンタ)、作中で人からもらえるポケモン(キョダイマックスヒトカゲ・タイプ:ヌル)など

*6:2021/10/7現在。筆者調べ。Arduino Leonardo系列を用いたポケモン剣盾自動化において「ミント」系アイテムの自動回収を主題としたもの。あったらごめんなさい

【Arduino自動化20】Switch2台で「完全」自動!レイドバトル自動周回【デリバード・色違いレイドなど】

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

今回はSwitchを2台使った、「完全自動」レイドバトル自動周回の記事です。

本ブログのArduino自動化記事記念すべき「第20回」に相応しい内容ですね。YouTubeにも動画をアップしました!

 

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

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

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

 

概要

Nintendo SwitchArduino Leonardoを2組を使い、片方でレイド募集、もう片方でレイド周回を行います。おそらく、完全自動で同一レイド周回ができる最適解です。

※1人(1台)で周回する場合は第19回の記事、ローカル配布だけを行いたい場合は第18回の記事をご覧ください
※本記事は、2組の機材が必要です。Arduinoの接続にはジャンパーケーブル(オス-オス)が3本必要です(後述)

f:id:tangential_star:20211009123347p:plain
f:id:tangential_star:20211009122137g:plain
Switch2台でレイドバトルを「完全」自動で周回。レイド報酬稼ぎや乱数色違いの捕獲が自動化できる!

 

もくじです。

ソースコードだけ欲しい、という方はどうぞ→読み飛ばす

概要だけを知りたい方は⇒YouTube動画をご覧ください。

【注1】今回の自動化はArduino Leonardoを2組使います。また、今までの記事よりも機材構成・接続の難易度が高いです(といっても繋ぐだけですけど)。機材の接続手順が非常に大事ですので、今回はできる限り読み飛ばさず読んでいただいたほうが良いかもしれません。
【注2】本記事では、シリアル通信を使います。筆者自身、このあたり専門家では無いのでうまく行かなくてもご了承ください><
【注3】本記事では「Arduino Leonardo」は純正かつ「ソケットヘッダ付」のものを利用している想定で書いています。特に互換機など、ご自身がお持ちのボードに応じて、適宜読み替え・ソースコード修正が必要になる場合があります。なお、互換機の中には、シリアル通信ができないボードも一部あります。

 

YouTubeにも動画をアップしました!

本ブログの内容は、YouTubeにもアップロードしています。合わせてご視聴いただけると、より理解が深まると思います。

★2021/10/11 AM7:00 公開!

 

0.できたこと(まず見てほしい)

結論ファーストです。「Arduino Leonardo」2台を有線接続し、それぞれがNintendo Switchを操作することで、レイドバトルの自動周回を、「完全放置」でできるようになりました。結果、デリバードレイドを一晩中自動周回させ、マックスこうせき・けいけんアメがカンストしました

本記事では、必要機材・やり方(配線方法)・ソースコードをすべて紹介します。

f:id:tangential_star:20211009161947j:plain
f:id:tangential_star:20211009181507p:plain
自動でレイド周回させれば、育成アイテム(けいけんアメ)のカンストも夢じゃない

1.前提知識

ここは、開発経緯や、前提になる知識について余談的に書いています。

お急ぎの方は⇒2章「事前準備」まで読み飛ばす

開発の背景:いままでのレイドバトル自動化の課題

本ブログでは、レイドバトルの周回作業の簡略化・省力化を目的とした記事をいくつか執筆してきました。例えば、前々回(第18回)の記事ではレイドバトルのローカル配信(=募集側)の自動化、前回(第19回)の記事ではソロ・レイド周回(=セルフ周回)の自動化を、それぞれ実装しました。

しかしこれらは、完全に独立したプログラムであり、「募集側と周回側を一緒に動かす」ことはできませんでした。言い換えれば、目的のレイドバトルを周回するためには、何らかの「手動作業」が必要だった*1のです。

個別には非常に便利な自動化でした(私もしばらく愛用していました)が、こと「放置」という観点からは、もう一歩踏み込みたい課題が残っていました。そこで、今回は「募集側」「参加側」を両方自動化しよう!と考えました

f:id:tangential_star:20210503200445g:plain
f:id:tangential_star:20210919200733g:plain
今まで「レイド配布」「ソロレイド周回」はあったが、2台で連動するものはなかった
(左:第18回 ローカル配布/右:第19回 ソロ・レイド周回)

Arduino2台を通信させて、レイドバトル「完全」自動周回を実現!

レイドバトルの「募集側」「参加側」の両方を自動化するにあたって、2台の操作が「ちゃんとタイミングよく動いてくれる」ことが前提となります。ところが、Arduinoはマクロコントローラーと同様、単体では「●●秒待って、●●を押して…」と、単純な操作の自動化しかできません

要するに、きっちり時間を測って「募集側」「周回側」の操作を独立してプログラムを書いても、どうしてもだんだんと動作がズレてしまい、長時間の放置はできなくなってしまいます。

そこで、2台のArduinoの同期を取るために「シリアル通信*2」を利用しました。つまり、物理的に2台のArduino Leonardoを接続し、募集側の状況をもう1台のArduinoに報告するようにしたのです。

f:id:tangential_star:20211009142223p:plain
f:id:tangential_star:20211009142217p:plain
「シリアル通信」有無の比較のイメージ図。
要するに、2台のArduinoが会話しながら作業を自動化するのでズレないのだ!

より具体的には、募集側Arduinoから「今からレイドバトルを始めるよ!」とか「今から戦闘だからA連打してね!」といった状況を、参加(周回)側のArduinoにシリアル通信で送信するようにプログラムを書きました。

これにより、募集側と周回側のArduinoそれぞれの操作タイミングがズレずに、完全自動でのレイドバトル周回が実現できました。

通信のためには、Arduino2台の配線作業が必要

さて、今回はArduino2台の操作タイミングをあわせるために「シリアル通信」を採用することにしました。

この方式では、前述のとおり、物理的に2台のArduinoを接続するので、接続用の銅線*3がいります。さらに、Arduino Leonardoにはシリアル通信できるPinとできないPinが混在します(要するに、挿すところを間違えたら動きません!)。

f:id:tangential_star:20211009153416j:plain
f:id:tangential_star:20211009153354j:plain
Arduino同士を接続するためには「ジャンパーワイヤー(接続用の銅線)」が必要
Arduinoのpinがソケットならオス端子、ピンヘッダならメス端子が必要だ

そういう意味では、「シリアル通信」を使うためには物理的な配線作業が必要になるため、今までよりもちょっとだけ難易度が上がります。とはいえ、冒頭にも書いたとおり、原則として「差し込むだけ」ですので、頑張りましょう。詳しくは、次章で紹介します。

 

2.事前準備(プログラム書き込みと機材の接続)

配線に必要なもの(ハードウェア)

今回の自動化において、準備するものは下記の通りです。ジャンパーワイヤー以外は【番外編3】筆者の使用機材に購入リンクを含め載せています。

  • 【必須】Nintendo Switch(またはLite)2
  • 【必須】Arduino Leonardo 2
  • 【必須】SwitchとArduinoを繋ぐUSBケーブル(兼 PCからの書き込み用)2
  • 【新たに必要】ジャンパーワイヤー(オス-オス) 3
  • (あれば便利)Switchドック(またはハブスタンド)&純正充電アダプタ 2
  • (あれば便利)スイッチ付きUSBコネクタ 1

要するに、【Arduino自動化01】導入記事に書いた基本構成が2組+ジャンパーワイヤー3本があれば最低限OKです。

f:id:tangential_star:20211009163922p:plain

今回のレイドバトル自動化で使う機材の一覧(配線イメージ)
慣れるまでは、とりあえず画像保存して適宜確認すると良い。

お使いのArduino Leonardoが、【番外編3】筆者の使用機材で紹介している、純正のものであればソケットヘッダが付いているはずなので、ジャンパーワイヤーはオス-オスでOKです。

なお、ジャンパーワイヤーとは、つまるところただの銅線(=抵抗が無い)ですから、よほどのことがなければ、どこのメーカーから購入してもOKです。筆者はエントリーキットに入っていたものを使っています。
 → 持っていない人は「Arduino ジャンパーワイヤー オス オス」でAmazon検索

ただし、pinの形状すなわち、オス-オス、オス-メス、メス-メスが間違っていないかは注意して購入しましょう*4。今回使うのはわずか3本ですし、大量に買っても余らせがちなので、無駄遣いにならないように選びましょう(たぶん、本ブログでジャンパーワイヤーを使うことも今後は無いでしょうし)

f:id:tangential_star:20211009160827j:plain
f:id:tangential_star:20211009160757j:plain
左写真のようなソケットヘッダ付のArduino Leonardoなら、ジャンパーワイヤーは「オス-オス」でOK
※右写真の「メス」のジャンパーワイヤーと間違えないように注意!

 

プログラムを書き込んで配線(Switchを接続する前の作業)

手順1.2台のArduinoにそれぞれプログラムを書き込む

まずは、2台のArduino Leonardoそれぞれに後述の「ホスト側(配布側)」「参加側(周回側)」のプログラムをIDEで書き込みます。

ここの手順は普段となんら変わりありませんが、このとき、ArduinoはPCとのみ接続し、Arduino同士の配線は後回しにしてください(重要)Arduino同士を接続した状態でプログラムを書き込むと、場合によっては干渉を起こし、正しくプログラムが書き込まれない可能性があります。

また、1度にPCに接続するArduinoも1台にしておいた方が、何かと安定します。なお、どちらのArduinoにどちらのプログラムを書き込んだか、しっかりわかるようにしておきましょう。

f:id:tangential_star:20211009175616p:plain

まずはプログラムをArduinoに書き込もう。PCと同時に接続するArduinoは1台だと安定する

 

手順2.Arduino2台を接続(配線)する

プログラムをそれぞれのArduinoに書き込んだら、パソコンからArduinoを取り外してください。その後、ArduinoをSwitchに接続する前(重要)に、Arduino同士の配線を済ませます。具体的には、レイド募集側の11番ピンともう片方の9番ピンを、レイド募集側の10番ピンともう片方の10番ピンを、そして、レイド募集側の5Vピンともう片方の5Vピンを、それぞれ接続します。

なお、接続する順番や、ジャンパーワイヤーの色は気にしなくてOKです。コツとしては、しっかり差し込むことでしょうか。あくまでもアナログ的に接続するので、うまく動かない場合は接続不良や、ケーブルの断線も疑うようにしましょう

ちなみに、シリアル通信では、本来「GND」にもワイヤーの接続が必要らしいです。もし万が一、上記手順でうまく行かないor動作が安定しない場合は、更に1本追加して、募集側・参加側の両方のGND端子同士を繋ぐジャンパーワイヤーを接続してください。

f:id:tangential_star:20211010132220p:plain

両方のArduinoにプログラムを書き込んだら、PCから外して3本のジャンパーケーブルを接続しよう。

 

手順3.必要に応じてハブスタンドほかに接続しておく

あとは、Nintendo Switch以外の配線類をすべて済ませておきます。充電アダプタも、つけるのであれば、このタイミングで配線に加えておいてください*5

なお、配線が完了しても、Nintendo Switchはまだ差し込まないでください。

f:id:tangential_star:20211009183304p:plain

Arduinoの配線が終わったら、充電アダプタ・スタンドなどの配線も済ませよう。
この状態になれば、機材側は準備OK。あとはポケモン剣盾の準備だけだ。

 

さて、以上の手順で、今回の鬼門は終わりました。お疲れさまでした。あとは、Switchを差し込むだけ、なのですが、レイド周回側のSwitchを先に差し込んで動作が止まるのを確認してから、レイド募集側を差し込む必要がある点には注意しておきましょう。詳しくは後述します。

 

3.Switch(ポケモン剣盾)の準備

レイド1ターン周回要員の育成

基本的には、【Arduino自動化18】に掲載の流れに沿って、募集側・周回側ともに1匹ずつ最適なポケモンを手持ち先頭に加えておきましょう。なお、自動化の特性上、使える「わざ」は1つだけにしておき、火力強化系のアイテムをもたせておくと安心です。

自動下記に引用(一部改変)しますが、デリバードレイドの周回を行う場合には、CS極振りレジエレキと、AS極振りルガルガン(まよなかのすがた)がオススメです。

初撃オススメ:Cぶっぱレジエレキ「エレキボール」

レジエレキはS種族値200というトンデモ性能で、デオキシススピードフォルムすら抜き去る圧倒的な素早さの持ち主。そのユニークな特性「トランジスタ」により、でんきタイプのわざであれば実数値1.5倍の火力を出せるため、初撃に使うにはもってこいです。

f:id:tangential_star:20210504164042g:plain
f:id:tangential_star:20210504164006j:plain
初撃オススメはCぶっぱレジエレキによる圧倒的素早さからのエレキボール
追撃オススメ:ASぶっぱ夢ルガルガン@こだわりハチマキ「ストーンエッジ

続いて、追撃要員の紹介です。こちらは、こうげき努力値に極振りし、こだわりハチマキを持たせたルガルガンストーンエッジがオススメです。夢特性であれば「ノーガード」になるため命中不安なエッジを確定で当てることができます。また、いわタイプなので、デリバード(こおり・ひこう)の4倍弱点を突くことができることもポイントです。無論、天候による威力増減の影響も受けません。

f:id:tangential_star:20210504164026g:plain
f:id:tangential_star:20210504164011p:plain
追撃オススメはASぶっぱルガルガン夢特性:ノーガード)のストーンエッジ

これらのポケモンを準備したら、配信側・参加側の2プレイヤーがそれぞれ1匹ずつを持つように手持ちの先頭にあらかじめ加えておきましょう

 

レイド募集側の準備:デリバード★4のレイドバトルの巣穴の目の前でセーブ

ホスト側(募集側)では、クライアント側(周回側)で周回させたいレイドバトルを出現させた状態にしておきます。詳細説明は【Arduino自動化18】に譲りますが、デリバードレイドは最高効率のレイド報酬と言われていますので、今回は、デリバードレイド(★4)バトルを想定しています。

ホスト側の事前の準備は下記2つです。

  1. 「ねがいのかたまり」を入れて厳選済の巣穴の目の前でセーブ
  2. 手持ち先頭を1ターン周回要員にしておく

デリバードレイドでは、カンムリ神殿の巣穴が好アクセスポイントなので、うまく活用すると良いでしょう。

f:id:tangential_star:20210504173750g:plain
f:id:tangential_star:20210504173019g:plain
デリバードレイド探しは、アクセス良好なカンムリ神殿の巣穴がオススメ。
募集側では、目的のレイドバトルを厳選済の巣穴の目の前でセーブしておこう。


レイド周回側の準備:目の前に話しかけるものが無いところでセーブ

続いて、クライアント側(レイド周回側)の準備です。準備、と言っても、守るべきことは3つだけ。

  1. 十分な量のモンスターボールを所持し、リュック先頭に並び替えておくこと
  2. 目の前に話しかけるものが一切無い場所でセーブすること
  3. 手持ち先頭を1ターン周回要員にしておく

特に、2はループそのものが途切れる要員となりますので、周りから野生のポケモンなどが突進してきたり、ワイルドエリアで手持ちポケモンに話しかける方向を向いていたりなどは避けてください。

f:id:tangential_star:20211010004642p:plain
f:id:tangential_star:20211010004614p:plain
周回側は、大量のボールを持って、目の前になにもないところでセーブするだけだ

 

4.SwitchをArduinoに接続する

ここまで、準備お疲れ様でした。

あとは、Switchを差し込んで周回していくだけです。ただし、2台のSwitchは同時ではなく、時間を空けて、順番に差し込む必要があります。具体的には、先に「レイド参加側」のSwitchを接続し、Homeボタンが2回押されたような挙動を確認した上で、もう一台(募集側)のSwitchを接続する、という流れです。

このあたりは、スイッチ付USBアダプタを機材に入れているかどうかで煩雑さが変わるので、順番に説明します(無くても大丈夫ですのでご安心を)。

USBスイッチ付アダプタがある場合

接続の手順は以下の通りです。

  1. 周回側・募集側のNintendo SwitchをそれぞれのArduinoに接続する
  2. 周回側のArduinoのLEDが消灯するまで待つ
  3. USBスイッチを「ON」にする

f:id:tangential_star:20211010114939g:plain

実機でのイメージ。Switchを接続したらLED消灯を待ち、USBスイッチをONにする。

f:id:tangential_star:20211010111312p:plain

両方のSwitchを接続して、周回側のLED消灯後にUSBアダプタをONにすればOK

 

USBスイッチ付アダプタが無い場合

接続の手順は以下の通りです。

  1. 周回側のNintendo Switchを周回側のArduinoに接続する
  2. 周回側のArduinoのLEDが消灯するまで待つ
  3. 募集側のNintendo Switchを募集側のArduinoに接続する

f:id:tangential_star:20211010111713p:plain

まず周回側のSwitchを接続してLEDの消灯を待ってから、募集側のSwitchを挿せばOK

 

5.ソースコード

今回は、レイド自動周回「募集側(ホスト側)」「周回側(クライアント側)」の2つのソースコードを公開します。配線・接続手順については前述の通りです。

一応、ソースコード冒頭にもコメント部分に記載していますが、よくわからない人は、適宜本記事を読み直していただければと思います。

5-1.レイドバトル募集側(ホスト側)

レイド募集側のプログラムです。

/*  ★★★★★★★★★★★★★★★★★★★★★★★★★★★★
 *  ★2台で自動 デリバード レイド周回【ホスト側(送信側)】★
 *  ★★★★★★★★★★★★★★★★★★★★★★★★★★★★
 *  
 *  デリバードレイドの周回作業を自動で行ってくれるすごいやつ。
 *  
 *  ★デリバード星3または星4の巣穴を厳選しておきましょう
 *  ★巣穴の前でセーブの上、少なくともボールを1つ以上持っておきましょう
 *  
 *  【利用方法】
 *  注意:こちらのArduinoとSwitchとの接続は、手順の最後です!!!
 *  
 *  先に、2台のArduinoを配線してください。
 *  使うpinは、本ArduinoではRX, TXとしてそれぞれ10, 11を使います。
 *  すなわち、こちらのRX(10)を、ホスト側のTX(10pin)に接続し、
 *  こちらのTX(11)を、ホスト側のRX(9pin)に接続してください。
 *  配線したら、周回側のSwitchと周回側のArduinoを接続し、動作を確認します。
 *  
 *  周回側(受信側)のSwitchが、待機ループの中に入ったことを確認の上、
 *  配線後のホスト側Arduinoをホスト側Switchに差し込んでください。
 *  
 *  ※シリアル通信を使います。Arduino同士の配線が必要です。
 *  ※よくわからない人は、ご利用をお控えください(誤って破損する可能性もあります)。
 *  
 *  (c) 2021 ますたーの忘備録
 *  https://tangential-star.hatenablog.jp/
*/
#include <SwitchControlLibrary.h> // Switch自動化用のライブラリ

#include <SoftwareSerial.h> // 2台目のArduinoと通信するためのライブラリ
SoftwareSerial mySerial(10, 11); // RX, TX(pin10, 11を使います)


// うまく行かなかったら、下記いずれかで適宜入れ替えてみてください。
// 特に、Leonardoの互換ボード利用者はこの辺を触る必要性があるかもしれません。
// ※配線もTX, RXを0,1に変える必要があります【上級者向け】

#define SOFTWARE_SERIAL (1)
// #define USE_NORMAL_SERIAL (1)
// #define USE_SERIAL_1 (1)


#define HOLDTIME (95) // 1回のキー入力の長押し時間[ms]
#define WAIT_TIME (30)   // 「みんなで挑戦!」を押してから何秒待ち続けるか?
#define BATTLE_TIME (60)  // 挑戦後、何秒間レイドバトルを継続するか?[ここは60でのみ動作を確認してます]


// 通信で使う定数です。各状態に遷移時にこれらを2台目にシリアル通信で送信します。
enum {RECEIVE_BEGIN_PACKET=3, BEGIN_LOOP, START_YYCOMM, START_BATTLE, RESET_GAME } state;
#define BAND (300) // シリアルポートに接続するレートです。300[bps]で通信します。
int LED = 13; // ArduinoのLEDを簡易にデバッグ用に使います。Arduino LeonardoのLED pin番号を入力!


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

void setup() {
  unsigned long int current_time=0;
  unsigned long int start_time=0;

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

  pinMode(LED, OUTPUT);
  digitalWrite(LED, HIGH); // LED 点灯
  DoubleHome();
  delay(5000); // 5秒待機

#ifdef USE_SERIAL_1
  // Serial1
  Serial1.begin(BAND); // ソフトウェアシリアルの初期化
  start_time=millis();
  current_time=start_time;
  while (!Serial1) {
    if(current_time - start_time >= 2000UL){
      Serial1.begin(BAND); // 2秒ごとに再試行
      start_time=millis();
    }
  }
  DoubleHome();

#endif

#ifdef USE_NORMAL_SERIAL
  // Serial
  Serial.begin(BAND);
  start_time=millis();
  current_time=start_time;
  while (!Serial) {
    if(current_time - start_time >= 2000UL){
      Serial.begin(BAND); // 2秒ごとに再試行
      start_time=millis();
    }
  }
  DoubleHome();
#endif
  

#ifdef SOFTWARE_SERIAL
  /*while(!mySerial)*/
  mySerial.begin(BAND);
  DoubleHome();
  mySerial.write(RECEIVE_BEGIN_PACKET);
  mySerial.flush();
#endif

  
  delay(3000); // 3秒待機

}


void loop() {
  unsigned long int current_time=0;
  unsigned long int start_time=0;
  unsigned long int temp_time=0;

  // ★もう1台のArduinoに状態を通知
#ifdef USE_NORMAL_SERIAL
  Serial.print(BEGIN_LOOP);
  Serial.flush();
#endif
#ifdef USE_SERIAL_1
  Serial1.print(BEGIN_LOOP);
  Serial1.flush();
#endif

#ifdef SOFTWARE_SERIAL
  mySerial.print(BEGIN_LOOP);
  //mySerial.flush();
#endif

  digitalWrite(LED, LOW);
  delay(100);
  digitalWrite(LED, HIGH);
  delay(100);
  digitalWrite(LED, LOW);
  delay(100);  
  digitalWrite(LED, HIGH);
  delay(100);
  
  // 巣穴の前で乱数したい個体を出してセーブした状態。
  PushKey("A", HOLDTIME, 1500); // 募集画面に入る

  PushKey("A", HOLDTIME, 2500); // みんなで 挑戦!(→通信待機中 しばらくお待ち下さい)
  PushKey("UP", HOLDTIME, 300); //
  PushKey("A", HOLDTIME, 500); // 準備完了!
  PushKey("A", HOLDTIME, 500); // バトルを 開始する
  PushKey("A", HOLDTIME, 500); // 参加人数が足りません! ▼ (→サポートの トレーナーが 参加しますが よろしいですか?)

  // ★もう1台のArduinoに状態を通知
#ifdef USE_NORMAL_SERIAL
  Serial.print(START_YYCOMM);
  //Serial.flush();
#endif
#ifdef USE_SERIAL_1
  Serial1.print(START_YYCOMM);
  //Serial1.flush();
#endif
#ifdef SOFTWARE_SERIAL
  mySerial.print(START_YYCOMM);
  //mySerial.flush();
#endif

  digitalWrite(LED, LOW);
  delay(100);
  digitalWrite(LED, HIGH);
  delay(100);
  digitalWrite(LED, LOW);
  delay(100);  
  digitalWrite(LED, HIGH);
  delay(100);
  delay(1000UL*WAIT_TIME); // WAIT_TIME秒待機する

  PushKey("A", HOLDTIME, 300); // →はい
  // ★もう1台のArduinoに状態を通知
#ifdef USE_NORMAL_SERIAL
  Serial.print(START_BATTLE);
  //Serial.flush();
#endif
#ifdef USE_SERIAL_1
  Serial1.print(START_BATTLE);
  //Serial1.flush();
#endif
#ifdef SOFTWARE_SERIAL
  mySerial.print(START_BATTLE);
  //mySerial.flush();
#endif

  digitalWrite(LED, LOW);
  delay(100);
  digitalWrite(LED, HIGH);
  delay(100);
  digitalWrite(LED, LOW);
  delay(100);  
  digitalWrite(LED, HIGH);
  delay(100);
  
  // 挑戦画面~レイドバトル(誰かが「準備完了」を押しそこねていた場合のケアも兼ねてA連打)
  for( start_time=millis(), current_time=start_time ; current_time - start_time < (unsigned long)BATTLE_TIME*1000UL ; current_time=millis() ){
    if( current_time - start_time < (unsigned long)(BATTLE_TIME-3)*1000UL )PushKey("A", HOLDTIME, 300); // ひたすらA連打
  }

  // ★もう1台のArduinoに状態を通知
#ifdef USE_NORMAL_SERIAL
  Serial.print(RESET_GAME);
  //Serial.flush();
#endif
#ifdef USE_SERIAL_1
  Serial1.print(RESET_GAME);
  //Serial1.flush();
#endif
#ifdef SOFTWARE_SERIAL
  mySerial.print(RESET_GAME);
  //mySerial.flush();
#endif

  digitalWrite(LED, LOW);
  delay(100);
  digitalWrite(LED, HIGH);
  delay(100);
  digitalWrite(LED, LOW);
  delay(100);  
  digitalWrite(LED, HIGH);
  delay(100);

  // リセットして次の挑戦
  PushKey("Home", HOLDTIME, 1000); // ゲーム終了のためのHomeボタン
  PushKey("X", HOLDTIME, 500);     // 終了Xを押す→終了しますか?画面
  PushKey("A", HOLDTIME, 3000);    // はい→終了しています・・・
  PushKey("A", HOLDTIME, 1000);    // ゲームタイトルクリック→ユーザを選んでください画面(複数ユーザいる場合)
  PushKey("A", HOLDTIME, 18500);   // A押してから起動までおおよそ16秒(最短目安。環境に合わせて設定すること)
  PushKey("A", HOLDTIME, 10000);   // タイトル画面からフィールド遷移までおおよそ10秒(環境に合わせて設定すること)

}

void DoubleHome(void){
  SwitchControlLibrary().PressButtonHome();
  delay(95);
  SwitchControlLibrary().ReleaseButtonHome();
  delay(300);

  SwitchControlLibrary().PressButtonHome();
  delay(95);
  SwitchControlLibrary().ReleaseButtonHome();
  delay(300);

  delay(200);
  return;
}

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

 

5-2.レイドバトル周回側(クライアント側)

クライアント側のプログラムです。

/*  ★★★★★★★★★★★★★★★★★★★★★★★★★★★★
 *  ★2台で自動 デリバード レイド周回【周回側(受信側)】★
 *  ★★★★★★★★★★★★★★★★★★★★★★★★★★★★
 *  
 *  デリバードレイドの周回作業を自動で行ってくれるすごいやつ。
 *  Aを押しても大丈夫な土地で、かつ野生のポケモンと絶対エンカウントしないところでセーブしておくこと。
 *  
 *  ★十分な量のモンスターボールを準備しておきましょう。
 *  
 *  【利用方法】
 *  まずは周回側のArduinoと、ホスト側のArduinoとを、配線してください(Switchにはまだ挿さない)。
 *  
 *  使うpinは、本ArduinoではRX, TXとしてそれぞれ9, 10を使います。
 *  すなわち、こちらのRX(9)を、ホスト側のTX(11pin)に接続し、
 *  こちらのTX(10)を、ホスト側のRX(10pin)に接続してください。
 *  
 *  配線と本ArduinoとSwitchとの接続が終わったら、Home画面に入ってすぐ復帰する動作が2回、実行されます。
 *  2回実行されたら、本Arduinoは「待機状態(loop関数の中)」になっているので、この状態で、
 *  配線後のホスト側Arduinoをホスト側Switchに差し込んでください。
 *  
 *  ※シリアル通信を使います。Arduino同士の配線が必要です。
 *  ※よくわからない人は、ご利用をお控えください(誤って破損する可能性もあります)。
 *  
 *  (c) 2021 ますたーの忘備録
 *  https://tangential-star.hatenablog.jp/
*/

#include <SwitchControlLibrary.h> // Switch自動化用のライブラリ

#include <SoftwareSerial.h> // 2台目のArduinoと通信するためのライブラリ
SoftwareSerial mySerial(9, 10); // RX, TX(pin9, 10を使います)

// 通信で使う定数です。各状態に遷移時にこれらを2台目にシリアル通信で送信します。
enum {RECEIVE_BEGIN_PACKET='3', BEGIN_LOOP, START_YYCOMM, START_BATTLE, RESET_GAME } state;
#define BAND (300) // シリアルポートに接続するレートです。300[bps]で通信します。
int LED = 13; // ArduinoのLEDを簡易にデバッグ用に使います。Arduino LeonardoのLED pin番号を入力!

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

#define BATTLE_TIME (100)  // [秒]
// 挑戦後、何秒間Aを連打し続けるか?(=確実に倒しきり、捕獲完了、「つぎへ」を押下、「●●をボックスへ転送しました!」までの十分に長い時間)
// 90秒だと、特性発動時に足りなかった(ヨワシのぎょぐん+捕獲成功)
// てだすけ+このゆびとまれ+捕獲成功1'35''
// 正直、ギリギリが多かったので、105とか110とかでもいいかも。

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

inline void participate(void); // call this function to participate raid battle in YY comm.
inline int A_renda_inBattle(int battle_took_time); // (while battle, arduino try push A intervally)

void setup() {
  unsigned long int current_time=0;
  unsigned long int start_time=0;

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

  pinMode(LED, OUTPUT);
  digitalWrite(LED, HIGH); // LED 点灯
  DoubleHome();
  delay(5000);

  // もう一台のArduinoとの通信
  mySerial.begin(BAND); // ソフトウェアシリアルの初期化
  mySerial.listen();
  start_time=millis();
  current_time=start_time;

  DoubleHome();

}

int volatile now = '-';

// こちらは常にシリアルの受信待ち
void loop() {
  if ( mySerial.available() ) { // 受信データがあるか?
    digitalWrite(LED, HIGH);    // LED 点灯
    now = mySerial.read();      // データが無いときは呼ばれない
  }
  digitalWrite(LED, LOW); // LED 点灯
  switch( now ){
    case RECEIVE_BEGIN_PACKET:
    case 3:
      //PushKey("x",HOLDTIME, 3000);
      //while(1) delay(190);
      break;
  
    case BEGIN_LOOP:
     case 4:
      //PushKey("+",HOLDTIME, 3000);
      //while(1) delay(190);
     break;
  
    case START_YYCOMM:
    case 5:
      now = -1;
      participate();
      break;
  
    case START_BATTLE:
    case 6:
      now = -1;
      A_renda_inBattle(BATTLE_TIME);
      break;
  
    case RESET_GAME:
    case 7:
      break;
  
    default:
      break;
  }

}

void DoubleHome(void){
  SwitchControlLibrary().PressButtonHome();
  delay(95);
  SwitchControlLibrary().ReleaseButtonHome();
  delay(300);

  SwitchControlLibrary().PressButtonHome();
  delay(95);
  SwitchControlLibrary().ReleaseButtonHome();
  delay(300);

  delay(200);
  return;
}

void participate(void){
  delay(4000); // 4秒ほど待ってから…
  PushKey("Y", HOLDTIME, 1300); // YY通信の画面に入る
  mySerial.peek();
  for(int i = 0 ; i < 7 ; i++) PushKey("X", HOLDTIME, 200); // 更新作業(募集の案内を待つ)
  mySerial.peek();

  PushKey("right", HOLDTIME, 300); // レイドバトルにカーソルをあわせる
  PushKey("A", HOLDTIME, 700);     // レイドを選択
  PushKey("A", HOLDTIME, 1700);    // →参加する
  PushKey("A", HOLDTIME, 2100);    // →挑戦する (通信待機中…)
  PushKey("A", HOLDTIME, 500);     // →A(準備完了!)
  PushKey("A", HOLDTIME, 500);     // →A(準備完了!) ※予備
  PushKey("A", HOLDTIME, 500);     // →A(準備完了!) ※予備
  PushKey("A", HOLDTIME, 500);     // →A(準備完了!) ※予備
  PushKey("A", HOLDTIME, 500);     // →A(準備完了!) ※予備
  mySerial.peek();

  return;
}

int A_renda_inBattle(int RENDAJIKAN){
  unsigned long int current_time=0;
  unsigned long int start_time=0;
  unsigned long int temp_time=0;

  // 決められた時間ずっとA連打
  for( start_time=millis(), current_time=start_time ; current_time - start_time < (unsigned long)RENDAJIKAN*1000UL ; current_time=millis() ){
    if( current_time - start_time < (unsigned long)(BATTLE_TIME-3)*1000UL ){
      PushKey("A", HOLDTIME, 300); // ひたすらA連打
      mySerial.peek();
    }
  } 
  return;
}


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

 

6.あとがき

まずは、本ブログ開設から11ヶ月目にしてようやく、Arduino自動化の記事「第20回」の更新を迎えることができました。まずは、本ブログを読んでいただいている皆様に、厚く御礼申し上げます。また、引き続き本ブログをよろしくお願いいたします。

 

さて、今回の記事では、前々回(第18回:レイド配布)前回(第19回:ソロレイド周回)の延長線として、諸々苦戦しながらも、ついにレイドバトルの「完全自動周回」を実現することができました。おまたせしました!!!

本プログラムを使えば、デリバードレイドの周回作業はもちろん、例えば乱数調整で出した色違いの巣穴で使えば、複数回、周回側で捕獲チャレンジを自動で行うことができます。さらに、ロトミIDくじなどで予め十分な量のマスターボールを調達しておけば、色違い量産も可能になります。本当にパワフルなプログラムとして仕上がったと思いますので、是非ともご参考いただければと思います。

 

 

本自動化については、以前より「根強い需要」が垣間見えていて、私自身も「いつか欲しいなぁ」と常日頃から感じていました。しかしながら、なかなか着手できないのには理由がありました。

実は、私自身が平日・土日も仕事に追われていることもあり、なかなか着手するモチベーションが上がらなかった上、実装のハードルがそれなりに高かったのです。今回は「シリアル通信」を使うことで2台のArduinoをタイミング同期させて動作させることができましたが、電子回路の知識が一切ない筆者にとっては「すべて手探り状態での検証・実装」でした。

そのような経緯でなかなか着手できずにいたのですが、普段より本ブログをご覧いただいている皆様からの温かいコメントや、複数人からのリクエストコメント、YouTubeチャンネルでの高評価などが増えてきており、「本当にありがたいなぁ」「リクエストにもなんとか応えてみたいなぁ」と感じていました。

そんな折、本ブログ記事も気づけば第19回まで更新されていて、次は「第20回に相応しい記事にしたいなぁ」とおぼろげながらに考え、せっかくならチャレンジしてみよう!と、1週間ほど前にようやく奮起しました。

予想以上の難しさの中、仕様検討とプログラミング、そしてトライ・アンド・エラーを経て、ついに、本記事の公開まで気合で漕ぎ着けることができました!

そんなこんなで、「おまたせしました!」「できたてほやほやのプログラムです!」と、皆様に向けて胸を張って紹介できますし、皆様に支えていただけたので最後まで仕上げることができたな、と感じています。皆様にも是非ともご参考いただければ幸いです。

 

もともと、ブログタイトルの通り「忘備録」として作った本ブログの記事が色々な人のお役に立てていて、私自身にとっても支えになっていて、非常に嬉しく思います。

これからも、マイペースな更新になると思いますが、「中身のある忘備録」として続けていけたらと考えますので、引き続きどうぞ、本ブログをよろしくお願いいたします。

そして、「参考になったよ」という方は、是非ともコメントをいただければ幸いです。

 

重ねて第20回の記事公開を迎えられたことを記念し、これを支えていただいた皆様への感謝の辞にて結ばせていただきます。

 

2021年10月10日 ますたー c⌒っ.ω.)っ

 

前回記事:【Arduino自動化19】ねがいのかたまりで自動レイドバトル周回【完全放置で「けいけんアメ」「ヨロイこうせき」】

前々回記事:【Arduino自動化18】ローカルレイド自動配布【デリバードレイドでマックスこうせき】

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

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

他のArduino自動化:ポケモン剣盾Arduino自動化 カテゴリーの記事一覧

YouTubeチャンネル:ますたーの忘備録 - YouTube 【NEW!!】

---

*1:レイド配信を自動化しても参加側の手動操作が必要、ソロ周回を自動化しても目的のレイドとは限らない

*2:シリアル通信:有線で1本ないし2本の接続を行い、1bitごとにデータを送受信する方法

*3:いわゆる「ジャンパーコード」。ピンソケットが付いているArduinoならオス端子、ピンヘッダが付いている場合はメス端子が必要

*4:ご自身のボードのpin形状に応じたものを購入します。例えば、ピンヘッダが付いているボードを使っている方は「メス」のジャンパーワイヤーが必要になります。ただし、本ブログと同じものを使っているのであれば「オス」で大丈夫なので、間違えないようにしましょう

*5:もし、充電アダプタをつけていることにより、プログラムが先に動いているような挙動をする場合は、充電アダプタを外した状態で自動化を始め、途中で充電アダプタを差し込みましょう。ただし、その際は入力が2~3キーほどスキップされるような振る舞いをするため、必要に応じて別途手動で調整してください

【速報】Nintendo Switch ver. 13.0アプデ対応に関して【重要】

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

今回は、2021年9月15日に公開されたNintendo Switchファームウェアアップデート「Ver. 13.0」に関する速報記事です。5日遅れですが速報にしておきます!

 

概要(というか結論)

結論から言うと、今回のアップデートにより、一部のArduino自動化プログラムはそのままでは動作しなくなりました。ただし、本ブログで公開しているArduino Leonardo自動化プログラムについては、2021/9/20 23:20時点で修正対応が完了しております。

(現在は修正版のソースコードを公開しています)

Ver. 13.0へのアップデートより前に書き込んだArduinoを使っている人は、再度プログラムのコンパイルと書き込みをし直す必要がありますので注意してください。

内容としては以上です。

 

※一応、詳しくは下記にまとめています。

続きを読む

【Arduino自動化19】ねがいのかたまりで自動レイドバトル周回【完全放置で「けいけんアメ」「ヨロイこうせき」】

ますたーです。こんにちは(お久しぶりです)。

前回の記事は完全に趣味(レジンキーキャップづくり)の話でしたが、今回は「ポケモン自動化」19件目の記事となります。

 

今回の内容は「『ねがいのかたまり』を自動で投げ入れて、レイドバトルに挑戦し続ける自動化」のプログラム紹介です。けいけんアメ・ダイマックスアメ稼ぎに使えます。

また、例えば、ハニカーム島で使えば「ヨロイこうせき(教え技習得)」と「ダイミツ(ウーラオスのキョダイマックス習得)」も合わせて収集できます。

 

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

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

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

 

概要

1台のSwitchで、レイドバトルに完全自動で「ひとりで挑戦!」し続けます。
※ローカル通信でのレイド周回(みんなで挑戦)する場合は、第18回の記事を御覧ください
※2台のSwitchで周回を自動化する場合は、第20回の記事を御覧ください(2021/10/10追記)

f:id:tangential_star:20210919200733g:plain
f:id:tangential_star:20210919221732p:plain
レイドバトルを自動で周回できる。けいけんアメ・ヨロイこうせきなどを集めよう

 

もくじです。

第18回ではローカルレイド配布の記事(すなわち、Switchが2台以上必要)でしたが、今回はSwitch1台で完結するソロ・レイド周回の記事です。「ハニカーム島」での巣穴に焦点を当てた自動周回の構成で書いています。

概要だけ知りたい方はYouTube動画もご覧ください。1分でまとめています⇒YouTube動画

プログラムだけほしい!という方は、どうぞ⇒読み飛ばす

 

YouTube動画「1分でわかる!Arduinoでレイドバトル自動周回!」

本ブログ記事の内容を1分でまとめています。合わせて御覧ください。

 

苦痛なレイド周回「作業」を自動化

まずは、本プログラムを使うモチベーション、つまりは自動化した目的についてです。第18回(ローカルレイド自動配布)の記事でも触れましたが、レイドの周回作業は文字通り「作業」なので、時間がガッツリ取られてしまいます。

多くの方にとって「アイテム集め」や「育成」は、ほぼ「作業」と化しているのが実態です。特に、ひたすら育成用のアイテムを手に入れるためだけのアイテム集めの周回(例えば、自演レイド周回マックスレイドバトル羽集めBP稼ぎなど)は、対戦勢の前提と化しており、特に育成・対戦の初心者には敷居が高く感じられます。

f:id:tangential_star:20210111131407p:plain
f:id:tangential_star:20201205223042p:plain
努力値を振ったり特性を変えたり。「育成」をするためにも、まずは道具集めから。大変だ。

こういった、苦行じみた作業をArduinoで自動化して時間短縮を図ることで、プレイヤーは「ポケモン」というタイトルが持つ本来の楽しさを存分に味わうことができ、充実したポケモンライフが送れると考えます。

ポケモンの面白さは、ストーリー(シナリオ)、対戦、図鑑埋め、カレー作り、色・証厳選、キャンプでのポケモンとのふれあいなど枚挙に暇がありません。プレイヤーそれぞれが楽しみ方を深く見つけられるのがポケモンの良いところだと思います。

特に対戦は根強い人気を誇り、剣盾発売から約2年経った今でも、その勢いはとどまることを知りません。今年11月にはダイパリメイクBDSPの販売も控えており、より新しくなった環境での剣盾レーティングバトルが加熱すると思われますし*1、ますます、こういった育成に必要なアイテム類の需要は、これからも増していくことでしょう

f:id:tangential_star:20210919214146p:plain
f:id:tangential_star:20210919214012p:plain
BDSPには「ランクバトル」が無いため、剣盾での対戦環境が加熱するかも?(Pokémon Presents 2021.8.18より引用)

 

レイドバトルをソロで周回する目的

さて、本題。レイドバトルのソロ周回を自動化しますが、その目的についてです。

様々な意見があると思いますが、レイドバトルを周回する目的はやはり道具(レイド報酬)集めです。レイドバトル報酬のポイントについて、下記にまとめました。

そんなことは知っとるわ!という方はどうぞ⇒「事前準備」まで読み飛ばす

1.対戦用ポケモンの育成に必ず使う「けいけんアメ」「ダイマックスアメ」

レイド報酬の最も大きな恩恵は「けいけんアメ」「ダイマックスアメ」の存在です。特に後者は、与える量がダイマックス時の最大HP量に直結するため、ある意味レベルよりも重要視されるパラメーターです。1匹あたり10個必要となるため、ポケモン育成を進めるほどに不足しがちです。

ちなみに、ポケモンでは毎作、経験値の効率的な稼ぎ方が話題になります。今作もその例に漏れず、発売当時は「ヌケニンレイド」の話題で持ち切りとなり*2DLC第2弾のデリバードレイド解禁時にも大きな話題となりました*3。しかし、これらは複数のSwitchを準備する必要があり、ソロ周回には向きません。さらに、目当てではないポケモンのタイプがばらつき、一貫して弱点をつきにくい巣穴という点も不向きという課題もありました。

ちなみに、ソロ周回で「ラッキー」「ハピナス」を狙う際には、弱点が突きやすい「ミツハニー」「ビークイン」しか出現ポケモンがいない「ハニカームじま(鎧の孤島)」がオススメです。

f:id:tangential_star:20210919233425p:plain
f:id:tangential_star:20210919233740p:plain
「けいけんアメ」稼ぎには「ラッキー」などが出る、DLC巣穴がオススメ

2.わざおしえ・W稼ぎができる「ヨロイこうせき」

育成環境を整える、という観点で「レイドバトルの周回」を考えた際、やはり欠かせない特筆すべきターゲットとしては「ヨロイこうせき」が挙げられます。

皆様もご存知の通り、マスターどうじょうにいる技マニアに「ヨロイこうせき」5個を渡すと、DLC第1弾で追加された「わざ」を教えてくれます

教えてくれる技の中には「グラススライダー*4」や「ワイドフォース*5」など非常に強力なものも多く、対戦環境を整える上で「ヨロイこうせき」は無くてはならない存在です。

また、鍛錬平原にいる掘り出しオヤジに「ヨロイこうせき」を渡せば、わずか数秒で数十万ワットを掘り出してくれるため、場合によっては日付変更でW回収を行うよりも短時間で効率的に稼ぐことができます。

ゆえに、ガラル本土や冠の雪原でのレイド報酬に、特にこだわりがなければ、「ヨロイこうせき」が手に入るヨロイじまでのレイド周回が適しています

f:id:tangential_star:20210919234643p:plain
f:id:tangential_star:20210919234651p:plain
「ヨロイこうせき」があれば「わざおしえ(マスター道場)」「W掘り出し(鍛錬平原)」ができる

3.ウーラオスのキョダイマックスに欠かせない「ダイミツ」

前述の「ヨロイこうせき」と同じくDLC第1弾を代表するアイテムとしては「ダイミツ」が挙げられます。

ご存知でない方のために紹介すると、「ダイミツ」とは、DLC第1弾で登場した「げんきのかたまり」と同様の効果(ひんしのポケモンに与えるとHP全回復)を持つ回復アイテムです。同じくDLC第1弾で登場したポケモン「ウーラオス」のキョダイマックス習得にも必要です*6

特に、ウーラオスのキョダイマックスわざはダイウォール貫通効果を持っており、複数個体のダクマを育成する上では欠かせないアイテムと言えるでしょう。

肝心の入手方法ですが、「ミツハニー」または「ビークイン」のレイドバトル報酬です。

f:id:tangential_star:20210920003258p:plain
f:id:tangential_star:20210919234058p:plain
ウーラオスのキョダイマックスに必要な「ダイミツ」は、ミツハニービークインからドロップする

1~3を満たせる「ハニカームじま(ヨロイじま)」の巣穴

前述の3つ「ソロでも周回しやすいこと(出現ポケモンのタイプ相性がバラつかない)」「ヨロイこうせきが手に入ること(ヨロイじま)」「ダイミツが手に入ること」。これらを同時に満たせる絶好のポイントが「ハニカームじま」です

ちなみに、ハニカーム島の巣穴は6つありますが、いずれも全く同じ出現率です。

f:id:tangential_star:20210920003703p:plain
f:id:tangential_star:20210920003240p:plain
「ハニカームじま」の巣穴にはラッキー系統・ミツハニー系統しか出現しない。周回にオススメだ。
(左:ポケモン徹底攻略様より画像引用)

 

事前準備

プログラムのソースコードは後述後述しますが、ソロでレイドバトルに参加するにあたって準備が4つ必要です。下記手順の1, 2, 4は説明不要かと思いますが、手順3の「機内モードON」は徹底してください

  1. 十分な量の『ねがいのかたまり』を準備すること
  2. わざを1つだけ覚えたポケモンを準備すること
  3. Switch本体を『機内モード』にすること【重要】
  4. 目的の巣穴に向かう

それぞれカンタンに説明します。

 

準備1.十分な量の「ねがいのかたまり」を準備

まずは、巣穴を建てるために「ねがいのかたまり」を準備してください。できるだけ多いほうが、長い時間自動化ができるのでオススメです。

最もカンタンな入手方法は「ハシノマはらっぱ」にいる穴掘り兄弟の左側(兄)です。A連打だけで掘り起こしてくれるので、Arduino自動化01の記事を参考にかき集めてください。

なお、プログラムの都合上、ポケモン捕獲を行う場合がありますので、「ねがいのかたまり」を超える量のモンスターボールを準備し、必ず先頭に並び替えておいてください。

f:id:tangential_star:20210920010012p:plain
f:id:tangential_star:20210920010017p:plain
プログラムは「ねがいのかたまり」がある限り稼働する。モンスターボールの補充・並び替えも忘れずに。

準備2.わざを1つだけ覚えたポケモンを先頭にする

続いて、レイド周回を行うためのポケモンを準備します。必要な準備(条件)は「わざを1つだけにする」ことだけです。ただし、巣穴に出現するポケモンに対して有利な相性・わざをセットしておくことをオススメします。

また、味方NPCポケモンのレベルは、自分が使うポケモンの0.7掛けのレベルになるので、できるだけ高いレベルのものを準備したほうが効率的です*7

「ハニカームじま」の巣穴を攻略する場合、Arduino自動化18の記事(デリバードレイド周回)の際にオススメした「ASぶっぱ夢ルガルガン@ハチマキ」はオススメです。

f:id:tangential_star:20210920011609p:plain
f:id:tangential_star:20210920011613p:plain
高火力な「わざ」を1つだけにしたポケモンを先頭に準備。努力値振り・PP増強も忘れずに

準備3.Switch本体を『機内モード』にする【重要】

準備の仕上げとして、Switch本体の設定で「機内モード」をONにします

やり方は、SwitchのHomeボタンを長押しして「機内モード」の項目をタップするだけです。ただし、ドックに接続されたままだと切り替えが出来ないので、TVモードで遊んでいる方は、一度ドックから取り出して、Switch本体で実施してください。

機内モードがONになっていれば、プログラム中で万が一「みんなで挑戦!」をクリックしてしまっても募集画面に入らないため、ループの破綻が起こりません。

なお、当たり前のことですが、「機内モード」をONにすると、無線コントローラー(Joy-Con含む)は使えません。やむなく無線コントローラーを使っている人は注意しましょう。

※もとより、無線コントローラーが接続されているとArduinoが認識されないのであまり気にすることはありませんが…

f:id:tangential_star:20210920011744p:plain
f:id:tangential_star:20210920011748p:plain
Switch本体が「機内モード」になっていれば、「みんなで挑戦!」を押しても募集画面に入らない

準備4.目的の巣穴の目の前に立つ

手順1~3を満たしていれば、あとは目的の巣穴の前に立つだけです。今回の例では「ハニカームじま(ヨロイじま)」の巣穴に向かいます。

「ハニカームじま」の最寄りは「チャレンジビーチ」です。そらとぶタクシーで降り立ったら左手に見える岩間をまっすぐ進めば到着です。

巣穴の目の前に立ったら、準備完了です。お疲れさまでした。

f:id:tangential_star:20210920012841p:plain
f:id:tangential_star:20210920012809g:plain
目的の巣穴の前に立とう。野生ポケモンが周りの草むらから飛び出してこない巣穴が望ましい。

 

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

おまたせしました。プログラムのソースコードです。

使い方(簡易説明)

使い方はSwitchを機内モードにした上で、巣穴の前に立ってArduinoを挿すだけです。

仕組み(アルゴリズム

今回のソースコードは、Arduino自動化06(マックスこうせき自動化)と同様、決められた下記のキー入力をひたすら繰り返す実装になっています。ただし、「17個」のコマンド入力となるため、16個のコマンドまでしか登録できない、一般的なマクロコントローラーには非対応(だと思います)です。

自動周回のコマンド入力列(筆者考案・検証;単位:秒)

1 2 3 4 5 6 7 8 9
0.5 0.5 0.5 1.6 0.45 0.45 0.45 0.45 0.45
10 11 12 13 14 15 16 17 18
0.5 0.5 1.6 B 0.5 A 1.6 B 1.5 B 1.5 1.5 - -

この入力配列は筆者が2時間くらいかけて検証したものなので、参考としてご活用ください。一応、3時間連続で稼働する検証は取りました。

ソースコード

/* 
 *  全自動ヨロイこうせき集め @ハニカーム島c⌒っ.ω.)っ 
 *  警告:ねがいのかたまりを入れるときにセーブされるので貴重アイテム・ボールの管理には注意ください。
 *  
 *  ★使い方
 *  十分な量の『ねがいのかたまり』を準備します。また、それ以上の数の「ボール」を準備してください。
 *  手持ちの先頭には、「わざ」を1つだけ覚えたポケモンを準備してください。
 *  そして、Switch本体を『機内モード』にしておいてください!【重要】
 *  あとは、巣穴の前で差し込むだけでOKです。
 *  
 *  原則として、戦闘中に道具を使用することはありませんので、予めバッグ(回復ポケット)を空にする必要はありません。
 *  (ただし、自己責任でお願いします。貴重な道具やボールが使われるなどの損害があってもしりません)
 *  
 *  (c) 2021 ますたーの忘備録
 *  https://tangential-star.hatenablog.jp/
*/

#include <SwitchControlLibrary.h>

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

void PushRL(int delay_time_ms);
int PushKey(char* keyname, long int holdtime, long int delaytime);
void TiltLeftStick(int direction_deg, double power, long int holdtime, long int delaytime);

void Debug(void){
  PushKey("HOME", HOLDTIME, 1000);
  PushKey("HOME", HOLDTIME, 1000);
  return;    
}

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

void loop() {

  // A↓AA
  PushKey("A",    HOLDTIME, (500-HOLDTIME)  );
  PushKey("Down", HOLDTIME, (500-HOLDTIME)  );
  PushKey("A",    HOLDTIME, (500-HOLDTIME)  ); 
  PushKey("A",    HOLDTIME, (1600-HOLDTIME) ); // 待機

  // ↓A↓A↓A↓A
  PushKey("DOWN", HOLDTIME, (450-HOLDTIME) );
  PushKey("A",    HOLDTIME, (450-HOLDTIME) );
  PushKey("DOWN", HOLDTIME, (450-HOLDTIME) );
  PushKey("A",    HOLDTIME, (450-HOLDTIME) );
  PushKey("DOWN", HOLDTIME, (450-HOLDTIME) );
  PushKey("A",    HOLDTIME, (450-HOLDTIME) );
  PushKey("DOWN", HOLDTIME, (450-HOLDTIME) );
  PushKey("A",    HOLDTIME, (1600-HOLDTIME) ); // 待機

  // BA
  PushKey("B",    HOLDTIME, (500-HOLDTIME) );
  PushKey("A",    HOLDTIME, (1600-HOLDTIME) ); // 待機

  // B
  PushKey("B",    HOLDTIME, 1500);

  // B
  PushKey("B",    HOLDTIME, 1500);

  // ↓
  PushKey("DOWN", HOLDTIME, 1500);

  // ループ動作確認用(デバッグ)
  // ※もし、現在、動作ループのどの時点にいるのかを確認したい場合は、コメントはずしてください
  // Debug();  
}

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, long int holdtime, long 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, long int holdtime, long 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;
}

 

あとがき

今回は、Arduino自動化記事第19回として、「ソロでのローカルレイド周回」を実装しました。最近、筆者の仕事が本当に忙しく、久しくArduinoを触っていなかったこともあり、プログラミングがある種の息抜きになりました。ブログの執筆も久々で楽しかったです。

特に、久々にポケモンで遊びながら「どうやったら自動的にプログラム組めるかな?」と頭を悩ませながらゲームをポチポチ押して、裏紙に汚くメモをとって色々検証するトライ・アンド・エラーがしっかり実を結んでいくのは、とても気持ちが良かったです。

f:id:tangential_star:20210920022846j:plain
f:id:tangential_star:20210920023906g:plain
メモを頼りにプログラミング。1ループごとにHomeボタンを挟んでちゃんと動いているか確認

思い返せば、今回実装した「自動でのレイド周回」は、今年の6月にコメント欄でリクエストを頂いていたのですが、なかなか着手できていなかったなぁ、と改めて思いました。

筆者はゲームを起動すると時間を忘れて仕事そっちのけで遊んでしまうこともあり、6月からSwitchをほぼ触らずで過ごしておりました。その代わりに、趣味を増やそうと、前回の記事でも紹介しましたが「レジン(樹脂)でキーキャップを自作」したり、付随してYouTube動画の編集・投稿をしたりしていました。実は、こちらも好評でして、Yahoo!からの検索で本ブログに流入してきた方のうち、10%を占めています(如何に自動化がニッチかが分かりますね笑)。でも、こうして新たな方にも本ブログならびにArduinoの魅力・ポケモンの魅力を知っていただけていると感じられて、嬉しく思います。

ちなみに、下記が、筆者のYouTubeチャンネルです(突然の広告)。

www.youtube.com

 

ところで、本ブログのアクセス数がここ1ヶ月ほど、お陰様で増えてきており、YouTubeチャンネルの方にも温かいコメントもいただけるように少しずつなってきていて、皆様に支えられて運営できているなぁ、と感じます*8

この場を借りて、皆様の配慮と温かい心遣いに、感謝申し上げます

文字通り「忘備録」を兼ねて始めた本ブログですが、これからも引き続き皆様のお役に立てるような、中身のある記事で更新をしていけたらと思います。

もし、本ブログやYouTubeチャンネルなどが役に立っていたり、楽しみにしておられる方がいらっしゃいましたら、ぜひともコメントをいただければと思います。一筆いただけますと私自身のモチベーションにも繋がります。

 

これからもマイペースな更新になると思いますが、温かく見守っていただければ幸いです。(応えられるかはわかりませんが、もしリクエストもあれば検討させていただきます)

 

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

 

次回記事:【Arduino自動化20】Switch2台で「完全」自動!レイドバトル自動周回【デリバード・色違いレイドなど】

前回記事:【趣味】メカニカルキーボードのキーキャップをレジンで作る【YouTube同時更新!】

関連記事:【Arduino自動化18】ローカルレイド自動配布【デリバードレイドでマックスこうせき】

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

他のArduino自動化:ポケモン剣盾Arduino自動化 カテゴリーの記事一覧

YouTubeチャンネル:ますたーの忘備録 - YouTube 【NEW!!】

---

*1:2021/8/18公開の「Pokémon Presents」では、BDSPに「ランクバトル」実装の予定が無い旨が告知されました

*2:ヌケニンレイド:ヌケニンのHP1固定の仕様による高速1ターン周回が話題に

*3:デリバードレイド:「マックスこうせき」が2個確定+確率でドリームボールがドロップという魅力的な報酬で話題に

*4:グラススライダー:グラスフィールド下だと先制攻撃できる命中100威力70の草技

*5:サイコフィールド下だと威力が1.5倍になる命中100威力80の超技

*6:あらかじめ「ダイミツ」を手に入れていても、専用イベントを進めないとウーラオスはダイスープを飲んでくれません

*7:例えば、自分がLv.100を使っている場合、味方のNPCはLv.70のポケモンを繰り出します

*8:2021/9/20 AM2:50時点で総PV数:36,715。今月4,528PV;開設から317日

【趣味】メカニカルキーボードのキーキャップをレジンで作る【YouTube同時更新!】

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

 

今回は、完全に趣味のお話です。メカニカルキーボードのキーキャップを自作してデコレーションする、という記事です。

なんと、このブログ始まって以来初めてのポケモン自動化「以外」の記事になります(ポケモンにデコレーションしてるのでセーフ?)。

 

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

 

概要

本記事では、カニカルキーボード のオリジナルキーキャップを「レジン」で作る、その手順を紹介します。

f:id:tangential_star:20210820190822j:plain
f:id:tangential_star:20210820190804g:plain
キーボードの「キャップ」をレジンで作るとオリジナルなものに早変わり!おっしゃれー
続きを読む

【Arduino自動化09ex】「カレーのあかし」持ちポケモンの完全自動厳選【キャンプでカレー】

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

 

今回は、ポケモンキャンプで「カレー」を自動で調理し、時々遊びに来る「カレーのあかし」を持ったポケモンを自動で集め続けるArduino自動化です。

前回記事(Arduino自動化09)の改良版となります。もちろん、本記事だけでも読めるように書きますが、前回記事も個人的に気に入っている記事なのでそちらも合わせて是非読んでほしいです。

 

なお、Arduino Leonardo自動化の導入と、機材構成については導入記事をご覧ください。

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

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

 

概要

本記事では、ポケモン剣盾の「ポケモンキャンプ」の「カレー」づくりを自動で行います。さらに、調理後には左右を確認します。より具体的には、「クラボのみ」を6個ずつ消費(食材不要)して、マホミル級のカレーを作りカレー調理後には広場にいるポケモンに話しかけてくれますこれらを完全放置で行えます

すなわち、自動で「カレーのあかし」を持ったポケモンを仲間にしてくれます。 

f:id:tangential_star:20210608214015g:plain

カレーの調理~匂いに釣られたポケモンを仲間に加えるまですべてを完全自動化

 

目次です。ソースコードのみ欲しいという方は⇒読み飛ばす

 

カレーのあかしを持ったポケモンについて

まずは、カレーを自動で調理する目的についてのおさらいです。詳細は前回記事(Arduino自動化09)に載っていますが、こちらでも改めて説明します。

ポケモンキャンプでカレーを作ると時々、野生ポケモンがキャンプに遊びに来ることがあります。遊びに来たポケモンに話しかけると仲間にすることができます。

f:id:tangential_star:20210608220406p:plain
f:id:tangential_star:20210608220449p:plain
カレーを作ると時々野生のポケモンが遊びにくる。話しかけると仲間にできるぞ

ポケモンキャンプに来たポケモンは「カレーのあかし」というリボンを確定で持っており、このリボンを選択することで「カレーずきな」という二つ名を付けることができます。従来の野生ポケモン(あかしを持つ確率は2%~4%)と異なり、キャンプで仲間になるポケモンは確定でリボンを持っていることから、いわゆる「証厳選」の中でも比較的簡単な部類と言われています。

f:id:tangential_star:20210102011021p:plain
f:id:tangential_star:20210102011451g:plain
「カレーのあかし」を持ったポケモンは、登場時に「カレーずきな」という二つ名が付く

なお、ポケモンキャンプでカレー作りをすれば、どこでも野生ポケモンが遊びに来てくれるわけではありません。出現するポケモンの種類・場所はあらかじめ決まっています。

具体的には、ワイルドエリア・DLCの島などを除く、道路・ダンジョン(ルミナスメイズのもり・ガラルこうざんなど)で出会うことができます。また、出会えるポケモンは、その場所でランダム出現するポケモン(シンボルとして出現しているポケモンではなく「!」のマークが出て出現するポケモン)です。

出現ポケモンと場所については前回記事(Arduino自動化09)でも紹介した通り、だま氏がまとめた画像(下記参照)などで確認ができます。

f:id:tangential_star:20210102134430p:plain
f:id:tangential_star:20210102133513j:plain
「カレーのあかし」を持ったポケモンの出現場所について
左:「ポケモン徹底攻略」より画像引用・改変     右:だま氏のTwitter投稿@monst_dama)より画像引用

なお、手持ちの「なかよし度」と、作ったカレーの出来栄え(ソーナンス級・マホミル級など)によって、カレー調理後に野生ポケモンの遊びに来てくれる確率が変動します

最も効率的な組み合わせは、「手持ち6匹のなかよし度がMAX」かつ「カレーのグレードが『マホミル級』」の2つを満たした時で、その確率は「1/7(≒14.28%)」です。

f:id:tangential_star:20210102102016p:plain
f:id:tangential_star:20210102120251p:plain
「マホミル級」のカレーを作ると野生のポケモンが来やすい。確率は最大で1/7ほど

 

プログラムの使い方(事前準備)

早速プログラムの使い方の紹介です。ソースコード後述します

まずは、準備として下記を確認してください。

  1. 手持ちが6匹埋まっている
  2. 充分な量の「きのみ(クラボのみ999個など)」がリュックの一番上にある
  3. 食材を1つ以上持っている(一切消費しませんが、食材欄が空っぽだとうまく動きません)
  4. 充分な量の「モンスターボール」「スーパーボール」「ハイパーボール」いずれかを持っている(これらのいずれかにしか入りません。原則としてモンスターボールが優先されます)
  5. ボックスが空いている

これらを満たしたら、メニューから「ポケモンキャンプ」を開いてください。

そして、ポケモンキャンプが開いたら、手動で一度Xボタンを押し、カーソルを「料理」に合わせてからBボタンでメニューを閉じてください(重要)。

上記5つを満たしていれば、準備OKです。Arduinoにプログラムを書き込んでSwitchに差し込むだけでカレーづくり&調理後キョロキョロ見回しながら話しかけて野生ポケモンを仲間に加えてくれます。

f:id:tangential_star:20210102141132p:plain
f:id:tangential_star:20210102141141p:plain
事前準備の上、目的の道路に着いたら「ポケモンキャンプ」を開こう
f:id:tangential_star:20210102141454p:plain
f:id:tangential_star:20210102141503p:plain
キャンプでXボタンを押した時に、カーソルが「料理」に合う状態にしてからBを押そう

ちなみに、手持ちポケモンがなかよし度MAXだと、野生ポケモンが遊びに来てくれる確率が上がりますが、なかよし度はカレーを作ることで自動的に上がっていきますのでご安心ください。つまり、初めてカレー作りに挑戦する人や、「カレーのあかし」厳選初心者の方については、きのみとボールを準備して何も考えずに手持ち6匹埋めてキャンプするだけでOKです。

 

動作検証:15時間以上破綻なくプログラムが稼働しました! 

ここで1つ動作報告です。読み飛ばしOKです⇒読み飛ばす

作ったプログラムのデバッグ作業も兼ねて、リュックに「クラボのみ」「オレンのみ」「カゴのみ」「モモンのみ」をそれぞれ999個ずつ詰め込み、耐久デバッグ(SwitchにArduinoを挿して動作が止まるまでひたすら放置)をしました。

結果、実に15時間49分もの間、一切のループ破綻なく、カレーを作り続けることができました(画面録画の動画容量はなんと19.2GB!)。

※検証のため、プログラム中の定数「CHERI_BERRY」は「(999*3)」に書き換えて定義しています。一応、プログラムでの想定では999を上限にしていましたが、きちんと動きました

f:id:tangential_star:20210608224732p:plain
f:id:tangential_star:20210608224113p:plain
Arduinoを挿すと同時に録画スタート。15時間31分連続稼働している(右下注目)

この検証のために使用したきのみは合計2940個、仲間になったポケモンは61匹でした。

すなわち、期待値として、1時間あたり3.85匹と仲間になれることが分かります。なお、手持ちのきのみは先頭4行を999個で埋めて、手持ちポケモンは6匹ともなかよし度MAXにしてあります。また、1回の調理に使うきのみは6個にしました。

f:id:tangential_star:20210608225846g:plain

検証の結果、15時間40分の放置で「カレーのあかし」を持ったポケモン61匹が仲間に

 

注意すべき点として、コーディングの都合上、一部、プログラム上では「B」ボタンを使用しています

稀(15匹くらいに1匹くらいの割合?)に遊びに来たポケモンを仲間に加えなかったり、極稀(筆者環境では述べ50時間の検証中1回だけ発生)に途中でキャンプを終了してしまったりすることがあり得ます。

あくまでも、決められたキー操作を自動で繰り返しているだけ、という点には重々承知の上、自己責任にてご利用くださいませ。

f:id:tangential_star:20210608231721g:plain
f:id:tangential_star:20210608231741g:plain
極稀にタイミングがずれるなどで、仲間にし損なったりキャンプを終了したりすることも

 

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

前回(Arduino自動化09)のソースコードと同様、ポケモンキャンプでカーソルが「料理」に合う状態でメニューを閉じてからArduinoを挿すだけの簡単設計です。

2箇所、あらかじめプログラムを必要に応じて書き換えてください。上限とする「きのみの数(1~999)」と、1回の「調理で消費するきのみの量(1~10)」をそれぞれ「CHERI_BERRY」「USING_BERRY」で指定することができます。

#define CHERI_BERRY (999) // 「クラボのみ」の所有数
#define USING_BERRY (6) // 1回の調理で何個のきのみを利用するか

残りの量が消費予定量を上回った場合にプログラムは自動で止まるように設計しています。なお、非推奨ですが、耐久デバッグのためにCHERI_BERRYを(999*3)で利用し、15時間稼働させた実績もあるので、きのみ3~4種類をそれぞれ999個ずつお持ちであれば、そういった長時間前提の利用も可能かと思います。

いずれにしても、自己責任にて利用ください。

/* 
 *  全自動カレー作り&ポケモン捕獲c⌒っ.ω.)っ 
 *  自動でポケモンを捕まえます!_(:3 ⌒゙)_
 *  (c) 2021 ますたーの忘備録
 *  https://tangential-star.hatenablog.jp/
*/

#include <SwitchControlLibrary.h>

#define HOLDTIME (95) // 1回のキー入力の長押し時間
#define KAKIMAZE_CYCLE (435) // かき混ぜ時周期(1回転の必要ミリ秒)
#define RENDA_CYCLE (150) // 連打のサイクル(1回仰ぐのにかかるミリ秒)
#define CHERI_BERRY (999) // 「クラボのみ」の所有数(これをオーバーしないように止まります)
#define USING_BERRY (6) // 1回の調理で何個のきのみを利用するか
 // ↑6,7,10で「マホミル級」検証済。1,5は「ソーナンス級」検証済。
 // メモ:6個の時、極稀にソーナンス級になることがあった。理由は不明。心配な人は10で。
#define AIJO_BASETIME 3200 // 愛情を込めるタイミング(どうしてもソーナンス級が続く場合にはここの数字を調整してみてください)
#define AIJO_OFFSET 60 // きのみの数でズレる単位時間(ミリ秒)

#define HANASHIKAKE_CYCLE (125) // 最後の連打のサイクル(ポケモンに呼びかけ)

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

int CheriBerryNum = 0;

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

void loop() {
  int i=0;
  unsigned long int current_time=0;
  unsigned long int start_time=0;
  unsigned long int temp_time=0;
  float holdA = 0;
  int isholding = 0;
  float temp_deg = 0;
  int isfirsttime =0;
  int aijo_kome = AIJO_BASETIME+AIJO_OFFSET*USING_BERRY;

  if(CheriBerryNum < USING_BERRY){
    // 残りの「クラボのみ」がUSING_BERRY個未満ならプログラムをストップ
    for(;;) delay(1000);  
  }
  
  // メニューを開く
  PushKey("X",    HOLDTIME, 1000);
  // 料理を選択
  PushKey("A",    HOLDTIME, 750);
  // 「料理を始める」
  PushKey("A",    HOLDTIME, 2500);
  // 食材⇒いれない
  PushKey("A",    HOLDTIME, 350); // 空白のA
  PushKey("+",    HOLDTIME, 550);
  PushKey("A",    HOLDTIME, 350); // 空白のA
  // きのみ⇒「クラボのみ(一番上)」USING_BERRY個
  #if(USING_BERRY == 10)
    PushKey("A",    HOLDTIME, 550);
    PushKey("Down", HOLDTIME, 550);
    PushKey("A",    HOLDTIME, 1500); // クラボのみが10個まな板の上に乗るのを待つ
  #else
    PushKey("A", HOLDTIME, 550);
    for(i=0;i<USING_BERRY-1;i++)PushKey("UP",HOLDTIME, 150);
    PushKey("A", HOLDTIME, 180*USING_BERRY);
    PushKey("+", HOLDTIME, 350); // +で材料を決定
  #endif
  // A押してから確実に5秒ジャストで引き渡し
  for( start_time=millis(), current_time=start_time,isfirsttime=1 ; current_time - start_time < 5000 ; current_time=millis() ){
    // ループ初回のみAを押す
    if(isfirsttime){
      // 選んだきのみで始めますか?⇒はい
      PushKey("A", 100, 300);
      isfirsttime=0;
    }
  }
  // 火起こし「21秒」
  for( start_time=millis(), current_time=start_time, isholding=0 ; current_time - start_time < 21000 ; current_time=millis() ){

     // RENDA_CYCLEミリ秒ごとにAを押す
     temp_time = (current_time - start_time) % (RENDA_CYCLE+1); // 経過ミリ秒のRENDA_CYCLE剰余を計算
     holdA = (float)temp_time / (float)RENDA_CYCLE * 100.0; // 剰余から100%に変換
     if( holdA <= 35.0 ){ // RENDA_CYCLEの3.5割の時間はA押し、それ以外はAを離す
       if(!isholding){
         SwitchControlLibrary().PressButtonA();
         isholding = 1;
       }
     }else{
       if(isholding){
         SwitchControlLibrary().ReleaseButtonA();
         isholding = 0;
       }
     }
  }
  if(isholding) SwitchControlLibrary().ReleaseButtonA();

  // かき混ぜ「18秒」
  for( start_time=millis(), current_time=start_time ; current_time - start_time < 18000 ; current_time=millis() ){
     // KAKIMAZE_CYCLEミリ秒ごとに1周スティック回転
     temp_time = (current_time - start_time) % (KAKIMAZE_CYCLE+1); // 経過ミリ秒のKAKIMAZE_CYCLE剰余を計算
     temp_deg = (float)temp_time / (float)KAKIMAZE_CYCLE * 360.0; // 剰余から360°角度に変換
     TiltLeftStick( (int)temp_deg, 1.0, 0, 0); // 経過時間とKAKIMAZE_CYCLEの周期に応じた角度に倒す
  }
  TiltLeftStick( 0, 0.0, 0, 0); // スティックを初期位置に戻す

  // 愛情込め~配膳「20秒」
  for( start_time=millis(), current_time=start_time,isfirsttime=1 ; current_time - start_time < 20000 ; current_time=millis() ){
    // 愛情込めは3.8秒
    temp_time = (current_time - start_time);
    if( temp_time > aijo_kome && isfirsttime==1){ // aijo_komeミリ秒を超えたタイミングで愛情込め
      if(isfirsttime) PushKey("A", 100, 300); // 愛情込め
      isfirsttime=0;
    }
  }
  PushKey("A", 100, 300); // 配膳されたカレーを眺めた後のA
  // もぐもぐ~マホミル級のおいしさ「13秒」
  delay(13000); // カレー実食中・・・
  PushKey("A", 100, 1500); // フィールドに戻る


  // 2021/06/06 改善
  // あたりを見回してカレー好きのポケモンがいないか確認(目視確認から、自動A連打キョロキョロで手持ち加えるまで自動化をする)
  TiltLeftStick( 270, 1.0, 2000, 0); // 左端までカメラを倒す
  
  // キョロキョロしている間、A連打してくれる
  TiltLeftStick( 90, 0.9, 0, 0); // 右側スティック入力
  for( start_time=millis(), current_time=start_time, isholding=0 ; current_time - start_time < 2.5*1000UL ; current_time=millis() ){
     // HANASHIKAKE_CYCLEミリ秒ごとにAを押す
     temp_time = (current_time - start_time) % (HANASHIKAKE_CYCLE+1); // 経過ミリ秒のHANASHIKAKE_CYCLE剰余を計算
     holdA = (float)temp_time / (float)HANASHIKAKE_CYCLE * 100.0; // 剰余から100%に変換
     if( holdA <= 35.0 ){ // HANASHIKAKE_CYCLE3.5割の時間はA押し、それ以外はAを離す
       if(!isholding){
         SwitchControlLibrary().PressButtonA();
         isholding = 1;
       }
     }else{
       if(isholding){
         SwitchControlLibrary().ReleaseButtonA();
         isholding = 0;
       }
     }
  }
  if(isholding) SwitchControlLibrary().ReleaseButtonA();
  TiltLeftStick( 0, 0, 0, 0); // スティック位置を戻す

  // 自分のポケモンに話しかけている場合は、会話キャンセル
  // 野生のポケモンの場合は「↓」Key 2回なので「はい」
  delay(1000);
  PushKey("A",    HOLDTIME, 1100); // →もっときかせて!loop or 仲間に加えてあげますか?→はい
  PushKey("Down", HOLDTIME, 700); // →遊んでおいで or →いいえ
  PushKey("Down", HOLDTIME, 700); // →なんでもない or →はい
  PushKey("A",    HOLDTIME, 1200); // →クリック


  // ここまでで左端から探索は完了
  // ※万が一途中で話しかけてしまいカメラが止まっても、ここからで
  // 右端から探索をかけて野生ポケモンを探す

  TiltLeftStick( 90, 1.0, 2000, 0); // 右端までカメラを倒す

  TiltLeftStick( 270, 0.9, 0, 0); // 左側スティック入力
  for( start_time=millis(), current_time=start_time, isholding=0 ; current_time - start_time < 2.5*1000UL ; current_time=millis() ){
     // HANASHIKAKE_CYCLEミリ秒ごとにAを押す
     temp_time = (current_time - start_time) % (HANASHIKAKE_CYCLE+1); // 経過ミリ秒のHANASHIKAKE_CYCLE剰余を計算
     holdA = (float)temp_time / (float)HANASHIKAKE_CYCLE * 100.0; // 剰余から100%に変換
     if( holdA <= 35.0 ){ // HANASHIKAKE_CYCLE3.5割の時間はA押し、それ以外はAを離す
       if(!isholding){
         SwitchControlLibrary().PressButtonA();
         isholding = 1;
       }
     }else{
       if(isholding){
         SwitchControlLibrary().ReleaseButtonA();
         isholding = 0;
       }
     }
  }
  if(isholding) SwitchControlLibrary().ReleaseButtonA();
  TiltLeftStick( 0, 0, 0, 0); // スティック位置を戻す

  PushKey("A",    HOLDTIME, 150); 
  PushKey("A",    HOLDTIME, 150); 
  PushKey("A",    HOLDTIME, 150); 
  PushKey("A",    HOLDTIME, 150); 
  PushKey("A",    HOLDTIME, 150); 
  
  // 自分のポケモンに話しかけている場合は、会話キャンセル
  // 野生のポケモンの場合は「↓」Key 2回なので「はい」
  delay(1000);
  PushKey("A",    HOLDTIME, 1100); // →もっときかせて!loop or 仲間に加えてあげますか?→はい
  PushKey("Down", HOLDTIME, 700); // →遊んでおいで or →いいえ
  PushKey("Down", HOLDTIME, 700); // →なんでもない or →はい
  PushKey("A",    HOLDTIME, 1600); // →クリック

  // ちょっと待機
  delay(1600);
  // 仮に話しかけていてもBボタンを2回(0.5秒くらいの間隔)だと1回分
  // Bを2回なので、キャンプ終了にはならない。
  PushKey("B",    HOLDTIME, 500); // キャンプを終了しますか?
  PushKey("B",    HOLDTIME, 500); // いいえ

  // 次のカレーを作ります。
  delay(2000);
  CheriBerryNum -= USING_BERRY; // 手持ちの「クラボのみ」個数をUSING_BERRY個減らす

}

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

 

あとがき

今回の記事では、前回記事(Arduino自動化09)の改良版として、ポケモンキャンプでのカレー作りの自動化に、遊びに来たポケモンを仲間に加える機能を追加しました。

実は、「カレー自動調理」の記事がこのブログの中で執筆当初より根強い人気を誇る記事の一つだったこと、個人的にも本ブログの執筆記事で特に気に入っているものだったこともあり、「いつか絶対、『完全』自動化できるように改良しよう!」と意気込んでいたのでした。2021年1月2日の執筆から約半年後の本日6月9日に、ついにその夢を実現させ、完全自動化に成功しました。

やっぱり、Arduinoを使ったSwitch操作の自動化を考えたときに、スティック操作もボタン操作も余すこと無く活用している本プログラムは、動作する様子を眺めていて楽しいです。

ただ、今回のプログラムは、ポケモンが遊びに来る確率が最大でも1/7と低く、1試行にも2分ほどかかるという超長時間のデバッグを要するため、結構たいへんでした。

とはいえ、このプログラムの実装ができたことで、「『カレーのあかし』持ち色違い厳選」が現実的なものになりました。要するに、「証持ち色違い」という今までのポケモンの厳選の、さらにその上を行く難易度の厳選作業に、完全放置可能というアシストを付けて、足を踏み入れることができます

 

このプログラムを使って「カレーのあかし持ち色違いポケモンを捕まえることができた」という人が現れることを願って、あるいは、私がそれに出会えることを願って、本記事を公開させていただきます。

 

もし、この記事や本ブログが参考になっているよ!という方がいらっしゃいましたら、是非ともコメントやはてなスターを残してもらえると、私自身のやる気とモチベに繋がりますので、引き続きよろしくお願いいたします。

 

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

 

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

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

他のArduino自動化:ポケモン剣盾Arduino自動化 カテゴリーの記事一覧

YouTubeチャンネル:ますたーの忘備録 - YouTube 【NEW!!】