wordle のヒントサイトをJavaScriptのテキストファイル操作と正規表現で作る

## 人気の wordle …… でも英単語知らな過ぎてぴえん😢

https://www.powerlanguage.co.uk/wordle/

とっても面白いのですがさすがに知らない英単語はきつい・・・ちょっとくらいヒントが欲しい。
ということでヒントを出してくれるサイトを作ってみます。
（場合によってはほぼ答え）

wordle の説明は省略。

:::note
JavaScript使ってプログラムを組むチュートリアルとしてもお使いください。
fetch でデータ取ってきて正規表現で検索するサンプルです。
:::

最終的なサイトはここにしばらくアップしておきます。しばらくしたら消します…。
https://wonderful-mahavira-53578c.netlify.app/

## まず戦略を考える

バックエンドの準備が手間なのでデータベースは使わない。
インメモリでフロントエンドだけで力技でどうにかしたい。
👉 **テキストファイルの英単語一覧から正規表現で検索をかけよう！**

## インタフェース（UI）を考える

まずインタフェースを考えます。
機能はインタフェースの実装なので機能から考えるのはだいたいのケースで悪手です、インタフェースから考えましょう。

5文字の英単語なのでインプットの箱を5つ用意する。
「緑」と「黄色」を「大文字」「小文字」で使い分けるようにしてみる。
結果を下に出す。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33095/30513197-4ae6-4076-9f6a-355efbfbd425.png)


## JavaScriptでの実装を考える

### 英単語リストを読み込む

こんな感じの英単語が並んだテキストファイルを用意します。
探すと色々なところに置いてあります。

```
Apple
appleberry
Appleby
appleblossom
:
```

これをサーバーにおいてJavaScriptで読み込む処理を作ります。
難しいこと考えずに `fetch` で読み込んで、改行コードで `split` をして配列に乱暴につっこみます。

```javascript
    let words = []
    fetch('words.txt')
    .then(response => response.text())
    .then(data => {
        words = data.split(/\n/);
    });
```

### 正規表現をつくる

`input` に文字が入力されたタイミングで単語リストから検索をするための正規表現を作ります。
入力されたタイミングで処理をしたいので `addEventListener(input)` でイベントを仕掛けておきます。

```javascript
    document.querySelectorAll('input').forEach( function(target) {
        target.addEventListener('input', function() {
            // ここに正規表現つくって検索する処理
        });
    });
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33095/cd46565f-7207-5f0a-9e9c-d003cabc387e.png)

文字の位置が正しくわかってる場合にはこんな正規表現を作ります。

```javascript
    '^..P..$'
```

- `^` … 文字列の先頭
- `$` … 文字列の末尾
- `.` … 何でもいい1文字

実際に正規表現を作るのはベタなテキスト操作で十分です。
`toUpperCase()` つまり大文字にしたものと元の文字が等しければ、つまり、もともと大文字だったならその文字を使って、それ以外なら `.` にしてパターンに足してくだけです。

```javascript
    function getPatternChara(name){
        const tmp = document.getElementById(name).value
        return tmp!=0 && tmp===tmp.toUpperCase()? tmp : '.' 
    }

    let pattern = '^'
    pattern += getPatternChara('c1') // 箱の1つ目
    pattern += getPatternChara('c2') // 箱の2つ目
    pattern += getPatternChara('c3') // 箱の3つ目
    pattern += getPatternChara('c4') // 箱の4つ目
    pattern += getPatternChara('c5') // 箱の5つ目
    pattern += '$'
```

次に 背景黄色 のパターンです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33095/1abe20ab-4c7a-04f7-87c7-f2a980bd491f.png)

その文字は単語内にあるけど文字の位置は正しくないって場合はこんな2つの正規表現を作ります。

```javascript
    '^(?=.*p)'
    '^..[^p]..$'
```

- `.*p` … p が含まれている
- `..[^p]..` 3文字目が p ではない

もうす少し複雑なパターンで、例えば、、、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33095/ffa506fe-4974-d0bf-6347-1a5598e51047.png)

これだとこんな3つの正規表現を作ります。

```javascript
    "^..P..$"
    "^(?=.*l)(?=.*a)"
    "^.[^l].[^a]."
```

- P が3文字目で、
- l か a がどこかにあって、
- 2文字目が l じゃない、4文字目が a じゃない

### 作った正規表現を使ってフィルターする

```javascript
        const results = words.filter(word =>
            word.length==5 &&
            word.match(new RegExp(pattern,'i')) &&
            word.match(new RegExp(pattern2,'i')) &&
            word.match(new RegExp(pattern3,'i')));
        const result  = document.getElementById('result');
        result.value = results.join('\n');
```

`RegExp` を使って3つのパターンを正規表現として扱い `match` に与えます。（ `'i'` は大文字小文字を区別しないためのオプションです ）
そういえば単語5文字だったなと思って最初に `length==5` の条件も加えています。
これらを英単語の配列の `filter` に渡して絞り込みを行います。

結果が出たところで、改行でつなぎなおして結果を画面に出力しておしまい。

## まとめ

簡単なテキスト操作ならWebサーバーにファイルおいちゃってJavaScriptで読み込ませればサクッとできる。
最近のPCはスペックいいのでこれくらいならインメモリでできちゃう。
・・・とはいえ、5MBくらいの英語辞書をページ起動時にいっきに読み込む迷惑このうえない作りなのであくまでこんな方法もあるよ！くらいで。