初心者による初心者のためのPOKECON自動化【基礎編】

1.はじめに

この記事は、POKECON初心者である私が以下のリンクようなプログラムを作成するために得た知識や、同様のPOKECON初心者様がPythonでプログラムを書く上で知っておくと役立ちそうなことを簡単にまとめたものとなっています。
「プログラムを書きたいけれど、どうやって書いたらいいのかわからない…」といった方々の一助になれば幸いです。なお、これからの記事はmodified版を利用する前提で書かれていますので、ご了承ください。

(以降、7000字くらいあります。長いです。全部読もうと思っていらっしゃる方はトイレに行っておいてください)

2.Pythonってなぁに?

POKECONで利用しているプログラムは、主にPythonと呼ばれる言語で記述されています。あまりプログラム作成には関係ないですが、豆知識程度に…

Pythonは数あるプログラミング言語の1つであり、近年では機械学習やAIなどの分野にもよく用いられる今人気の言語です。Pythonは、他の言語と比べて文法が単純であり英語のような構文でプログラムを作成できることから、他人が自分の書いたプログラムを読む際にどのような挙動になるのかを理解しやすいという特徴があります。

3.プログラムを書く上であるとよいもの(任意)

3.1 Visual Studio Codeなどのエディタ

私はVisual Studio Codeを使っていますが、他にもいいものがあるかもしれませんので、調べてみたり試用してみたりして自分に一番合うものを選ぶといいと思います。ソースコードエディタを利用すると変数や主要な関数に色づけがされるため、ソースコードの記述にはテキストエディタよりもこちらを利用するほうがいいかも。

一応、Windowsを使っていてVisual Studio Codeを利用してみたいという方に向けてインストール手順を軽く書き残しておきます。
まず、以下のリンクから「今すぐダウンロード」をクリックし、ご自身の環境にあったインストーラをダウンロードします。

次に、インストーラを実行してVisual Studio Codeをインストールします。「デスクトップ上にアイコンを作成する」にチェックを入れておくと、デスクトップにVisual Studio Codeのアイコンが表示されるので、迷子にならずに済みますね。
Visual Studio Codeはおそらく英語がデフォルト言語ですので、インストール後に左側のバーからExtensions(拡張機能の意味)を選択し、"Japanese Language Pack for Visual Studio Code"を検索、インストールしましょう。インストール、再起動後、表示言語が日本語になるはずです。
最後に、同様の手順で拡張機能"Python"と"Pylance"のインストールを行います。後者は任意ですが、文法の間違いやスペルミスの検知、入力補完などをしてくれるのでおすすめです!

Japanese Language Pack for Visual Studio Code
Python
Pylance

3.2 Google Colaboratory

Google様が無償で提供しているツールです。環境構築が不要で、Googleアカウントを所持していれば誰でも利用できます。
POKECON同様のシリアルコントロールはおそらくできませんが、簡単な計算式を試したり、Pythonプログラムを実際に弄って学習する際に便利です。この記事の4.1の部分にあるコードは実際にここで実行することが可能です。

なお、左上のメニューの「ファイル」から「ノートブックを新規作成」を選択すれば、エディタ画面を表示することができます。

Google Colaboratoryの編集画面。コードを入力後に ctrl+Enter で実行可能。
シンプルなデザインと色分けのおかげで見やすい

4.プログラムの基礎

4.1 一般のPythonプログラム編

4.1.1 ソースコードの入力
Pythonに限らず、ソースコードの入力は原則としてすべて半角英数字を利用して行います。例外として、コメントアウトされた部分(「#」の直後の1文、または「'''」(シングルクオーテーション3つ)で囲まれた区間)はプログラムとして認識されないため、作成した変数などがどんな意味だったのかなどをメモすると見やすくなるかもしれません。

4.1.2 変数の宣言
Pythonに限らず、多くのプログラムは変数を工夫して利用することによって動作します。そのため、変数の宣言はプログラムを書く上で必須事項です。
Pythonでは、”(変数名) = __”のように変数宣言を行います。なお、変数宣言の時点で型(整数型、浮動小数点型のように「この変数はこういう形式ですよ~」と表現するもの)が自動的に設定されるため、C言語などのようにいちいち型宣言をする必要は特にありません。

a = 1              #数値はそのまま(整数型)
b = 1.0            #数値はそのまま(浮動小数点型)
c = "Hello World"  #文字列はシングルクオーテーションまたはダブルクオーテーションで囲む(文字列型)
d = True           #条件判定などに使うTrue、Falseもそのまま(ブール型)

4.1.3 print文
print文を用いることにより、現在のプログラムの稼働状態などを自由に表示させることができます。

a = "Hello World!" #文字列指定ではダブルクオーテーションまたはシングルクオーテーションで挟む
b = "ニンフィア"
month = 8  #数字指定はそのまま

print(a) #文字列を表示
print("私は" + b + "が好きです") #文字列を代入した変数を間に表示させたいときは+で挟む
print("今月は" , month , "月です") #数を代入した変数を間に表示させたいときは,で挟む


#実行結果
#Hello World!
#私はニンフィアが好きです
#今月は 8 月です

4.1.4 算術演算子
Pythonでは主に以下のような算術演算子が利用されています。

算術演算子と計算例
(表示の関係上、スラッシュ以外はあえて全角で表示しています。書くときは半角で!)

4.1.5 比較演算子
Pythonでは主に以下のような比較演算子が利用されています。これらの演算子を用いることにより、それぞれの条件を満たすときにTrueを、満たさないときにFalseを返すことができます。

比較演算子と意味

この比較演算子と、後述のif文やwhile文を組み合わせることにより、比較演算子の条件を満たしたとき(または満たさなかったとき)に特定の処理を行わせるなどの条件分岐の作成ができるようになります。

4.1.6 ブール演算子(and , or , notなど)
ブール演算子は、if文やwhile文において、複数の条件式が複雑な関係を満たしたときに指定の動作を行いたい場合などに利用されます。これによって、詳細な条件分岐を記述することが可能です。
以下の内容は条件式が2つの場合ですが、3つ以上でも同じように処理が実行されます。

  • and
    ”(条件式1) and (条件式2)”の形で使用し、条件式がともにTrueであるときにTrueを、どちらかがFalseである場合はFalseを返します。両方の条件式を満たしたときに処理を実行したい場合などに使用します。

  • or
    ”(条件式1) or (条件式2)”の形で使用し、条件式のどちらかがTrueであるときにTrueを、ともにFalseである場合はFalseを返します。どちらかの条件式を満たしたときに処理を実行したい場合などに使用します。

  • not
    ”not (条件式)”の形で使用し、条件式がTrueであるときにFalseを、Falseである場合はTrueを返します。

ブール演算子と全体の戻り値

4.1.7 if文
if文を用いることにより、その後に記載した条件式が真である(すなわちTrueとなった)ときに、それ以降の処理を実行することができます。なお、条件式が偽である(すなわちFalseである)ときは、そのブロック内の処理は実行されずにスルーされます。
注意点として、エディタなどで編集をするとコロン(:)の後の改行で自動的にタブスペースが挿入されることがありますが、if文内に入れたい処理はすべてこのタブスペースと同じレベルで書くようにしてください。if文内に入れたくない処理を続けて記述する場合には、ifと同じレベルまでタブスペースを消去すればOKです。

a = 2
b = 3

if a < b :  #a < bを満たすときTrueが返るため、if文内の処理が実行される
  print("a は b 未満のようだ...")

print("タブスペースがない場所はif文の外になるよ~~")


#実行結果
#a は b 未満のようだ...
#タブスペースがない場所はif文の外になるよ~~

なお、if文以降にさらに条件分岐をつけたい場合やすべての条件分岐に引っかからなかった場合の処理を指定したい場合には、それぞれelif(エルイフ)やelseを使用します。

a = 1
b = 2

if a > b:    #a > bを満たさないときはFalseが返るため、if文内の処理は実行されない
  print("b は a 未満のようだ...")
elif a < b:  #a < bを満たすときはTrueが返るため、elif文内の処理が実行される
  print("a は b 未満のようだ...")
else:        #すでに条件分岐でelifに引っかかったので、ここはスルーとなる(最後まで引っかからなかったらここが実行される)
  print("a と b は同じ値のようだ...")


#実行結果
#a は b 未満のようだ...

4.1.8 while文
while文を用いると、直後の判定条件がTrueである間、そのブロック中にある処理を繰り返すことができます。POKECONでは「この画像が出るまでは何もせず待機」や「この画像が出るまでは○ボタンを押し続ける」などの処理で使われることが多いです。(後述)

i = 0 #初期化

while i < 10:    #i < 10を満たすときTrueが返るため、この条件下では10回繰り返しが実行される
  print("現在" , i , "回目のサイクルです")
  i = i + 1  #既存のiの値に1を加え、新しいiの値とする

print("処理が終了しました") #ここはwhileと同じレベルのため、while文の外と認識される
                          #つまりwhile文の処理が終わった後に実行される


#実行結果
#現在 0 回目のサイクルです
#現在 1 回目のサイクルです
#現在 2 回目のサイクルです
#現在 3 回目のサイクルです
#現在 4 回目のサイクルです
#現在 5 回目のサイクルです
#現在 6 回目のサイクルです
#現在 7 回目のサイクルです
#現在 8 回目のサイクルです
#現在 9 回目のサイクルです
#処理が終了しました

4.1.9 for文
for文を用いると、直後に示した数値の繰り返し範囲内で、そのブロック中にある処理を繰り返すことができます。よく"for i in range(__)"のような形で使われますが(下記参照)、これは1回処理ごとに変数 i に1を加えていき、6になった瞬間に for 文を抜け出すイメージです。range関数は他にもいろいろな使い方があるので、興味がある人は調べてみるといいでしょう。
また、他にもfor文のループ記述方法はあるので、目的に合わせてネットで調べてみるのもアリですね。

i = 0

for i in range(6):  #6回繰り返す(iは0から始まって1回繰り返しごとに1増え、5となるまで処理が続きます)
  print("現在" , i+1 , "回目のサイクルです")

print("処理が終了しました") #ここはforと同じレベルのため、for文の外と認識される
                          #つまりwhile文の処理が終わった後に実行される


#実行結果
#現在 1 回目のサイクルです
#現在 2 回目のサイクルです
#現在 3 回目のサイクルです
#現在 4 回目のサイクルです
#現在 5 回目のサイクルです
#現在 6 回目のサイクルです
#処理が終了しました

ちなみに、while文との差別化要素としては、while文は条件を満たすまでは無限に繰り返し続けるのに対して、for文は指定した有限回数分のみで繰り返し続ける点にあります。

4.1.10 try文
try文を用いると、ブロック内に含まれる例外が発生しうる処理について、例外発生時の処理を指定することができます。この構文のメリットは、例外が発生しうる処理でいちいちエラーにより停止することなく、以降の処理を滞りなく実行できることにあります。

try文の有無による0除算の処理([1]:try文あり、[2]:try文なし)
[2]ではエラーで処理が停止しているが、[1]は処理が正常に終了している

ただし、エラー回避ができるからといって闇雲にtry文を多用するのは、プログラムを学習する上では悪手となります。確かにエラー回避という目的は達成できますが、この回避の性質上、どの部分でエラー処理となってしまったのかが分からなくなってしまうからです。そのため、try文を利用するときにはこの点に留意して記述する必要があります。
また、プログラム実行に関してよく出るエラーは、下記のサイトを参照するとよいかもしれません。

4.1.11 リスト
リストを用いると、多くの数値を一括で管理することができます。リストには主に1次元リスト、2次元リストがあります。
なお、リストから数値を読み出したり書き換えたりするときには、左から何番目の数値なのかを数えて、それから1を引いた数を指定すればリストから抽出することが可能です。リストから範囲を指定して抽出するときは、範囲左側は前述の通りで、範囲右側は何番目か数えたときの番号をそのまま入れれてコロンでつなげばOKです(以下参照)。なぜこうなるかは、長くなってしまうのでここでは説明しません。

P = [2 , 8 , 9 , 5 , 3]      #1次元リストの宣言
Q = [[4 , 5 , 8 , 0 , 2],
     [3 , 0 , 2 , 6 , 8],
     [1 , 8 , 0 , 0 , 4]]    #2次元リストの宣言

print(P[3])     #Pの4番目がほしいときは、4-1=3を指定
print(P[0:3])   #Pの1番目から3番目がほしいときは、左側は1-1=0、右側は3番目の3を入れる 
print(Q[1][3])  #2次元リストQで上から2番目、左から4番目がほしいときは、それぞれ2-1=1、4-1=3を指定

P[2] = 1        #Pの3番目の要素9を1に書き換え
print(P)        #リストPを表示

#実行結果
#5
#[2, 8, 9]
#6
#[2, 8, 1, 5, 3]

ちなみに、角かっこ[]を丸かっこ()にすると読み取り専用のタプルとなります。こちらでは要素を書き換えようとするとエラー吐いて怒られるので、興味のある方はやってみてください。

4.2 POKECON限定のPythonプログラム編

以下の項目は、主にPOKECON限定で利用するソースコードについてまとめたものです。そのため、そのままGoogle Colaboratoryなどで実行しても実行結果を確認することはできません(多分)。
実際の使い方は”初心者による初心者のためのPOKECON自動化【実践編】”にて。

4.2.1 押下ボタンなどの設定

  • Button
    "Button.(ボタン名)"でボタンの指定ができます。(ボタン名)の場所はA、B、X、Y、L、ZL、R、ZR、MINUS、PLUS、LCLICK、RCLICK、HOME、CAPTUREから指定可能です。

  • Direction
    "Direction.(方向名)"で方向の指定ができます。(方向名)の場所はUP、DOWN、RIGHT、LEFTなどから指定可能です。

4.2.2 ボタンの押下

  • 1回限りのボタンの押下
    1回限りのボタンの押下では、"self.press((ボタン指定) , duration= _ , wait = _ )"の形でボタンの押下状態を設定できます。durationはボタンを押し始めてから離すまでの時間を、waitはボタンを放してから待機する時間を指定することができます。

  • 繰り返しのボタンの押下
    繰り返しのボタンの押下では、"self.pressRep((ボタン指定) , repeat = _ , wait = _ , duration = _ , interval = _ )"の形でボタンの押下状態を設定できます。repeatは繰り返してボタンを押す回数を、waitはボタンを放してから待機する時間を、durationはボタンを押し始めてから離すまでの時間を、intervalは1回のボタン押下が終了してから次のボタン押下を行うまでの時間間隔を指定することができます。

#Aボタンを1秒押したのち、0.5秒待機するとき
self.press(Button.A , duration = 1.0 , wait = 0.5)

#左スティックを下に8秒間倒したのち、1秒待機するとき
self.press(Direction.DOWN , duration = 8.0 , wait = 1.0)


#Bボタンを10回押すとき
self.pressRep(Button.B , repeat = 10 , wait = 0.1 , duration = 0.1 , interval = 0.2)

4.2.3 指定時間の待機
指定時間だけ何もせず待機する場合には、"self.wait((待機時間[秒]))"の形で待機時間の設定が行えます。

#何もせず2秒間待機するとき
self.wait(2.0)

4.2.4 画像認識
POKECONで最もよく利用する機能ですね。POKECONでは、SerialControllerフォルダ配下にあるtempleteフォルダ内に存在する画像を利用して、その画像と現在表示されている画面で最も一致する箇所の一致率を利用して判定を行います。
画像認識の設定は"self.isContainTemplate('(画像名)', threshold = _ , use_gray = _ , show_value = _)"の形で行います。use_gray はグレースケールで判定を行うか(Trueでグレースケール下の判定となる)、show_value は一致率を表示するか(Trueで一致率を表示する)を設定できます。画像名は拡張子を含めて指定し、templete内の任意のフォルダを参照したい場合にはパス設定を加えます(下記参照)。この条件で1を最大とする一致率の判定を行い、threshold(閾値)に指定した値以上であったときに True を、そうでないときに False を返します。

#templete配下のSampleというフォルダに含まれる、gazo.pngを閾値0.9で認識するとき
a = self.isContainTemplate('Sample/gazo.png', threshold=0.9 , use_gray=True , show_value=False)
print(a)

#出力結果(例)
#True

これだけでは有り難みが感じられませんが(というか ↑ のように使うことは多分ありませんが)、画像認識をif文やwhile文に組み込むことによって有用度の高いブログラムを作成することができます。

while not(self.isContainTemplate('gazo1.png', threshold = 0.8 , use_gray=True , show_value=False)):
  self.wait(0.1)
  '''gazo1.pngを認識していない間はwhileの後が"not(False)"すなわち"True"になるため、
     認識するまでは0.1秒待機を繰り返す(つまり認識した時点で次の処理に移る)'''

print("画像1を認識。")

while True:  #処理を無限に繰り返す
  if self.isContainTemplate('gazo2.png', threshold = 0.9 , use_gray=True , show_value=False):
    break  #gazo2.pngを認識したら、while処理をbreak(強制終了)する
  else:
    self.press(Button.A , duration = 0.1 , wait = 0.2)
    self.wait(0.1)  #gazo2.pngを認識しなかったら、Aを押して少し待機した後繰り返し

print("画像2を認識。")

4.2.5 プログラムの終了
終了処理を行いたい際には、"self.finish()"を用います。特定の条件下でプログラムを終了したい際などに便利です。

while True:  #処理を無限に繰り返す
  if self.isContainTemplate('gazo2.png', threshold = 0.9 , use_gray=True , show_value=False):
    print("画像2を認識しました。処理を終了します")
    self.finish()  #gazo2.pngを認識したら、プログラムを終了する
  else:
    print("画像認識なし。処理を継続します")  #gazo2.pngを認識しなかったら、printの後繰り返しを継続

5. おわりに

お疲れ様でした!以上が自分がPOKECONのプログラムを書く上でよく利用していた知識となります。(どこか誤りや誤植がありましたら教えていただけるとありがたいです)
次回はプログラムを実際に書くときに、どのような構造で記述するべきなのかを解説しようと思っています。

 ↓ 次回リンクはこちら ↓ 

6. Special Thanks!!

  • こちゃてす様(https://twitter.com/kochatece12 )
     try文回りに関係する箇所で記事のアドバイスをいただきました。ありがとうございました!

7. 編集履歴

  • 2022/09/05:記事のアップロード

  • 2022/09/08:4.1.10において、try文やエラーに関して追記

  • 2022/09/12:【構造編】のリンクを追加


この記事が気に入ったらサポートをしてみませんか?