こんにちは。ますたーです。
今回は、FRLG自動化の第3弾として、野生ポケモン戦闘でのレベル上げを自動化しました。
なお、Arduino Leonardo自動化の導入・機材構成については導入記事を参考にしてください。
導入記事:【Arduino自動化01】Arduino開発環境の導入
※本ブログに初めてお越しの方は「本ブログについて」もぜひ、ご覧ください。
概要
今回は、シオンタウンのポケモンタワー5階で「あまいかおり」による野生エンカウント~ポケモン戦闘~結界を使ったHP/PPの回復を自動化しました。1時間あたり17,900程度の経験値を稼げます*1。これは、2時間程度あればLv30から40程度までレベルアップできる時間効率です。
なお、ゆうれいの正体がわからないと戦闘ができませんので「シルフスコープ」を持っていることが前提となります(先にタマムシシティの攻略が必要です)。

ポケモンタワーで野生ポケモンを倒しまくってレベル上げを行うのだ。
1時間で経験値17,900程度の効率。HP/PPが減ったら結界で回復するぞ
目次です。経験値の効率について詳しくは、3章・4章でまとめています。
ソースコードだけ欲しい方は5章をご覧ください。
1.ポケモンタワーの5階ではHPとPPが回復できる「結界」がある
シオンタウン北東に位置する7階建てのタワー「ポケモンタワー」は、作中では「ポケモンのおはか」として知られており、2階~7階には祈祷師がいて、ゆうれいがウロウロしているといういわく付きの場所です。その5階の中央にはポケモンのHPやPPを回復してくれる「結界」が祈祷師の手によって張られています。
ポケモンのレベル上げを行う際に、戦ってすぐにHPやPPを回復できるこの結界の存在は非常に心強いです。

ポケモンタワーの5階中央には結界が張ってある。
足を踏み入れるだけで手持ちポケモン全員を全回復できるのだ
2.ポケモンタワーで戦闘を行うためには「シルフスコープ」が必要
さて、ポケモンタワーでレベル上げを行うためには、当然戦闘を行う必要があるのですが、実はイワヤマトンネルを抜けてシオンタウンに到着した時点では、野生ポケモンと戦闘を行うことができません。より厳密には、野生ポケモンとのエンカウントすると、ポケモンではなく「ゆうれい」が登場しますが、手持ちポケモンが怖がってしまい、わざを繰り出すことができないのです。また、モンスターボールを投げても避けられるため、捕まえることもできません。
シルフスコープが無いと「ゆうれい」の正体がわからず戦闘にならないのだ。
先にタマムシシティのゲームコーナーに行って、ロケット団のアジトを攻略しよう
「ゆうれい」の状態ではレベル上げどころか、戦闘すらままなりませんから、まずは、ゆうれいの正体を暴くことができるアイテム「シルフスコープ」を手に入れる必要があります。
シルフスコープは、タマムシシティのゲームコーナーの地下にあるロケットだんのアジトにあるので、まずはここを攻略しておきましょう。
ロケットゲームコーナーのポスターの裏には、秘密のスイッチが…!
ロケットだんのボス・サカキに勝てば「シルフスコープ」を貰えるぞ
3.ポケモンタワーのレベル上げ効率は1時間あたり約17,900経験値
さて、ロケットだんアジトを攻略し、シルフスコープを持っている状態でポケモンタワーに行くと、「ゆうれい」の正体が掴めるようになり、ゴース・ゴースト・カラカラを認知できるようになります。すなわち、通常通り戦闘を行うことができます。
ポケモンタワーの5階では、ゴース(Lv13~19)が86%、ゴースト(Lv20)が5%、カラカラ(Lv15, Lv17)が9%の確率で出現します。また、ゴースから獲得できる経験値は平均216.71、ゴーストから獲得できる経験値は360、カラカラは平均198.5ですから、1戦あたりの獲得経験値の期待値は「222.24」になります。ここで、筆者の実測値として、56分38秒で76回の戦闘を行うことができました。これは、1時間あたりの戦闘回数に換算すると80.51回に相当します。
すなわち、ポケモンタワー5階における1時間あたりの獲得経験値の効率は、約17,900と計算できます。この計算を図にしたのが下記です。

ポケモンタワー5階で、1時間戦闘を続けると、約17,900経験値を稼ぐことができるのだ
4.ポケモンのレベルアップに必要な経験値の一覧表
レベルアップに必要な経験値はポケモンの種類ごとに異なり、その成長の早さの違いで6タイプに分類できます。
実はFRLGでシナリオ中に登場するポケモン(いわゆる第1世代のポケモン)は「80万タイプ」「100万タイプ」「105万タイプ」「125万タイプ」の4種類しかありません。
例えば、キャタピーやピカチュウなどの序盤のポケモンは「100万タイプ」、ヒトカゲなどの御三家やポッポ、ニドランなどのほとんどのポケモンは「105万タイプ」に属します。ミュウツーやミニリュウなどの終盤で入手できるポケモンは「125万タイプ」など、おおよそ直感的にわかりやすく分類されています*2。
さて、レベルアップに必要な経験値を、タイプごとの表にまとめたのが下記です。

ポケモンの種類別(経験値タイプ別)での必要経験値を一覧表にまとめた
FRLGのストーリー攻略で出会える4タイプの平均(表の右端)に注目すると使いやすいぞ
先述の通り、FRLGのストーリー攻略時点では、80万タイプ~125万タイプの4種類しか登場しませんから、レベルごとに平均値を取れば、ある程度の傾向が読み取れます。そこで、図表の右端に、経験値4タイプの平均値を掲載しておきました。
この表から、Lv20~30で18,563、Lv30~36で19,549、Lv36~40で17,388の経験値が必要だと読み取ることができます。前章にて、ポケモンタワー5階における「1時間あたりの獲得経験値は約17,900」と計算しましたが、これはつまり、2時間程度あれば、Lv30から40程度までレベルアップできる時間効率だと言い換えることができます。
5.ソースコード
今回は、シオンタウン北東の「ポケモンタワー」の5階中央・結界のそばで「あまいかおり」を使ってエンカウントしたポケモンを倒すことで経験値を稼ぎ続ける、いわゆる「放置レベル上げ」を実装しました。
5-1.事前準備
事前準備として、手持ちの先頭ポケモン(レベルを上げたいポケモン)は、わざの1番目は、絶対にノーマル・かくとう・でんき・じめん以外の攻撃技にしてください。また、命中率は100または必中のわざにしましょう。
さらに、野生ポケモンとの確実なエンカウントを実現するために、手持ちに「あまいかおり」が使えるポケモンを加えておきます。このポケモンには、他のフィールドわざを覚えさせないように注意しましょう。
また、「せってい」でボタンモードは「LR」にしておきましょう。
そして、「ポケモン」にカーソルをあわせた状態で、メニューを閉じてから、ポケモンタワーの5階の結界のすぐ下のマスに立ってください。

手持ち1番目のポケモンの1つ目のわざは命中100の「攻撃技」にしておこう。
なお、カラカラ・ゴース・ゴーストを倒せるわざタイプにすることをお忘れなく
「あまいかおり」が使えるポケモンを手持ちに加え、ボタンモードはLRにしておこう
「ポケモン」にカーソルがある状態でメニューを閉じて準備完了だ!
続いて、プログラムのソースコードを修正します。
具体的には、41行目・44行目・47行目・50行目の数字を、環境に合わせて修正します。「『あまいかおり』が使えるポケモンが何番目か」「手持ちポケモンが何匹か」「戦闘開始~レベルアップ(技覚えや進化を含む)にかかるA連打に何秒かかるか」「結界でHP/PPを回復するまで何回連続で戦闘を行うか」をそれぞれ数字で入力してください。
const int AMAI_KAORI = (3);
const int TEMOCHI_NUM = (6);
const long int A_RENDA_TIME = (30);
const int REPEAT_TIME = (2);
5-2.ソースコード全文
下記がソースコードの全文です。ソースコードの冒頭の注意事項もよく読んで利用してください。なお、「シルフスコープ」を所持している前提ですので、まだシナリオが進んでいない人はそこまで進めてください。
#include <SwitchControlLibrary.h>
#define HOLDTIME (95)
#define SWITCH_VER (20)
const int AMAI_KAORI = (3);
const int TEMOCHI_NUM = (6);
const long int A_RENDA_TIME = (30);
const int REPEAT_TIME = (2);
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 setup() {
for(int i=0;i<3;i++)PushKey("B", HOLDTIME, 300);
PushKey("B", HOLDTIME, 300);
delay(1000);
}
void loop() {
for(int i = 0; i < REPEAT_TIME ; i++){
useSweetScent(AMAI_KAORI);
delay(9300);
renda("A", A_RENDA_TIME);
}
PushKey("up",600, 1800);
PushKey("A",HOLDTIME, 700);
PushKey("A",HOLDTIME, 700);
PushKey("down",600, 200);
}
int useSweetScent(int who_can_use){
if(who_can_use <= 0 || who_can_use > 6) return 0;
int lpcnt;
PushKey("X",HOLDTIME, 800);
PushKey("A",HOLDTIME, 1600);
for(lpcnt = (TEMOCHI_NUM+2); lpcnt > who_can_use ; lpcnt-- ){
PushKey("Up",HOLDTIME, 100);
}
PushKey("A",HOLDTIME, 500);
PushKey("Down",HOLDTIME, 400);
PushKey("A",HOLDTIME, 10);
return lpcnt;
}
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;
}
6.動作確認
6-1.放置でレベル上げを実現
無事に放置してレベル上げを実現できました。今やゲームコーナーでLv24で手に入れたミニリュウは、Lv55のカイリューになりました。
放置してどんどんレベルが上がっていくのは、見ていて楽しい。
ゴース・ゴーストは「とくこう」の努力値が溜まるので、知らないうちに極振りになる
ちなみに、手持ちのレベルが低い間は1発で倒しきれず、相手の攻撃や「のろい」で体力が減ってしまうので、プログラム中の「REPEAT_TIME」を「2」にして、2回ごとに結界に入るようにしていました。ある程度レベルが上がってきたら、「REPEAT_TIME」を「7」にしてより効率的に周回を行うようにプログラムを利用しました。
皆様も、適宜プログラムの数字を書き換えて使ってみてくださいね。

手持ちのレベルが低く、相手を一撃で倒せない間は、こまめにHP/PPを回復しよう。
筆者はレベル24~33までは、「2回ごと」に結界に入るようにしていた。
レベル34以降は確1で相手を倒せるようになったので「7回ごと」に変更したぞ
6-2.攻撃技以外を覚えるレベルに注意!
さて、このブログ記事を書きながら、筆者も無事に自動で放置レベル上げを実行していましたが、その途中、ハクリューがLv38に到達した時に「こうそくいどう」を習得するために1番目の攻撃技「たつまき」を忘れてしてしまう事故が発生しました。
当然、このプログラムはA連打で戦闘を終わらせるシンプルなものですから、1番目のわざの上書きも、次の戦いも止まらずに進行してしまいます。私はまだ「2のしま」には到達しておらず「わざおもいだし」もできないため、シナリオ攻略上、ここで「たつまき」を失うのはかなりの痛手になるため、やむなくリセットをしました。
何が言いたいかというと、このプログラムを使って放置する場合は、レベルアップに寄って発生する「進化」や「わざ覚え」のタイミングを見計らって、手動に切り替える必要がある、ということです。
なので、長時間放置する場合には、次のわざを覚えるレベルをあらかじめ調べておき、そのレベルの手前でプログラムを止められるようにある程度時間を読んで放置するようにしましょう。
ハクリューがLv38で「こうそくいどう」を習得し、1番目のわざを上書きする事故が発生。
それ以降はA連打では相手ポケモンを倒せないので、ループが破綻してしまうのだ
7.あとがき
いやぁ~!ついに!レベル上げの自動化を組みました!
我ながらグッジョブです。ストーリーを進める上で、レベル上げが一番の苦痛でしたが、今ではバッジ4個(タマムシシティを攻略直後)でありながら、ピジョットがLv.75、カイリューがLv.55までほぼ放置で育てることができました。
これで安心してシナリオを進めることができます。

バッジ4つでありながら、手持ちの先頭・ピジョットはLv75なのだ
個人的に、3月中にまさか3回もブログ更新するとは思いませんでした…!本当にFRLGって楽しいですね…!というか、個人的にGBA時代の操作を自動化ができるのはインスピレーションが湧きます。
ところが、仕事もプライベートもめちゃくちゃ忙しく、ゲームで遊ぶ時間がなかなか取れない中で、Switch2を置き去りにすることも多く、こういう放置系の自動化は我ながらありがたいです。
先月に引っ越してPC環境どころか、家の環境もまだまだ整っていない中で、優先順位をつけてゲームをしたりブログを書いたり自動化をしたり、合間を縫ってなんとか生活できています。
とは言え、やっぱり「趣味」としてこういう生活が続けられているのは、我ながら本当に楽しいですので、マイペースながらも、これからも続けていけたらなと思います。
皆様も温かく見守っていただければ幸いです。
ちなみに!皆様の成功報告や拡散は今でもすごく励みになっていますので、よければ拡散やコメントで応援してもらえると嬉しいです。
ではではc⌒っ.ω.)っ
前回の記事:【FRLG自動化02】野生ポケモン色違い厳選【あまいかおり】
--
本ブログについて:本ブログについて
Arduino導入記事:【Arduino自動化01】Arduino開発環境の導入
ポケモン剣盾の記事:ポケモン剣盾Arduino自動化 カテゴリーの記事一覧
ポケモンBDSPの記事:ダイパリメイクArduino自動化 カテゴリーの記事一覧
レジェンズアルセウスの記事:レジェンズアルセウスArduino自動化 カテゴリーの記事一覧
スカーレット・バイオレットの記事:スカーレットバイオレットArduino自動化 カテゴリーの記事一覧
レジェンズZ-Aの記事:レジェンズZA自動化 カテゴリーの記事一覧
ポケモンFRLGの記事:ポケモンFRLG自動化 カテゴリーの記事一覧
YouTubeチャンネル:ますたーの忘備録 - YouTube
www.youtube.com