こんにちは。ますたーです。
今回はFRLG自動化の第6弾として、準伝説ポケモン・フリーザーの色厳選を自動化しました。
※一般ポケモンの色厳選はこちら→【FRLG自動化02】野生ポケモン色違い厳選【あまいかおり】
※サンダーの色厳選はこちら→【FRLG自動化04】色違いサンダー厳選【むじんはつでんしょ】
※ファイヤーの色厳選はこちら→【FRLG自動化07】色違いファイヤー厳選【ともしびやま】
※デオキシスの色厳選はこちら→【FRLG自動化08】色違いデオキシス厳選【たんじょうのしま】
なお、Arduino Leonardo自動化の導入・機材構成については導入記事を参考にしてください。
導入記事:【Arduino自動化01】Arduino開発環境の導入
※本ブログに初めてお越しの方は「本ブログについて」もぜひ、ご覧ください。
概要
今回は、準伝説ポケモン・フリーザーの色厳選を自動化しました。「ふたごじま」の地下3階・4階を色違いが出るまで往復し続けます。ゴールドスプレー利用想定で、5試行あたり4分34秒、ざっくり15時間放置で1,000回、丸1日で1,574回の色厳選が行えます。
ただし、「なみのり」が不安定なので目を離さないことを推奨します。

ふたごじまの地下3階・4階を行き来して、フリーザーが色違いじゃなかったら逃げるを繰り返す。
前回(サンダー色厳選)と同様に、ゴールドスプレーを使った野生ポケモン対策は必須だぞ
目次です。今回の準備はサンダーの色厳選とほぼ一緒なので、比較的あっさり!
※先にサンダーの色厳選をみておくことをおすすめします(見なくても理解はできます)。
tangential-star.hatenablog.jp
0.成功報告
筆者は延べ142時間10分52秒の放置(=約5.9日間)、9,330試行目で色違いのフリーザーに会えました。使用した「ゴールドスプレー」は延べ1,866本です。

筆者は無事に色違いのフリーザーと遭遇できた。
延べ9,330試行、経過時間は5日間22時間10分52秒だった
なお、自動化自体は(途中で動作不良を起こしたり、持っていた「ゴールドスプレー」の上限を迎えてしまったりの都合上)7回に分けて実施しています。また、下記に詳細を記しますが、動作が一部不安定です。
具体的な連続稼働時間は下記の通りです。5回目まではゴールドスプレーを207個所持した状態、6回目以降は999個持った状態で自動化を回しました。
- 1回目:12時間2分20秒(790試行)※途中で動作破綻
- 2回目:15時間46分21秒(1035試行)
- 3回目:15時間46分21秒(1035試行)
- 4回目:15時間46分21秒(1035試行)
- 5回目:15時間46分21秒(1035試行)
------- ここまで207個、ここから999個で設定 --------
- 6回目:8時間54分54秒(585試行)※途中で動作破綻
- 7回目:1日22時間1分20秒(3020試行)※途中で動作破綻
- 8回目:19時間34分54秒(795試行)※色違い遭遇
2~5回目は設定していた「ゴールドスプレー」の個数分、すなわち207個すべてを使い切りました(プログラムが無事に終了しました)。
それなりに安定していますが、不定期に動作が破綻し(なみのり時に1歩足りなくなったり、なみのりから復帰後、1歩足りなくなったりなどで動作がずれ)ています。
具体的には、1回目は158個、6回目は117個、7回目は604個、それぞれ消費したあたりで動作が破綻しました。
スタートボタンなどを使わないため、動作破綻後にレポートを書いてしまうなどの大きな問題になることは少ないですが、目を離さずにご利用いただくことを推奨します。

今回の自動化では、特に「なみのり」の動作が不安定なので、極力目を離さないように。
可能な限りデバッグし、最長46時間の連続稼働を記録できたが、やはり依然不安定だった

おわかりいただけるだろうか…この録画の数こそ、凄絶なデバッグの記録なのだ…。
途中で動作が破綻したものを含め、画面収録ビデオが207本にもなったぞ
(デバッグ時の動画は、合計25時間25分46秒です)
1.FRLG(Switch版)の三鳥は逃げてもマップ切り替えで復活
まずは前提のお話です。
前回(サンダーの自動色厳選の時)にも紹介した通り、Switch版のFRLGにおいて、三鳥をはじめとするシンボルポケモンは、「にげる」を選択してマップを切り替えれば、いつの間にか同じ位置に復活します。フリーザーにおいても同様で、戦闘で逃げた後、「ふたごじま」地下4階から、はしごを登って降りれば復活します。
つまり、色厳選を行う際に、A,B,X,Yの同時押しによるタイトルリセット(いわゆる固定リセット)を行う必要が無いということです。

Switch版のFRLGでは、三鳥は逃げてもマップを切り替えれば復活するのだ
今回のフリーザーの色厳選の自動化においても、この方法でやっていきます。
【フリーザーの色厳選の手順】
- 「A」ボタンでフリーザーに話しかける
- フリーザーの色を確認する。
- 色違いでなければ、「にげる」を選んで戦闘を終える
- 「フリーザーは どこかへ とびさって いった!」となる
- 右下のはしごで一度地下3階に登り、地下4階に降りる
- フリーザーの固定シンボルが復活している
- 再びフリーザーに話しかける
- 手順3に戻る

筆者の色違いフリーザー。ニックネームは「ふりふり」にしたぞ
2.フリーザーの色厳選では「ゴールドスプレー」が必要
さて、固定リセットを用いない、マップ切り替えで色違いの厳選を行う場合、野生ポケモンとのエンカウントが大きな懸念となります。そこで、野生ポケモンとの戦闘を避けることができる「ゴールドスプレー」を使うことが重要です。
三鳥であれば、サンダー(むじんはつでんしょ)・フリーザー(ふたごじま)についてはダンジョン内に生息しているので「ゴールドスプレー」が必要になります。
なお、ポケモン剣盾より前の(今回のFRLGを含む)時代において、「ゴールドスプレー」を使って250歩歩いたあと、継続してゴールドスプレーを使い直してくれないので、バッグの一番上に置いておくと便利です。

「ゴールドスプレー」を大量に所持して、バッグの一番上に置いておこう。
セキチクシティで買えるので、道中に買っておこう
3.ソースコード
今回は、「ふたごじま」の地下4階にいるサンダーの色厳選を、Arduino Leonardoで自動化しました。時間効率は、約1時間で65試行(1試行あたり55秒)です。
3-1.自動化準備(ゲーム側の準備とソース修正)
まず、「ふたごじま」地下4階のフリーザーのすぐ右のマスに立って、主人公は左を向いている状態にします(Aを押したらそのまま戦闘に入れる状態)。
続いて、「せってい」から、「はなしの はやさ」「せんとう アニメ」「ボタンモード」をそれぞれ「はやい」「みない」「LR」にしてください。
最後に、バッグを開き「ゴールドスプレー」を一番上に並び替えて、それにカーソルを合わせた状態でメニューを閉じてください。

必ず「ボタンモード」を「LR」にして、「はなしのはやさ」を「はやい」にしておこう
「バッグ」の一番上に「ゴールドスプレー」を準備してカーソルを合わせてメニューを閉じよう
ソースコードは、ソース「RepelNum」の数字に、自分が所持している「ゴールドスプレー」の数を入力してください。
なお、「RepelSteps」にはスプレーの種類に応じた歩数(250)を入力します。例えば「シルバースプレー」を使う場合は「200」にしてください。
const int RepelSteps = (250);
const int RepelNum = (159);
3-2.ソースコード全文
ソースコードの全文です。コード冒頭の注意事項もよく読んで利用してください。
なお、特に「なみのり」の動作が不安定なので、使用中は目を離さないことをおすすめします。
#include <SwitchControlLibrary.h>
#define HOLDTIME (95)
#define SWITCH_VER (20)
const int RepelSteps = (250);
const int RepelNum = (999);
volatile bool usingRepel = false;
const bool IGNORE_REPRL = false;
volatile int used_Repel = 0;
volatile int steps_taken = 0;
volatile bool needRepelUse = true;
int PushKey(char* keyname, int holdtime, int delaytime);
void TiltLeftStick(int direction_deg, double power, int holdtime, int delaytime);
void TiltRightStick(int direction_deg, double power, int holdtime, int delaytime);
void renda(char* key ,long int time_to_press_repeatedly);
void die(void);
bool useRepel(void);
bool SkipRepelMessage(void);
void setup() {
for(int i=0;i<3;i++)PushKey("B", HOLDTIME, 300);
PushKey("B", HOLDTIME, 300);
delay(1000);
}
void loop() {
if(IGNORE_REPRL==false){
if( (used_Repel >= RepelNum) ){
die();
}
if(needRepelUse){
if( usingRepel ){
}else{
useRepel();
}
}
}
PushKey("A", HOLDTIME, 1700);
PushKey("B", HOLDTIME, 100);
PushKey("right", 100, 100);
delay(1000);
PushKey("A", 100, 1000);
PushKey("A", 100, 10);
delay(2670);
PushKey("A", HOLDTIME, 100);
PushKey("A", HOLDTIME, 100);
delay(2700);
PushKey("right",HOLDTIME, 20);
PushKey("down", HOLDTIME, 20);
delay(2100);
PushKey("B", HOLDTIME, 300);
PushKey("B", HOLDTIME, 2800);
PushKey("A", HOLDTIME, 1100);
PushKey("B", HOLDTIME, 4200);
PushKey("B", HOLDTIME, 300);
PushKey("down", 760, 20);
PushKey("left", 120, 20);
PushKey("down", 120, 20);
steps_taken+= 2;
PushKey("A", HOLDTIME, 1200);
PushKey("B", HOLDTIME, 1200);
PushKey("A", HOLDTIME, 1200);
PushKey("B", HOLDTIME, 2900);
PushKey("left", 160, 20);
PushKey("down", 1120, 20);
PushKey("right", 500, 20);
PushKey("up", 1600, 20);
PushKey("right", 650, 1000);
steps_taken+= 18;
PushKey("right", 500, 20);
PushKey("left", 400, 1000);
steps_taken+= 2;
PushKey("left", 500, 20);
PushKey("down", 1000, 20);
steps_taken+= 5;
PushKey("A", HOLDTIME, 1200);
PushKey("B", HOLDTIME, 1200);
PushKey("A", HOLDTIME, 1200);
PushKey("B", HOLDTIME, 2900);
PushKey("left", 460, 20);
PushKey("up", 200, 600);
PushKey("up", 100, 10);
PushKey("left", 40, 400);
PushKey("up", 1220, 600);
PushKey("up", 100, 10);
PushKey("right", 40, 400);
PushKey("up", 160, 1100);
PushKey("up", 560, 300);
steps_taken+= 15;
SwitchControlLibrary().PressButtonB();
SwitchControlLibrary().MoveHat(2);
delay(630);
SwitchControlLibrary().MoveHat(8);
delay(10);
SwitchControlLibrary().MoveHat(4);
delay(100);
SwitchControlLibrary().MoveHat(8);
delay(10);
SwitchControlLibrary().MoveHat(0);
delay(100);
SwitchControlLibrary().MoveHat(8);
delay(10);
SwitchControlLibrary().MoveHat(6);
delay(630);
SwitchControlLibrary().MoveHat(8);
SwitchControlLibrary().ReleaseButtonB();
delay(200);
steps_taken+= 10;
SkipRepelMessage();
PushKey("left", 150, 200);
}
void die(void){
for(;;) delay(100);
}
bool useRepel(void){
PushKey("X",HOLDTIME, 800);
PushKey("right",HOLDTIME, 90);
PushKey("A",HOLDTIME, 1000);
PushKey("A",HOLDTIME, 400);
PushKey("A",HOLDTIME, 400);
PushKey("A",HOLDTIME, 1200);
PushKey("B",HOLDTIME, 500);
PushKey("B",HOLDTIME, 1000);
PushKey("B",HOLDTIME, 300);
used_Repel++;
steps_taken = 0;
usingRepel = true;
return true;
}
bool SkipRepelMessage(void){
if( steps_taken >= RepelSteps){
usingRepel = false;
delay(150);
PushKey("B", HOLDTIME, 300);
needRepelUse=true;
return true;
}
return false;
}
int PushKey(char* keyname, int holdtime, int delaytime){
if(strlen(keyname)==1){
switch(keyname[0]){
case 'A': case 'a':
SwitchControlLibrary().PressButtonA(); delay(holdtime);
if(holdtime>0)SwitchControlLibrary().ReleaseButtonA(); delay(delaytime);
break;
case 'B': case 'b':
SwitchControlLibrary().PressButtonB(); delay(holdtime);
if(holdtime>0)SwitchControlLibrary().ReleaseButtonB(); delay(delaytime);
break;
case 'X': case 'x':
SwitchControlLibrary().PressButtonX(); delay(holdtime);
if(holdtime>0)SwitchControlLibrary().ReleaseButtonX(); delay(delaytime);
break;
case 'Y': case 'y':
SwitchControlLibrary().PressButtonY(); delay(holdtime);
if(holdtime>0)SwitchControlLibrary().ReleaseButtonY(); delay(delaytime);
break;
case 'L': case 'l':
SwitchControlLibrary().PressButtonL(); delay(holdtime);
if(holdtime>0)SwitchControlLibrary().ReleaseButtonL(); delay(delaytime);
break;
case 'R': case 'r':
SwitchControlLibrary().PressButtonR(); delay(holdtime);
if(holdtime>0)SwitchControlLibrary().ReleaseButtonR(); delay(delaytime);
break;
case 'H': case 'h':
SwitchControlLibrary().PressButtonHome(); delay(holdtime);
if(holdtime>0)SwitchControlLibrary().ReleaseButtonHome(); delay(delaytime);
break;
case '+': case 'p': case 'P':
SwitchControlLibrary().PressButtonPlus(); delay(holdtime);
if(holdtime>0)SwitchControlLibrary().ReleaseButtonPlus(); delay(delaytime);
break;
case '-': case 'm': case 'M':
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':
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':
SwitchControlLibrary().MoveHat(2); delay(holdtime);
if(holdtime>0)SwitchControlLibrary().MoveHat(8); delay(delaytime);
break;
case 'l': case 'L':
SwitchControlLibrary().MoveHat(6); delay(holdtime);
if(holdtime>0)SwitchControlLibrary().MoveHat(8); delay(delaytime);
break;
case 'u': case 'U':
SwitchControlLibrary().MoveHat(0); delay(holdtime);
if(holdtime>0)SwitchControlLibrary().MoveHat(8); delay(delaytime);
break;
case 'd': case 'D':
SwitchControlLibrary().MoveHat(4); delay(holdtime);
if(holdtime>0)SwitchControlLibrary().MoveHat(8); delay(delaytime);
break;
case 'H': case 'h':
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){
delay(holdtime);
SwitchControlLibrary().MoveLeftStick(128,128);
}
if(delaytime>0) delay(delaytime);
return;
}
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){
delay(holdtime);
SwitchControlLibrary().MoveRightStick(128,128);
}
if(delaytime>0) delay(delaytime);
return;
}
void renda(char* key ,long int time_to_press_repeatedly){
unsigned long int current_time=0;
unsigned long int start_time=0;
for( start_time=millis(), current_time=start_time ; current_time - start_time < (unsigned long)time_to_press_repeatedly*1000UL ; current_time=millis() ){
PushKey(key, 50, 50);
}
return;
}
4.動作確認
さて、プログラムも無事に組み上がり、フリーザーの色厳選の自動化ができました。
ただ、ふたごじまの中で、とくに「なみのり」の動作が不安定で、1試行当たり55秒もかかってしまうことも相まって、時間がかかっていたなと感じます。特に、就寝前や仕事に行く前に自動化をセットしていても、途中で動作破綻が起こって、起床後や帰宅後にうまく動いていなかった、ということもしばしばありました。
厳選期間としては1週間近くかかりました。とは言え、最長で46時間もの長時間の放置も実現できていたので、(動作が安定しない「ふたごじま」の環境内においては)比較的安定する自動化が組めたと考えます。
最終的には9,330試行目で無事に色違いフリーザーに出会うことができました。
延べ142時間10分52秒の色厳選の末、ようやく色違いのフリーザーに出会えた。
筆者が色違いフリーザーに出会うまでに使ったゴールドスプレーは延べ1866個だった
5.あとがき
ついに5月も下旬ですねぇ。早いものです。
仕事は新体制になって早2ヶ月、プライベートは新婚3ヶ月が経過し、それぞれ充実に充実を重ねており、超多忙な毎日です。FRLGで遊ぶ時間もあまり取れておらずですが、満足感の高い毎日を送っています。
ちなみに、FRLGのシナリオプレイ状況ですが、ようやくチャンピオンロードを抜けることができました!あとは四天王を倒すだけです(待ってろよ、オーロラチケット…!)。
あと、個人的には「4のしま」以降のシナリオも楽しみです。当時、「知らない島(原作にない島)を冒険できるなんて!」と嬉々としたことを覚えています。暇になったらきっと遊びます。
まだバッジ8つ・四天王クリア前だが、プレイ時間は926時間58分だ(自動化のやりすぎ)。
筆者は、この「これまでの あらすじ」が大好きなのだ
さて、久々にブログを更新できましたが、次はファイヤーの色厳選プログラムを公開したいな、と思っています。というか、すでに捕まえているので、後はブログ記事を書くだけ、なのですが…。なかなか時間が取れなくて…。
筆者はファイヤーの色厳選プログラムも完成しており、すでに成功しているのだ。
次回のブログはファイヤー色厳選を予定しているぞ
ところで、たくさんの応援コメントをいただきありがとうございます!成功報告も含めて、色々と励みになっています。
このブログは2020/11/7に開設してから実に2,000日以上経っていますが、定期的に足を運んでくださる皆様のおかげで、マイペースながらも続けることができています。
このブログは引き続き更新していきたいと思っていますので、これからも、どうぞよろしくお願いします!!
ではではc⌒っ.ω.)っ
前回の記事:【FRLG自動化05】ものひろい自動化【ふしぎなアメ・きんのたま】
次の記事:【FRLG自動化07】色違いファイヤー厳選【ともしびやま】
(FRLGの記事一覧はこちらから→ ポケモンFRLG自動化 カテゴリーの記事一覧)
--
本ブログについて:本ブログについて
Arduino導入記事:【Arduino自動化01】Arduino開発環境の導入
ポケモン剣盾の記事:ポケモン剣盾Arduino自動化 カテゴリーの記事一覧
ポケモンBDSPの記事:ダイパリメイクArduino自動化 カテゴリーの記事一覧
レジェンズアルセウスの記事:レジェンズアルセウスArduino自動化 カテゴリーの記事一覧
スカーレット・バイオレットの記事:スカーレットバイオレットArduino自動化 カテゴリーの記事一覧
レジェンズZ-Aの記事:レジェンズZA自動化 カテゴリーの記事一覧
ポケモンFRLGの記事:ポケモンFRLG自動化 カテゴリーの記事一覧
YouTubeチャンネル:ますたーの忘備録 - YouTube
www.youtube.com