sato2ru commited on
Commit
9ebea09
Β·
verified Β·
1 Parent(s): 40cd0a2

Upload app.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +37 -38
app.py CHANGED
@@ -1,13 +1,11 @@
1
-
2
  import json, math, torch, gradio as gr
3
  from collections import Counter
4
  import numpy as np
5
  from huggingface_hub import hf_hub_download
6
  import torch.nn as nn
7
 
8
- REPO_ID = "sato2ru/wordle-solver" # ← update this
9
 
10
- # ── Load assets from HF Hub ──────────────────────────────────────
11
  config = json.load(open(hf_hub_download(REPO_ID, "config.json")))
12
  ANSWERS = json.load(open(hf_hub_download(REPO_ID, "answers.json")))
13
  ALLOWED = json.load(open(hf_hub_download(REPO_ID, "allowed.json")))
@@ -19,7 +17,6 @@ OUTPUT_DIM = config["output_dim"]
19
  OPENING = config["opening_guess"]
20
  WIN_PATTERN = (2,2,2,2,2)
21
 
22
- # ── Model ────────────────────────────────────────────────────────
23
  class WordleNet(nn.Module):
24
  def __init__(self):
25
  super().__init__()
@@ -36,15 +33,17 @@ model = WordleNet()
36
  model.load_state_dict(torch.load(hf_hub_download(REPO_ID, "model_weights.pt"), map_location="cpu"))
37
  model.eval()
38
 
39
- # ── Helpers ──────────────────────────────────────────────────────
40
  def get_pattern(guess, answer):
41
  pattern = [0]*5
42
  counts = Counter(answer)
43
  for i in range(5):
44
- if guess[i] == answer[i]: pattern[i] = 2; counts[guess[i]] -= 1
 
 
45
  for i in range(5):
46
- if pattern[i] == 0 and counts.get(guess[i],0) > 0:
47
- pattern[i] = 1; counts[guess[i]] -= 1
 
48
  return tuple(pattern)
49
 
50
  def filter_words(words, guess, pattern):
@@ -63,26 +62,23 @@ def encode_board(history):
63
  return vec
64
 
65
  def model_suggest(history, possible):
66
- if len(possible) == 1: return possible[0]
67
- if not history: return OPENING
 
 
68
  state = torch.tensor(encode_board(history)).unsqueeze(0)
69
  with torch.no_grad():
70
  logits = model(state)[0]
71
  top5 = [ALLOWED[i] for i in logits.topk(5).indices.tolist()]
72
  return max(top5, key=lambda w: entropy_score(w, possible))
73
 
74
- # ── State ─────────────────────────────────────────────────────────
75
- def init_state():
76
- return {"possible": list(ANSWERS), "history": [], "done": False}
77
-
78
  def render_board(history):
79
  colours = {0: "⬜", 1: "🟨", 2: "🟩"}
80
  rows = []
81
  for word, pattern in history:
82
- tiles = " ".join(f"{colours[s]}{c.upper()}" for c, s in zip(word, pattern))
83
  rows.append(tiles)
84
- return "
85
- ".join(rows) if rows else "(no guesses yet)"
86
 
87
  def process_guess(guess_input, pattern_input, state):
88
  if state["done"]:
@@ -90,50 +86,53 @@ def process_guess(guess_input, pattern_input, state):
90
 
91
  guess = guess_input.strip().lower()
92
  if len(guess) != 5:
93
- return render_board(state["history"]), "⚠️ Guess must be 5 letters", state
94
  if len(pattern_input) != 5 or not all(c in "012" for c in pattern_input):
95
- return render_board(state["history"]), "⚠️ Pattern must be 5 digits (0/1/2)", state
96
 
97
  pattern = tuple(int(c) for c in pattern_input)
98
  state["history"].append((guess, pattern))
99
 
100
  if pattern == WIN_PATTERN:
101
  state["done"] = True
102
- msg = f"πŸŽ‰ Solved in {len(state["history"])} turns!"
103
- return render_board(state["history"]), msg, state
104
 
105
  state["possible"] = filter_words(state["possible"], guess, pattern)
 
106
  if not state["possible"]:
107
  state["done"] = True
108
- return render_board(state["history"]), "❌ No words left. Check your input.", state
109
 
110
  suggestion = model_suggest(state["history"], state["possible"])
111
- msg = f"Try: **{suggestion.upper()}** | {len(state["possible"])} words left"
 
112
  return render_board(state["history"]), msg, state
113
 
114
- def reset(_state):
115
- s = init_state()
116
- return render_board([]), f"Try: **{OPENING.upper()}** to start", s
 
 
 
117
 
118
- # ── Gradio UI ─────────────────────────────────────────────────────
119
  with gr.Blocks(title="Wordle Solver", theme=gr.themes.Monochrome()) as demo:
120
- gr.Markdown("# 🟩 Wordle Solver
121
- Entropy-trained neural network. Enter each guess + the colour pattern.")
122
- gr.Markdown("**Pattern key:** `0` = ⬜ grey · `1` = 🟨 yellow · `2` = 🟩 green")
123
 
124
- state = gr.State(init_state())
125
- board_out = gr.Textbox(label="Board", lines=7, interactive=False)
126
- msg_out = gr.Markdown(f"Try: **{OPENING.upper()}** to start")
127
 
128
  with gr.Row():
129
- guess_in = gr.Textbox(label="Your guess", placeholder="crane", max_lines=1)
130
- pattern_in = gr.Textbox(label="Pattern (5 digits)", placeholder="02100", max_lines=1)
131
 
132
  with gr.Row():
133
- submit_btn = gr.Button("Submit", variant="primary")
134
  reset_btn = gr.Button("Reset")
135
 
136
- submit_btn.click(process_guess, [guess_in, pattern_in, state], [board_out, msg_out, state])
137
- reset_btn.click(reset, [state], [board_out, msg_out, state])
138
 
139
  demo.launch()
 
 
1
  import json, math, torch, gradio as gr
2
  from collections import Counter
3
  import numpy as np
4
  from huggingface_hub import hf_hub_download
5
  import torch.nn as nn
6
 
7
+ REPO_ID = "sato2ru/wordle-solver"
8
 
 
9
  config = json.load(open(hf_hub_download(REPO_ID, "config.json")))
10
  ANSWERS = json.load(open(hf_hub_download(REPO_ID, "answers.json")))
11
  ALLOWED = json.load(open(hf_hub_download(REPO_ID, "allowed.json")))
 
17
  OPENING = config["opening_guess"]
18
  WIN_PATTERN = (2,2,2,2,2)
19
 
 
20
  class WordleNet(nn.Module):
21
  def __init__(self):
22
  super().__init__()
 
33
  model.load_state_dict(torch.load(hf_hub_download(REPO_ID, "model_weights.pt"), map_location="cpu"))
34
  model.eval()
35
 
 
36
  def get_pattern(guess, answer):
37
  pattern = [0]*5
38
  counts = Counter(answer)
39
  for i in range(5):
40
+ if guess[i] == answer[i]:
41
+ pattern[i] = 2
42
+ counts[guess[i]] -= 1
43
  for i in range(5):
44
+ if pattern[i] == 0 and counts.get(guess[i], 0) > 0:
45
+ pattern[i] = 1
46
+ counts[guess[i]] -= 1
47
  return tuple(pattern)
48
 
49
  def filter_words(words, guess, pattern):
 
62
  return vec
63
 
64
  def model_suggest(history, possible):
65
+ if len(possible) == 1:
66
+ return possible[0]
67
+ if not history:
68
+ return OPENING
69
  state = torch.tensor(encode_board(history)).unsqueeze(0)
70
  with torch.no_grad():
71
  logits = model(state)[0]
72
  top5 = [ALLOWED[i] for i in logits.topk(5).indices.tolist()]
73
  return max(top5, key=lambda w: entropy_score(w, possible))
74
 
 
 
 
 
75
  def render_board(history):
76
  colours = {0: "⬜", 1: "🟨", 2: "🟩"}
77
  rows = []
78
  for word, pattern in history:
79
+ tiles = " ".join(colours[s] + c.upper() for c, s in zip(word, pattern))
80
  rows.append(tiles)
81
+ return "\n".join(rows) if rows else "(no guesses yet)"
 
82
 
83
  def process_guess(guess_input, pattern_input, state):
84
  if state["done"]:
 
86
 
87
  guess = guess_input.strip().lower()
88
  if len(guess) != 5:
89
+ return render_board(state["history"]), "Word must be 5 letters", state
90
  if len(pattern_input) != 5 or not all(c in "012" for c in pattern_input):
91
+ return render_board(state["history"]), "Pattern must be 5 digits (0/1/2)", state
92
 
93
  pattern = tuple(int(c) for c in pattern_input)
94
  state["history"].append((guess, pattern))
95
 
96
  if pattern == WIN_PATTERN:
97
  state["done"] = True
98
+ turns = len(state["history"])
99
+ return render_board(state["history"]), f"Solved in {turns} turns!", state
100
 
101
  state["possible"] = filter_words(state["possible"], guess, pattern)
102
+
103
  if not state["possible"]:
104
  state["done"] = True
105
+ return render_board(state["history"]), "No words left. Check your input.", state
106
 
107
  suggestion = model_suggest(state["history"], state["possible"])
108
+ remaining = len(state["possible"])
109
+ msg = f"Try: {suggestion.upper()} | {remaining} words remaining"
110
  return render_board(state["history"]), msg, state
111
 
112
+ def reset_game(state):
113
+ new_state = {"possible": list(ANSWERS), "history": [], "done": False}
114
+ return render_board([]), f"Try: {OPENING.upper()} to start", new_state
115
+
116
+ def init_state():
117
+ return {"possible": list(ANSWERS), "history": [], "done": False}
118
 
 
119
  with gr.Blocks(title="Wordle Solver", theme=gr.themes.Monochrome()) as demo:
120
+ gr.Markdown("# Wordle Solver")
121
+ gr.Markdown("Enter each guess and the colour pattern. **0** = grey **1** = yellow **2** = green")
 
122
 
123
+ state = gr.State(init_state())
124
+ board = gr.Textbox(label="Board", lines=8, interactive=False)
125
+ message = gr.Markdown(f"Try: **{OPENING.upper()}** to start")
126
 
127
  with gr.Row():
128
+ guess_box = gr.Textbox(label="Your guess", placeholder="crane", max_lines=1)
129
+ pattern_box = gr.Textbox(label="Pattern", placeholder="00000", max_lines=1)
130
 
131
  with gr.Row():
132
+ submit_btn = gr.Button("Submit", variant="primary")
133
  reset_btn = gr.Button("Reset")
134
 
135
+ submit_btn.click(process_guess, [guess_box, pattern_box, state], [board, message, state])
136
+ reset_btn.click(reset_game, [state], [board, message, state])
137
 
138
  demo.launch()