File size: 11,491 Bytes
a16932a
 
78d662e
a16932a
4f625d4
 
 
a6975b2
 
 
4f625d4
a6975b2
 
4f625d4
78d662e
 
4f625d4
78d662e
a6975b2
78d662e
 
 
 
 
6ca84dc
 
 
 
78d662e
 
a6975b2
 
4f625d4
a6975b2
 
 
 
 
4f625d4
f449a3a
a6975b2
 
 
 
4f625d4
 
d786c85
 
 
 
 
4f625d4
09427c9
f0df64e
 
 
 
09427c9
 
 
 
 
 
f0df64e
1a984ea
f449a3a
1a984ea
 
a6975b2
 
 
 
 
 
 
b7b95df
 
 
 
 
f449a3a
b7b95df
 
a6975b2
 
 
 
082223e
a6975b2
 
f449a3a
583f150
 
 
 
 
 
b7b95df
a6975b2
f449a3a
 
 
 
a6975b2
 
 
 
 
 
 
 
f0df64e
09427c9
a6975b2
 
 
 
 
 
09427c9
f449a3a
 
09427c9
4f625d4
 
 
09427c9
c1c3381
 
850b1df
4f625d4
c1c3381
4f625d4
 
 
 
 
 
f449a3a
4f625d4
 
 
a6975b2
09427c9
 
d786c85
 
4f625d4
d786c85
4f625d4
 
09427c9
4f625d4
09427c9
4f625d4
09427c9
4f625d4
 
 
 
 
 
a6975b2
6829252
09427c9
 
 
 
 
 
 
4f625d4
 
a6975b2
 
 
09427c9
a6975b2
 
4f625d4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a6975b2
4f625d4
 
 
a6975b2
 
4f625d4
 
 
 
 
 
 
 
 
 
 
 
a6975b2
 
 
 
 
 
 
 
 
 
 
 
 
09427c9
a6975b2
082223e
a6975b2
 
 
 
 
 
 
 
 
 
 
 
4f625d4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f449a3a
 
 
4f625d4
a6975b2
 
 
 
 
f449a3a
d786c85
a6975b2
 
 
 
d786c85
a6975b2
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
# CLAUDE

Wrdler v0.2.12

# Wrdler - Project Context

## Project Overview
Wrdler is a simplified vocabulary puzzle game based on BattleWords:
- **Python project** (Streamlit, Python 3.12.8)
- **8x6 grid** (8 columns Γ— 6 rows, one word per row, horizontal only)
- **No scope/radar visualization**
- **2 free letter guesses at game start** (all instances revealed)
- **Word composition:** 2 four-letter, 2 five-letter, 2 six-letter words per puzzle

**Current Version:** 0.2.12
**Last Updated:** 2025-12-21
**Repository:** https://github.com/Oncorporation/Wrdler.git
**Branch:** main

## Recent Changes (v0.2.12)
- Layout changes for improved usability
- Fixed static spinner graphic and favicon
- Background enable/disable toggles improved
- Sidebar disabled for streamlined UI
- Minor grid layout tightening
- Added a couple words to classic.txt
- HF server fix
- Documentation updates

## Current Features (v0.2.12)

### Core Gameplay
- 8x6 grid with 6 hidden words (one per row, horizontal only)
- Players choose 2 free letters at start; all instances are revealed
- Click cells to reveal letters or empty spaces
- Guess words for points (word length + bonus for unrevealed letters)
- Game ends when all words guessed or all word letters are revealed
- Incorrect guess history display (toggleable, default enabled)
- 10 incorrect guess limit per game
- **All leaderboard and challenge submissions use the latest st.session_state, including challenge overrides.**

### Game Modes
1. **Classic Mode:** Allows consecutive guessing after correct answers
2. **Too Easy Mode:** Single guess per reveal

### Scoring Tiers
- **Legendary:** 45+ points
- **Fantastic:** 42-44 points
- **Great:** 39-41 points
- **Good:** 35-38 points
- **Keep practicing:** < 35 points

### Settings Page & Management
- All game settings moved from sidebar to a dedicated Settings page (`?page=settings`)
- Accessible via footer navigation (`βš™οΈ Settings` link)
- Local JSON-based settings persistence in `wrdler/settings/`
- Latest settings auto-loaded on startup
- Enhanced settings management: create, update, rename, and delete settings files
- Split "Save Settings" into "Create Settings" and "Update Settings" actions
- Improved settings loading and user feedback
- Default sound effects enabled in settings
- New default configuration: `classic-classic-full_sound_free_letters.json`
- Deprecated configuration removed: `classic-classic-2.json`

### Word List Management
- Sort and filter word lists (filter using `assets/filter.txt` blocklist)
- Dialog display of removed words after filtering

### Challenge Mode & Remote Storage
- Short URL-based challenge sharing via `?game_id=<sid>`
- Each player gets different random words from same wordlist
- Multi-user challenge leaderboards (top 5 display)
- Remote storage via HuggingFace datasets
- Word list difficulty calculation
- "Show Challenge Share Links" toggle (default OFF)
- **Integration:**
  - Automatic submission after game completion (opt-in via game over popup)
  - Challenge scores also contribute to daily/weekly leaderboards
  - Source tracking via `source_challenge_id` field
  - Unified JSON format with `entry_type` field (daily/weekly/challenge)
- **Challenge settings override defaults on load, and all submissions use the current session state.**

**Access:** 'Leaderboard' link in the footer navigation at the bottom of the page

### Daily & Weekly Leaderboards
- **Settings-Based Separation:** Each unique settings combo creates separate leaderboard
  - Settings: `game_mode`, `wordlist_source`, `show_incorrect_guesses`, `enable_free_letters`, `puzzle_options` (spacer, may_overlap)
- **Auto Score Submission:** Checks qualification for top 25 after game completion
- **Storage:** Folder-based discovery at `games/leaderboards/{daily|weekly}/{period}/{file_id}/settings.json`
- **File ID Format:** `{wordlist_source}-{game_mode}-{sequence}` (e.g., `classic-classic-0`)
- **Leaderboard Page:** Four tabs (Today, Daily, Weekly, History) accessible via `?page=today|daily|weekly|history` using query parameter routing and custom navigation links (not Streamlit native tabs)
- Leaderboard files use UTC for all period boundaries.
- When displaying daily leaderboards, show the UTC period as a PST date range.
- Example: For UTC file date 2025-12-08, display:
  2025-12-08 00:00:00 UTC to 2025-12-08 23:59:59 UTC
  and
  2025-12-07 16:00:00 PST to 2025-12-08 15:59:59 PST
  The leaderboard expander label should show: `Mon, Dec 08, 2025 4:00 PM PST – Tue, Dec 09, 2025 3:59:59 PM PST [settings badge]`

### Game Over Dialog & Leaderboard Integration
- Game over dialog now integrates leaderboard submission and displays qualification results (rankings)
- After submitting your score, the dialog will show if you qualified for the daily or weekly leaderboard and your rank

### AI Word Generation
- Topic-based word list generation via HuggingFace Spaces or local transformers
- Automatic word saving (max 1000 words per file)
- Retry mechanism (up to 3 attempts) for insufficient word counts
- Fallback to dictionary words if AI unavailable

### Audio & Visuals
- Ocean-themed gradient background with wave animations
- Toggleable background music with volume control (configured via Settings page, played globally)
- Sound effects (hit/miss/correct/incorrect) with volume control (configured via Settings page, enabled by default)

### PWA Support
- Installable as Progressive Web App on desktop and mobile
- Service worker for offline caching of static assets
- Works offline for basic functionality

### Footer Navigation
- Navigation links to Leaderboard, Play, and Settings pages are in the footer (not the sidebar)
- Footer navigation prevents reloading active pages

## Technical Architecture

### Technology Stack
- **Framework:** Streamlit 1.52.1
- **Language:** Python 3.12.8 (requires >=3.12, <3.13)
- **Remote Storage:** huggingface_hub (>=0.20.0)
- **AI Generation:** transformers, gradio_client
- **Testing:** Pytest
- **Package Manager:** UV or pip

### Project Structure
```
wrdler/
β”œβ”€β”€ app.py                    # Streamlit entry point
β”œβ”€β”€ wrdler/                   # Main package
β”‚   β”œβ”€β”€ __init__.py          # Version: 0.2.10
β”‚   β”œβ”€β”€ models.py            # Data models (Coord, Word, Puzzle, GameState)
β”‚   β”œβ”€β”€ generator.py         # Puzzle generation with deterministic seeding
β”‚   β”œβ”€β”€ logic.py             # Game mechanics (reveal, guess, scoring)
β”‚   β”œβ”€β”€ ui.py                # Streamlit UI with query param routing
β”‚   β”œβ”€β”€ oauth.py             # HuggingFace OAuth utilities
β”‚   β”œβ”€β”€ settings_page.py     # Settings page UI (enhanced)
β”‚   β”œβ”€β”€ leaderboard.py       # Leaderboard system (daily/weekly)
β”‚   β”œβ”€β”€ leaderboard_page.py  # Leaderboard UI page
β”‚   β”œβ”€β”€ word_loader.py       # Word list management
β”‚   β”œβ”€β”€ word_loader_ai.py    # AI word generation
β”‚   β”œβ”€β”€ game_storage.py      # HF game storage wrapper
β”‚   β”œβ”€β”€ version_info.py      # Version display
β”‚   β”œβ”€β”€ modules/             # Shared utility modules
β”‚   β”‚   β”œβ”€β”€ __init__.py      # Module exports
β”‚   β”‚   β”œβ”€β”€ storage.py       # HuggingFace storage & URL shortener
β”‚   β”‚   β”œβ”€β”€ storage.md       # Storage module documentation
β”‚   β”‚   β”œβ”€β”€ constants.py     # Storage-related constants
β”‚   β”‚   └── file_utils.py    # File utility functions
β”‚   └── words/               # Word list files
β”‚       β”œβ”€β”€ classic.txt      # Default word list
β”‚       β”œβ”€β”€ fourth_grade.txt # Elementary word list
β”œβ”€β”€ tests/                   # Unit tests
β”œβ”€β”€ specs/                   # Documentation
β”œβ”€β”€ static/                  # PWA assets (manifest.json, service-worker.js)
β”œβ”€β”€ .env                     # Environment variables (HF credentials)
β”œβ”€β”€ pyproject.toml           # Project metadata
β”œβ”€β”€ requirements.txt         # Dependencies
β”œβ”€β”€ uv.lock                  # UV lock file
β”œβ”€β”€ Dockerfile               # Container deployment
β”œβ”€β”€ README.md                # User-facing documentation
β”œβ”€β”€ CLAUDE.md                # This file - project context for Claude
β”œβ”€β”€ GAMEPLAY_GUIDE.md        # User guide with tips and strategies
```

### Page Navigation System
Uses **query parameter-based routing** (NOT Streamlit multi-page):
- `?page=today|daily|weekly|history` β†’ Leaderboard pages
- `?page=settings` β†’ Settings page
- `?game_id=<sid>` β†’ Challenge mode
- No query params β†’ Main game page

## Data Models

### Core Classes
```python
@dataclass
class Coord:
    x: int  # row, 0-based
    y: int  # col, 0-based

@dataclass
class Word:
    text: str
    start: Coord
    direction: Direction  # "H" or "V"
    cells: List[Coord]

@dataclass
class Puzzle:
    words: List[Word]
    may_overlap: bool
    spacer: int
    uid: str  # Unique identifier

@dataclass
class GameState:
    grid_rows: int  # 6 for Wrdler
    grid_cols: int  # 8 for Wrdler
    puzzle: Puzzle
    revealed: Set[Coord]
    guessed: Set[str]
    score: int
    last_action: str
    can_guess: bool
    game_mode: str
    points_by_word: Dict[str, int]
    start_time: Optional[datetime]
    end_time: Optional[datetime]
```

## Environment Variables

Create a `.env` file in the project root:

```bash
# Challenge Mode & Leaderboards (Remote Storage)
HF_API_TOKEN=hf_xxxxxxxxxxxxxxxxxxxxx  # HuggingFace API token with write access
HF_REPO_ID=YourUsername/YourRepo       # Dataset repo for challenge storage

# AI Word Generation
USE_HF_WORDS=false                     # Enable HF Space API for word generation
HF_WORD_LIST_REPO_ID=YourUsername/WordRepo  # Dataset repo for AI word lists

# OAuth Admin Access
ADMIN_USERS=username1,username2        # Comma-separated list of admin usernames
MAX_DISPLAY_ENTRIES=25                 # Max leaderboard entries to display (default: 25)
```

### HF_REPO_ID Structure
```
HF_REPO_ID/
β”œβ”€β”€ shortener.json                   # URL shortener mappings
β”œβ”€β”€ games/{uid}/settings.json        # Challenge data (entry_type: "challenge")
└── games/leaderboards/
    β”œβ”€β”€ daily/{YYYY-MM-DD}/{file_id}/settings.json    # Daily leaderboards
    └── weekly/{YYYY-Www}/{file_id}/settings.json     # Weekly leaderboards
```

## Development Workflow

### Running Locally
```bash
# Install dependencies
uv pip install -r requirements.txt --link-mode=copy

# Run app
streamlit run app.py
```

### Testing
```bash
pytest tests/
```

## OAuth-Protected Settings Page
- Settings page at `?page=settings`, protected by HuggingFace OAuth (admin-only access)
- Uses query parameter routing and checks admin access via `ADMIN_USERS` env var

## Technical Notes
- **8Γ—6 grid:** `grid_rows=6`, `grid_cols=8`
- **Horizontal-only placement:** One word per row
- **Query param routing:** All pages use `?page=<name>` system
- **Session state management:** Heavy use of `st.session_state`
- **All leaderboard and challenge submissions use the latest st.session_state, including challenge overrides.**

## Deployment Platforms
1. **HuggingFace Spaces** (Primary) - Dockerfile deployment with OAuth support
2. **Local Development** - Streamlit run
3. **Docker** - Containerized deployment

## Git Configuration
- **Current Branch:** AI (working branch)
- **Main Branch:** main
- **Remotes:**
  - origin: https://github.com/Oncorporation/Wrdler.git
  - Hugging: https://huggingface.co/spaces/Surn/Wrdler