Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import re | |
| from collections import defaultdict | |
| # --- Core Wordle Solver Logic --- | |
| def load_words(): | |
| """Load 5-letter words from the words.txt file.""" | |
| words = [] | |
| try: | |
| with open('words.txt', 'r') as f: | |
| for line in f: | |
| word = line.strip().lower() | |
| if len(word) == 5 and word.isalpha(): | |
| words.append(word) | |
| except FileNotFoundError: | |
| print("FATAL ERROR: words.txt not found. Please create it in the same directory.") | |
| return None | |
| return words | |
| WORD_LIST = load_words() | |
| # --- Advanced Parsing Logic (to handle flexible input) --- | |
| def parse_guess(line): | |
| """Parse a single line of a guess, like 'π₯ π¨ π₯ π₯ π© πππ‘ππ' or 'IRATE π¨π₯π¨π₯π©'.""" | |
| # Mapping for special bold characters to regular ASCII | |
| math_bold_to_regular = { | |
| 'π': 'A', 'π': 'B', 'π': 'C', 'π': 'D', 'π': 'E', 'π': 'F', 'π': 'G', 'π': 'H', | |
| 'π': 'I', 'π': 'J', 'π': 'K', 'π': 'L', 'π ': 'M', 'π‘': 'N', 'π’': 'O', 'π£': 'P', | |
| 'π€': 'Q', 'π₯': 'R', 'π¦': 'S', 'π§': 'T', 'π¨': 'U', 'π©': 'V', 'πͺ': 'W', 'π«': 'X', | |
| 'π¬': 'Y', 'π': 'Z' | |
| } | |
| def convert_math_bold_to_regular(text): | |
| return ''.join(math_bold_to_regular.get(char, char) for char in text) | |
| line = convert_math_bold_to_regular(line.upper()) | |
| # Pattern 1: Emojis first, then word (e.g., π₯ π¨ ... π© HINGE) | |
| pattern1 = r'([\s]*[π₯π¨π©β¬][\s]*){5}([\s]*[A-Z]{5})' | |
| match1 = re.search(pattern1, line) | |
| if match1: | |
| emojis = re.sub(r'\s+', '', match1.group(0).replace(match1.group(2).strip(), '')) | |
| word = match1.group(2).strip().lower() | |
| return word, emojis | |
| # Pattern 2: Word first, then emojis (e.g., HINGE π₯ π¨ ... π©) | |
| pattern2 = r'([A-Z]{5}[\s]*)' | |
| match2 = re.search(pattern2, line) | |
| if match2: | |
| word = match2.group(1).strip().lower() | |
| emojis = re.sub(r'\s+', '', line.replace(match2.group(1), '')) | |
| if len(emojis) == 5: | |
| return word, emojis | |
| return None, None | |
| def parse_multiline_input(raw_text): | |
| """Parses a multi-line string of guesses into a list of clues.""" | |
| clues = [] | |
| errors = [] | |
| for i, line in enumerate(raw_text.strip().split('\n'), 1): | |
| if not line.strip(): | |
| continue | |
| word, emojis = parse_guess(line) | |
| if word and emojis and len(word) == 5 and len(emojis) == 5: | |
| clues.append((word, emojis)) | |
| else: | |
| errors.append(f"Line {i}: Could not parse -> '{line}'") | |
| return clues, errors | |
| # --- Core Filtering and Suggestion Logic (Unchanged) --- | |
| def word_matches_clue(word, guess_word, emoji_result): | |
| for i, (guess_char, emoji) in enumerate(zip(guess_word, emoji_result)): | |
| if emoji == 'π©' and word[i] != guess_char: return False | |
| for i, (guess_char, emoji) in enumerate(zip(guess_word, emoji_result)): | |
| if emoji == 'π¨' and (guess_char not in word or word[i] == guess_char): return False | |
| for i, (guess_char, emoji) in enumerate(zip(guess_word, emoji_result)): | |
| if emoji in ['π₯', 'β¬']: | |
| clue_count = sum(1 for j, gc in enumerate(guess_word) if gc == guess_char and emoji_result[j] in ['π©', 'π¨']) | |
| if word.count(guess_char) > clue_count: return False | |
| return True | |
| def filter_words_by_clues(words, clues): | |
| if not clues: return words | |
| remaining = list(words) | |
| for guess, emoji in clues: | |
| remaining = [w for w in remaining if word_matches_clue(w, guess, emoji)] | |
| return remaining | |
| def get_letter_frequency(words): | |
| freq = defaultdict(int) | |
| for word in words: | |
| for char in set(word): freq[char] += 1 | |
| return freq | |
| def score_word(word, letter_freq): | |
| return sum(letter_freq[char] for char in set(word)) | |
| def get_best_guess(words, all_words): | |
| if not words: return None, "No words match the clues." | |
| if len(words) <= 2: return words[0], f"{len(words)} possible word(s) remain." | |
| letter_freq = get_letter_frequency(words) | |
| best_word = max(all_words, key=lambda w: score_word(w, letter_freq)) | |
| return best_word, f"{len(words)} possible words remain." | |
| # --- Gradio Interface Functions --- | |
| def process_guesses(raw_text_input): | |
| """Handles the 'Submit Guesses' button click.""" | |
| parsed_clues, errors = parse_multiline_input(raw_text_input) | |
| if errors: | |
| error_msg = "\n".join(errors) | |
| history_md = format_clues_history([]) | |
| return [], gr.update(visible=True, value=error_msg), "", "Parsing Error", history_md | |
| if not parsed_clues: | |
| error_msg = "Input is empty or invalid. Please check the format." | |
| history_md = format_clues_history([]) | |
| return [], gr.update(visible=True, value=error_msg), "", "Invalid Input", history_md | |
| remaining_words = filter_words_by_clues(WORD_LIST, parsed_clues) | |
| best_guess, status_text = get_best_guess(remaining_words, WORD_LIST) | |
| history_md = format_clues_history(parsed_clues) | |
| if not remaining_words: | |
| best_guess_str = "π" | |
| status_text = "No words match your clues. Check for typos and try again." | |
| elif len(remaining_words) == 1: | |
| best_guess_str = remaining_words[0].upper() | |
| status_text = "π Found it! This is the only possible word." | |
| else: | |
| best_guess_str = best_guess.upper() | |
| return parsed_clues, gr.update(visible=False, value=""), best_guess_str, status_text, history_md | |
| def get_other_suggestions(current_clues_list): | |
| """Provides alternative word suggestions based on the current parsed clues.""" | |
| if not current_clues_list: | |
| return "Enter at least one guess to get suggestions." | |
| remaining_words = filter_words_by_clues(WORD_LIST, current_clues_list) | |
| if not remaining_words: return "No words match the current clues." | |
| if len(remaining_words) == 1: return f"Only one word remains: **{remaining_words[0].upper()}**" | |
| letter_freq = get_letter_frequency(remaining_words) | |
| scored_words = sorted([(word, score_word(word, letter_freq)) for word in remaining_words], key=lambda x: x[1], reverse=True) | |
| suggestions_md = f"### Top Suggestions ({len(remaining_words)} words remain)\n" | |
| suggestions_md += "\n".join([f"- **{word.upper()}**" for word, _ in scored_words[:10]]) | |
| if len(scored_words) > 10: | |
| suggestions_md += f"\n\n...and {len(scored_words) - 10} more." | |
| return suggestions_md | |
| def reset_session(): | |
| """Clears all inputs, outputs, and the session state.""" | |
| return [], "", "", "", "No guesses yet. Enter your game history above.", "", gr.update(visible=False, value="") | |
| def format_clues_history(clues_list): | |
| """Converts the list of clues into a markdown string for display.""" | |
| if not clues_list: | |
| return "No guesses yet. Enter your game history above." | |
| md = "### Your Parsed Guesses\n" | |
| for word, emojis in clues_list: | |
| md += f"- `{word.upper()}` -> {emojis}\n" | |
| return md | |
| # --- Gradio UI Layout --- | |
| with gr.Blocks(theme=gr.themes.Soft(), title="Wordle Solver Bot") as demo: | |
| session_clues = gr.State([]) | |
| gr.Markdown("# π§© Wordle Solver Bot (Single Input)") | |
| gr.Markdown("Paste your entire game history into the text box below. Each line should contain one guess and its results.") | |
| #[Image of a Wordle game grid] | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| guesses_input = gr.Textbox( | |
| label="Enter Guesses (One Per Line)", | |
| lines=5, | |
| placeholder="Example:\nπ₯ π¨ π₯ π₯ π© HINGE\nπ¨ π₯ π¨ π₯ π© IRATE\n..." | |
| ) | |
| error_box = gr.Textbox(label="Error", visible=False, interactive=False, lines=3) | |
| with gr.Row(): | |
| submit_button = gr.Button("Find Best Guess", variant="primary") | |
| other_button = gr.Button("Get Other Suggestions") | |
| reset_button = gr.Button("Reset Session", variant="stop") | |
| with gr.Column(scale=3): | |
| best_guess_output = gr.Textbox(label="π‘ Best Next Guess", interactive=False) | |
| status_output = gr.Textbox(label="π Status", interactive=False) | |
| with gr.Row(): | |
| history_output = gr.Markdown("No guesses yet. Enter your game history above.") | |
| other_suggestions_output = gr.Markdown("") | |
| # --- Event Handlers --- | |
| submit_button.click( | |
| fn=process_guesses, | |
| inputs=[guesses_input], | |
| outputs=[session_clues, error_box, best_guess_output, status_output, history_output] | |
| ) | |
| other_button.click( | |
| fn=get_other_suggestions, | |
| inputs=[session_clues], | |
| outputs=[other_suggestions_output] | |
| ) | |
| reset_button.click( | |
| fn=reset_session, | |
| inputs=[], | |
| outputs=[session_clues, guesses_input, best_guess_output, status_output, history_output, other_suggestions_output, error_box] | |
| ) | |
| if __name__ == "__main__": | |
| if not WORD_LIST: | |
| print("Could not start the application because 'words.txt' is missing or empty.") | |
| else: | |
| print("Starting Gradio app... Access it at the URL provided below.") | |
| demo.launch() | |