【SV自動化03】コレクレーのコイン回収【パルデア十景めぐり】

あけましておめでとうございます。ますたーです。

 

今回はポケモンSVの「コレクレーのコイン」を自動で回収する記事です(お年玉かな?)。パルデア十景を巡ることで、景観も楽しめます(初日の出かな?)

ポケモンSV自動化の記事、第3弾となります。

 

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

導入記事:【Arduino自動化01】Arduino開発環境の導入
 (または 【2022年12月最新版】ZIP版IDEでのArduino開発環境の構築

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

 

概要

パルデア十景をめぐり、各地にいるコレクレー(とほフォルム)に話しかけて「コレクレーのコイン」を自動で集めてくれます。パルデア十景を5分52秒で1周し、コレクレーのコインを平均12.86枚を拾い集めることができました*1。単位時間に換算すると、1時間あたりの回収実績が131枚となります*2約8時間で999枚カンストできる計算になりますね*3

パルデア十景(10箇所のフォトスポット)の看板にいるコレクレーに話しかけてコインを集める
1周あたり5分52秒(日付変更・リセット込)で、拾えたコインは平均12.86枚だった

※警告:本プログラムは動作中に移動とセーブを行うため、決して目を離さず、利用は「自己責任」でお願いします。特に、貴重な道具やボール・アイテムを利用したり、ポケモンを逃したりする可能性があります*4

もくじです。いつも通り検証を含みます。
(個人的には2-4節と3章がおもしろいと思います←)

 

1.コレクレーは「コレクレーのコイン」999枚を使ってサーフゴーに進化する

サーフゴーというポケモンをご存知でしょうか。コレクレーから進化する、金ピカボディが印象的なポケモンです。見た目の通り、「おうごんのからだ」というとくせいを持っています*5。はがね・ゴーストという恵まれたタイプ*6に加え、合計種族値が550、とくこうの種族値も133と高く、2022年12月現在の対戦環境において猛威を振るっている、紛れもない強ポケです。

この、サーフゴーの入手方法ですが、バッグに「コレクレーのコイン」を999枚持った状態でレベルアップさせることで進化させることができます。

「コレクレーのコイン」999枚を所持した状態でレベルを上げるとサーフゴーに進化するぞ
(ちなみに進化させるとコレクレーのコインは使い切ってしまう。また集め直そう!)

 

2.コレクレーのコインは、パルデア地方各地にいるコレクレーからドロップする

さて、サーフゴーへの進化のためには「コレクレーのコイン」999枚が必要ですが、進化させると「コレクレーのコイン」999枚は1枚残らず使い切ってしまいます。

肝心の「コレクレーのコイン」の集め方ですが、パルデア各地にいるコレクレーに話しかけることでドロップ入手できます。このとき、話しかけるコレクレーのフォルムによって「入手できる枚数」に違いがあるため、それぞれについて紹介します。

2-1.看板の影などにいる「とほフォルム」のコレクレーから1~10枚ドロップ

パルデア地方の各地にある看板の裏や上、影などには「とほフォルム」のコレクレーがいることがあります。話しかけると「!」マークが出てすぐさま逃げてしまいますが、その際にランダムにコレクレーのコインを落としていきます。

ドロップする「コレクレーのコイン」の枚数はまちまちですが、ほとんどの場合1枚・3枚・5枚・10枚のいずれかになります。低確率ながら、「30枚」「50枚」「100枚」のコインがドロップすることもあります*7。新年の運試しにピッタリですね。

とほフォルムのコレクレーが近くにいる場合、鳴き声がするので簡単に気づけると思います。

パルデア各地の看板や屋根の上などにコレクレー(とほフォルム)がいる。
話しかけるとすぐに逃げ出すが、コレクレーのコインを1~10枚ほどドロップする。
運が良ければ「30枚」「50枚」「100枚」のコインがもらえることもある。当たればラッキー!
(筆者は6時間かけて195匹のコレクレーと出会って「30枚」は2回、「50枚」は1回だけだった)

2-2.物見塔などにいる「はこフォルム」のコレクレーから30~80枚ドロップ

パルデア地方には、各地に物見塔や遺跡などがあり、そこに「宝箱」のような見た目のポケモンがいることがあります。その宝箱にAで話しかけることで戦闘になり、はこフォルムのコレクレーと戦うことができます。はこフォルムのコレクレーを倒すと、30枚~80枚ほどの「コレクレーのコイン」をドロップ入手することができます。

ちなみに、はこフォルムのコレクレーは捕獲することもできますので、育成用の個体厳選がてら、各地の物見塔を捕まえて回るのも良いかもしれませんね。

パルデア各地にある物見塔などに、コレクレー(はこフォルム)がいる。
倒すか捕まえれば30~80枚ほどのコレクレーのコインがドロップするぞ

2-3.とほフォルムのコレクレーは日付変更+セーブリセットで、確率で復活(リポップ)する

ところで、看板の上や影に隠れている「とほフォルム」のコレクレーは、みなさまもご存知の通り、いつの間にか復活していることがあります

復活(リポップ)の条件ですが、日付変更をすることで、時々復活することが確認できたため、(おそらく)Switchの日付で24時の経過後に抽選判定が行われると考えられます。なお、日付変更後は、そらとぶタクシーで別マップに移動するなどで、マップの更新をしないと、復活しているかどうかの確認ができませんので注意しましょう。

ちなみに、「はこフォルム」のコレクレーの復活条件は、筆者検証では分かりませんでした。日付変更の度に5%の確率でリポップしているなどの報告もありますが、筆者未検証情報になります。

セーブ&ゲーム終了、日付変更して再起動すると、時々コレクレーが復活する
ただし、そらとぶタクシーなどで一度、画面を再読込しないと出現しないから注意が必要だ!

2-4.日付変更時、コレクレー(とほフォルム)はパルデア中で44.7%の確率で同時に復活している

肝心のリポップ確率について、5時間かけて検証(「セーブ&リセット&日付変更」と「パルデア十景を巡って看板を調べる作業」を47回=延べ376箇所の看板の確認を行いました)を行いました。具体的には、これらの試行でパルデア十景*8の看板をすべて見て回ることで、コレクレーがどのように復活するのかを確認・検証しました。結果、非常に興味深いことがわかりました

【コレクレーの復活確率(筆者調べ。日付変更の度に8箇所の看板を確認。全47試行)

  • 44.7%の割合で、コレクレーが8箇所すべて復活した
  • 8.5%の割合で、コレクレーが8箇所のうち一部のみで復活した
  • 46.8%の割合で、どこのコレクレーも8箇所すべてで復活しなかった

検証の結果、とほフォルムのコレクレーの復活(リポップ)の確率は53.2%でした。
さらに、日付変更の度に8箇所を巡ったことで、その復活が「一部だけ」なのか「全体」なのかに分かれることも発見し、「とほフォルムのコレクレーが、全体で同時に復活する確率が44.7%」という内訳を発見しました。わかりやすく円グラフにしたものを下図に掲載します。

コレクレー(とほフォルム)のコインのドロップ数と、リポップ(復活)の確率を調べた。
ドロップはほとんど1~5枚の範囲内で、10枚以上はラッキーだ。2日に1回のペースで復活するらしい。

ここで、コレクレーがすべての場所で一斉に復活するのではなく、仮に各スポットで5割のリポップ率で設定されていると仮定すると、8箇所が同時に復活する確率・8箇所が同時に復活しない確率というのは非常に小さく、偶然発生するとは考えにくいと言えます(統計学で「符号検定」と言われる考え方です)。
 ※参考式 P = (1/2)^8 ×2 = 0.0078125 < 0.01

このように、各スポットごとに50%のリポップ率が設定されているとは(統計学的には危険率1%で)考えにくい検証結果が得られており、このことからも、少なくとも「パルデア十景」の看板8箇所では「同時に復活している」と考えるのが妥当だといえます

 

3.コレクレーのコイン集めにはパルデア十景巡りが最適解

さて、「コレクレーのコイン」について、上記の検証結果をまとめます。

  1. 看板の裏や上にいる「とほフォルム」のコレクレーから回収することができる
  2. とほフォルムのコレクレーは、日付変更&セーブリセットで、確率で復活する
  3. 復活していても、そらとぶタクシーなどで画面を更新しないと出現が確認できない
  4. 約4割の確率で、パルデア各地で復活している

これらを踏まえると、「日付変更」と「そらとぶタクシー」での移動はセットにしつつ、1度のセーブリセット試行の中で、複数拠点を巡ってコレクレーの有無を確認することが最適な動きであると結論付けられます

そらとぶタクシーを利用する所以で、コレクレーの出現場所へのアクセスが容易な箇所を検討する必要がありますが、これは、「パルデア十景」が最適です。理由は明白で、そらとぶタクシーの行き先が、看板の眼の前に設定されているためです。

これらを踏まえ、パルデア十景の10箇所の看板を調べるための最適な動きを地図にしてみましたので下図に示します。

「コレクレーのコイン」集めに最適な、パルデア十景 コイン周回ツアーMAPを作ってみた。
1週あたりわずか6分足らずで、平均(期待値)で12.86枚ものコインを入手できるぞ

そらとぶタクシーでのカーソル移動ができるだけ最短になるようにパルデア十景の各スポットをつなぎ合わせることで、最小の時間で最大効率での「コレクレーのコイン」の回収を実現できるように設計しています。この経路での周回を行うことで、1周を6分弱に抑えつつ12.86枚ものコイン回収を期待することができるようになりました。

1時間あたり131枚*9ものコインを集めることができます。すなわち、わずか8時間足らずで999枚のコインを集めることができるという時間効率と言えます。

パルデア十景の看板を巡り、セーブして日付変更&リセットを繰り返せばOKだ。
1周あたり5分50秒で、平均12.86枚のコインを入手できるぞ

 

4.コレクレーのコイン集めの自動化

さて、ここからはArduino Leonardoでのコレクレーのコイン集めの自動化について紹介します。

前述の通り、パルデア十景をひたすら周回して日付変更を行うことが「最適」な戦略であると結論付けましたので、今回紹介するプログラムも、それに倣ったアルゴリズムを採用します。すなわち、パルデア十景を順番にそらとぶタクシーで移動する、という方法です。

ひそやかビーチ→オリーブ大農園→列柱洞→ありがた岩→オージャの大滝→ナッペの手→パルデア最高峰→みだれつきの滝→100万ボルトの夜景→…(以下ループ)で時計回りにぐるぐると10箇所を巡り、セーブして日付変更を繰り返します。

4-1.自動化準備:ひそやかビーチに空を飛んで、東を向いてセーブしよう

今回の自動化では、パルデア十景を巡るのですが、デフォルトでは「ひそやかビーチ」を始点に選びました。好戦的な野生ポケモンが少なく不意に突進されることが少ないと感じたからです。

Yボタンの「そらとぶタクシー」でひそやかビーチに降り立ったら、デフォルトでカメラは東向き・主人公は西向き(こっちを向いている状態)になっていると思います。上方向にちょいっと入力を行って、主人公も東を向けた状態(背を向けた状態)にしてください。セーブも推奨です。

そらとぶタクシーで「ひそやかビーチ」に降り立ったら、主人公の向きを東(上)に向けてセーブだ

ちなみに、プログラム上はパルデア十景のどこからでも始めることができるように設計しています。

具体的には、ソースコードの「STARTPOINT」を0~9の任意の数字に書き換えることで、パルデア十景の中から好きな地点を開始位置に変更することができます(もっとも、恩恵はほぼないと思うので、6のままでよいかと思いますが)。

  // ↓みなさまの環境に応じて数字を書き換えてください!!
  const int STARTPOINT = 6; 
// 0:ありがた岩, 1:オージャの大滝, 2:ナッペの手, 3:パルデア最高峰, 4:みだれつきの滝, // 5:100万ボルトの夜景, 6:ひそやかビーチ, 7:オリーブ大農園, 8:ビシャビシャの斜塔, 9:列柱洞

 

4-2.自動化準備:Switch本体は「自動で日付を設定しない」状態にしておこう

本プログラムでは、日付変更を行うため、「インターネットで時間をあわせる」機能はOFFにしておきます。

Switchの本体設定から「インターネットで時間をあわせる」はOFFにしておこう

 

4-3.ソースコード

予めパルデア十景には訪問しておいて、そらとぶタクシーの行き先に加えておいてください。「ひそやかビーチ」に空を飛んだら、必ず主人公の向きを看板の方向に向けてからセーブ(←大事)してください。

その状態でArduino Leonardoを差し込むだけで、あとは自動でパルデア十景を巡って看板のコレクレーからコインを回収し、ひそやかビーチまで戻ってきます。その後セーブして日付変更&リセットを繰り返します。

 

※1試行5分以上でセーブを挟むという特性上、くれぐれも目を離さずにご利用ください意図しない挙動(野生ポケモンに突進される、そらとぶタクシーのカーソルが吸われるなど)が発生した場合、動作がズレて貴重なアイテムなどを消費してセーブしてしまう可能性などがあります。不審な挙動が発生した際は、Arduinoを抜いてください(筆者は8時間放置で、野生ポケモンに突進されてもセーブ位置から再開してちゃんとループ再開しましたが、保証はできません)。ご利用に際して発生した不具合等には責任は取れません。

/*  ポケモンSV パルデア十景めぐり(自動でコレクレーのコインを集めるプログラム)
 *   
 *   【説明】
 *   なんとパルデア十景を巡ってくれます。すごい!
 *   1周あたり5分50秒で「パルデア十景」の10箇所を巡るツアーになります。画面を見ているだけで、さながらキャビンアテンダンドの気分かも!?
 *   
 *   ひそやかビーチ→オリーブ大農園→列柱洞→ありがた岩→オージャの大滝→ナッペの手→パルデア最高峰→みだれつきの滝→100万ボルトの夜景→……(無限ループ!)
 *   
 *   【準備】
 *   ・Switchの設定で、日付は自動設定にしないようにしておく
 *   ・パルデア十景すべてに訪問しておく!!(そらとぶタクシーの行き先にすべてがある前提です)
 *   ・マップを開いた時に、北を固定するモードにしておく!!!!(キャラの向きにしない。Rスティック押し込みで切り替えられます)
 *   ・コライドン/ミライドンからは降りておく(ライドしない)
 *   
 *   【使い方】
 *   1.ひそやかビーチに空を飛んだところで、正面に看板が見えている状態でセーブする(カメラの向き・主人公の向きを東向きにしておく)。
 *   2.Arduinoを差し込む。
 *   3.ひたすら見守る←【重要】
 *   
 *   【重要なおやくそく】
 *   ・このプログラムは、そらとぶタクシーを使った移動、セーブを行います。
 *   ・1試行あたり5分50秒ほどかかるため、途中で不意にポケモンと遭遇したり、思わぬ挙動を示したりする場合があります。
 *   ・セーブしてしまうため、【自己責任】において、必ず目を離さずに利用ください。
 *    (大事な道具・ボールを誤って使ったり、フレンドリィショップで購入・売却したり、ポケモンを逃したりするかもしれません)
 *    ※筆者も8時間ほど放置して、3度だけ予想外の挙動(ポケモン遭遇2回、テラスタルレイドによるそらとぶタクシーのカーソルが合わず別の場所に移動)がありました。
 *   
 *   【よくある対処方法】
 *   ・そらとぶタクシー(ファストトラベル)時に、飛び先付近にテラスタルレイドが発生してしまった場合、そちらにカーソルが吸われてそこを選択する挙動をしたら、安全のためにArduinoを抜いてください。
 *   ・野生ポケモンと戦闘になったら安全のためにArduinoを抜いてください。
 *   
 *   【よくありそうな質問】
 *   Q1. パルデア十景にこだわる理由はありますか?
 *   A1. ありませんが、ファストトラベルに対応していて、とほフォルムのコレクレーまで徒歩で行ける距離なので採用しました。
 *   Q2.「列柱洞」と「みだれつきの滝」にコレクレーが出現しません
 *   A2. そうなんですよねー。たぶんこれをスキップしたらもっと時間効率が上がる気がします。
 *   Q3. マップで空を飛ぶ時に、よくテラスタルレイドにカーソルを持ってかれてしまいます。
 *   A3. そうなんですよねー。単純に距離で判断しているわけじゃなさそうですし、謎なんですよね。
 *   Q4. その場でA連打→セーブ→日付変更で1箇所でやったらあかんの?
 *   A4. ええんちゃう?こっちのほうが時間効率が良いだけですので。安定性を求めるならそちらで自作してください。
 *   Q5. 好きなポケモンはなんですか?
 *   A5. マグカルゴとチャーレムとパッチルドンです。SVの推しは今から探します。
 *   Q6. 2023年、あけましておめでとうございます!
 *   A6. あけましておめでとうございます。今年もどうぞよろしくお願いします!!
 *  
 *  (c) 2023 ますたーの忘備録
 *  https://tangential-star.hatenablog.jp/
*/

#include <SwitchControlLibrary.h>
#define HOLDTIME (95) // 1回のキー入力の長押し時間
#define SWITCH_VER (15)
// 2021/9/20、SwitchのVer13アップデートに伴う追記
// Switchのバージョンを整数で入力(例:12.2.1⇒12, 13.0.0⇒13, 15.0.0→15)


#define A_TIME (75) // 何秒間競りに使うか。(A連打時間)
#define B_TIME (3)  // 会話キャンセル用・競りキャンセル用(B連打時間)

// ボタン押しラッパー関数
int PushKey(char* keyname, int holdtime, int delaytime);
void TiltLeftStick(int direction_deg, double power, int holdtime, int delaytime);
void Hit_Button_repeatedly(char *keyname, unsigned long int while_sec);

// SV用汎用関数
void Save_for_SV(void); // セーブ
void GameStart_for_SV(void); // ホーム画面からポケモンSV起動
void GameQuit_for_SV(void);  // ホーム画面に戻ってポケモンSVを終了
void NextDay_in_HomeMenu(void); // ホーム画面から日付変更

// 意図があるやつ。
void JumpTo(double deg, double sec, int expand_level = 0);
void Dash_for_Gimmighoul(double deg, double sec, bool need_reset_cam = false);

void setup() {
  for(int i=0;i<7;i++)PushKey("UP", HOLDTIME, 300);
  PushKey("B", HOLDTIME, 300);
  delay(1000);
}

struct{
  int dash_deg;
  double dash_sec;
  bool dash_ZL;
  int jump_deg;
  double jump_sec;
  int jump_zoom;
}command_list[] = {
  // ■ありがた岩
  { 90, 0.50, true,  // OK:Lを押して向き変更→右手(90度)
   105, 3.40, 1},    // (3.49)マップ開く→縮小(ZL)→106度 105, 1.85, 0},

  // ■オージャの大滝
  {315, 0.72, false, // OK:そのまま左上(315度)
   118, 0.95, 1},    // (0.91)マップ開く→118度 118, 0.48, 0},

  // ■ナッペの手
  {290, 0.71, true,  // OK:Lを押して向き変更→左手(290度くらい)
   101, 2.48, 1},    // (2.36) マップ開く→縮小(ZL)→101度 101, 1.24, 0},

  // ■パルデア最高峰
  {340, 0.88 , false,// そのまま上(ちょい左)356度くらい
   101, 3.28, 1},    // (102/2.93)マップ開く→縮小(ZL)→102度 102, 1.54, 0},

  // ■みだれつきの滝
  { 34, 0.80, false, // そのまま右上(ちょい上気味)30度くらい
   189, 2.91, 1},    // (2.26)マップ開く→縮小(ZL)→189度【画面外】 189, 1.19, 0},

  // ■100万ボルトの夜景
  { 25, 0.68, false, // そのまま右上(ちょい上気味)25度くらい
   191, 4.74, 1},    // (190/4.50)マップ開く→ZL(縮尺変更)→190度【画面外2】 190, 2.37, 0},

  // ■ひそやかビーチ
  {  0, 0.80, false, // そのまま真上(0度)
   292, 5.86, 1},    // (292/5.37)マップ開く→ZL(縮尺変更)→292度【画面外2】 292, 3.02, 0},

  // ■オリーブ大農園
  {287, 0.79, true,  // Lを押して向き変更→左手(285度くらい)
   238, 2.80, 1},    // (2.62)マップ開く→ZL(縮尺変更)→238度 238, 1.38, 0},

  // ■ビシャビシャの斜塔
  { 58, 1.24, false, // ほぼ右寄りのちょい上(85度くらい)
     0, 5.62, 1},    // [5.65](359/4.71)マップ開く→ZL(縮尺変更)→359度【画面外2】 359, 2.48, 0},

  // ■列柱洞
  {350, 1.10, false, // そのままほぼ上ちょい左(357度)
   351, 3.80, 1},    // マップ開く→ZL(縮尺変更)→351度【画面外】 //351, 0.90, -1},   // マップ開く→ZL(縮尺変更)→351度【画面外】

};


void loop() {

  // よくわからない人は「6」のままにして、ひそやかビーチに空を飛んでからArduino Leonardoを差し込んでください。
  // ↓みなさまの環境に応じて数字を書き換えてください!!
  const int STARTPOINT = 6; // 0:ありがた岩, 1:オージャの大滝, 2:ナッペの手, 3:パルデア最高峰, 4:みだれつきの滝,
                            // 5:100万ボルトの夜景, 6:ひそやかビーチ, 7:オリーブ大農園, 8:ビシャビシャの斜塔, 9:列柱洞



  // 1.パルデア十景のツアー(10箇所でコレクレーのコイン回収)
  for(int i=0; i< 10 ; i++){
    int j = (i+STARTPOINT)%10;
    Dash_for_Gimmighoul(command_list[j].dash_deg, command_list[j].dash_sec, command_list[j].dash_ZL);
    JumpTo( command_list[j].jump_deg, command_list[j].jump_sec, command_list[j].jump_zoom);
  }  
  // 2.主人公の方向を修正
  // ※空を飛んだ後は、カメラの向きは東、プレイヤーは西を向いている。
  // ※セーブ前に補正しないと(=そのままセーブ後リセットすると)、カメラ方向が主人公の向き(=西)に変わってしまうため)
  TiltLeftStick(0, 0.5, 150, 550); // 上方向に極小時間だけスティックを微弱に倒す
  // 3.セーブ
  Save_for_SV();
  // 4.ゲーム終了
  GameQuit_for_SV();
  // 5.日付変更
  NextDay_in_HomeMenu();
  // 6.ゲーム起動
  GameStart_for_SV();

}

void Dash_for_Gimmighoul(double deg, double sec, bool need_reset_cam = false){
  // 看板に向かって走るやつ
  // 第1引数:行き先の角度
  // 第2引数:長押しの秒数
  // 第3引数:Lボタンによるプレイヤー向きにカメラを向けるか

  if(need_reset_cam){
    PushKey("L", HOLDTIME, 800);
  }
  // マップカーソルの移動
  TiltLeftStick(deg, 1.0, (int)((double)sec*1000UL), 300);
  // Aでコレクレーのコインを回収(ボタン抜けがあるので連打でフォールアウト)
  Hit_Button_repeatedly("A", 3);
  delay(500);
  return;
}

void JumpTo(double deg, double sec, int expand_level = 0){
  // マップを開いて行き先に飛ぶやつ
  // 第1引数:行き先の角度
  // 第2引数:長押しの秒数
  // 第3引数:マップを拡大率(-1=縮小/0=デフォルト/+1=拡大)

  PushKey("Y", HOLDTIME, 2500); // マップを開く

  // マップの拡大/縮小
  switch(expand_level){
    case -1:
      PushKey("ZL", HOLDTIME, 1700); // マップを縮小(広域に)
      break;
    case 1:
      PushKey("ZR", HOLDTIME, 1700); // マップを拡大(詳細に)
      break;
    case 0:
    default:
      break;
  }

  // マップカーソルの移動
  TiltLeftStick(deg, 1.0, (int)((double)sec*1000UL), 300);

  // 近似位置にフェイルセーフ
  while(expand_level >= 0){
    PushKey("ZL", HOLDTIME, 1700); // マップを縮小(広域に)
    expand_level--;
  }
  
  // Aで空を飛ぶ(ボタン抜けがあるので連打でフォールアウト)
  Hit_Button_repeatedly("A", 6);

  delay(5000); // ロード時間

  return;
}

void Save_for_SV(void){
  // ポケモンSV用のセーブ。汎用。
  PushKey("X", HOLDTIME, 1200); // メニューを開く
  PushKey("R", HOLDTIME, 1000); // Rでセーブ(ここまでの 冒険を レポートに 記録しますか?)→はい
  PushKey("A", HOLDTIME, 3200); // ますたーは レポートに しっかり 書き残した! ▼
  PushKey("B", HOLDTIME, 1200); // ダイアログを閉じる
  PushKey("B", HOLDTIME, 1500); // メニューを閉じる
  return;  
}

void NextDay_in_HomeMenu(void){
  // ホーム画面で日付変更。汎用。

  // Home画面で「設定」を選ぶ
  PushKey("down", HOLDTIME, 105);
  PushKey("right", HOLDTIME, 105);
  PushKey("right", HOLDTIME, 105);
  PushKey("right", HOLDTIME, 105);
  PushKey("right", HOLDTIME, 105);
  PushKey("right", HOLDTIME, 105);
  PushKey("A", HOLDTIME, 1500);

  // 設定画面で「日付と時刻」を開く
  PushKey("down", 1500, 105); // 1.5秒「↓」長押し
  PushKey("right", HOLDTIME, 105); 

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

  PushKey("A", HOLDTIME, 500);

  // 時間設定(現在の日付と時刻を選ぶ)
  PushKey("down", HOLDTIME, 105);
  PushKey("down", HOLDTIME, 105);
  PushKey("A", HOLDTIME, 500);

  // 時刻設定(日付の部分のみ回していく
  // 時間の変更
  PushKey("right", HOLDTIME, 105);
  PushKey("right", HOLDTIME, 105);
  PushKey("up",    HOLDTIME, 105);
  PushKey("A", HOLDTIME, 105);
  PushKey("A", HOLDTIME, 105);
  PushKey("A", HOLDTIME, 105);
  PushKey("A", HOLDTIME, 105);
  PushKey("Home", HOLDTIME, 2000);

  return;
}

void GameStart_for_SV(void){
  // ゲーム起動。汎用。
  PushKey("A", HOLDTIME, 1500);    // ゲームタイトルクリック→ユーザを選んでください画面(複数ユーザいる場合)
  PushKey("A", HOLDTIME, 24000);   // A押してから起動までおおよそ15秒(最短目安。環境に合わせて設定すること)[2024/1/6修正 DLCにより起動時間が伸びたため18500→24000]
  PushKey("A", HOLDTIME, 23500);   // タイトル画面からフィールド遷移までおおよそ21秒(環境に合わせて設定すること)
  return;
}

void GameQuit_for_SV(void){
  // ゲーム終了。汎用。
  PushKey("Home", HOLDTIME, 1200); // ホーム画面に戻る
  PushKey("X", HOLDTIME, 500);     // 終了Xを押す→終了しますか?画面
  PushKey("A", HOLDTIME, 4000);    // はい→終了しています・・・
  return;
}

void Hit_Button_repeatedly(char *keyname, unsigned long int while_sec){
  // 指定秒数だけボタンを連打(0.3秒間隔)する関数。
  // 第1引数:連打したいボタン名(例:"A")
  // 第2引数:連打したい時間[秒](例:20)
  // 返り値:なし
  unsigned long int current_time=0;
  unsigned long int start_time=0;
  unsigned long int temp_time=0;

  // 200万秒を超える時間を連打することはまずないと思うけど。オーバーフローには気をつけよう。
  if( while_sec > 2147483){
    Hit_Button_repeatedly(keyname, while_sec - 2147483); // 万が一オーバーフローしそうなときは再帰的に対応。
    while_sec = 2147483; // 残りのオーバーフロー分を下記で対応
  }

  for( start_time=millis(), current_time=start_time ; current_time - start_time < (unsigned long)while_sec*1000UL ; current_time=millis() ){
    if( current_time - start_time < (unsigned long)while_sec*1000UL )PushKey(keyname, HOLDTIME, 300); // ひたすら連打
  }
  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;
}

 

4-4.動作の様子(YouTube

2024年1月6日(←1年経っちゃった…)、DLCを導入したことで起動時間が多少長くなったことを受けて、void GameStart_for_SV(void)の中身の数字を調整しました。

それを受けて、同ソースコードで動作が確認できたので、その様子をYouTubeにアップしております。

 

 

あとがき

2023年、初の更新となる今回は「コレクレーのコイン」を回収する自動化を取り扱いました。

コイン集めはさながら「お年玉」、そしてパルデア十景から眺める日の出はまさに「初日の出」。全身が金色の”サーフゴーは「縁起が良い」ですし、まさに新年を飾るのにふさわしいトピックだったと思います。

2023年、あけましておめでとうございます
コレクレー・サーフゴーと一緒にパルデア最高峰からの初日の出を眺めました。

今回のプログラムでは、そらとぶタクシーのデバッグ作業だけ、非常に骨が折れました。

そらとぶタクシーのカーソル移動が、雨や雪などで多少伸び縮みしたり、近くのテラスタルレイドバトルにフォーカスされたりなど、かなり難航を極めました。

結論として、今回のプログラムでは「地図の縮尺を最小(最大まで拡大)にしてからカーソル移動」をすることで、それらのズレの影響を最小限にしつつ、万が一カーソルが合わなかった際にも、カーソル移動後にマップ縮尺を拡大することで、目的地を選択しやすくする工夫を行いました。

これにより、非推奨ながら、8時間の放置*10も実現できました。

マップの拡大率を最大(詳細)にしておけば、マップを縮小することでカーソルのズレを吸収できる

ちなみに、肝心のコイン集めの自動化という観点においても、マップ移動に苦労したこともあり、「時間効率」の点では良好で、わずか8時間ほどでカンストまで持っていけました。満足しています。

また、アルゴリズムという意味でも、プログラムの全体構成はほぼ一発でかけたため、気持ちよく新年を迎えることができました。

8時間ほどで、コレクレーのコインが999枚でカンストしました!はやい!!

特に、この検証という意味で、デバッグ作業を兼ねて5時間にも渡る録画記録を見ながらExcelで集計し、時間効率についてもしっかりと検証を重ねました。結果、単純平均で1周あたり12.86枚という高効率を叩き出すことができていて、納得の仕上がりでした。

興味深かったのは、「列柱洞」と「みだれづきの滝」には48試行すべてでコレクレーが一度も出現しなかったことです。もし、このプログラムを改修する場合には、まっさきにこれら2つの行き先をカットすることで、より効率よくコイン集めをすることができるようになると考えます。

パルデア十景(10箇所のフォトスポット)のうち8箇所の看板にはコレクレーが出現する。
(集計の結果、「列柱洞」と「みだれづきの滝」にはコレクレーが出現しないことがわかった)

以上のように、コレクレーのコインを集めるという話題から、色々と論じることもできて、このブログ記事としてもしっかり厚くまとめられたことは、嬉しく思います。

これからも、本ブログでは、自分の気が向いた内容についてより詳しく、より楽しくまとめられるように工夫を凝らして行きたいと思いますし、マイペースながらもしっかりと続けていきたいと考えますので、引き続き応援のほどよろしくお願いいたします。


改めまして、2023年、新年あけましておめでとうございます。本年もどうぞご贔屓のほどよろしくお願いします!

「お役に立った」という方は、ぜひとも本ブログをお気に入り登録していただきつつ拡散していただけるとすごく嬉しいです(ダイレクト広告)。

 

ではまたーc⌒っ.ω.)っ

 

--

筆者の利用マイコンArduino Leonardo)の購入リンク:

--

本ブログについて:本ブログについて

 

導入記事:【Arduino自動化01】Arduino開発環境の導入
(または 【2022年12月最新版】ZIP版IDEでのArduino開発環境の構築

ポケモンSVの記事:スカーレットバイオレットArduino自動化 カテゴリーの記事一覧

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

ポケモンBDSPの記事:ダイパリメイクArduino自動化 カテゴリーの記事一覧

レジェンズアルセウスの記事:レジェンズアルセウスArduino自動化 カテゴリーの記事一覧

 

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

 

*1:平均12.86枚とは、48試行の平均値です。コレクレーが復活していないなどの理由で「何も拾えない(0枚)」の試行も含みます

*2:51試行・4時間58分30秒の実績値656枚から計算

*3:長時間の放置は大変リスクが高いため、推奨しません。自己責任での利用をお願いします

*4:稀に、意図せず野生ポケモンにタックルされて戦闘に入ったり、近くに湧いたテラスタルレイドにそらとぶタクシーのカーソルが吸われたりすることがあります。日付変更のためにリセットを挟むため、セーブさえされなければループは破綻しませんが、セーブされた場合、貴重な道具やアイテムを利用してしまう可能性があります。またその特性上、セーブ後は意図しない挙動となるため、非常に危険です。なお、筆者は、7時間の検証中、2度、野生ポケモンにタックルされて戦闘が発生しました。

*5:サーフゴーのとくせい「おうごんのからだ」は「相手や味方の変化技を受けない。」という効果を持ちます。すなわち「あくび」「キノコのほうし」等の状態異常技はもちろん、「きんぞくおん」等のデバフ効果、「ちょうはつ」「おきみやげ」などの厄介な変化技を無効化してしまいます。

*6:「はがね」「ゴースト」の組み合わせは、ポケモン剣盾にいた「ギルガルド」と同じ複合タイプですね

*7:筆者検証では、4時間58分30秒でパルデア十景の看板を48周を調べ、延べ195匹のコレクレーと遭遇しました。30枚のドロップはわずか2回(1%)、50枚のドロップは1回のみ(0.5%)でした

*8:パルデア十景(じっけい):パルデア地方の名勝10箇所の総称。これらは「そらとぶタクシー」の行き先にも登録される。これらのうち、列柱洞・みだれづきの滝を除く8箇所の看板にはコレクレーが出現する

*9:筆者による5時間の検証による実績値から単位時間に換算。

*10:重ねて言いますが、今回の自動化は動作がずれることにより予期しない行動を起こす可能性があるため、決して目を離さずに利用いただきますようお願いします。