Pythonで最近流行りのWordleをチートしてみる

<h2>はじめに</h2>

しばらく前から<a href="https://www.nytimes.com/games/wordle/index.html">Wordle</a>なるものにハマっているんだけれども。ある日ふと、「これって、Pythonでチート出来るんじゃないか」と思い立ったので、やってみた。

<h2>1. 辞書を正規表現で検索できるようにする</h2>
まずは以下のサイトを参考に英和辞書を正規表現で検索できるようにする。

参考サイト:

https://news.mynavi.jp/techplus/article/zeropython-33/

<ul>
<li>何はともあれ、ライブラリを読み込む</li>
</ul>

```python
#正規表現を扱うライブラリ
import re
```

<ul>
<li>パブリックドメインの辞書を読み込み、5文字の単語のみを抽出</li>
</ul>

辞書は<a href="https://kujirahand.com/web-tools/EJDictFreeDL.php">こちら</a>から「辞書データ(テキスト形式)」をダウンロード。

```python
#ダウンロードした辞書を読む(環境に合わせてパスを編集)
txt = open('ejdict-hand-utf8.txt', 'rt',encoding='utf-8').read()

#改行で分割して見出しごとにリストに格納
lines = txt.split("\n")

#分割したリストをタブで分割して、見出しだけを取得
words = list(map(lambda s: s.split("\t")[0], lines))

#綴り違いの単語が見出しにカンマ区切りで格納されているため、それらの単語を改めて見出しに追加
for word in words:
    if ',' in word:
        new_words = word.replace(' ','').split(',')
        for new_word in new_words:
            words.append(new_word)

#小文字5文字の単語のみを抽出
fives = list(filter(lambda w: re.search(r'^[a-z]{5}$', w), words))

#重複を排除しソート
fives = list(set(fives))
fives.sort()
```

<ul>
<li>辞書から正規表現で検索する関数を定義</li>
</ul>

```python
#正規表現でリストを作成する関数
m = lambda pat: list(filter(lambda w: re.search(pat, w), fives))
```

<h2>2. ここからが本番</h2>

<ul>
<li>インプット関数で標準入力から、Wordle上、黄色・灰色・緑色で表されるアルファベットを入力する関数を定義</li>
</ul>

```python
#入力処理
def input_txt():
    global include, exclude, exact
    include = input('黄:含まれる(例 abc):')
    exclude = input('灰:含まれない(例 def):')
    exact = input('緑:位置がわかってる(例 b...c):')
```

<ul>
<li>この次が最も肝となる正規表現を組み立てる関数を定義</li>
</ul>
例えば、<br>
　① A,B,Cが含まれるが(Wodle上黄色)<br>
　② D,E,Fは含まれない(同灰色)<br>
　③ 位置がわかっている文字がBとCでそれぞれ最初と最後にある(同緑)<br>
を検索する正規表現は以下のように組み立てる。

<code>'^(?=b...c)(?=.\*[a])(?=.\*[b])(?=.\*[c])(?!.\*[def]).*$'</code>

この解釈は
<code>(?=b...c)</code>
bで始まって何か文字が3文字あってc
かつ
<code>(?=.\*[a])(?=.\*[b])(?=.\*[c])</code>
なんか文字があったりなかったりして、aが含まれてて、bも含まれてて、cも含まれてて
かつ
<code>(?!.\*[def])</code>
なんか文字があったりなかったりするけど、dもeもfも含まれない

<code>^(省略).*$</code>
そんな文字列がありませんか~?と聞いている(つもり)。

```python
#正規表現の組立て
def reg_edit(include, exclude, exact):
    reg_txt_incld = ''
    reg_txt_excld = ''
    reg_txt_exact = ''
    reg_txt = ''
    
    for in_letter in include:
        reg_txt_incld = reg_txt_incld + r'(?=.*[' + in_letter +r'])'
    
    if len(exclude) > 0:
        reg_txt_excld = r'(?!.*[' + exclude + r'])'
        
    if len(exact) > 0:
        reg_txt_exact = r'(?=' + exact + r')'
    
    reg_txt = r'^' + reg_txt_exact + reg_txt_incld + reg_txt_excld + r'.*$'
    return reg_txt
```

<ul>
<li>メイン処理を定義</li>
</ul>

```python
#メイン処理
def main():
    while True:
        input_txt()
        print(m(reg_edit(include, exclude, exact)))
```

<h2>3. 動かしてみよう(ふぅ...)</h2>

```python
#メイン処理をコール
main()
```

処理を実行すると、以下のように聞かれるので、

[黄:含まれる(例 abc):]　にWordle上、黄色で表示される含まれる文字
[灰:含まれない(例 def):]にWordle上、灰色で表示される含まれない文字
[緑:位置がわかってる(例 b...c)]にWordle上、緑色で表示される位置がわかっている文字
(わかる箇所に英字、わからない箇所は.(ドット))

をそれぞれ小文字で入力。

例に記載の通りに入力すると候補として'basic'が表示されます。

<pre><code>黄:含まれる(例 abc):abc
灰:含まれない(例 def):def
緑:位置がわかってる(例 b...c):b...c
['basic']
</code></pre>



<h2>まとめ</h2>
ここまで読んでいただける奇特な方がいらっしゃいましたら、ものすごく感謝したいと思います。<br><br>
実際のWordleで黄色で表される文字は「含まれる」だけではなくて、「位置が合ってない」という意味を持つのですが、このチートツールはその表現が面倒で実装してないので、使う人間の脳内処理に頼っています...<br><br>

さて、勉強がてらWordleをチートするコードを書いてみました。過去に勉強ついでにサイトも立ち上げたのですが、誰も来てくれないので、閉じました...

記念に使い方は次項に残しておきます。

<h2>Wordle Hintの使い方</h2>

Include:
　　Wordle上、黄色で表示される含まれる文字

Exclude:
　　Wordle上、灰色で表示される含まれない文字

Position:
　　Wordle上、緑色で表示される位置がわかっている文字
　　(わかる箇所に英字、わからない箇所は.(ドット))

というルールでアルファベットを入力します。小文字でも大文字でも構いません。

Hide Word List:
　これをオンにすると候補リストは表示されず、候補の単語数だけが表示されます。どのくらい絞れ込めたかを確認できたりします。

![スクリーンショット 2022-10-09 23.46.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2784725/2fa21b0e-7e4e-2000-1217-8afe41647852.png)


参考サイト:

https://news.mynavi.jp/techplus/article/zeropython-33/