1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Python で Wordle を実装する

Last updated at Posted at 2022-01-28

Wordleとは

モチベーション

何かコードを書きたかったので、最近流行りのWordleを実装した。

成果物

実装時間: 1〜2時間
単語リストはこちらより拝借
(日本語の単語リストを作れば日本語対応も可能)

スクリーンショット 2022-01-28 18.31.10.png

感想

プログラミングさわりたての人にちょうどいいくらいの実装難易度と感じた。

入力の判定部分(Wordle.evalueate)が地味に面倒で、もう少しいい書き方がありそう。

ソルバー(Solver, StupidSolver)を簡単に書いて満足したので、続きを書くことがあればいい感じのソルバーを書きたい。

コード全文

  • Python 3.8.2
from __future__ import annotations

import os
from logging import getLogger, basicConfig, INFO
import random
from collections import Counter

basicConfig(level=INFO)
logger = getLogger(__name__)


class Color:
    BLACK = '\033[30m'
    bgGREEN = '\033[42m'
    bgYELLOW = '\033[43m'
    END = '\033[0m'

    @classmethod
    def green(cls, word: str) -> str:
        return cls.bgGREEN + cls.BLACK + word + cls.END

    @classmethod
    def yellow(cls, word: str) -> str:
        return cls.bgYELLOW + cls.BLACK + word + cls.END


class Solver:
    def choice(self) -> str:
        return input('>> ').lower()

    def update(self, guess: str, score: list[int]) -> None:
        pass


class StupidSolver(Solver):
    def __init__(self) -> None:
        self.guess_list = Wordle.load_dict()

    def update(self, guess: str, score: list[int]):
        # TODO: fix bug
        inclusion = [g for g, s in zip(guess, score) if s == 2]
        exclusion = [g for g, s in zip(
            guess, score) if s == 0 and g not in inclusion]

        self.guess_list = list(filter(lambda x: not any(
            set(exclusion) & set(x)), self.guess_list))

    def choice(self):
        return random.choice(self.guess_list)


class Wordle:
    END_TURN = 6
    WORD_LENGTH = 5

    def __init__(self, seed: int | None = None) -> None:
        self.dict_ = self.load_dict()
        self.turn = 0

        random.seed(seed)
        self.answer = random.choice(self.dict_)
        assert len(self.answer) == self.WORD_LENGTH

    # ゲーム終了判定
    def is_done(self) -> bool:
        return self.END_TURN <= self.turn

    # 使用辞書のロード
    @staticmethod
    def load_dict(path: str = 'wordles.txt'):
        path = os.path.join(os.path.dirname(__file__), path)
        if not os.path.exists(path):
            import requests
            url = 'https://slc.is/data/wordles.txt'
            logger.info(f'[Download] wordles dictionary from "{url}"')
            res = requests.get(url)
            content = res.content
            with open(path, 'wb') as f:
                f.write(content)
        else:
            with open(path, 'rb') as f:
                content = f.read()
        content = content.decode('utf-8')
        return content.splitlines()

    def evalueate(self, guess: str):
        item_list = sum([[g, a]
                        for g, a in zip(guess, self.answer) if g != a], [])

        c1, c2 = Counter(item_list[::2]), Counter(item_list[1::2])
        items: list[str] = []
        for e in set(item_list[::2]) & set(item_list[1::2]):
            for _ in range(min(c1[e], c2[e])):
                items.append(e)

        buf: list[int] = []
        for g, a in zip(guess, self.answer):
            if g == a:
                buf.append(2)
            elif g in items:
                items.remove(g)
                buf.append(1)
            else:
                buf.append(0)
        return buf

    def predict(self, word: str) -> str:
        if len(word) != self.WORD_LENGTH or word not in self.dict_:
            return ''

        self.turn += 1
        if self.answer == word:
            self.turn += self.END_TURN
            return Color.green(self.answer)

        buf = ''
        for i, e in enumerate(self.evalueate(word)):
            if e == 2:
                buf += Color.green(word[i])
            elif e == 1:
                buf += Color.yellow(word[i])
            else:
                buf += '*'
        return buf

    def start(self, solver: Solver):
        while not self.is_done():
            print(f'[Round {self.turn+1}]')
            guess = solver.choice()
            print('[Solver]:', guess)
            print(self.predict(guess))

            score = self.evalueate(guess)
            solver.update(guess, score)

        if self.turn == self.END_TURN:
            print('Answer: ', self.answer)


if __name__ == '__main__':
    Wordle(0).start(Solver())

    # Wordle().start(StupidSolver())

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?