62 lines
1.6 KiB
Python
62 lines
1.6 KiB
Python
"""Feedback simulation matching Wordle's own coloring algorithm."""
|
|
|
|
from enum import IntEnum
|
|
|
|
|
|
class Clue(IntEnum):
|
|
GREY = 0
|
|
YELLOW = 1
|
|
GREEN = 2
|
|
|
|
|
|
CLUE_CHARS = {Clue.GREEN: "G", Clue.YELLOW: "Y", Clue.GREY: "B"}
|
|
|
|
# Accept a few common shorthand alphabets when parsing user input.
|
|
_CHAR_TO_CLUE = {
|
|
"G": Clue.GREEN, "2": Clue.GREEN,
|
|
"Y": Clue.YELLOW, "1": Clue.YELLOW,
|
|
"B": Clue.GREY, "0": Clue.GREY, ".": Clue.GREY, "-": Clue.GREY,
|
|
}
|
|
|
|
|
|
def score_guess(guess: str, answer: str) -> tuple:
|
|
"""Return the clue Wordle would show for `guess` against the true `answer`.
|
|
|
|
Two-pass algorithm: greens first, then yellows against leftover letters,
|
|
so duplicate letters are handled the same way the real game does.
|
|
"""
|
|
n = len(guess)
|
|
result = [Clue.GREY] * n
|
|
remaining = list(answer)
|
|
|
|
for i in range(n):
|
|
if guess[i] == remaining[i]:
|
|
result[i] = Clue.GREEN
|
|
remaining[i] = None
|
|
|
|
for i in range(n):
|
|
if result[i] == Clue.GREEN:
|
|
continue
|
|
letter = guess[i]
|
|
if letter in remaining:
|
|
result[i] = Clue.YELLOW
|
|
remaining[remaining.index(letter)] = None
|
|
|
|
return tuple(result)
|
|
|
|
|
|
def pattern_to_str(pattern) -> str:
|
|
return "".join(CLUE_CHARS[c] for c in pattern)
|
|
|
|
|
|
def pattern_to_digits(pattern) -> str:
|
|
return "".join(str(int(c)) for c in pattern)
|
|
|
|
|
|
def str_to_pattern(s: str) -> tuple:
|
|
s = s.strip()
|
|
try:
|
|
return tuple(_CHAR_TO_CLUE[ch.upper()] for ch in s)
|
|
except KeyError as e:
|
|
raise ValueError(f"Unrecognized clue character: {e.args[0]!r}") from None
|