wordle-solver / streamlit.py
santit96's picture
Add streamlit app to play wordle with ai and change play model base dir
38ae408
raw
history blame
4.57 kB
import random
import streamlit as st
from a3c.play import get_play_model_path, suggest
from wordle_env.wordle import get_env
from wordle_env.words import complete_vocabulary, target_vocabulary
def initial_config() -> None:
"""
Function to configure the app
"""
st.title("Wordle Game 🔠")
if "secret_word" not in st.session_state:
st.session_state["secret_word"] = random.choice(target_vocabulary)
if "guesses" not in st.session_state:
st.session_state["guesses"] = []
if "won" not in st.session_state:
st.session_state["won"] = False
def reset_state() -> None:
"""
Deletes all the game state to start again
"""
for key in st.session_state.keys():
del st.session_state[key]
def game_not_finished() -> bool:
"""
Condition that tells if the game is finished or not
It could because there are not more attempts or because the user won
"""
return not st.session_state.get("won") and len(st.session_state.guesses) < 6
def get_ai_guess() -> str:
"""
Call the AI to make a guess based on the game state
"""
env = get_env()
model_path = get_play_model_path()
words = list(map(lambda guess: guess[0], st.session_state.get("guesses")))
states = list(map(lambda guess: guess[2], st.session_state.get("guesses")))
ai_guess = suggest(env, words, states, model_path)
return ai_guess
def render() -> None:
"""
Function to render the app
"""
st.markdown(
"<style>.big-font {font-size:30px; display: inline-block; margin: 2px; padding: 10px; border-radius: 5px; text-align: center;}</style>",
unsafe_allow_html=True,
)
if game_not_finished():
guess_input = st.text_input("Enter your guess:", key="guess_input")
col1, col2 = st.columns([0.2, 1])
with col1:
st.button("Submit", on_click=lambda: process_guess(guess_input))
with col2:
st.button("Ask AI ✨", on_click=lambda: process_guess(get_ai_guess()))
elif st.session_state.get("won"):
st.success("Congratulations! You guessed the word!")
else:
st.error("Game Over! The word was: " + st.session_state.secret_word)
# Display guesses and feedback centered
display_guesses()
def process_guess(guess: str) -> None:
"""
Process the user guess and sets if the user won or not
"""
guess = guess.upper()
if not len(guess) == 5 or not guess.isalpha():
st.warning("Please enter a valid 5-letter word.")
elif guess not in complete_vocabulary:
st.warning("That is not a valid word.")
else:
if guess == st.session_state.secret_word:
st.session_state["won"] = True
feedback, state = check_guess(guess)
st.session_state.guesses.append((guess, feedback, state))
def display_guesses() -> None:
"""
Display the user guesses with the feedback in a pretty format
"""
for guess, feedback, _ in st.session_state.guesses:
guess_display = '<div style="text-align: center;">'
for i, letter in enumerate(guess):
color = feedback[i]
guess_display += f'<span class="big-font" style="background-color: {color};">{letter}</span>'
guess_display += "</div>"
st.markdown(guess_display, unsafe_allow_html=True)
if game_not_finished():
st.write("You have {} guesses left.".format(6 - len(st.session_state.guesses)))
else:
if st.button("Reset game"):
reset_state()
st.rerun()
def check_guess(guess: str) -> tuple[list[str], list[str]]:
"""
Takes a guess and returns the guess feedback
depending if the letters are in the right place or not
"""
feedback = ["grey"] * 5
word_list = list(st.session_state.secret_word)
processed_letters = []
state = ["0"] * 5
# Check for correct position (green)
for i, letter in enumerate(guess):
if letter == word_list[i]:
feedback[i] = "green"
state[i] = "2"
processed_letters.append(letter)
# Check for correct letter but wrong position (yellow)
for i, letter in enumerate(guess):
if (
feedback[i] == "grey"
and letter in word_list
and word_list.count(letter) > processed_letters.count(letter)
):
feedback[i] = "yellow"
state[i] = "1"
processed_letters.append(letter)
return feedback, state
# Main function
if __name__ == "__main__":
initial_config()
render()