Wordleとは
モチベーション
何かコードを書きたかったので、最近流行りのWordleを実装した。
成果物
実装時間: 1〜2時間
単語リストはこちらより拝借
(日本語の単語リストを作れば日本語対応も可能)
感想
プログラミングさわりたての人にちょうどいいくらいの実装難易度と感じた。
入力の判定部分(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())