From 6c1055c49d5d979fe272bc940a3c35215eb41339 Mon Sep 17 00:00:00 2001 From: Max Regan Date: Mon, 31 Jan 2022 20:54:06 -0500 Subject: [PATCH] Use multiprocessing --- src/wordle.py | 56 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/src/wordle.py b/src/wordle.py index 39c5b3c..de2c35e 100644 --- a/src/wordle.py +++ b/src/wordle.py @@ -2,8 +2,11 @@ import copy import logging import random + from dataclasses import dataclass, field from enum import Enum, auto +from itertools import repeat +from multiprocessing import Pool from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple from pathlib import Path import string @@ -41,13 +44,12 @@ class DictionaryProvider: def __init__(self, limit: Optional[int]=None): self.limit = limit + self.dictionary = self._get_dictionary(self._word_filter(WORD_LENGTH)) + if limit: + self.dictionary = random.sample(self.dictionary, limit) - def provide(self, ) -> List[Word]: - words = self._get_dictionary(self._word_filter(WORD_LENGTH)) - if self.limit: - return words[:self.limit] - else: - return words + def provide(self) -> List[Word]: + return self.dictionary.copy() def _get_dictionary(self, filter_fun: Callable[str, bool] = None) -> List[Word]: l = [] @@ -159,33 +161,41 @@ class WordleMoveCalculator: word for word in self.dictionary if self.word_playable.is_word_playable(state, word) ] - move_rankings = [] # type: List[Tuple[Word, int]] - for guess in words: - word_total = 0 - for answer in words: - temp_state = WordleState( - board=state.board.copy(), - letter_constraints=state.letter_constraints.copy(), - answer=answer, - letters_eliminated=[ d.copy() for d in state.letters_eliminated ], - ) - new_state = self.move_processor.process(temp_state, guess) - word_total += self.state_evaluator.evaluate(temp_state) - - word_score = word_total / len(words) - move_rankings.append((guess, word_score)) - + with Pool(4) as p: + move_rankings = p.starmap(self.calc_guess, zip(repeat(state, len(words)), words)) move_rankings = sorted(move_rankings, key=lambda x: x[1], reverse=True) + if limit: move_rankings = move_rankings[0:limit] return move_rankings + def calc_guess(self, state: WordleState, guess: Word): + # TODO: Don't recompute me, already determined in `calculate` + words = [ + word for word in self.dictionary if self.word_playable.is_word_playable(state, word) + ] + + word_total = 0 + for answer in words: + temp_state = WordleState( + board=state.board.copy(), + letter_constraints=state.letter_constraints.copy(), + answer=answer, + letters_eliminated=[ d.copy() for d in state.letters_eliminated ], + ) + new_state = self.move_processor.process(temp_state, guess) + word_total += self.state_evaluator.evaluate(temp_state) + + word_score = word_total / len(words) + return (guess, word_score) + + def main(): logging.basicConfig(format='%(asctime)s %(message)s', level=logging.INFO) - dictionary_provider = DictionaryProvider(limit=100) + dictionary_provider = DictionaryProvider(limit=200) word_playable = WordleWordPlayable() state_evaluator = WordleRemainingWordsStateEvaluator(dictionary_provider, word_playable) move_processor = WordleMoveProcessor()