最近巷で流行っているゲームです。
入力された単語から「その文字は含まれない」「その文字は含まれるが別の場所に入る」「その文字はその場所に含まれる」の3通りの判定結果が出るので、それをもとにお題の英単語を当てるというゲームです。
やってみると意外と難しくて、2, 3個の単語を入れた後にすべての条件を満たす単語をいろいろと考えてもなかなか思いつきません。辞書を使っても絶妙に単語が見つけられないくらいのゲームバランスは何といっても絶妙です。
また、日替わりで全員が同じお題でやるのですが、3色の文字色を使ったSNS共有も印象的です。私もTwitterでWordleのツイートが流れてきたのを見て知ったくちです。
Wordle 245 5/6
— EH500金太郎 のんすとっぷ (@EH500_Kintarou) February 19, 2022
🟩⬜⬜🟨⬜
⬜⬜⬜⬜⬜
⬜🟨⬜⬜⬜
🟩⬜🟩🟩🟩
🟩🟩🟩🟩🟩
候補の単語を知る
さて、攻略をしようとすると 真っ先に必要なのは候補の単語です。実は単語リストは 公開されています。
12,972語あります。この中から条件を満たす単語を抽出することができればゲームをスムーズに進められるようになります。チートですが。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | public static IReadOnlySet< string > NominateCandidates(IEnumerable<IWordHint> wordhints) { var original = WordList.All; // 正しい場所の文字を抽出 var exactpos = wordhints .SelectMany(p => p.Characters.Select((p, i) => (character: p, pos: i)).Where(p => p.character.Hint == HintState.ExactPosition)) .Select(p => (character: char .ToLower(p.character.Character), pos: p.pos)) .Distinct() .ToArray(); if (exactpos.Length > 0) { var filtered = new SortedSet< string >(); foreach (var w in original) { if (exactpos.All(ep => w[ep.pos] == ep.character)) filtered.Add(w); } original = filtered; } // 含まれていない文字を除外 var notcontained = wordhints .SelectMany(p => p.Characters) .Where(p => p.Hint == HintState.NotContained) .Select(p => char .ToLower(p.Character)) .Distinct() .ToArray(); if (notcontained.Length > 0) { var filtered = new SortedSet< string >(); foreach (var w in original) { if (notcontained.All(nc => !w.Contains(nc))) filtered.Add(w); } original = filtered; } // 異なる場所の文字を抽出 var notinpos = wordhints .SelectMany(p => p.Characters.Select((p, i) => (character: p, pos: i)).Where(p => p.character.Hint == HintState.NotInPosition)) .Select(p => (character: char .ToLower(p.character.Character), pos: p.pos)) .Distinct() .ToArray(); if (notinpos.Length > 0) { var filtered = new SortedSet< string >(); foreach (var w in original) { if (notinpos.All(nip => w[nip.pos] != nip.character && w.Contains(nip.character))) filtered.Add(w); } original = filtered; } return original; } |
Wordleに単語を入力すると、それぞれの文字が3種類の判定結果になるので、3パターンの検索をします。
正しい場所の文字を抽出→含まれていない文字を除外→異なる場所の文字を抽出の順で処理をしているのは、数がグッと減る検索を先にやったほうが効率が良いと考えたからです。本当にこの順で数がグッと減るかどうかはわかりませんが。
ちなみに、上のコードに出てくるインターフェースや列挙体などは以下の通りです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public interface IWordHint { IReadOnlyList<ICharacterHint> Characters { get ; } } public interface ICharacterHint { char Character { get ; } HintState Hint { get ; } } public enum HintState { NotContained, NotInPosition, ExactPosition, } |
あとはIWordHintを入力するUIを実装してやればいいだけです。
Wordleに入力した単語とその時に返された文字の色を入力すると候補の単語を表示してくれるチートツールを作ってしまった…。 pic.twitter.com/DZ45iWtCCp
— EH500金太郎 のんすとっぷ (@EH500_Kintarou) February 19, 2022
ここに置いておきます。
次に何の単語を入れたらいいか考える
さて、じゃあ肝心の「次に入力する単語」はどうやって選べば良いでしょうか。
これを体系化して導くのはおそらく一筋縄にいきません。また機会があったら(めどが立ったら)記事にしてみようかと思います。