"""
- wordle solver
"""
import random
import time
import json
def read_words_alpha(word_len: int = 5, excluded_words: list = []) -> list:
    """
    - word_len 길이에 속하고, excluded_words에 속하지 않는 단어만 리턴
    """
    return_lst = list()
    with open("words_alpha.txt", "r") as f:
        for i, each_line in enumerate(f.readlines()):
            word = each_line.strip()
            if (len(word) == word_len) and (word not in excluded_words):
                return_lst.append(word)
    return return_lst
def find_words_excluding_char(
        total_words: list, ex_char: str = "", limit_n: int = 5):
    """
    - ex_char를 보유하지 않은 단어들만 출력
    """
    word_print_count = 0
    return_w_lst = list()
    for each_word in total_words:
        if len(set(each_word).intersection(set(ex_char))) == 0:
            if len(set(each_word)) == 5:
                print(each_word)
                return_w_lst.append(each_word)
                word_print_count += 1
                if word_print_count == limit_n:
                    break
    return return_w_lst
def current_state_of_word(target_word: str, guess_word: str) -> list:
    """
    target_word와 guess_word를 비교하여 정보를 리턴
    - 2: char, index equal
    - 1: char equal
    - 0: None
    """
    return_lst = list()
    for ca, cb in zip(target_word, guess_word):
        if ca == cb:
            return_lst.append(2)
        else:
            if cb in set(target_word):
                return_lst.append(1)
            else:
                return_lst.append(0)
    return return_lst
class WordleSolver:
    def __init__(self):
        self.total_words = read_words_alpha(5, [])
        self.exclude_char = set()
        self.candidate_char = set()
        self.index_exclude_dict = {
            i: set() for i in range(0, 5)
        }
        self.target_word = ["_" for i in range(0, 5)]
        self.excluded_words = []
    def refresh(self):
        self.exclude_char = set()
        self.candidate_char = set()
        self.index_exclude_dict = {
            i: set() for i in range(0, 5)
        }
        self.target_word = ["_" for i in range(0, 5)]
        self.excluded_words = []
    def set_constant(self, exclude_c=1.0, index_c=0.2, correct_c=5.0):
        """
        - word를 검색할 때 획득할 수 있는 정보들에 대한 weight
        """
        self.EXCLUDE_C = exclude_c
        self.INDEX_C = index_c
        self.CORRECT_C = correct_c
    def print_self(self):
        print(f"exclude_char: {self.exclude_char}")
        print(f"candidate_char: {self.candidate_char}")
        print(f"index_exclude_dict: {self.index_exclude_dict}")
    def scoring_word(self, word: str) -> int:
        """
        - 각 정보에 대한 weight를 사용하여 단어의 정보획득량을 계산
        """
        return_score = 0.0
        # EXCLUDE_C
        before_exclude = len(self.exclude_char)
        after__exclude = len(self.exclude_char.union(set(word)))
        exclude_score = (after__exclude - before_exclude) * self.EXCLUDE_C
        exclude_score = exclude_score / ((27 - before_exclude) / 27)
        return_score += exclude_score
        # INDEX_C
        index_count = 0
        for i, c in enumerate(word):
            if c != self.target_word[i]:
                if c in self.candidate_char:
                    if c not in self.index_exclude_dict[i]:
                        index_count += 1
        index_score = index_count * self.INDEX_C
        index_score *= len(self.candidate_char)
        return_score += index_score
        # CORRECT_C
        correct_count = 0
        for i, c in enumerate(self.target_word):
            if word[i] == c:
                correct_count += 1
        return_score += correct_count * self.CORRECT_C
        return return_score
    def updating_info(self, guess_word: str, feedback: list):
        """
        - feedback: guess list
        """
        self.excluded_words.append(guess_word)
        for i, (c, f) in enumerate(zip(guess_word, feedback)):
            if f == 2:
                self.candidate_char.update(c)
                self.target_word[i] = c
            elif f == 1:
                self.candidate_char.update(c)
                self.index_exclude_dict[i].update(c)
            elif f == 0:
                self.exclude_char.update(c)
                for i in range(0, 5):
                    self.index_exclude_dict[i].update(c)
            else:
                print("ERROR")
    def guessing_word(self) -> str:
        """
        - 전체 단어 pool중에서 가장 정보 획득량(score)이 높은 단어를 리턴
        """
        random_index = random.randint(0, len(self.total_words))
        guess_word = self.total_words[random_index]
        guess_score = self.scoring_word(guess_word)
        for curr_word in self.total_words:
            curr_score = self.scoring_word(curr_word)
            if curr_word not in self.excluded_words:
                if guess_score < curr_score:
                    guess_word = curr_word
                    guess_score = curr_score
            else:
                continue
        if False:
            print(f"{guess_word}, {guess_score:7.3f}")
        return guess_word
if __name__ == "__main__":
    wordle_solver = WordleSolver()
    total_words = read_words_alpha()
    file_name = f"log/result.json"
    wf = open(file_name, "w")
    result_list = list()
    """
    - exc_c, idx_c, cor_c 에 대한 Grid Search 를 진행하여
    평균적인 trial 수를 json파일에 저장한다.
    """
    problem_n = 50
    param_range = [i * 0.1 for i in range(1, 11)]
    for exc_c in param_range:
        for idx_c in param_range:
            for cor_c in param_range:
                start_time = time.time()
                param_and_result_dict = dict()
                param_and_result_dict['param'] = {
                    'exc_c': exc_c, 'idx_c': idx_c, 'cor_c': cor_c
                }
                wordle_solver.set_constant(exc_c, idx_c, cor_c)
                log = f"== EXC_C: {exc_c:6.2f}, IDX_C: {idx_c:6.2f}, COR_C: {cor_c: 6.2f} ::::  "
                print(log, end="")
                trial_lst = list()
                for each_problem in range(0, problem_n):
                    random.seed(each_problem)
                    wordle_solver.refresh()
                    target_index = random.randint(0, len(total_words))
                    target_word = total_words[target_index]
                    # print(target_word)
                    trial_to_success = 0
                    # auto input
                    for each_trial in range(0, 100):
                        trial_to_success += 1
                        # print(f"== Trial {each_trial: 2d} :: ", end="")
                        predicted_word = wordle_solver.guessing_word()
                        feedback = current_state_of_word(target_word, predicted_word)
                        wordle_solver.updating_info(predicted_word, feedback)
                        if predicted_word == target_word:
                            break
                    if False:
                        print(f"== Problem: {each_problem:2d} :: {target_word} :: ", end="")
                        print(f"Trial to success: {trial_to_success:3d}")
                    trial_lst.append(trial_to_success)
                avg_trial = sum(trial_lst) / problem_n
                param_and_result_dict['result'] = avg_trial
                result_list.append(param_and_result_dict)
                log = f"Avg. Trial: {avg_trial: 6.2f} "
                print(log, end="")
                print(f"== {time.time() - start_time:.2f} seconds")
    json.dump(result_list, wf, indent=4)
    # find_words_excluding_char(total_words, "fghtoceansurlypowerkemps", limit_n=10)
댓글남기기