Don't reguess letters in same positions

This commit is contained in:
2022-01-31 12:01:40 -05:00
parent 7c50c06436
commit 3336d47732

View File

@@ -9,7 +9,7 @@ from pathlib import Path
import string
WORD_LENGTH = 5 # TODO: Make this configurable at runtime
logger = logging.getLogger(__name__)
log = logging.getLogger(__name__)
Letter = chr
Position = int
@@ -33,7 +33,9 @@ class WordleState:
c: WordleLetterConstraint.UNKNOWN for c in string.ascii_lowercase
}
)
letters_eliminated: List[Dict[Letter, None]] = field(
default_factory=lambda: [dict() for i in range(WORD_LENGTH)]
)
class DictionaryProvider:
def provide(self) -> List[Word]:
@@ -43,7 +45,7 @@ class DictionaryProvider:
l = []
if not filter_fun:
filter_fun = lambda s: True
with open("/usr/share/dict/words") as d:
with open("/home/max/Desktop/clean-words-5.txt") as d:
return [
word
for word in filter(filter_fun, map(lambda w: w.strip(), d.readlines()))
@@ -55,7 +57,7 @@ class DictionaryProvider:
len(word) == length
and all(map(lambda x: x in string.ascii_lowercase, word))
and not all(map(lambda c: c in "xiv", word))
and (word.startswith("s") or word.startswith("z"))
#and (word.startswith("s") or word.startswith("z"))
) # hack to remove some roman numerals
return f
@@ -92,6 +94,10 @@ class WordleStateEvaluator:
for letter in state.board:
if letter:
score += 100
for letters in state.letters_eliminated:
score += len(letters)
return score
@@ -102,15 +108,20 @@ class WordleMoveProcessor:
board=state.board,
letter_constraints=state.letter_constraints.copy(),
answer=state.answer,
letters_eliminated=[ d.copy() for d in state.letters_eliminated ],
)
for idx, ch in enumerate(guess):
if ch in new_state.answer:
new_state.letter_constraints[ch] = WordleLetterConstraint.INCLUDED
if state.answer[idx] == ch:
new_state.board[idx] = ch
else:
new_state.letter_constraints[ch] = WordleLetterConstraint.EXCLUDED
if new_state.answer[idx] == ch:
new_state.board[idx] = ch
else:
new_state.letters_eliminated[idx][ch] = None
return new_state
@@ -130,7 +141,7 @@ class WordleMoveCalculator:
self.move_processor = move_processor
def calculate(
self, state: WordleState, num_guesses: int, evaluation_depth: int
self, state: WordleState, evaluation_depth: int, limit: Optional[int]=None,
) -> List[Tuple[Word, int]]:
guesses = []
words = [
@@ -145,6 +156,7 @@ class WordleMoveCalculator:
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)
@@ -152,8 +164,10 @@ class WordleMoveCalculator:
word_score = word_total / len(words)
move_rankings.append((guess, word_score))
move_rankings = sorted(move_rankings, key=lambda x: x[1])
return move_rankings[0:num_guesses]
move_rankings = sorted(move_rankings, key=lambda x: x[1], reverse=True)
if limit:
move_rankings = move_rankings[0:limit]
return move_rankings
def _is_word_playable(self, state: WordleState, word: Word):
for idx, ch in enumerate(word):
@@ -165,12 +179,20 @@ class WordleMoveCalculator:
# Letter is not already excluded
if constraint == WordleLetterConstraint.EXCLUDED:
return False
if ch in state.letters_eliminated[idx]:
return False
for letter, constraint in state.letter_constraints.items():
if constraint == WordleLetterConstraint.INCLUDED and letter not in word:
return False
return True
def main():
logging.basicConfig()
logging.basicConfig(format='%(asctime)s %(message)s', level=logging.INFO)
dictionary_provider = DictionaryProvider()
state_evaluator = WordleStateEvaluator(dictionary_provider)
@@ -182,15 +204,24 @@ def main():
answer = random.choice(dictionary_provider.provide())
state = WordleState(board=[None] * WORD_LENGTH, answer=answer)
iteration = 1
guesses = [] # type: List[str]
while not all(state.board):
print("Round:", iteration)
print("Board:", str(state.board))
move = move_calculator.calculate(state, 5, 1)[0]
guess = move[0]
print("Guess:", guess)
log.info("Round: %d", iteration)
log.debug("State: %s", str(state))
if iteration == 1:
guess = "sales"
else:
moves = move_calculator.calculate(state, 5, limit=5)
log.info("Best Moves: %s", moves)
guess = moves[0][0]
log.info("Guess: %s", guess)
state = move_processor.process(state, guess)
log.info("Board: %s", str(state.board))
guesses.append(guess)
iteration += 1
print("Board:", str(state.board))
print(f"Answer: {answer}")
print(f"Iterations: {iteration - 1}")
print(f"Guesses: {str(', '.join(guesses))}")
if __name__ == "__main__":