「ポケモンWordle」攻略のため、Pythonでポケモンに多く使われている文字を探してみる

Webブラウザゲーム「ポケモンWordle」をPythonで攻略しよう企画の第二回です。前回記事の続きとなりますので、まだの方は先にこちらをご覧ください。

今回やるのは、「ポケモンによく使われている文字を多く含むポケモンを探す」という作業です。そこまで大きなヒントにはならないと考えますが、あくまで自力でポケモンWordleを解きたいという方は、閲覧を控えたほうがいいかもしれません。

記事の最後にコード全文を掲載しています。途中の説明がいらない方はそちらからどうぞ。

スポンサーリンク

集計の準備

ライブラリ、ファイルを読み込む

import pandas as pd
poke_five = pd.read_csv("ファイルのパス", encoding="shift_jis")

pandasライブラリ、および前回作成した5文字のポケモンリストを読み込みます。前回記事と重複しますので詳しい説明は避けます。

辞書型をつくる

name_dict = {}

今回は「辞書」という仕組みを使って文字をカウントします。pythonの辞書とは、「キー」と「値」がセットになったデータ型のこと。{}で囲まれており、例えば
Toshi = {“Tokyo":1, “Osaka":2, “Nagoya":3}
のような形式のデータです。追加や削除、検索が簡単なのが特徴です。

今回はまず、「name_dictという辞書を作りますよ」という意味のコードを書いています。中身はこれから追加するので、からっぽのままでOKです。

for文で文字と個数を辞書に追加

for i in poke_five["name"]:
  uni_name = set(i)
  for j in uni_name:
    if j in name_dict:
      name_dict[j] += 1
    else:
      name_dict[j] = 1

name_dict

for文とは

for文は、繰り返しの処理をするときによく用いられる命令です。「for A in B:」の形式で、「Bに入っているデータを一つづつAに入れ、: より後の行に書いてある処理を実行します」という意味です。Bに入っているデータの数だけ、: の後に書いてある処理を繰り返します。

: の次の行から、行頭にスペースが入っています。これは「インデント」といい、pythonでは同じスペースの数のコードをひとかたまりとして扱うという決まりがあります。始めのうちは、「:で改行すると次の行頭にスペースが入る」と覚えておけばよいでしょう。

「for i in poke_five[“name"]:」は、「poke_fiveという表のname行から、データを一つづつi に入れて、: より後の行に書いてある処理を実行します。」という意味です。「i」の部分は、一部を除いてなんでも構いません。

set型に変換して重複を消す

「uni_name = set(i)」は、「iに入ったデータをset型にし、uni_nameの中に入れます。」という意味です。setは集合を扱うデータ型で、同じ値が重複しないという特徴があります。例えば「ギギギアル」であれば、setにすると{“ギ","ア","ル"}のみが保存されます。辞書と同じ{}を使いますが別物です。

Wordle序盤では、なるべく多くの可能性を試したいので、文字にかぶりのないポケモンを入力するのがセオリー。「ギギギアル」より「イシツブテ」を入れたほうが絞りやすそうというのは、直感的に分かっていただけるのではないでしょうか。ギギギアルをそのまま通すと「ギ」が3つ分カウントされてしまうので、set型でそれを避けます。

for文の二重ループ

「for j in uni_name:」は、「uni_nameからデータを一つづつjに入れて、: より後の行に書いてある処理を実行します。」という意味です。for文の中にさらにfor文を書くことにより、複雑な処理を可能にします。今回のケースであれば最初のfor文でポケモンの名前を取り出し、次のfor文で一文字ずつ取り出しています。

if文で条件分岐

「if」文は条件式。「if j in name_dict:」は、「もしname_dictの中にjが入っているなら、: より後の行に書いてある処理を実行します。」という意味です。ifの行に書いてある条件を満たさなかった場合には、「else」の次の行に書いてある処理が実行されます。

else文の処理を先に見ましょう。「name_dict[j] = 1」は、「name_dictという辞書の中に、キーをj、値を1としてデータを入れます」という意味です。

続いてif文の処理を見ましょう。「name_dict[j] += 1」は、「name_dictという辞書のjというキーの値に、1を足します」という意味です。

「name_dict」で中身を覗いてみましょう。

こんな感じで、文字ごとにちゃんとカウントしてくれます。

言葉で説明するとこんな感じ

for文の二重ループは頭が混乱しがちですので、具体例を挙げて説明しましょう。

最初のfor文で、まずpoke_five[“name"]の一番上にある「フシギダネ」がi の中に入ります。これがset型で{“フ", “シ", “ギ", “ダ", “ネ"}と変換され、uni_nameの中に入ります。

次のfor文では、{“フ", “シ", “ギ", “ダ", “ネ"}から1文字ずつ順にj の中に入ります。もし「フ」が辞書の中にあれば、フの値にプラス1をし、なければ"フ": 1がセットされます。この作業をシ,ギ,ダ,ネの順に行います。

終わったら最初のfor文に戻り、poke_five[“name"]からフシギダネの次にある「フシギソウ」を取り出します。この繰り返し作業を、poke_five[“name"]の最後の値まで行います。

多い順にソートする

dict_sorted = sorted(name_dict.items(), key=lambda x:x[1], reverse=True)
dict_sorted

先ほどの結果を見て、どの文字が多いかを目視で確認してもいいのですが、せっかくですので多い順に並び替えてみましょう。

sortedを使ってソートする

「sorted()」は、「()内の条件で並び替えますよ」という意味です。「条件に合わせて並び替えたものを、dict_sortedの中に入れますよ」となります。

()の中の最初、「name_dict.items()」で、「ソートの対象をname_dictに含まれるキーと値をすべてとします」と指定します。

続く「key=lambda x:x[1]」は、「値を基準にソートします」という意味です。lambda式はPythonの中でもやや難しい概念なので、今回はとりあえずこういうもんだと思っておいてください。

最後の「reverse=True」は、「降順(大きいもん順)に並べますよ」という意味です。これをつけないと昇順(小さいもん順)に並びます。

「dict_sorted」で中身を覗いてみましょう。

大きい順に並んでくれました。

トップ10を取り出す

top_list = []

for a, _ in dict_sorted[:10]:
  top_list.append(a)

top_list

ソートした結果から、多い順に10文字で別のリストを作って取り出します。

for文でリスト化

「top_list = []」は、「top_listというリストを作りますよ」という意味です。リストとは、複数のデータが順番に格納されたデータ型のこと。例えば、[1, 5, 6, 9]や、[“dog", “cat", “bird"]みたいな形式です。

「for a, _ in dict_sorted[:10]:」は、「dict_sortedの上から10個のデータをひとつづつ取り出してaに入れ、: より後の行に書いてある処理を実行します。」という意味です。

dict_sortedの中には、('ン’, 151)のように2つのデータが入っています。このとき、for文の後に2つの変数を指定することにより、それぞれの変数に分けて文字を取り出すことができます。

1つめの変数「a」には文字を取り出しますが、今回は2番目のデータ、つまり各文字が何個あったかのデータは使わないので、2つ目の変数名を「_」(アンダースコア、アンダーバー)として、「これは使いませんよ」と表しています。bでもcでもいいのですが、使わない変数を増やさないためにこうすることがよくあります。

「:10」で10個のデータを取り出しますが、pythonではカウントするときに0が最初になりますので、「0~9番目」という意味での10個のデータとなります。

「top_list.append(a)」は、「top_listというリストの最後にaを追加します」という意味です。

「top_list」で中身を覗いてみましょう。

使用回数が多いTOP10の文字のリストが出来上がりました。

TOP10リストの文字が多いポケモンを探す

for name in poke_five["name"]:
  num = 0
  for moji in top_list:
    if moji in name:
      num += 1
  if num >= 4:
    print(name,num)

いよいよラスト。TOP10に入った文字を多く含むポケモンを探しましょう。

「for name in poke_five[“name"]:」は、「poke_fiveという表のname行から、データを一つづつnameに入れて、: より後の行に書いてある処理を実行します。」という意味です。

「num = 0」は、「numという、数字が入る変数を作ります。」という意味です。最初のfor文の中に作ることで、poke_five[“name"]からnameにポケモンの名前が入るたびに、数字が0にリセットされます。

「for moji in top_list:」は、「top_listからデータを1つずつmojiに入れて、: より後の行に書いてある処理を実行します。」という意味です。

「if moji in name:」「num += 1」は、「もしmojiに入った文字がnameの中にあったなら」「numに1を足す」という意味です。先ほどは「if ~ else」をセットで使いましたが、条件を満たさないときに何もしない場合は、elseを書く必要はありません。

「 if num >= 4:」「print(name,num)」は、「もしnumが4以上なら」「nameとnumを出力する」という意味です。print()は、()の中身を出力する命令です。今回は、nameに入っているポケモンの名前と、含まれる文字数を出力します。

果たして結果は?

以上のコードを実行した結果、以下のように出力されました。(黒塗りは最新作のネタバレ防止)

5文字すべてがTOP10文字のポケモンはいないようです。ポケモンWordleで1手目や2手目にこのあたりのポケモンを入力すると、効率よく候補を絞れることが分かりました。(マが重複するマルマインは除く)

コード全文

最後に、コード全文を記載します。途中の出力部分は省略しました。

import pandas as pd
poke_five = pd.read_csv("ファイルのパス", encoding="shift_jis")
poke_five.head()

name_dict = {}

for i in poke_five["name"]:
  uni_name = set(i)
  for j in uni_name:
    if j in name_dict:
      name_dict[j] += 1
    else:
      name_dict[j] = 1

dict_sorted = sorted(name_dict.items(), key=lambda x:x[1], reverse=True)

top_list = []

for a, _ in dict_sorted[:10]:
  top_list.append(a)

for name in poke_five["name"]:
  num = 0
  for moji in top_list:
    if moji in name:
      num += 1
  if num >= 4:
    print(name,num)

【終わりに】

Pythonを使ってポケモンWordleを効率よく解けないか試行錯誤してみました。ヒントにはなると思いますが、結局はポケモンの名前を頭の中からスムーズに引っ張り出してくる能力が必要ですので、そこまで影響はないような気もします。

Pythonを使えば、答えを簡単に割り出すツールも作ることも可能でしょう。ただ、パズル好きとしてはそこまでやってしまうと面白味がないので、今回はこのへんでとどめておきます。

プログラミングって難しいイメージがあるかもしれませんが、すべて覚えないと使えないというわけではなく、「そういえばこういう処理ってできるよな」という発想のほうが大切です。どうやって書くかは、ネットで検索したり本で調べたりすれば分かりますので。

基礎知識の習得ばかりに時間を割くよりも、まずなにか作ってみるの精神で取り組んだほうがうまくいくのではないでしょうか。

スポンサーリンク