2023/05/07

【ポケモンSV×pokecon01】テラレイドローカルゲスト周回自動化【arduino04と連携】

 ※本記事は、ポケモンSVにて固定したテラレイドのローカル通信のゲスト側のレイド周回によるアイテム・ポケモン入手を自動化するプログラムについて解説した記事です。

今回はPoke-Controllerを利用しつつ、前回公開したホスト側の自動化プログラム(SV×Arduino04(マイコン単体))と併用することを前提としています。

 

Poke-Controllerについては、以下のPoke-Controller Guideにて導入等詳しいことが解説されております。

[注] 本記事の内容は、ローカル通信で通信できるNintendo Switch本体とポケモンスカーレット/バイオレットを2台以上所持している方向けの内容となります。ローカル通信のホスト側の操作を自動化することで、アイテム稼ぎのために2台以上を同時操作しなければならない周回作業を自動化します。 本記事の内容はゲスト側の自動化の解説記事です。ホスト側との連携を前提としていますので、以下の記事も合わせてご覧ください。

また、ホスト側の画面を画像認識に用いるため、ホスト側のSwitch本体はTV出力のできるものである必要があります(Switch liteは不可)。ゲスト側は画像認識不要のためliteでも可能かと思います。


 2023/04/09:Ver1.0を公開しました。レイドローカルゲスト側の自動化(Poke-Controller用)の記事は作成中です。一応、ゲスト側のPythonプログラムおよびTemplete画像を仮公開しますが、こちらは記事作成まで質問等は受け付けません。

本記事では、このプログラムでできることや原理を解説したうえで、Poke-Controller用プログラムを配布しています。とにかくスケッチが欲しいという方は下へと読み飛ばしてください。

 

きっかけ、動機

前回、Arduino04の記事にて、ローカルレイド周回のためのホストの操作を自動化しました。2台同時に操作する必要がなくなり、稼ぎのための周回操作が少し楽になりました。

しかし、レイドを操作し続ける労力を考えると、ゲストの操作も含めて完全に自動化したい。というわけで、今まで扱ってこなかった画像認識を利用するときが来ました。以前にPoke-Controller環境を1台だけ構築していましたが、画像認識を使用したプログラムの作成や利用をしていなかったため、初めてのPoke-Controllerの利用となります。

巷ではPoke-Controller環境を複数用意し、同時に起動することでテラレイドバトルを自動化している方もいますが、キャプチャーボードやシリアル変換を複数台買い足すのも面倒だったので、本来の使い方と異なる接続方法によって無理やりPoke-Con環境1台+マイコン単体1台の組み合わせでレイド周回を自動化してみました。

これで何ができるの?

前回公開したホスト側の自動化プログラム(SV×Arduino04(マイコン単体))と連携し、特定のレイドを固定したホスト側のソフトにてレイドの開催とソフトリセットを繰り返す操作に合わせて、もう1台のゲスト側ソフトにてレイド参加から撃破、報酬受け取りまでの周回操作を自動化します。

前回ホスト側に対応するように、①レイド開始直後一撃で倒す場合(メタモンレイドや弱い★3,4レイドを想定)と、②制限時間ぎりぎりまでパラボラチャージ等のドレイン系の技を連打する場合(積み技をもたない★6レイドを想定)の2通りを作成しました。前者はテラピース集め、後者はテラピースに加えて秘伝スパイスや特性パッチなども集められます。

ホスト、ゲスト双方の操作を自動化することで、完全放置で同じレイドに挑み続け、ゲスト側でアイテム(+ポケモン)を回収し続けることが可能になります。Poke-Controllerの環境1台とマイコン1台があれば実行可能なため、おそらく他所で公開されているレイド自動化よりも必要な機材が少ないかと思います。

以下、固定したレイドを開催してローカル通信で募集をかけ、戦闘後にリセットしてレイド開催を繰り返す1台目をホスト、ホストの開いたレイドに参加を繰り返す2台目をゲストと呼びます。

原理、おおまかな流れ

概要

本プログラムでは、マイコン単体で周回操作をしているホスト側の画像をPoke-Controller(以下Poke-Con)で認識しつつ、Poke-Conでゲスト側のレイド周回操作を行います。

前回のホスト用プログラムと併せて使用することで

 [ホストが目の前のレイドを開催]
<ホスト画面のあいことばを認識し、ゲスト側で入力>
→ホスト・ゲスト共に戦闘
[ホストは戦闘終了後にソフトリセット→再びソフトを開始]
 <ゲストはレイド後ポケモン捕獲&アイテム入手、リセットしない>
[目の前のレイドが元に戻っているため再びレイド開催]→...

を繰り返します。

ホスト側はソフトリセットするためレイド報酬を受け取れませんが、2台目以降のゲスト側は同じレイドに参加を繰り返すことができるため、あらかじめ報酬の良いレイドを厳選しておくことで何度もアイテム(+ポケモン)を回収できます。


 

 Poke-Controllerとは

詳しくはPoke-Controller Guideをご覧ください(他力本願)。

プログラミング言語のPythonを使用し、PCからNintendo Switchへ入力をできるようにしている「Switch-FightStick」をポケモン用に使用しやすいようにパッケージングしてあるものです。マイコンのみでは、難しかった画像解析を用いた分岐など複雑な処理を行うことができます。(Guideより引用)

画像認識を用いたSwitch自動化ができるため、表示されている画面に応じて操作を分岐させることができます。今回は、ローカル通信でのテラレイドバトルの参加にあいことばが必要なため、ホストの画面に表示されたあいことば(数字4桁)を認識し、ゲスト側で入力します。

導入については、Poke-Controller Guideの 2.ハードウェアの導入 および 3.ソフトウェアの導入をご覧ください。

現在は通常のPoke-Controllerではなく、機能拡張がされているModified版がおすすめのようです。が、本記事のプログラムは通常Poke-Con用に作成されていますので、改めてModified版を導入しなおす必要はありません。 互換性はあるかと思います。

 

Poke-Controllerの接続方法について

今回のプログラムの使用に際して、Poke-Controllerについては通常想定されるものとは異なる使い方をしており、PC-Switchとの接続方法が異なります。

 通常、Poke-ControllerではUSB端子(マイコン接続)とHDMI端子(画面接続)をそれぞれPCに接続し、Switch1台をPoke-Con環境1セットで操作します(画像左)。

そのため、複数のSwitchを同時にPoke-Controllerで操作する場合、本体の数だけPoke-Con環境(ソフトウェアの導入、キャプチャーボード、シリアル変換、マイコン)が必要となります(画像中央)。キャプボやシリアル変換を何台も購入したり、複数起動に耐えられるPCが必要だったり、複数Poke-Conを連携させたりと、色々難しそうな印象。

 

一方、今回のプログラムの使用には画像右のようなつなぎ方をします

今回のレイド自動化で画像認識が必須となるのはあいことば入力時であり

①ホスト側の画面の読み取り→②ゲスト側の操作への反映

ができれば問題ないため、

①キャプボをホスト側のSwitchに繋げることで画面読み取り
②読み取ったあいことばをゲスト側で入力

するために画像のようないびつなつなぎ方をします。


レイド周回操作について

本自動化では、ホストゲストの操作を連携することで

 [ホストが目の前のレイドを開催]
<ホスト画面のあいことばを認識し、ゲスト側で入力>
→ホスト・ゲスト共に戦闘
[ホストは戦闘終了後にソフトリセット→再びソフトを開始]
 <ゲストはレイド後ポケモン捕獲&アイテム入手、リセットしない>
[目の前のレイドが元に戻っているため再びレイド開催]→...

を繰り返します。

 動作は概ね前回記事と同様のため、各動作ごとに補足する形で解説します。

 

レイド開催~あいことば入力

自動化を開始すると、ホスト側があらかじめ固定したレイドを開催し、ゲスト側はメニューからテラレイドバトルを選択します。直前にテラレイドバトルを選択していれば、メニューを開いたときにテラレイドバトルにカーソルが合っているはずです。

ホスト側、前回プログラムを用いたマイコン単体での操作によってレイド開催、あいことば表示画面で1分ほど待機します。

Poke-Con用プログラムでは、Xボタン、A連打をしばらく繰り返した後、一定時間ごとにホスト画面からあいことばを読み取ろうとします。4桁の数字が表示されていれば数字を1つずつ画像認識で読み取ります(数字読み取りについては「画像認識テンプレートについて」にて後述)。 

万が一、1分間の待機時間の間に読み取りがうまくいかずレイド参加できなかった場合、ホストは1台でレイド開始してソフトリセットまで再びレイド開催までの動作を変わらずに行います。ゲストは参加できずに再びあいことば入力画面に戻ったままXボタン、A連打、あいことば読み取りの動作を繰り返し、ホストが再びレイド開催した際に読み取りを試みます。

 

レイド戦闘開始

レイドの募集が終わりレイドを開始したら、45秒程度で「たたかう」などのコマンドが表示されます。レイド戦闘時の動きについては、

①レイド開始直後一撃で倒す場合(メタモンレイドや弱い★3,4レイドを想定)
制限時間ぎりぎりまでパラボラチャージ等のドレイン系の技を連打する場合(積み技をもたない★6レイドを想定)

の2通りを想定し、それぞれ作成、公開しています。詳しくは前回記事をご覧ください。

①の場合

ホストはレイド開始から1分ほど何も操作せず、次のソフトリセット操作に移ります。

ゲストはしばらくA連打をおこない、1番上の技で一撃で倒し、捕獲、報酬をもらってレイドを終了します。

②の場合、ホスト・ゲスト共に共通して

レイド開始から30秒(たたかう表示より前)待機、
90秒ほどA連打でドレイン系の技を連打(3回ほど攻撃)、
1分ほどAとRを交互に連打してテラスタル起動、
さらに3分半ほどA連打でレイド撃破or時間切れのまま終了

の順に動作します。

 

レイド終了後

レイドが終了する頃に、ホスト側はHOME画面からソフトリセット、再起動をおこないます。

ゲストは、A連打のままテラレイドのポケモンを捕獲(ボールやボックス空きが枯渇していれば捕獲しない)します。捕獲に用いるボールは最後に使用したボールとなるため、あらかじめ1度手動で捕獲しておくと、以後同じボールで捕獲します。ボールにこだわりがなければ、あらかじめモンスターボールを多めに買っておくことをおすすめします。

そして、報酬を受け取った後にXボタン、A連打をしばらく繰り返します。その結果、メニューを開き再びテラレイドを選択します。あいことば入力画面でA連打をしても先には進まないため、余計な動作をすることはありません。

ホスト側が再起動したころに、上記「レイド開催~あいことば入力」に戻り、再びレイドに参加します。


使用ポケモンについて

レイド参加に使用するポケモンについては前回記事をご覧ください。

 

画像認識テンプレートの準備について

 本プログラムでは、ホスト側のレイド開催時に表示されるあいことばをPoke-Conで画像認識し、ゲスト側で入力します

画像認識には、あいことばの数字4桁を1桁ずつ順番にテンプレート画像と一致するかを判定しています。座標を指定したり、数字として認識したりしているわけではありません。

具体的には、

1桁目(千の位)の数字として、左側と下側に文字を含まない空白をもつ数字の画像を判定する。0、1、2...と順に判定し、画像が一致する数字が見つかったら次へ進む。

2桁目(百の位)の数字として、 上側の[あいことば]の文字の一部が含まれる数字の画像を判定する。 0、1、2...と順に判定し、画像が一致する数字が見つかったら次へ進む。

3桁目 (十の位)の数字として、 上側の[あいことば]の文字の一部が含まれる数字の画像を判定する。以下同様

4桁目 (一の位)の数字として、 右側と下側に文字を含まない空白をもつ数字の画像を判定する。以下同様

のように1つずつ判定しています。 

なお、言語選択によって[あいことば]の文字がLink Codeなどに変わったり、数字フォント(半角/全角?)が異なっていたりするため、言語ごとにテンプレート画像(4桁×10種類=40枚)を用意する必要があります

また、スカーレット・バイオレットで背景の色や模様がわずかに異なります。SVの違いによるテンプレートに影響があるかは未検証です。必要に応じて、プログラム内で

self.isContainTemplate('SV_RAID_0xxx_v_jpn.png', 0.95, use_gray=True, show_value=False) 

のように書かれている箇所の0.95(閾値)を下げると認識しやすくなるかもしれません(下げ過ぎると誤認識しやすくなる可能性もあります。)

 

現時点で、 

「バイオレット版・日本語 (SV_RAID_0xxx_v_jpn.png など)」
「スカーレット版・英語 (SV_RAID_0xxx_s_eng.png など)」

の2種類の環境の画像テンプレートを用意しています。

新たに画像を用意する場合、 

ホスト側でローカルレイドを募集し、あいことば画面を表示したらPoke-Conウィンドウ上段にある「Capture」ボタンを押して画像を保存する。4桁それぞれ0~9までの数字を保存するため、レイド募集と中止を繰り返し、さまざまな数字を保存する(とりあえず20枚くらい)。保存画像はPoke-Conフォルダ内SerialController>Captures内。

あらかじめSwitch本体のキャプチャーボタンで保存しておき、ギャラリーから「Capture」することも可能。

上の画像や公開しているテンプレートを参考にして、4桁それぞれの数字周辺の画像をペイントソフトなどで切り取る。 執筆者は「MediBang Paint」を使用したが、おそらくWindows標準のペイントでもトリミング可能。足りない数字は①に戻ってCapture。

公開している画像データは

SV_RAID_[4桁の数字]_[ソフトバージョン]_[言語].pngのように保存しており、

「バイオレット版・1桁目(千の位)の数字が0・日本語の画像」であればSV_RAID_0xxx_v_jpn.png となるため、名前を統一して保存することを推奨。

公開しているpythonプログラムファイルをコピーしたうえで、中身を書き換える

画像認識を利用している箇所では画像ファイル名を指定しているため、テキストエディタ等でファイルを開き、SV_RAID_0xxx_v_jpn.png などを書き換える。②の通りに画像名を統一しているなら、検索(Ctrl+F)で「v_jpn」を新たに用意したものに置換する。

ついでに、プログラム内の上の方にある、NAME = '~~~'を変更することをおすすめします。Poke-Conで表示されるプログラム名が変更されます。

 

準備、必要なもの

本プログラムでは、以下の準備が必要となります。

0-1. Poke-Controller環境を1台分導入する。また、前回記事のスケッチを導入したマイコン単体も用意し、ホスト側のSwitchにてマイコンを挿してスタートする直前まで準備を済ませる。

0-2. プログラムのpyファイル(言語・レイド種類によって適したもの)を Poke-ControllerのSerialController>Commands>PythonCommandsフォルダに入れ、画像テンプレート(数字周辺の切り取り画像セット)をSerialController>Templateフォルダにpngの状態で入れる。

0-3. ホストSwitch本体のHDMIケーブルをキャプチャーボード-PCへ繋ぎ、 Poke-Controller上に表示させる。また、PC-シリアル変換-Poke-Con用マイコンをゲストSwitch本体に繋ぐ。

1.ゲスト側のソフトを起動し、野生ポケモンやNPC等と接触しない安全な場所(町の中)で手持ちの1番上にレイドで使用するポケモンを置く。使用する技を1番上に置く。

2. メニューを開き、ポケポータルからテラレイドバトルを選択する。ローカル通信(インターネットにつないでいない状態)にてあいことば(数字)を入力する画面まで進む。

3. ホストSwitch本体にマイコン単体(前回記事)を挿し、ホスト側の自動化を開始する。併せて、Poke-Controllerのプログラムを起動し、ゲスト側の自動化も開始する。


作成したプログラム

レイドローカルゲスト周回自動化のスケッチ最新版2023.05.07_ver1.0

こちらにてプログラムのpyファイル、画像テンプレートのpngファイルをまとめて公開します。 

現在、「バイオレット版・日本語」「スカーレット版・英語」を公開しています。

pyファイルについては、言語ごとに①レイド開始直後一撃で倒す場合、②ドレイン系の技を連打する場合の2種類を用意しています。

SV_RAID_forVioJpn.py...バイオレット版・日本語、①用

SV_RAID_forVioJpn_harabarry.py...バイオレット版・日本語、②用

SV_RAID_forScarEng.py ...スカーレット版・英語、①用

SV_RAID_forScarEng_harabarry.py...スカーレット版・英語、②用

のように対応しています。必要なものを使用してください。

別の言語等に対応させる場合は、上記「画像認識テンプレートの準備について」をご確認ください。

 

本プログラムは、マイコン単体でのローカルレイドホスト自動化との併用を前提としています。ホスト側の自動化についてはこちら↓

  

本プログラムを実際に使用した動画はこちら↓(ホスト・ゲスト共通)

 

↓以下はバイオレット版・日本語、②ドレイン技連打のpyファイルVer1.0。①はレイド開始後に1回だけゲストが攻撃して撃破。


#!/usr/bin/env python3
# -*- coding: utf-8 -*-


from Commands.PythonCommandBase import PythonCommand, ImageProcPythonCommand
from Commands.Keys import KeyPress, Button, Hat, Direction, Stick

# ポケモンSVのローカルレイドをArduinoLeonardo1台、pokecon1台で周回するプログラム
# ホスト(Arduinoで操作、pokeconで画像認識)、ゲスト(ホストの画像認識に応じてpokeconで操作、画面は映さない)
# 以下のプログラムでゲストのコントローラー操作をおこない、あいことば入力時のみホストの画面を参照する
# ホストはArduinoのsv_raidHostAutoにて目の前のレイドを募集、開始したらドレイン系の技連打、倒した頃にソフトリセット、再起動でレイド前に戻る。を繰り返し
# ゲストはX,Aを交互に押し続け、ホスト画面にレイド募集の合言葉が表示されたら合言葉を入力する、ホスト同様にドレイン系の技を連打する

# 本プログラムは、ホスト&ゲスト各1台:HCハラバリー,メトロノーム,パラボラチャージ、相手:ドヒドイデ,★6フェアリー 用に作成
# 相手に合わせて使用ポケモンおよび技を変更することを推奨

# 初期条件は以下の通り
# 0-1. Poke-Controller環境を1台分導入する。また、前回記事のスケッチを導入したマイコン単体も用意し、ホスト側のSwitchにてマイコンを挿してスタートする直前まで準備を済ませる。
# 0-2. プログラムのpyファイル(言語・レイド種類によって適したもの)を Poke-ControllerのSerialController>Commands>PythonCommandsフォルダに入れ、画像テンプレート(数字周辺の切り取り画像セット)をSerialController>Templateフォルダにpngの状態で入れる。
# 0-3. ホストSwitch本体のHDMIケーブルをキャプチャーボード-PCへ繋ぎ、 Poke-Controller上に表示させる。また、PC-シリアル変換-Poke-Con用マイコンをゲストSwitch本体に繋ぐ。
# 1.ゲスト側のソフトを起動し、野生ポケモンやNPC等と接触しない安全な場所(町の中)で手持ちの1番上にレイドで使用するポケモンを置く。使用する技を1番上に置く。
# 2. メニューを開き、ポケポータルからテラレイドバトルを選択する。ローカル通信(インターネットにつないでいない状態)にてあいことば(数字)を入力する画面まで進む。
# 3. ホストSwitch本体にマイコン単体(前回記事)を挿し、ホスト側の自動化を開始する。併せて、Poke-Controllerのプログラムを起動し、ゲスト側の自動化も開始する。


class ScarletViolet(ImageProcPythonCommand):
    
# Poke-Conで指定する名前を設定
    NAME = 'SV自動★6_VioJpn'
    
    
# テンプレ
    def __init__(self, cam):
        super().__init__(cam)
        self.cam = cam

    
# do(self)内のコードを実行
    def do(self):
        print("\nレイド周回開始")
        while True:
           
# 9秒ほどXA連打
            for num in range(3):
                self.press(Button.X, wait=1.5)
                self.press(Button.A, wait=0.5)
                self.press(Button.A, wait=0.5)
                self.press(Button.A, wait=0.5)
                self.wait(0.1)
           
# 3秒ほどA連打
            for num in range(6):
                self.press(Button.A, wait=0.5)
            self.wait(2.5)
            print("\n合言葉読み取り開始")
           
# 合言葉入力に移る
            self.press(Button.X, wait=2.5)
            self.press(Button.A, wait=2.5)
           
# 千の位、初期位置は1
            if (self.isContainTemplate('SV_RAID_0xxx_v_jpn.png', 0.95, use_gray=True, show_value=False)):
                self.press(Hat.BTM, duration=1.2, wait=0.3)
                self.press(Button.A, wait=0.5)
            elif (self.isContainTemplate('SV_RAID_1xxx_v_jpn.png', 0.95, use_gray=True, show_value=False)):
                self.press(Button.A, wait=0.5)
            elif (self.isContainTemplate('SV_RAID_2xxx_v_jpn.png', 0.95, use_gray=True, show_value=False)):
                self.press(Hat.RIGHT, wait=0.3)
                self.press(Button.A, wait=0.5)
            elif (self.isContainTemplate('SV_RAID_3xxx_v_jpn.png', 0.95, use_gray=True, show_value=False)):
                self.press(Hat.RIGHT, wait=0.3)
                self.press(Hat.RIGHT, wait=0.3)
                self.press(Button.A, wait=0.5)
            elif (self.isContainTemplate('SV_RAID_4xxx_v_jpn.png', 0.95, use_gray=True, show_value=False)):
                self.press(Hat.BTM, wait=0.3)
                self.press(Button.A, wait=0.5)
            elif (self.isContainTemplate('SV_RAID_5xxx_v_jpn.png', 0.95, use_gray=True, show_value=False)):
                self.press(Hat.RIGHT, wait=0.3)
                self.press(Hat.BTM, wait=0.3)
                self.press(Button.A, wait=0.5)
            elif (self.isContainTemplate('SV_RAID_6xxx_v_jpn.png', 0.95, use_gray=True, show_value=False)):
                self.press(Hat.RIGHT, wait=0.3)
                self.press(Hat.RIGHT, wait=0.3)
                self.press(Hat.BTM, wait=0.3)
                self.press(Button.A, wait=0.5)
            elif (self.isContainTemplate('SV_RAID_7xxx_v_jpn.png', 0.95, use_gray=True, show_value=False)):
                self.press(Hat.BTM, wait=0.3)
                self.press(Hat.BTM, wait=0.3)
                self.press(Button.A, wait=0.5)
            elif (self.isContainTemplate('SV_RAID_8xxx_v_jpn.png', 0.95, use_gray=True, show_value=False)):
                self.press(Hat.RIGHT, wait=0.3)
                self.press(Hat.BTM, wait=0.3)
                self.press(Hat.BTM, wait=0.3)
                self.press(Button.A, wait=0.5)
            elif (self.isContainTemplate('SV_RAID_9xxx_v_jpn.png', 0.95, use_gray=True, show_value=False)):
                self.press(Hat.RIGHT, wait=0.3)
                self.press(Hat.RIGHT, wait=0.3)
                self.press(Hat.BTM, wait=0.3)
                self.press(Hat.BTM, wait=0.3)
                self.press(Button.A, wait=0.5)
           
# 認識に失敗した場合は始めに戻り、しばらく待機する
            else:
                print("\n合言葉読み取り失敗")
                continue
           
# カーソルを左上の「1」に戻す
            self.press(Hat.TOP, duration=1.2, wait=0.3)
            self.press(Hat.LEFT, duration=1.5, wait=0.3)
           
# 百の位
            if (self.isContainTemplate('SV_RAID_x0xx_v_jpn.png', 0.95, use_gray=True, show_value=False)):
                self.press(Hat.BTM, duration=1.2, wait=0.3)
                self.press(Button.A, wait=0.5)
            elif (self.isContainTemplate('SV_RAID_x1xx_v_jpn.png', 0.95, use_gray=True, show_value=False)):
                self.press(Button.A, wait=0.5)
            elif (self.isContainTemplate('SV_RAID_x2xx_v_jpn.png', 0.95, use_gray=True, show_value=False)):
                self.press(Hat.RIGHT, wait=0.3)
                self.press(Button.A, wait=0.5)
            elif (self.isContainTemplate('SV_RAID_x3xx_v_jpn.png', 0.95, use_gray=True, show_value=False)):
                self.press(Hat.RIGHT, wait=0.3)
                self.press(Hat.RIGHT, wait=0.3)
                self.press(Button.A, wait=0.5)
            elif (self.isContainTemplate('SV_RAID_x4xx_v_jpn.png', 0.95, use_gray=True, show_value=False)):
                self.press(Hat.BTM, wait=0.3)
                self.press(Button.A, wait=0.5)
            elif (self.isContainTemplate('SV_RAID_x5xx_v_jpn.png', 0.95, use_gray=True, show_value=False)):
                self.press(Hat.RIGHT, wait=0.3)
                self.press(Hat.BTM, wait=0.3)
                self.press(Button.A, wait=0.5)
            elif (self.isContainTemplate('SV_RAID_x6xx_v_jpn.png', 0.95, use_gray=True, show_value=False)):
                self.press(Hat.RIGHT, wait=0.3)
                self.press(Hat.RIGHT, wait=0.3)
                self.press(Hat.BTM, wait=0.3)
                self.press(Button.A, wait=0.5)
            elif (self.isContainTemplate('SV_RAID_x7xx_v_jpn.png', 0.95, use_gray=True, show_value=False)):
                self.press(Hat.BTM, wait=0.3)
                self.press(Hat.BTM, wait=0.3)
                self.press(Button.A, wait=0.5)
            elif (self.isContainTemplate('SV_RAID_x8xx_v_jpn.png', 0.95, use_gray=True, show_value=False)):
                self.press(Hat.RIGHT, wait=0.3)
                self.press(Hat.BTM, wait=0.3)
                self.press(Hat.BTM, wait=0.3)
                self.press(Button.A, wait=0.5)
            elif (self.isContainTemplate('SV_RAID_x9xx_v_jpn.png', 0.95, use_gray=True, show_value=False)):
                self.press(Hat.RIGHT, wait=0.3)
                self.press(Hat.RIGHT, wait=0.3)
                self.press(Hat.BTM, wait=0.3)
                self.press(Hat.BTM, wait=0.3)
                self.press(Button.A, wait=0.5)
           
# 認識に失敗した場合は始めに戻り、しばらく待機する
            else:
                print("\n合言葉読み取り失敗")
                continue
           
# カーソルを左上の「1」に戻す
            self.press(Hat.TOP, duration=1.2, wait=0.3)
            self.press(Hat.LEFT, duration=1.5, wait=0.3)
           
# 十の位
            if (self.isContainTemplate('SV_RAID_xx0x_v_jpn.png', 0.95, use_gray=True, show_value=False)):
                self.press(Hat.BTM, duration=1.2, wait=0.3)
                self.press(Button.A, wait=0.5)
            elif (self.isContainTemplate('SV_RAID_xx1x_v_jpn.png', 0.95, use_gray=True, show_value=False)):
                self.press(Button.A, wait=0.5)
            elif (self.isContainTemplate('SV_RAID_xx2x_v_jpn.png', 0.95, use_gray=True, show_value=False)):
                self.press(Hat.RIGHT, wait=0.3)
                self.press(Button.A, wait=0.5)
            elif (self.isContainTemplate('SV_RAID_xx3x_v_jpn.png', 0.95, use_gray=True, show_value=False)):
                self.press(Hat.RIGHT, wait=0.3)
                self.press(Hat.RIGHT, wait=0.3)
                self.press(Button.A, wait=0.5)
            elif (self.isContainTemplate('SV_RAID_xx4x_v_jpn.png', 0.95, use_gray=True, show_value=False)):
                self.press(Hat.BTM, wait=0.3)
                self.press(Button.A, wait=0.5)
            elif (self.isContainTemplate('SV_RAID_xx5x_v_jpn.png', 0.95, use_gray=True, show_value=False)):
                self.press(Hat.RIGHT, wait=0.3)
                self.press(Hat.BTM, wait=0.3)
                self.press(Button.A, wait=0.5)
            elif (self.isContainTemplate('SV_RAID_xx6x_v_jpn.png', 0.95, use_gray=True, show_value=False)):
                self.press(Hat.RIGHT, wait=0.3)
                self.press(Hat.RIGHT, wait=0.3)
                self.press(Hat.BTM, wait=0.3)
                self.press(Button.A, wait=0.5)
            elif (self.isContainTemplate('SV_RAID_xx7x_v_jpn.png', 0.95, use_gray=True, show_value=False)):
                self.press(Hat.BTM, wait=0.3)
                self.press(Hat.BTM, wait=0.3)
                self.press(Button.A, wait=0.5)
            elif (self.isContainTemplate('SV_RAID_xx8x_v_jpn.png', 0.95, use_gray=True, show_value=False)):
                self.press(Hat.RIGHT, wait=0.3)
                self.press(Hat.BTM, wait=0.3)
                self.press(Hat.BTM, wait=0.3)
                self.press(Button.A, wait=0.5)
            elif (self.isContainTemplate('SV_RAID_xx9x_v_jpn.png', 0.95, use_gray=True, show_value=False)):
                self.press(Hat.RIGHT, wait=0.3)
                self.press(Hat.RIGHT, wait=0.3)
                self.press(Hat.BTM, wait=0.3)
                self.press(Hat.BTM, wait=0.3)
                self.press(Button.A, wait=0.5)
           
# 認識に失敗した場合は始めに戻り、しばらく待機する
            else:
                print("\n合言葉読み取り失敗")
                continue
           
# カーソルを左上の「1」に戻す
            self.press(Hat.TOP, duration=1.2, wait=0.3)
            self.press(Hat.LEFT, duration=1.5, wait=0.3)
           
# 一の位
            if (self.isContainTemplate('SV_RAID_xxx0_v_jpn.png', 0.95, use_gray=True, show_value=False)):
                self.press(Hat.BTM, duration=1.2, wait=0.3)
                self.press(Button.A, wait=0.5)
            elif (self.isContainTemplate('SV_RAID_xxx1_v_jpn.png', 0.95, use_gray=True, show_value=False)):
                self.press(Button.A, wait=0.5)
            elif (self.isContainTemplate('SV_RAID_xxx2_v_jpn.png', 0.95, use_gray=True, show_value=False)):
                self.press(Hat.RIGHT, wait=0.3)
                self.press(Button.A, wait=0.5)
            elif (self.isContainTemplate('SV_RAID_xxx3_v_jpn.png', 0.95, use_gray=True, show_value=False)):
                self.press(Hat.RIGHT, wait=0.3)
                self.press(Hat.RIGHT, wait=0.3)
                self.press(Button.A, wait=0.5)
            elif (self.isContainTemplate('SV_RAID_xxx4_v_jpn.png', 0.95, use_gray=True, show_value=False)):
                self.press(Hat.BTM, wait=0.3)
                self.press(Button.A, wait=0.5)
            elif (self.isContainTemplate('SV_RAID_xxx5_v_jpn.png', 0.95, use_gray=True, show_value=False)):
                self.press(Hat.RIGHT, wait=0.3)
                self.press(Hat.BTM, wait=0.3)
                self.press(Button.A, wait=0.5)
            elif (self.isContainTemplate('SV_RAID_xxx6_v_jpn.png', 0.95, use_gray=True, show_value=False)):
                self.press(Hat.RIGHT, wait=0.3)
                self.press(Hat.RIGHT, wait=0.3)
                self.press(Hat.BTM, wait=0.3)
                self.press(Button.A, wait=0.5)
            elif (self.isContainTemplate('SV_RAID_xxx7_v_jpn.png', 0.95, use_gray=True, show_value=False)):
                self.press(Hat.BTM, wait=0.3)
                self.press(Hat.BTM, wait=0.3)
                self.press(Button.A, wait=0.5)
            elif (self.isContainTemplate('SV_RAID_xxx8_v_jpn.png', 0.95, use_gray=True, show_value=False)):
                self.press(Hat.RIGHT, wait=0.3)
                self.press(Hat.BTM, wait=0.3)
                self.press(Hat.BTM, wait=0.3)
                self.press(Button.A, wait=0.5)
            elif (self.isContainTemplate('SV_RAID_xxx9_v_jpn.png', 0.95, use_gray=True, show_value=False)):
                self.press(Hat.RIGHT, wait=0.3)
                self.press(Hat.RIGHT, wait=0.3)
                self.press(Hat.BTM, wait=0.3)
                self.press(Hat.BTM, wait=0.3)
                self.press(Button.A, wait=0.5)
           
# 認識に失敗した場合は始めに戻り、しばらく待機する
            else:
                print("\n合言葉読み取り失敗")
                continue
           
# 4桁を入力したらPLUSボタンでレイドに参加する
            self.wait(1.0)
            self.press(Button.PLUS, wait=1.5)
            self.wait(2.0)
            print("\n合言葉読み取り完了")
           
# 準備完了
            self.press(Button.A, wait=0.5)
            self.press(Button.A, wait=0.5)
            self.press(Button.A, wait=0.5)
            self.press(Button.A, wait=0.5)
            self.press(Button.A, wait=0.5)
           
# 失敗したときのために、1回だけBボタン
            self.wait(5.0)
            self.press(Button.B, wait=1.0)
           
# レイド終了、次のレイドが始まるまでの時間を目安にA連打
            # 1分半ほどA連打

            print("\nA連打開始")
            for num in range(180):
                self.press(Button.A, wait=0.5)
           
# RA連打でテラスタル起動1分
            # チャージが足りないとメッセージ4秒ほど、Rの4秒後にAを押せるようにする

            print("\nテラスタル起動開始")
            for num in range(90):
                self.press(Button.R, wait=0.5)
                self.press(Button.A, wait=0.3)
           
# XA連打でメニューを開き、次のテラレイドまで待機3分半
            # 繰り返すレイドに応じてここのループ回数を調整
            # レイド時間いっぱいかかる場合は75程度。

            print("\nX&A連打開始")
            for num in range(15):
                self.press(Button.X, wait=1.5)
                self.press(Button.A, wait=0.5)
                self.press(Button.A, wait=0.5)
                self.press(Button.A, wait=0.5)
            self.wait(1.0)

 

最後に

本プログラムを前回スケッチと併用して使用することで、ポケモンSVのテラレイドバトルのローカル通信のホスト・ゲスト双方の周回操作を自動化できます。前回記事だけでは労力の軽減程度でしたが、今回のものと組み合わせることで、やっと寝ている間にテラピースや特性パッチなどを稼ぐことができるようになりました。

Poke-Controller環境1セット+マイコン単体1台の組み合わせでSwitch2台を制御できるため、環境構築の難易度は低いかと思います。一方で、あいことば入力時以外は画像認識を使用していないため、レイド戦闘中に複雑な操作を設定することはできません。あくまでハラバリーのパラボラチャージ連打などで倒せるポケモン限定となるため、Poke-Controller環境を2セット以上用意できる方は画像認識をフル活用した方が柔軟に対応できそうです。

 

プログラムの不備、改善点などありましたらコメントやお問い合わせからお願いいたします。

Arduino Arduino Leonardo (ピンソケット・ピンヘッダ実装済) 【A000057】
by カエレバ

0 件のコメント:

コメントを投稿

【ポケモンSV×arduino06】道具プリンター乱数自動化(藍の円盤)

 ※本記事は、ポケモンSVの藍の円盤(DLC)にて、 道具プリンター を決まった日時に起動することで 特定のアイテム、ボールを狙って量産する操作を自動化 するプログラムについて解説した記事です。ポケモンSVの操作をArduino Leonardoで自動化しております。 本記事の内...