Surn commited on
Commit
a6975b2
·
1 Parent(s): 256cf75

Documentation Update

Browse files
Files changed (5) hide show
  1. CLAUDE.md +175 -546
  2. README.md +141 -29
  3. specs/requirements.md +21 -126
  4. specs/specs.md +20 -14
  5. wrdler/oauth.py +229 -0
CLAUDE.md CHANGED
@@ -1,85 +1,31 @@
1
  # Wrdler - Project Context
2
 
3
  ## Project Overview
4
- Wrdler is a simplified vocabulary puzzle game based on BattleWords, with these key differences:
5
- - **8x6 grid** (instead of 12x12)
6
- - **One word per row, horizontal only** (no vertical words)
7
  - **No scope/radar visualization**
8
- - **2 free letter guesses at game start** (all instances of chosen letters are revealed)
 
9
 
10
- **Current Version:** 0.2.0
11
  **Repository:** https://github.com/Oncorporation/Wrdler.git
12
- **Live Demo:** [DEPLOYMENT_URL_HERE]
13
-
14
- ## Recent Changes
15
-
16
- **v0.2.0 (Current):**
17
- - ✅ Daily and Weekly Leaderboard System implemented
18
- - Settings-based leaderboard separation (unique leaderboards per settings combination)
19
- - Folder-based discovery without index.json
20
- - Top 20 displayed entries per leaderboard (can store more)
21
- - Automatic score qualification checking
22
- - Period-based organization: daily (`YYYY-MM-DD`), weekly (`YYYY-Www`)
23
- - File ID format: `{wordlist_source}-{game_mode}-{sequence}`
24
- - Unified JSON format with `entry_type` field (daily/weekly/challenge)
25
- - ✅ Leaderboard Page with full UI
26
- - Today tab: Current daily/weekly leaderboards with query param filtering
27
- - Daily tab: Last 7 days of daily leaderboards
28
- - Weekly tab: Current week leaderboard
29
- - History tab: Browse past leaderboards with selectors
30
- - Settings badges showing game configuration for each leaderboard
31
- - ✅ Automatic leaderboard submission on game completion
32
- - ✅ Integration with challenge mode (source_challenge_id tracking)
33
- - ✅ Enhanced storage.py with folder listing capabilities
34
-
35
- **v0.1.1 (Previous):**
36
- - ✅ Enhanced AI word generation logic with intelligent word saving
37
- - ✅ Automatic retry mechanism for insufficient word counts (up to 3 retries)
38
- - ✅ 1000-word file size limit to prevent dictionary bloat
39
- - ✅ Better new word detection (separates existing vs. new words before saving)
40
- - ✅ Improved HF Space API integration with graceful fallback to local models
41
- - ✅ Additional word generation when initial pass doesn't meet MIN_REQUIRED threshold
42
- - ✅ Enhanced logging for word generation pipeline visibility
43
-
44
- **v0.1.0 (Previous):**
45
- - ✅ Version updated to 0.1.0 across all files
46
- - ✅ AI word generation functionality added
47
- - ✅ Word list management enhanced with AI support
48
- - ✅ Utility modules integrated
49
- - ✅ Documentation synchronized across all files
50
- - ✅ Project structure validated and consistent
51
- - ✅ All Phase 1 requirements complete (7 sprints)
52
- - ✅ 100% test coverage (25/25 tests passing)
53
- - **Status: Ready for deployment!** 🚀
54
-
55
- **v0.0.2-0.0.3 (Previous):**
56
- - ✅ All 7 sprints complete (12.75 hours development time)
57
- - ✅ Core data models updated for rectangular 8×6 grid
58
- - ✅ Generator refactored for horizontal-only, one-per-row placement
59
- - ✅ Radar/scope visualization removed (~217 lines)
60
- - ✅ Free letter selection UI with circular green gradient buttons
61
- - ✅ Grid UI updated for 8×6 display with responsive layout
62
- - ✅ Comprehensive integration testing suite
63
- - ✅ Complete documentation (GAMEPLAY_GUIDE.md)
64
- - ✅ Fixed duplicate rendering call bug
65
- - ✅ PWA support with service worker and manifest
66
-
67
- ## Core Gameplay
68
  - 8x6 grid with 6 hidden words (one per row, horizontal only)
69
- - **Word composition:** 2 four-letter words, 2 five-letter words, 2 six-letter words
70
- - No scope/radar visualization
71
- - Players start by choosing 2 letters; all instances are revealed
72
- - Players click cells to reveal letters or empty spaces
73
- - After revealing a letter, players can guess words
74
- - Scoring: word length + bonus for unrevealed letters
75
- - Game ends when all words are guessed or all word letters are revealed
76
- - Incorrect guess history with optional display (enabled by default)
77
  - 10 incorrect guess limit per game
78
- - **✅ IMPLEMENTED:** Challenge Mode with game sharing via short URLs
79
- - **✅ IMPLEMENTED:** Remote storage via Hugging Face datasets
80
- - **✅ IMPLEMENTED:** Daily and Weekly Leaderboards (v0.2.0+)
81
- - **✅ IMPLEMENTED:** PWA install support (v0.2.28+)
82
- - **PLANNED:** Local persistent storage for game results and high scores (v0.3.0)
83
 
84
  ### Scoring Tiers
85
  - **Legendary:** 45+ points
@@ -88,15 +34,44 @@ Wrdler is a simplified vocabulary puzzle game based on BattleWords, with these k
88
  - **Good:** 35-38 points
89
  - **Keep practicing:** < 35 points
90
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  ## Technical Architecture
92
 
93
  ### Technology Stack
94
  - **Framework:** Streamlit 1.51.0
95
  - **Language:** Python 3.12.8 (requires >=3.12, <3.13)
96
- - **Visualization:** Matplotlib (>=3.8)
97
- - **HTTP Requests:** requests (>=2.31.0)
98
  - **Remote Storage:** huggingface_hub (>=0.20.0)
99
- - **Environment:** python-dotenv (>=1.0.0)
100
  - **AI Generation:** transformers, gradio_client
101
  - **Testing:** Pytest
102
  - **Package Manager:** UV or pip
@@ -110,14 +85,13 @@ wrdler/
110
  │ ├── models.py # Data models (Coord, Word, Puzzle, GameState)
111
  │ ├── generator.py # Puzzle generation with deterministic seeding
112
  │ ├── logic.py # Game mechanics (reveal, guess, scoring)
113
- │ ├── ui.py # Streamlit UI
 
 
114
  │ ├── leaderboard.py # Leaderboard system (daily/weekly)
115
  │ ├── leaderboard_page.py # Leaderboard UI page
116
  │ ├── word_loader.py # Word list management
117
  │ ├── word_loader_ai.py # AI word generation
118
- │ ├── audio.py # Background music system
119
- │ ├── sounds.py # Sound effects management
120
- │ ├── generate_sounds.py # Sound generation utilities
121
  │ ├── game_storage.py # HF game storage wrapper
122
  │ ├── version_info.py # Version display
123
  │ ├── modules/ # Shared utility modules (from OpenBadge)
@@ -129,19 +103,9 @@ wrdler/
129
  │ └── words/ # Word list files
130
  │ ├── classic.txt # Default word list
131
  │ ├── fourth_grade.txt # Elementary word list
132
- │ └── wordlist.txt # Full word list
133
  ├── tests/ # Unit tests
134
- │ ├── test_sprint6_integration.py # Comprehensive integration tests
135
- │ └── test_leaderboard.py # Leaderboard system tests
136
  ├── specs/ # Documentation
137
- ├── specs.md # Game specifications
138
- │ ├── requirements.md # Implementation requirements
139
- │ ├── leaderboard_spec.md # Leaderboard system specification
140
- │ └── wrdler_implementation_plan.md # Sprint planning summary
141
- ├── static/ # PWA assets
142
- │ ├── manifest.json # PWA manifest
143
- │ ├── service-worker.js # Service worker for offline caching
144
- │ └── icons/ # App icons
145
  ├── .env # Environment variables (HF credentials)
146
  ├── pyproject.toml # Project metadata
147
  ├── requirements.txt # Dependencies
@@ -150,162 +114,21 @@ wrdler/
150
  ├── README.md # User-facing documentation
151
  ├── CLAUDE.md # This file - project context for Claude
152
  ├── GAMEPLAY_GUIDE.md # User guide with tips and strategies
153
- └── RELEASE_NOTES_v0.1.0.md # Complete release documentation
 
 
 
 
 
 
154
  ```
155
 
156
- ## Key Features
157
-
158
- ### Game Modes
159
- 1. **Classic Mode:** Allows consecutive guessing after correct answers
160
- 2. **Too Easy Mode:** Single guess per reveal
161
-
162
- ### Audio & Visual Effects
163
- - **Background Music:** Toggleable ocean-themed background music with volume control
164
- - **Sound Effects:** Hit/miss/correct/incorrect guess sounds with volume control
165
- - **Ocean Theme:** Gradient animated background with wave effects
166
- - **Incorrect Guess History:** Visual display of wrong guesses (toggleable in settings)
167
-
168
- ### ✅ Daily and Weekly Leaderboards (v0.2.0+)
169
- - **Settings-Based Leaderboards:** Each unique combination of game settings creates a separate leaderboard
170
- - Settings that define a unique leaderboard: `game_mode`, `wordlist_source`, `show_incorrect_guesses`, `enable_free_letters`, `puzzle_options` (spacer, may_overlap)
171
- - Players compete only with others using identical settings
172
- - **Automatic Score Submission:** Every game completion checks qualification for both daily and weekly leaderboards
173
- - Top 20 scores displayed per leaderboard (can store more)
174
- - Sorting: score (desc) → time (asc) → difficulty (desc)
175
- - **Storage Structure:**
176
- - Folder-based discovery without index.json
177
- - Path: `games/leaderboards/{type}/{period}/{file_id}/settings.json`
178
- - File ID format: `{wordlist_source}-{game_mode}-{sequence}` (e.g., `classic-classic-0`)
179
- - Period IDs: daily (`YYYY-MM-DD`), weekly (`YYYY-Www` ISO week)
180
- - **Leaderboard Page:**
181
- - **Today Tab:** Current daily and weekly leaderboards (query param filtering via `?gidd=` and `?gidw=`)
182
- - **Daily Tab:** Last 7 days of daily leaderboards with expandable settings groups
183
- - **Weekly Tab:** Current week leaderboard with all settings combinations
184
- - **History Tab:** Searchable past leaderboards with date/week selectors
185
- - Settings badges display full game configuration for each leaderboard
186
- - **Integration:**
187
- - Source challenge tracking (tracks if score came from a challenge)
188
- - Unified JSON format with `entry_type` field ("daily", "weekly", "challenge")
189
- - Backward compatible with existing challenge system
190
-
191
- ### ✅ Challenge Mode & Remote Storage (v0.2.20+)
192
- - **Game ID System:** Short URL-based challenge sharing
193
- - Format: `?game_id=<sid>` in URL (shortened URL reference)
194
- - Each player gets different random words from the same wordlist
195
- - Enables fair challenges between players
196
- - Stored in Hugging Face dataset repository
197
- - **Remote Storage via HuggingFace Hub:**
198
- - Per-game settings JSON in `games/{uid}/settings.json`
199
- - Shortened URL mapping in `shortener.json`
200
- - Multi-user challenge leaderboards with score, time, and difficulty tracking
201
- - Results sorted by: highest score → fastest time → highest difficulty
202
- - **Challenge Features:**
203
- - Submit results to existing challenges
204
- - Create new challenges from any completed game
205
- - Top 5 leaderboard display in Challenge Mode banner
206
- - Optional player names (defaults to "Anonymous")
207
- - Word list difficulty calculation and display (v0.2.29)
208
- - "Show Challenge Share Links" toggle (default OFF) to control URL visibility (v0.2.27)
209
- - Challenge results can contribute to daily/weekly leaderboards (tracked via `source_challenge_id`)
210
-
211
- ### ✅ Progressive Web App (PWA) Support (v0.2.28+)
212
- - **PWA Installation:** App is installable as a Progressive Web App on desktop and mobile
213
- - Service worker for basic offline caching of static assets
214
- - Manifest.json with app metadata and icons
215
- - Platform-specific installation instructions in INSTALL_GUIDE.md
216
- - No gameplay logic changes required
217
- - Works offline for basic functionality
218
-
219
- ### ✅ AI Word Generation (v0.1.0+)
220
- - **AI-Powered Word Lists:** Generate custom word lists using Hugging Face Spaces or local transformers
221
- - **Topic-Based Generation:** Create words related to specific themes (e.g., "Ocean Life", "Space")
222
- - **Automatic Word Expansion:** New AI-generated words are saved to local files for future use
223
- - Intelligent word detection: separates existing dictionary words from new AI-generated words
224
- - Only new words are saved to prevent duplicates
225
- - Automatic retry mechanism (up to 3 attempts) if insufficient words generated
226
- - 1000-word file size limit prevents dictionary bloat
227
- - Files auto-sorted by length then alphabetically
228
- - **Fallback Support:** Gracefully falls back to dictionary words if AI is unavailable
229
- - **Word Distribution:** Ensures exactly 25 words each of lengths 4, 5, and 6 per topic
230
- - **Dual Generation Modes:**
231
- - **HF Space API** (primary): Uses Hugging Face Space for word generation when `USE_HF_WORDS=true`
232
- - **Local Models** (fallback): Falls back to local transformers models if HF Space unavailable
233
- - **Enhanced Logging:** Detailed pipeline visibility for debugging and monitoring
234
-
235
- ### PLANNED: Local Player Storage (v0.3.0)
236
- - **Local Storage:**
237
- - Location: `~/.wrdler/data/`
238
- - Files: `game_results.json`, `highscores.json`
239
- - Privacy-first: no cloud dependency, offline-capable
240
- - **Personal High Scores:**
241
- - Top 100 scores tracked automatically on local machine
242
- - Filterable by wordlist and game mode
243
- - High score sidebar expander display
244
- - **Player Statistics:**
245
- - Games played, average score, best score
246
- - Fastest completion time
247
- - Per-player history on local device
248
-
249
- ### Puzzle Generation
250
- - Horizontal-only word placement (one per row in 8×6 grid)
251
- - **Word length distribution:** Each puzzle contains exactly 2 four-letter words, 2 five-letter words, and 2 six-letter words
252
- - Deterministic seeding support for reproducible puzzles
253
- - No word spacing configuration (fixed one word per row)
254
- - Validation ensures no overlaps, proper bounds, correct word distribution
255
-
256
- ### UI Components (v0.0.2 - Implemented)
257
- - **Game Grid:** Interactive 8×6 button grid (48 cells) with responsive layout
258
- - **Free Letter Selection:** Circular green gradient buttons (2 at game start)
259
- - **Score Panel:** Real-time scoring with client-side JavaScript timer
260
- - **Settings Sidebar:**
261
- - Word list picker (classic, fourth_grade, wordlist, AI Generated)
262
- - Game mode selector (Classic, Too Easy)
263
- - Audio volume controls (music and effects separate)
264
- - Toggle for incorrect guess history display
265
- - Player name input
266
- - "Show Challenge Share Links" toggle (default OFF)
267
- - Enable/disable sound effects checkbox
268
- - Enable/disable background music checkbox
269
- - **Theme System:** Ocean gradient background with CSS animations
270
- - **Game Over Dialog:** Final score display with tier ranking
271
- - **Incorrect Guess Display:** Shows history of wrong guesses with count
272
- - **Challenge Mode UI:**
273
- - Challenge Mode banner with leaderboard (top 5 players)
274
- - Share challenge button in game over dialog
275
- - Submit result or create new challenge options
276
- - Word list difficulty display
277
- - **Leaderboard Page UI (v0.2.0):**
278
- - Four-tab navigation (Today, Daily, Weekly, History)
279
- - Settings badges showing game configuration
280
- - Filterable leaderboards by date/week
281
- - Rank display with emoji indicators (🥇🥈🥉)
282
- - Entry count and last updated timestamps
283
- - Challenge indicator badges (🎯) for challenge-sourced scores
284
- - **PLANNED (v0.3.0):** Local high scores expander and personal statistics display
285
-
286
- ### Development Status
287
-
288
- **Current Version:** 0.2.0 (Complete)
289
- - ✅ All 7 sprints complete
290
- - ✅ 100% test coverage (25/25 tests)
291
- - ✅ AI word generation implemented
292
- - ✅ Daily and Weekly Leaderboards fully functional
293
- - ✅ Settings-based leaderboard separation
294
- - ✅ Folder-based discovery system
295
- - ✅ Leaderboard page with full UI (Today/Daily/Weekly/History tabs)
296
- - ✅ Automatic score submission and qualification checking
297
- - ✅ Ready for production deployment
298
- - ✅ PWA support implemented
299
- - ✅ Challenge Mode fully functional
300
- - 📊 Development time: ~12.75 hours (sprints 1-7) + ~10 hours (leaderboards)
301
- - 📚 Complete documentation
302
-
303
- **Next Version:** v0.3.0 (Planned)
304
- - 📋 Local storage module (`wrdler/local_storage.py`)
305
- - 📋 Personal high score tracking (local JSON files)
306
- - 📋 High score sidebar UI display
307
- - 📋 Player statistics tracking and display
308
- - 📋 Leaderboard caching and performance optimizations
309
 
310
  ## Data Models
311
 
@@ -326,14 +149,14 @@ class Word:
326
  @dataclass
327
  class Puzzle:
328
  words: List[Word]
329
- radar: List[Coord]
330
  may_overlap: bool
331
  spacer: int
332
- uid: str # Unique identifier for caching
333
 
334
  @dataclass
335
  class GameState:
336
- grid_size: int
 
337
  puzzle: Puzzle
338
  revealed: Set[Coord]
339
  guessed: Set[str]
@@ -346,6 +169,33 @@ class GameState:
346
  end_time: Optional[datetime]
347
  ```
348
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
349
  ## Development Workflow
350
 
351
  ### Running Locally
@@ -354,318 +204,97 @@ class GameState:
354
  uv pip install -r requirements.txt --link-mode=copy
355
 
356
  # Run app
357
- uv run streamlit run app.py
358
- # or
359
  streamlit run app.py
360
  ```
361
 
362
- ### Docker Deployment
363
- ```bash
364
- docker build -t wrdler .
365
- docker run -p 8501:8501 wrdler
366
- ```
367
-
368
  ### Testing
369
  ```bash
370
  pytest tests/
371
  ```
372
 
373
- ### Environment Variables
374
-
375
- Wrdler uses environment variables for optional features. Create a `.env` file in the project root:
376
-
377
- ```bash
378
- # ============================================
379
- # Challenge Mode (Remote Storage)
380
- # ============================================
381
- # Required for Challenge Mode features (game sharing, leaderboards)
382
- HF_API_TOKEN=hf_xxxxxxxxxxxxxxxxxxxxx # or HF_TOKEN - HuggingFace API token with write access
383
- HF_REPO_ID=YourUsername/YourRepo # Target HF dataset repo for challenge storage
384
- SPACE_NAME=YourUsername/Wrdler # Your HF Space name (for deployment)
385
-
386
- # Optional
387
- CRYPTO_PK= # Reserved for future challenge signing features
388
-
389
- # ============================================
390
- # AI Word Generation
391
- # ============================================
392
- # Controls AI-powered word list generation
393
- USE_HF_WORDS=false # Enable HF Space API for word generation
394
- # - true: Use Hugging Face Space API (primary)
395
- # - false: Use local transformers models (fallback)
396
-
397
- HF_WORD_LIST_REPO_ID=YourUsername/WordRepo # HF dataset repo for AI-generated word lists
398
- # (Required if USE_HF_WORDS=true)
399
-
400
- IS_LOCAL= # Override environment detection
401
- # - true: Force local development mode
402
- # - false: Force production mode
403
- # - (empty): Auto-detect based on environment
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
404
  ```
405
 
406
- #### Getting Your HF_API_TOKEN
407
- 1. Go to https://huggingface.co/settings/tokens
408
- 2. Create a new token with `write` access
409
- 3. Add to `.env` file as `HF_API_TOKEN=hf_...`
410
-
411
- #### HF_REPO_ID Structure (Challenge Mode & Leaderboards)
412
- The dataset repository will contain:
413
- - `shortener.json` - Short URL mappings
414
- - `games/{uid}/settings.json` - Per-game challenge data (entry_type: "challenge")
415
- - `games/{uid}/result.json` - Optional detailed results
416
- - `games/leaderboards/daily/{date}/{file_id}/settings.json` - Daily leaderboards (entry_type: "daily")
417
- - `games/leaderboards/weekly/{week}/{file_id}/settings.json` - Weekly leaderboards (entry_type: "weekly")
418
-
419
- #### HF_WORD_LIST_REPO_ID Structure (AI Word Generation)
420
- The dataset repository will contain:
421
- - `words/{topic}.txt` - AI-generated word lists by topic
422
- - Auto-managed by `word_loader_ai.py`
423
- - Maximum 1000 words per file
424
- - Sorted by length then alphabetically
425
-
426
- #### Environment Variable Behavior
427
- **Without these variables:**
428
- - Challenge Mode features (sharing, leaderboards) will be disabled
429
- - AI word generation will fall back to local transformers models
430
- - Core game functionality remains fully operational
431
-
432
- **With these variables:**
433
- - Challenge Mode enables game sharing via short URLs
434
- - AI generation can use HF Space API (faster, more reliable)
435
- - Remote storage for multi-user leaderboards
436
-
437
- ## Git Configuration & Deployment
438
- **Current Branch:** main
439
- **Purpose:** Wrdler - vocabulary puzzle game with simplified 8x6 grid
440
- **Main Branch:** main
441
-
442
- ### Remotes
443
- - **ONCORP (origin):** https://github.com/Oncorporation/Wrdler.git (main repository)
444
- - **Hugging Face Spaces:** https://huggingface.co/spaces/[USERNAME]/Wrdler (live deployment)
445
-
446
- ### Deployment Platforms
447
- 1. **Hugging Face Spaces** (Primary) - Dockerfile deployment
448
- 2. **Local Development** - Streamlit run
449
- 3. **Docker** - Containerized deployment
450
- 4. **PWA** - Installable web app on any platform
451
-
452
- ## Sprint Summary (v0.0.2 - Complete)
453
-
454
- | Sprint | Description | Time | Tests | Status |
455
- |--------|-------------|------|-------|--------|
456
- | Sprint 1 | Core Data Models | 3h | 13/13 ✅ | Complete |
457
- | Sprint 2 | Puzzle Generator | 3h | 5/5 ✅ | Complete |
458
- | Sprint 3 | Remove Radar | 0.5h | N/A | Complete |
459
- | Sprint 4 | Free Letters UI | 2h | Manual ✅ | Complete |
460
- | Sprint 5 | Grid UI Updates | 1.25h | Syntax ✅ | Complete |
461
- | Sprint 6 | Integration Testing | 2h | 7/7 ✅ | Complete |
462
- | Sprint 7 | Documentation | 1h | N/A | Complete |
463
- | **Total** | **All Features** | **12.75h** | **25/25** | **Complete ✅** |
464
-
465
- **Status:** Ready for deployment! 🚀
466
-
467
- ## Post-v0.0.2 Enhancements
468
-
469
- ### v0.2.0 (Daily and Weekly Leaderboards)
470
- - Daily and weekly leaderboard system with settings-based separation
471
- - Folder-based discovery without index.json
472
- - Automatic score qualification and submission
473
- - Leaderboard page with four-tab navigation (Today, Daily, Weekly, History)
474
- - Settings badges and query parameter filtering
475
- - Integration with challenge mode (source_challenge_id tracking)
476
- - Unified JSON format with entry_type field
477
- - Enhanced storage.py with folder listing functions
478
-
479
- ### v0.1.1 (AI Word Generation Enhancement)
480
- - Enhanced AI word generation with intelligent word saving
481
- - Automatic retry mechanism for insufficient word counts (up to 3 retries)
482
- - 1000-word file size limit to prevent dictionary bloat
483
- - Improved new word detection (separates existing vs. new words)
484
- - Better HF Space API integration with fallback to local models
485
- - Additional word generation when MIN_REQUIRED threshold not met
486
- - Enhanced logging for generation pipeline visibility
487
-
488
- ### v0.1.0 (AI Word Generation)
489
- - AI-powered word list generation using Hugging Face Spaces
490
- - Topic-based word creation with automatic saving
491
- - Enhanced word list management with AI fallback
492
- - Utility modules integration for storage and file handling
493
-
494
- ### v0.2.20-0.2.29 (Challenge Mode & PWA)
495
- - Remote storage and game sharing via HF datasets
496
- - Multi-user leaderboards
497
- - PWA support with offline caching
498
- - Word list difficulty calculation
499
- - Privacy controls for challenge sharing
500
- - Sound effect and music system improvements
501
-
502
- ## Future Roadmap
503
-
504
- ### v0.3.0 (Next Phase)
505
- - Local persistent storage module (`~/.wrdler/data/`)
506
- - High score tracking and display
507
- - Player statistics tracking
508
- - Enhanced UI animations
509
-
510
- ### v1.0.0 (Long Term)
511
- - Multiple difficulty levels
512
- - Daily puzzle mode
513
- - Internationalization (i18n)
514
- - Performance optimizations
515
- - Advanced word list management
516
-
517
- ## Deployment Targets
518
- - **Hugging Face Spaces:** Primary deployment platform (Dockerfile-based)
519
- - **Docker:** Containerized deployment for any platform
520
- - **Local:** Development and testing
521
- - **PWA:** Installable on desktop and mobile devices
522
-
523
- ### Privacy & Data (v0.1.0)
524
- - **Challenge Mode:** Optional remote storage via Hugging Face datasets
525
- - Player names optional (defaults to "Anonymous")
526
- - Only stores: word lists, scores, times, game modes
527
- - No PII beyond optional player name
528
- - User controls URL visibility via "Show Challenge Share Links" toggle
529
- - **Local Storage (v0.3.0 - Planned):**
530
- - Location: `~/.wrdler/data/`
531
- - Privacy-first, offline-capable
532
- - Easy to delete
533
- - No cloud dependency
534
-
535
- ## Notes for Claude
536
-
537
- ### Technical Implementation
538
- - ✅ Project uses modern Python features (3.12.8)
539
- - ✅ Requires Python >=3.12, <3.13 per pyproject.toml
540
- - ✅ Heavy use of Streamlit session state for game state management
541
- - ✅ Client-side JavaScript for timer updates without page refresh
542
- - ✅ CSS heavily customized for ocean theme aesthetics
543
- - ✅ All file paths should be absolute when working in WSL environment
544
- - ✅ Game IDs are deterministic for consistent sharing
545
- - ✅ 8×6 rectangular grid (grid_rows=6, grid_cols=8)
546
- - ✅ Horizontal-only word placement (one per row)
547
- - ✅ Radar/scope visualization removed entirely
548
- - �� Free letter selection UI implemented with circular buttons
549
- - ✅ PWA injection via Docker build script (`inject-pwa-head.sh`)
550
- - ✅ AI word generation via `word_loader_ai.py` with Hugging Face integration
551
- - ✅ Utility modules provide reusable functions for storage and file ops
552
- - ✅ Leaderboard system with folder-based discovery and settings separation
553
- - ✅ Daily/Weekly period-based organization with ISO week numbering
554
- - ⚠️ **IMPORTANT: Use Python syntax (colons `:`) NOT JavaScript syntax (curly braces `{}`)**
555
- - Python: `if condition:` followed by indented block
556
- - NOT: `if condition {` - this is JavaScript/C-style syntax
557
- - Python: `try:` / `except Exception:` / `else:`
558
- - NOT: `try {` / `} catch (Exception) {` / `} else {`
559
-
560
- ## Leaderboard System Architecture
561
-
562
- ### Storage Organization
563
- ```
564
- HF_REPO_ID/
565
- ├── games/
566
- │ ├── {challenge_id}/ # Challenge storage
567
- │ │ └── settings.json # entry_type: "challenge"
568
- │ └── leaderboards/
569
- │ ├── daily/
570
- │ │ └── {YYYY-MM-DD}/ # Daily period folders
571
- │ │ └── {file_id}/ # Settings-specific leaderboard
572
- │ │ └── settings.json # entry_type: "daily"
573
- │ └── weekly/
574
- │ └── {YYYY-Www}/ # Weekly period folders (ISO week)
575
- │ └── {file_id}/ # Settings-specific leaderboard
576
- │ └── settings.json # entry_type: "weekly"
577
- └── shortener.json # URL shortener mappings
578
- ```
579
 
580
- ### File ID Format
581
- File IDs encode settings for fast discovery: `{wordlist_source}-{game_mode}-{sequence}`
 
 
 
 
 
582
 
583
- **Examples:**
584
- - `classic-classic-0` - Classic wordlist, classic mode, first instance
585
- - `easy-easy-0` - Easy wordlist, easy mode, first instance
586
- - `classic-too_easy-1` - Classic wordlist, "too easy" mode, second instance
587
 
588
- ### Settings Matching
589
- Two leaderboards are considered the same if ALL of the following match:
590
- - `game_mode` (classic, easy, too easy)
591
- - `wordlist_source` (sanitized: .txt removed, lowercase)
592
- - `show_incorrect_guesses` (boolean)
593
- - `enable_free_letters` (boolean)
594
- - `puzzle_options.spacer` (0-2)
595
- - `puzzle_options.may_overlap` (boolean)
596
 
597
- ### Data Models
 
 
598
 
599
- #### LeaderboardSettings
600
- ```python
601
- @dataclass
602
- class LeaderboardSettings:
603
- challenge_id: str # Period + file_id (e.g., "2025-01-27/classic-classic-0")
604
- entry_type: EntryType # "daily", "weekly", or "challenge"
605
- game_mode: str # "classic", "easy", "too easy"
606
- wordlist_source: str # Wordlist file
607
- show_incorrect_guesses: bool
608
- enable_free_letters: bool
609
- puzzle_options: Dict # spacer, may_overlap
610
- users: List[UserEntry] # Sorted entries
611
- max_display_entries: int # Default 20
612
- created_at: str
613
- version: str
614
- game_title: str
615
  ```
616
 
617
- #### UserEntry
618
- ```python
619
- @dataclass
620
- class UserEntry:
621
- uid: str # Unique entry ID
622
- username: str # Player name
623
- word_list: List[str] # 6 words played
624
- score: int # Final score
625
- time: int # Seconds to complete
626
- word_list_difficulty: float # Calculated difficulty
627
- timestamp: str # ISO 8601 timestamp
628
- source_challenge_id: Optional[str] # If from challenge, original ID
629
- ```
630
 
631
- ### Key Functions
632
-
633
- #### `leaderboard.py`
634
- - `submit_score_to_all_leaderboards()` - Main entry point for score submission
635
- - `find_matching_leaderboard()` - Find leaderboard by scanning folders
636
- - `create_or_get_leaderboard()` - Get or create a leaderboard
637
- - `check_qualification()` - Check if score qualifies for top 20
638
- - `load_leaderboard()` - Load specific leaderboard by file ID
639
- - `list_available_periods()` - List available dates/weeks from folders
640
- - `list_settings_for_period()` - List all settings combos for a period
641
- - `get_current_daily_id()` - Get today's period ID (YYYY-MM-DD)
642
- - `get_current_weekly_id()` - Get this week's period ID (YYYY-Www)
643
-
644
- #### `leaderboard_page.py`
645
- - `render_leaderboard_page()` - Main page rendering with tab navigation
646
- - `_render_today_tab()` - Current daily/weekly leaderboards
647
- - `_render_daily_tab()` - Last 7 days of daily leaderboards
648
- - `_render_weekly_tab()` - Current week leaderboard
649
- - `_render_history_tab()` - Historical leaderboard browser
650
- - `_render_leaderboard_table()` - Styled table rendering with rank emojis
651
-
652
- ### Discovery Strategy
653
- 1. **List period folders**: Scan `games/leaderboards/{type}/` for date/week folders
654
- 2. **List file_id folders**: For each period, scan for settings folders
655
- 3. **Filter by prefix**: Match file_ids starting with `{wordlist_source}-{game_mode}-`
656
- 4. **Load and verify**: Load `settings.json` to verify full settings match
657
-
658
- ### Period Boundaries
659
- - **Daily**: Resets at UTC midnight (00:00:00)
660
- - **Weekly**: Resets Monday at UTC midnight, uses ISO week numbering
661
- - Week 1 is the week containing the first Thursday of the year
662
- - Format: `YYYY-Www` (e.g., `2025-W04`)
663
-
664
- ### Leaderboard URL Format
665
- Query parameters for direct leaderboard access:
666
- - `?gidd={file_id}` - Show specific daily leaderboard
667
- - `?gidw={file_id}` - Show specific weekly leaderboard
668
- - `?page=today` - Show Today tab
669
- - `?page=daily` - Show Daily tab
670
- - `?page=weekly` - Show Weekly tab
671
- - `?page=history` - Show History tab
 
1
  # Wrdler - Project Context
2
 
3
  ## Project Overview
4
+ Wrdler is a simplified vocabulary puzzle game based on BattleWords:
5
+ - **Python project** (Streamlit, Python 3.12.8)
6
+ - **8x6 grid** (8 columns × 6 rows, one word per row, horizontal only)
7
  - **No scope/radar visualization**
8
+ - **2 free letter guesses at game start** (all instances revealed)
9
+ - **Word composition:** 2 four-letter, 2 five-letter, 2 six-letter words per puzzle
10
 
11
+ **Current Version:** 0.2.1
12
  **Repository:** https://github.com/Oncorporation/Wrdler.git
13
+ **Branch:** AI (working branch)
14
+
15
+ ## Current Features (v0.2.1)
16
+
17
+ ### Core Gameplay
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  - 8x6 grid with 6 hidden words (one per row, horizontal only)
19
+ - Players choose 2 free letters at start; all instances are revealed
20
+ - Click cells to reveal letters or empty spaces
21
+ - Guess words for points (word length + bonus for unrevealed letters)
22
+ - Game ends when all words guessed or all word letters are revealed
23
+ - Incorrect guess history display (toggleable, default enabled)
 
 
 
24
  - 10 incorrect guess limit per game
25
+
26
+ ### Game Modes
27
+ 1. **Classic Mode:** Allows consecutive guessing after correct answers
28
+ 2. **Too Easy Mode:** Single guess per reveal
 
29
 
30
  ### Scoring Tiers
31
  - **Legendary:** 45+ points
 
34
  - **Good:** 35-38 points
35
  - **Keep practicing:** < 35 points
36
 
37
+ ### Challenge Mode & Remote Storage
38
+ - Short URL-based challenge sharing via `?game_id=<sid>`
39
+ - Each player gets different random words from same wordlist
40
+ - Multi-user challenge leaderboards (top 5 display)
41
+ - Remote storage via HuggingFace datasets
42
+ - Word list difficulty calculation
43
+ - "Show Challenge Share Links" toggle (default OFF)
44
+
45
+ ### Daily & Weekly Leaderboards
46
+ - **Settings-Based Separation:** Each unique settings combo creates separate leaderboard
47
+ - Settings: `game_mode`, `wordlist_source`, `show_incorrect_guesses`, `enable_free_letters`, `puzzle_options` (spacer, may_overlap)
48
+ - **Auto Score Submission:** Checks qualification for top 20 after game completion
49
+ - **Storage:** Folder-based discovery at `games/leaderboards/{daily|weekly}/{period}/{file_id}/settings.json`
50
+ - **File ID Format:** `{wordlist_source}-{game_mode}-{sequence}` (e.g., `classic-classic-0`)
51
+ - **Leaderboard Page:** Four tabs (Today, Daily, Weekly, History) accessible via `?page=today|daily|weekly|history`
52
+
53
+ ### AI Word Generation
54
+ - Topic-based word list generation via HuggingFace Spaces or local transformers
55
+ - Automatic word saving (max 1000 words per file)
56
+ - Retry mechanism (up to 3 attempts) for insufficient word counts
57
+ - Fallback to dictionary words if AI unavailable
58
+
59
+ ### Audio & Visuals
60
+ - Ocean-themed gradient background with wave animations
61
+ - Toggleable background music with volume control
62
+ - Sound effects (hit/miss/correct/incorrect) with volume control
63
+
64
+ ### PWA Support
65
+ - Installable as Progressive Web App on desktop and mobile
66
+ - Service worker for offline caching of static assets
67
+ - Works offline for basic functionality
68
+
69
  ## Technical Architecture
70
 
71
  ### Technology Stack
72
  - **Framework:** Streamlit 1.51.0
73
  - **Language:** Python 3.12.8 (requires >=3.12, <3.13)
 
 
74
  - **Remote Storage:** huggingface_hub (>=0.20.0)
 
75
  - **AI Generation:** transformers, gradio_client
76
  - **Testing:** Pytest
77
  - **Package Manager:** UV or pip
 
85
  │ ├── models.py # Data models (Coord, Word, Puzzle, GameState)
86
  │ ├── generator.py # Puzzle generation with deterministic seeding
87
  │ ├── logic.py # Game mechanics (reveal, guess, scoring)
88
+ │ ├── ui.py # Streamlit UI with query param routing
89
+ │ ├── oauth.py # HuggingFace OAuth utilities (NEW)
90
+ │ ├── settings_page.py # Settings page UI (IN PROGRESS)
91
  │ ├── leaderboard.py # Leaderboard system (daily/weekly)
92
  │ ├── leaderboard_page.py # Leaderboard UI page
93
  │ ├── word_loader.py # Word list management
94
  │ ├── word_loader_ai.py # AI word generation
 
 
 
95
  │ ├── game_storage.py # HF game storage wrapper
96
  │ ├── version_info.py # Version display
97
  │ ├── modules/ # Shared utility modules (from OpenBadge)
 
103
  │ └── words/ # Word list files
104
  │ ├── classic.txt # Default word list
105
  │ ├── fourth_grade.txt # Elementary word list
 
106
  ├── tests/ # Unit tests
 
 
107
  ├── specs/ # Documentation
108
+ ├── static/ # PWA assets (manifest.json, service-worker.js)
 
 
 
 
 
 
 
109
  ├── .env # Environment variables (HF credentials)
110
  ├── pyproject.toml # Project metadata
111
  ├── requirements.txt # Dependencies
 
114
  ├── README.md # User-facing documentation
115
  ├── CLAUDE.md # This file - project context for Claude
116
  ├── GAMEPLAY_GUIDE.md # User guide with tips and strategies
117
+ ├── pyproject.toml # Project metadata
118
+ ├── requirements.txt # Dependencies
119
+ ├── uv.lock # UV lock file
120
+ ├── Dockerfile # Container deployment
121
+ ├── README.md # User-facing documentation
122
+ ├── CLAUDE.md # This file - project context for Claude
123
+ ├── GAMEPLAY_GUIDE.md # User guide with tips and strategies
124
  ```
125
 
126
+ ### Page Navigation System
127
+ Uses **query parameter-based routing** (NOT Streamlit multi-page):
128
+ - `?page=today|daily|weekly|history` → Leaderboard pages
129
+ - `?page=settings` Settings page (IN PROGRESS - OAuth protected)
130
+ - `?game_id=<sid>` Challenge mode
131
+ - No query params → Main game page
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
 
133
  ## Data Models
134
 
 
149
  @dataclass
150
  class Puzzle:
151
  words: List[Word]
 
152
  may_overlap: bool
153
  spacer: int
154
+ uid: str # Unique identifier
155
 
156
  @dataclass
157
  class GameState:
158
+ grid_rows: int # 6 for Wrdler
159
+ grid_cols: int # 8 for Wrdler
160
  puzzle: Puzzle
161
  revealed: Set[Coord]
162
  guessed: Set[str]
 
169
  end_time: Optional[datetime]
170
  ```
171
 
172
+ ## Environment Variables
173
+
174
+ Create a `.env` file in the project root:
175
+
176
+ ```bash
177
+ # Challenge Mode & Leaderboards (Remote Storage)
178
+ HF_API_TOKEN=hf_xxxxxxxxxxxxxxxxxxxxx # HuggingFace API token with write access
179
+ HF_REPO_ID=YourUsername/YourRepo # Dataset repo for challenge storage
180
+
181
+ # AI Word Generation
182
+ USE_HF_WORDS=false # Enable HF Space API for word generation
183
+ HF_WORD_LIST_REPO_ID=YourUsername/WordRepo # Dataset repo for AI word lists
184
+
185
+ # OAuth Admin Access (NEW)
186
+ ADMIN_USERS=username1,username2 # Comma-separated list of admin usernames
187
+ ```
188
+
189
+ ### HF_REPO_ID Structure
190
+ ```
191
+ HF_REPO_ID/
192
+ ├── shortener.json # URL shortener mappings
193
+ ├── games/{uid}/settings.json # Challenge data (entry_type: "challenge")
194
+ └── games/leaderboards/
195
+ ├── daily/{YYYY-MM-DD}/{file_id}/settings.json # Daily leaderboards
196
+ └── weekly/{YYYY-Www}/{file_id}/settings.json # Weekly leaderboards
197
+ ```
198
+
199
  ## Development Workflow
200
 
201
  ### Running Locally
 
204
  uv pip install -r requirements.txt --link-mode=copy
205
 
206
  # Run app
 
 
207
  streamlit run app.py
208
  ```
209
 
 
 
 
 
 
 
210
  ### Testing
211
  ```bash
212
  pytest tests/
213
  ```
214
 
215
+ ## Next Step: OAuth-Protected Settings Page
216
+
217
+ ### Goal
218
+ Move all game settings from sidebar to a dedicated settings page at `?page=settings`, protected by HuggingFace OAuth (admin-only access).
219
+
220
+ ### Implementation Approach
221
+ 1. **Use existing query parameter routing** (like leaderboard pages)
222
+ 2. **HuggingFace OAuth integration:**
223
+ - Add `hf_oauth: true` to README.md YAML header ✅ DONE
224
+ - Create `wrdler/oauth.py` with utility functions DONE
225
+ - OAuth user info available at `st.session_state["oauth_user"]`
226
+ - Check admin access via `ADMIN_USERS` environment variable
227
+ 3. **Create `wrdler/settings_page.py`:**
228
+ - Check authentication with `require_admin()` from oauth.py
229
+ - Move settings UI from sidebar (word list, game mode, audio, etc.)
230
+ - Persist settings to session state
231
+ - Accessible via `?page=settings`
232
+ 4. **Modify `wrdler/ui.py`:**
233
+ - Add settings page route handler (similar to leaderboard routing)
234
+ - Remove settings from sidebar (keep minimal controls only)
235
+ - Add "⚙️ Settings" link in footer navigation
236
+ 5. **Keep sidebar minimal:**
237
+ - Version info
238
+ - User info (if logged in)
239
+ - Link to settings page
240
+
241
+ ### HuggingFace OAuth Flow
242
+ 1. User clicks login button (HF Spaces provides this automatically)
243
+ 2. User authorizes with HF account
244
+ 3. HF redirects back with OAuth token
245
+ 4. User info stored in `st.session_state["oauth_user"]`
246
+ 5. Check `username` against `ADMIN_USERS` env var
247
+ 6. Grant/deny access to settings page
248
+
249
+ ### Files to Modify
250
+ - `wrdler/ui.py` - Add settings page routing, remove sidebar settings
251
+ - `wrdler/settings_page.py` - NEW FILE - Settings UI with OAuth protection
252
+ - `wrdler/oauth.py` - Already created ✅
253
+
254
+ ### Key OAuth Functions (wrdler/oauth.py)
255
+ ```python
256
+ get_user_info() → Dict | None # Get authenticated user info
257
+ get_username() → str | None # Get username (preferred_username)
258
+ is_authenticated() → bool # Check if user is logged in
259
+ is_admin(allowed_users) → bool # Check if user is admin
260
+ require_admin(page_name) → bool # Validate admin access or show error
261
  ```
262
 
263
+ ## Technical Notes
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
264
 
265
+ ### Important Implementation Details
266
+ - **Python syntax only** - Use colons `:` not braces `{}`
267
+ - **8×6 grid:** `grid_rows=6`, `grid_cols=8`
268
+ - **Horizontal-only placement:** One word per row
269
+ - **Query param routing:** All pages use `?page=<name>` system
270
+ - **Session state management:** Heavy use of `st.session_state`
271
+ - **OAuth detection:** Check `st.session_state.get("oauth_user")`
272
 
273
+ ### Current Routing Pattern (ui.py)
274
+ ```python
275
+ params = st.query_params
276
+ page = params.get("page", "")
277
 
278
+ if page in {"today", "daily", "weekly", "history"}:
279
+ render_leaderboard_page(default_tab=page)
280
+ return
 
 
 
 
 
281
 
282
+ if page == "settings": # TO BE IMPLEMENTED
283
+ render_settings_page()
284
+ return
285
 
286
+ # Default: main game page
287
+ run_app()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
288
  ```
289
 
290
+ ## Deployment Platforms
291
+ 1. **HuggingFace Spaces** (Primary) - Dockerfile deployment with OAuth support
292
+ 2. **Local Development** - Streamlit run
293
+ 3. **Docker** - Containerized deployment
 
 
 
 
 
 
 
 
 
294
 
295
+ ## Git Configuration
296
+ - **Current Branch:** AI (working branch)
297
+ - **Main Branch:** main
298
+ - **Remotes:**
299
+ - origin: https://github.com/Oncorporation/Wrdler.git
300
+ - Hugging: https://huggingface.co/spaces/Surn/Wrdler
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
README.md CHANGED
@@ -21,10 +21,12 @@ thumbnail: >-
21
 
22
  # Wrdler
23
 
24
- > **This project is based on BattleWords, but adapted for a simpler word puzzle game with an 8x6 grid, horizontal words only, and free letter guesses at the start.**
25
 
26
  Wrdler is a vocabulary learning game with a simplified grid and strategic letter guessing. The objective is to discover hidden words on a grid by making smart guesses before all letters are revealed.
27
 
 
 
28
  ## Key Differences from BattleWords
29
 
30
  - **8x6 grid** (instead of 12x12) with **6 words total** (one per row)
@@ -80,7 +82,7 @@ Wrdler is a vocabulary learning game with a simplified grid and strategic letter
80
  - **"Show Challenge Share Links" toggle** (default OFF) to control URL visibility
81
  - Each player gets different random words from the same wordlist
82
 
83
- ### 🏆 Daily & Weekly Leaderboards (v0.2.0) ✅
84
  **Comprehensive Leaderboard System:**
85
  - **Daily Leaderboards:** Top 20 scores for each day (resets UTC midnight)
86
  - **Weekly Leaderboards:** Top 20 scores for each ISO week (resets Monday UTC 00:00)
@@ -118,11 +120,12 @@ Wrdler is a vocabulary learning game with a simplified grid and strategic letter
118
  - Includes `service worker` and `manifest.json` with basic offline caching of static assets
119
  - See `INSTALL_GUIDE.md` for platform-specific steps
120
 
121
- ### Planned
122
- - Local persistent storage for personal game history
123
- - Personal high scores sidebar (offline-capable)
124
- - Player statistics tracking
125
- - Deterministic seed UI for custom puzzles
 
126
 
127
  ## Challenge Mode & Leaderboard
128
 
@@ -204,11 +207,12 @@ CRYPTO_PK= # Reserved for future signing
204
  - `generator.py` – word placement logic (8x6, horizontal only)
205
  - `logic.py` – game mechanics (reveal, guess, scoring, free letters)
206
  - `ui.py` – Streamlit UI composition
207
- - `leaderboard.py` – Daily/weekly leaderboard system (v0.2.0)
208
- - `leaderboard_page.py` – Leaderboard UI page (v0.2.0)
 
 
209
  - `game_storage.py` – Hugging Face remote storage integration and challenge sharing
210
  - `local_storage.py` – local JSON storage for results and high scores
211
- - `storage.py` – (legacy) local storage and high scores
212
  - `modules/` – shared utilities (storage with folder listing, constants, file_utils)
213
  - `words/` – word list files (classic.txt, fourth_grade.txt, wordlist.txt)
214
  - `specs/` – documentation (`specs.md`, `requirements.md`, `leaderboard_spec.md`)
@@ -230,8 +234,8 @@ All test files must be placed in the `/tests` folder. This ensures a clean proje
230
 
231
  ## Changelog
232
 
233
- ### v0.2.0 (Current) ✅
234
- **Daily and Weekly Leaderboards Implemented**
235
  - ✅ Settings-based leaderboard separation (unique leaderboards per settings combo)
236
  - ✅ Folder-based discovery system (no index.json)
237
  - ✅ Top 20 displayed entries per leaderboard
@@ -243,6 +247,7 @@ All test files must be placed in the `/tests` folder. This ensures a clean proje
243
  - ✅ Period-based organization: daily (YYYY-MM-DD), weekly (YYYY-Www)
244
  - ✅ Enhanced storage.py with folder listing capabilities
245
  - ✅ Updated scoring tiers with "Legendary" (45+)
 
246
 
247
  ### v0.1.1
248
  - ✅ Enhanced AI word generation with intelligent word saving
@@ -267,7 +272,7 @@ All test files must be placed in the `/tests` folder. This ensures a clean proje
267
  ### v0.0.7
268
  - fix guess bug - allowing guesses only after word guessed or letter revealed
269
 
270
- ### v0.0.2 (Current - All Sprints Complete) 🎉
271
  - **Sprint 1-3:** Core data models, generator refactor, radar removal
272
  - **Sprint 4:** Implemented free letter selection UI with circular green gradient buttons
273
  - **Sprint 5:** Updated grid UI rendering for 8×6 display
@@ -415,7 +420,7 @@ For any issues or enhancements, please refer to the project documentation or con
415
 
416
  Happy gaming and sound designing!
417
 
418
- ## What's New in v0.2.0: Daily & Weekly Leaderboards 🏆
419
 
420
  ### Comprehensive Leaderboard System
421
  - **Settings-Based Competition:** Separate leaderboards for each unique settings combination
@@ -425,6 +430,7 @@ Happy gaming and sound designing!
425
  - Puzzle options (spacer, may_overlap)
426
  - **Daily & Weekly Periods:** Top 20 scores per day and per ISO week
427
  - **Automatic Qualification:** Submit scores after any game completion
 
428
 
429
  ### Leaderboard Page Navigation
430
  - **Today Tab:** Current daily and weekly leaderboards with direct link filtering
@@ -450,21 +456,127 @@ Happy gaming and sound designing!
450
  - Top 5 players displayed in Challenge Mode banner
451
  - "Show Challenge Share Links" toggle for privacy control (default OFF)
452
 
453
- ## What's Planned for v0.3.0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
454
 
455
- ### Local Player History (Coming Soon)
456
- - Personal game results saved locally in `~/.wrdler/data/`
457
- - Offline-capable high score tracking
458
- - Player statistics (games played, averages, bests)
459
- - Privacy-first: no cloud dependency for personal data
460
- - Easy data management (delete `~/.wrdler/data/` to reset)
 
 
461
 
462
- ### Leaderboard Optimizations
463
- - In-memory caching with TTL (60s for periods, 15s for leaderboards)
464
- - Pagination for entries beyond top 20
465
- - Retry logic with exponential backoff
466
- - Rate limiting per IP/session
467
- - Archival script for old periods (>365 days daily, >156 weeks weekly)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
468
 
469
- If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
470
- forums](https://discuss.streamlit.io).
 
21
 
22
  # Wrdler
23
 
24
+ > **Wrdler is a Python/Streamlit vocabulary puzzle game based on BattleWords, adapted for a simpler 8x6 grid, horizontal words only, and free letter guesses at the start.**
25
 
26
  Wrdler is a vocabulary learning game with a simplified grid and strategic letter guessing. The objective is to discover hidden words on a grid by making smart guesses before all letters are revealed.
27
 
28
+ **Current Version:** v0.2.1
29
+
30
  ## Key Differences from BattleWords
31
 
32
  - **8x6 grid** (instead of 12x12) with **6 words total** (one per row)
 
82
  - **"Show Challenge Share Links" toggle** (default OFF) to control URL visibility
83
  - Each player gets different random words from the same wordlist
84
 
85
+ ### 🏆 Daily & Weekly Leaderboards (v0.2.1) ✅
86
  **Comprehensive Leaderboard System:**
87
  - **Daily Leaderboards:** Top 20 scores for each day (resets UTC midnight)
88
  - **Weekly Leaderboards:** Top 20 scores for each ISO week (resets Monday UTC 00:00)
 
120
  - Includes `service worker` and `manifest.json` with basic offline caching of static assets
121
  - See `INSTALL_GUIDE.md` for platform-specific steps
122
 
123
+ ### Planned/Upcoming
124
+ - Move all game settings from sidebar to a dedicated settings page (`?page=settings`) requiring a logged in user (OAuth)
125
+ - Local persistent storage for personal game history (future)
126
+ - Personal high scores sidebar (future)
127
+ - Player statistics tracking (future)
128
+ - Deterministic seed UI for custom puzzles (future)
129
 
130
  ## Challenge Mode & Leaderboard
131
 
 
207
  - `generator.py` – word placement logic (8x6, horizontal only)
208
  - `logic.py` – game mechanics (reveal, guess, scoring, free letters)
209
  - `ui.py` – Streamlit UI composition
210
+ - `oauth.py` – HuggingFace OAuth utilities (NEW)
211
+ - `settings_page.py` – Settings page UI (IN PROGRESS)
212
+ - `leaderboard.py` – Daily/weekly leaderboard system (v0.2.1)
213
+ - `leaderboard_page.py` – Leaderboard UI page (v0.2.1)
214
  - `game_storage.py` – Hugging Face remote storage integration and challenge sharing
215
  - `local_storage.py` – local JSON storage for results and high scores
 
216
  - `modules/` – shared utilities (storage with folder listing, constants, file_utils)
217
  - `words/` – word list files (classic.txt, fourth_grade.txt, wordlist.txt)
218
  - `specs/` – documentation (`specs.md`, `requirements.md`, `leaderboard_spec.md`)
 
234
 
235
  ## Changelog
236
 
237
+ ### v0.2.1 (Current) ✅
238
+ **Daily and Weekly Leaderboards Improved**
239
  - ✅ Settings-based leaderboard separation (unique leaderboards per settings combo)
240
  - ✅ Folder-based discovery system (no index.json)
241
  - ✅ Top 20 displayed entries per leaderboard
 
247
  - ✅ Period-based organization: daily (YYYY-MM-DD), weekly (YYYY-Www)
248
  - ✅ Enhanced storage.py with folder listing capabilities
249
  - ✅ Updated scoring tiers with "Legendary" (45+)
250
+ - ✅ Settings page planned (move from sidebar, OAuth login required)
251
 
252
  ### v0.1.1
253
  - ✅ Enhanced AI word generation with intelligent word saving
 
272
  ### v0.0.7
273
  - fix guess bug - allowing guesses only after word guessed or letter revealed
274
 
275
+ ### v0.0.2 (All Sprints Complete) 🎉
276
  - **Sprint 1-3:** Core data models, generator refactor, radar removal
277
  - **Sprint 4:** Implemented free letter selection UI with circular green gradient buttons
278
  - **Sprint 5:** Updated grid UI rendering for 8×6 display
 
420
 
421
  Happy gaming and sound designing!
422
 
423
+ ## What's New in v0.2.1: Daily & Weekly Leaderboards 🏆
424
 
425
  ### Comprehensive Leaderboard System
426
  - **Settings-Based Competition:** Separate leaderboards for each unique settings combination
 
430
  - Puzzle options (spacer, may_overlap)
431
  - **Daily & Weekly Periods:** Top 20 scores per day and per ISO week
432
  - **Automatic Qualification:** Submit scores after any game completion
433
+ - **Settings page planned:** Move from sidebar to dedicated page, login required
434
 
435
  ### Leaderboard Page Navigation
436
  - **Today Tab:** Current daily and weekly leaderboards with direct link filtering
 
456
  - Top 5 players displayed in Challenge Mode banner
457
  - "Show Challenge Share Links" toggle for privacy control (default OFF)
458
 
459
+ ## Known Issues / TODO
460
+
461
+ - Word list loading bug: the app may not select the proper word lists in some environments. Investigate `word_loader.get_wordlist_files()` / `load_word_list()` and sidebar selection persistence to ensure the chosen file is correctly used by the generator.
462
+
463
+ ## Development Phases
464
+
465
+ - **Proof of Concept (0.1.0):** No overlaps, basic UI, single session.
466
+ - **Beta (0.5.0):** Responsive layout, leaderboards, keyboard support, deterministic seed.
467
+ - **Full (1.0.0):** Enhanced UX, Overlaps allowed on shared letters, persistence, daily/practice modes, advanced features.
468
+
469
+ See `specs/requirements.md` and `specs/specs.md` for full details and roadmap.
470
+
471
+ ## License
472
+
473
+ Wrdler is based on BattleWords. BattlewordsTM. All Rights Reserved. All content, trademarks and logos are copyrighted by the owner.
474
+
475
+ ## Hugging Face Spaces Configuration
476
+
477
+ Wrdler is deployable as a Hugging Face Space. You can use either the YAML config block or a Dockerfile for advanced/custom deployments.
478
+
479
+ To configure your Space with the YAML block, add it at the top of your `README.md`:
480
+
481
+ ```yaml
482
+ ---
483
+ title: Wrdler
484
+ emoji: 🎲
485
+ colorFrom: blue
486
+ colorTo: indigo
487
+ sdk: streamlit
488
+ sdk_version: 1.51.0
489
+ python_version: 3.12.8
490
+ app_file: app.py
491
+ tags:
492
+ - game
493
+ - vocabulary
494
+ - streamlit
495
+ - education
496
+ ---
497
+ ```
498
+
499
+ **Key parameters:**
500
+ - `title`, `emoji`, `colorFrom`, `colorTo`: Visuals for your Space.
501
+ - `sdk`: Use `streamlit` for Streamlit apps.
502
+ - `sdk_version`: Latest supported Streamlit version.
503
+ - `python_version`: Python version (default is3.10).
504
+ - `app_file`: Entry point for your app.
505
+ - `tags`: List of descriptive tags.
506
+
507
+ **Dependencies:**
508
+ Add a `requirements.txt` with your Python dependencies (e.g., `streamlit`, etc.).
509
+
510
+ **Port:**
511
+ Streamlit Spaces use port `8501` by default.
512
 
513
+ **Embedding:**
514
+ Spaces can be embedded in other sites using an `<iframe>`:
515
+
516
+ ```html
517
+ <iframe src="https://[YourUsername]-Wrdler.hf.space?embed=true" title="Wrdler"></iframe>
518
+ ```
519
+
520
+ For full configuration options, see [Spaces Config Reference](https://huggingface.co/docs/hub/spaces-config-reference) and [Streamlit SDK Guide](https://huggingface.co/docs/hub/spaces-sdks-streamlit).
521
 
522
+ # Assets Setup
523
+
524
+ To fully experience Wrdler, especially the audio elements, ensure you set up the following assets:
525
+
526
+ - Place your background music `.mp3` files in `wrdler/assets/audio/music/` to enable music.
527
+ - Place your sound effect files (`.mp3` or `.wav`) in `wrdler/assets/audio/effects/` for sound effects.
528
+
529
+ Refer to the documentation for guidance on compatible audio formats and common troubleshooting tips.
530
+
531
+ # Sound Asset Generation
532
+
533
+ To generate and save custom sound effects for Wrdler, you can use the `generate_sound_effect` function.
534
+
535
+ ## Function: `generate_sound_effect`
536
+
537
+ ```python
538
+ def generate_sound_effect(effect: str, save_to_assets: bool = False, use_api: str = "huggingface") -> str:
539
+ """
540
+ Generate a sound effect and save it as a file.
541
+
542
+ Parameters:
543
+ - `effect`: Name of the effect to generate.
544
+ - `save_to_assets`: If `True`, saves the effect to the assets directory;
545
+ if `False`, saves to a temporary location. Default is `False`.
546
+ - `use_api`: API to use for generation. Options are "huggingface" or "replicate". Default is "huggingface".
547
+
548
+ Returns:
549
+ - File path to the saved sound effect.
550
+ ```
551
+
552
+ ## Parameters
553
+
554
+ - `effect`: The name of the sound effect you want to generate (e.g., "explosion", "powerup").
555
+ - `save_to_assets` (optional): Set to `True` to save the generated sound effect to the game's assets directory. If `False`, the effect is saved to a temporary location. Default is `False`.
556
+ - `use_api` (optional): The API to use for generating the sound. Options are `"huggingface"` or `"replicate"`. Default is `"huggingface"`.
557
+
558
+ ## Returns
559
+
560
+ - The function returns the file path to the saved sound effect, whether it's in the assets directory or a temporary location.
561
+
562
+ ## Usage Example
563
+
564
+ To generate a sound effect and save it to the assets directory:
565
+
566
+ ```python
567
+ generate_sound_effect("your_effect_name", save_to_assets=True)
568
+ ```
569
+
570
+ To generate a sound effect and keep it in a temporary location:
571
+
572
+ ```python
573
+ temp_path = generate_sound_effect("your_effect_name", save_to_assets=False)
574
+ ```
575
+
576
+ ## Note
577
+
578
+ Ensure you have the necessary permissions and API access (if required) to use the sound generation service. Generated sounds are subject to the terms of use of the respective API.
579
+
580
+ For any issues or enhancements, please refer to the project documentation or contact the project maintainer.
581
 
582
+ Happy gaming and sound designing!
 
specs/requirements.md CHANGED
@@ -1,20 +1,21 @@
1
  # Wrdler: Implementation Requirements
2
- **Version:** 0.2.0
3
  **Status:** Production Ready - Leaderboards Implemented
4
  **Last Updated:** 2025-12-08
5
 
6
- This document breaks down the implementation tasks for Wrdler using the game rules described in `specs.md`. Wrdler is based on BattleWords but with a simplified 8x6 grid, horizontal-only words, and free letter guesses at the start.
7
 
8
- **Current Status:** ✅ All Phase 1 requirements complete, 100% tested (25/25 tests passing), AI word generation enhanced in v0.1.1, Daily/Weekly leaderboards implemented in v0.2.0
9
 
10
  ## Key Differences from BattleWords
 
11
  - 8x6 grid instead of 12x12
12
  - One word per row (6 total) instead of flexible placement
13
  - Horizontal words only (no vertical)
14
  - No radar/scope visualization
15
  - 2 free letter guesses at game start
16
 
17
- ## Implementation Details (v0.1.1)
18
  - **Tech Stack:** Python 3.12.8, Streamlit 1.51.0, numpy, matplotlib, transformers, gradio_client
19
  - **Architecture:** Single-player, local state in Streamlit session state
20
  - **Grid:** 8 columns × 6 rows (48 cells) with exactly six words
@@ -60,18 +61,20 @@ This document breaks down the implementation tasks for Wrdler using the game rul
60
  ## Folder Structure (Implemented)
61
  - `app.py` – Streamlit entry point ✅
62
  - `wrdler/` – Python package ✅
63
- - `__init__.py` (version 0.2.0)
64
  - `models.py` – data models and types (rectangular grid support)
65
  - `word_loader.py` – load/validate/cached word lists
66
  - `word_loader_ai.py` – AI word generation with retry logic
67
  - `generator.py` – word placement (8x6, horizontal only, one per row)
68
  - `logic.py` – game mechanics (reveal, guess, scoring, tiers, free letters)
69
  - `ui.py` – Streamlit UI composition (8×6 grid rendering)
70
- - `leaderboard.py` – Daily/weekly leaderboard system (v0.2.0)
71
- - `leaderboard_page.py` – Leaderboard UI page (v0.2.0)
72
  - `audio.py` – background music system
73
  - `sounds.py` – sound effects management
74
  - `game_storage.py` – HF storage wrapper for Challenge Mode
 
 
75
  - `modules/` – shared utilities (storage with folder listing, constants, file_utils)
76
  - `words/` – word list files (classic.txt, fourth_grade.txt, wordlist.txt)
77
  - `specs/` – documentation (specs.md, requirements.md, sprint reports)
@@ -187,14 +190,16 @@ This document breaks down the implementation tasks for Wrdler using the game rul
187
 
188
  **Test Results:** ✅ 25/25 tests passing (100%)
189
 
190
- ## Leaderboard System (v0.2.0) ✅ IMPLEMENTED
191
 
192
  ### Core Implementation
193
 
194
  **Files:**
195
- - `wrdler/leaderboard.py` - Core leaderboard logic (v0.2.0)
196
- - `wrdler/leaderboard_page.py` - Leaderboard UI page (v0.2.0)
197
  - `wrdler/modules/storage.py` - Enhanced with folder listing functions
 
 
198
 
199
  **Data Models:**
200
  - `GameSettings` - Settings that define a unique leaderboard
@@ -263,127 +268,17 @@ games/
263
  - No PII beyond optional player name
264
  - All data in HuggingFace repository
265
 
266
- **Known Limitations (v0.2.0):**
267
- - No caching (deferred to v0.3.0)
268
  - No pagination beyond top 20
269
  - Basic error handling
270
  - No rate limiting
271
  - No archival script
272
 
273
- **Future Enhancements (v0.3.0+):**
274
- - In-memory caching with TTL
275
- - Pagination for >20 entries
276
- - Retry logic with exponential backoff
277
- - Rate limiting per IP/session
278
- - Archival script for old periods
279
-
280
- ## Sprint Completion Summary (v0.0.2)
281
-
282
- | Sprint | Description | Time | Tests | Status |
283
- |--------|-------------|------|-------|--------|
284
- | Sprint 1 | Core Data Models | 3h | 13/13 ✅ | Complete |
285
- | Sprint 2 | Puzzle Generator | 3h | 5/5 ✅ | Complete |
286
- | Sprint 3 | Remove Radar | 0.5h | N/A | Complete |
287
- | Sprint 4 | Free Letters UI | 2h | Manual ✅ | Complete |
288
- | Sprint 5 | Grid UI Updates | 1.25h | Syntax ✅ | Complete |
289
- | Sprint 6 | Integration Testing | 2h | 7/7 ✅ | Complete |
290
- | Sprint 7 | Documentation | 1h | N/A | Complete |
291
- | **Total** | **All Features** | **12.75h** | **25/25** | **Complete ✅** |
292
-
293
- **All known issues resolved. All TODO items completed.**
294
-
295
- ## Future Roadmap
296
-
297
- ### v0.3.0 (Next Phase)
298
- - 📋 Local persistent storage in `~/.wrdler/data/`
299
- - 📋 High score tracking and display
300
- - 📋 Player statistics
301
- - 📋 Enhanced UI animations
302
- - 📋 Leaderboard caching and performance optimizations
303
- - 📋 Retry logic with exponential backoff
304
- - 📋 Rate limiting for submissions
305
- - 📋 Archival script for old leaderboard periods
306
-
307
- ### v0.4.0 (AI Expansion)
308
- - 📋 AI difficulty tuning based on player performance
309
- - 📋 Custom topic suggestions
310
- - 📋 Multi-language word generation
311
- - 📋 Word difficulty analysis and visualization
312
-
313
- ### v1.0.0 (Long Term)
314
- - 📋 Multiple difficulty levels
315
- - 📋 Daily puzzle mode
316
- - 📋 Internationalization (i18n)
317
- - 📋 Performance optimizations
318
-
319
- ## Deployment Targets ✅
320
-
321
- ### Supported Platforms (v0.0.2)
322
- - ✅ **Hugging Face Spaces** (primary) - Dockerfile deployment
323
- - ✅ **Docker** - Containerization with provided Dockerfile
324
- - ✅ **Local Development** - Run with `streamlit run app.py`
325
- - ✅ **PWA** - Installable as Progressive Web App on desktop and mobile
326
-
327
- ### Deployment Status
328
- **Ready for production deployment!** All features tested and documented.
329
-
330
- ---
331
-
332
- **Last Updated:** 2025-12-08
333
- **Version:** 0.2.0
334
- **Status:** Production Ready - Leaderboards Implemented 🚀
335
-
336
- ## AI Word Generation Pipeline (v0.1.1)
337
-
338
- ### Architecture
339
- ```
340
- User Input (Topic)
341
-
342
- Check USE_HF_WORDS flag
343
-
344
- ┌─────────────────────────────────────┐
345
- │ HF Space API (Primary) │
346
- │ - gradio_client integration │
347
- │ - Temperature: 0.95 │
348
- │ - Max tokens: 512 │
349
- └─────────────────────────────────────┘
350
- ↓ (if fails or USE_HF_WORDS=false)
351
- ┌─────────────────────────────────────┐
352
- │ Local Transformers (Fallback) │
353
- │ - Auto model selection │
354
- │ - Device auto-detection │
355
- │ - Temperature: 0.7 │
356
- └─────────────────────────────────────┘
357
-
358
- Parse & Filter Words
359
-
360
- Identify New vs Existing
361
-
362
- Check MIN_REQUIRED threshold
363
- ↓ (if insufficient)
364
- Generate Additional Words (up to 3 retries)
365
-
366
- Save New Words to File
367
-
368
- Validate & Sort File
369
-
370
- Return 75 Words for Game
371
- ```
372
-
373
- ### Word Saving Strategy
374
- 1. **Detection Phase**: Separate new AI words from existing dictionary words
375
- 2. **Validation Phase**: Check if file meets MIN_REQUIRED (25 words per length)
376
- 3. **Retry Phase**: If insufficient, generate additional words (up to 3 attempts)
377
- 4. **Save Phase**: Write only new words to topic-based file
378
- 5. **Sort Phase**: Auto-sort by length then alphabetically
379
- 6. **Limit Phase**: Stop adding if file reaches 1000 words
380
-
381
- ### Error Handling
382
- - **HF Space API failure**: Graceful fallback to local model
383
- - **Model loading failure**: Try multiple models in priority order
384
- - **Device compatibility**: Retry pipeline without device parameter on error 422
385
- - **Insufficient words**: Automatic retry with targeted prompts
386
- - **File operations**: Detailed logging and error recovery
387
 
388
  ## Test File Location
389
  All test files must be placed in the `/tests` folder. This ensures a clean project structure and makes it easy to discover and run all tests.
 
1
  # Wrdler: Implementation Requirements
2
+ **Version:** 0.2.1
3
  **Status:** Production Ready - Leaderboards Implemented
4
  **Last Updated:** 2025-12-08
5
 
6
+ This document breaks down the implementation tasks for Wrdler using the game rules described in `specs.md`. Wrdler is a Python/Streamlit project based on BattleWords but with a simplified 8x6 grid, horizontal-only words, and free letter guesses at the start.
7
 
8
+ **Current Status:** ✅ All Phase 1 requirements complete, 100% tested (25/25 tests passing), AI word generation enhanced in v0.1.1, Daily/Weekly leaderboards implemented in v0.2.1
9
 
10
  ## Key Differences from BattleWords
11
+ - Python project (Streamlit, Python 3.12.8)
12
  - 8x6 grid instead of 12x12
13
  - One word per row (6 total) instead of flexible placement
14
  - Horizontal words only (no vertical)
15
  - No radar/scope visualization
16
  - 2 free letter guesses at game start
17
 
18
+ ## Implementation Details (v0.2.1)
19
  - **Tech Stack:** Python 3.12.8, Streamlit 1.51.0, numpy, matplotlib, transformers, gradio_client
20
  - **Architecture:** Single-player, local state in Streamlit session state
21
  - **Grid:** 8 columns × 6 rows (48 cells) with exactly six words
 
61
  ## Folder Structure (Implemented)
62
  - `app.py` – Streamlit entry point ✅
63
  - `wrdler/` – Python package ✅
64
+ - `__init__.py` (version 0.2.1)
65
  - `models.py` – data models and types (rectangular grid support)
66
  - `word_loader.py` – load/validate/cached word lists
67
  - `word_loader_ai.py` – AI word generation with retry logic
68
  - `generator.py` – word placement (8x6, horizontal only, one per row)
69
  - `logic.py` – game mechanics (reveal, guess, scoring, tiers, free letters)
70
  - `ui.py` – Streamlit UI composition (8×6 grid rendering)
71
+ - `leaderboard.py` – Daily/weekly leaderboard system (v0.2.1)
72
+ - `leaderboard_page.py` – Leaderboard UI page (v0.2.1)
73
  - `audio.py` – background music system
74
  - `sounds.py` – sound effects management
75
  - `game_storage.py` – HF storage wrapper for Challenge Mode
76
+ - `oauth.py` – HuggingFace OAuth utilities (NEW)
77
+ - `settings_page.py` – Settings page UI (IN PROGRESS)
78
  - `modules/` – shared utilities (storage with folder listing, constants, file_utils)
79
  - `words/` – word list files (classic.txt, fourth_grade.txt, wordlist.txt)
80
  - `specs/` – documentation (specs.md, requirements.md, sprint reports)
 
190
 
191
  **Test Results:** ✅ 25/25 tests passing (100%)
192
 
193
+ ## Leaderboard System (v0.2.1) ✅ IMPLEMENTED
194
 
195
  ### Core Implementation
196
 
197
  **Files:**
198
+ - `wrdler/leaderboard.py` - Core leaderboard logic (v0.2.1)
199
+ - `wrdler/leaderboard_page.py` - Leaderboard UI page (v0.2.1)
200
  - `wrdler/modules/storage.py` - Enhanced with folder listing functions
201
+ - `wrdler/oauth.py` - HuggingFace OAuth utilities (NEW)
202
+ - `wrdler/settings_page.py` - Settings page UI (IN PROGRESS)
203
 
204
  **Data Models:**
205
  - `GameSettings` - Settings that define a unique leaderboard
 
268
  - No PII beyond optional player name
269
  - All data in HuggingFace repository
270
 
271
+ **Known Limitations (v0.2.1):**
272
+ - No caching (future)
273
  - No pagination beyond top 20
274
  - Basic error handling
275
  - No rate limiting
276
  - No archival script
277
 
278
+ **Future Enhancements (planned):**
279
+ - Local persistent storage, high score tracking, player statistics, leaderboard caching
280
+ - Enhanced UI animations, retry logic, rate limiting, archival script
281
+ - AI difficulty tuning, multi-language word generation, i18n
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
282
 
283
  ## Test File Location
284
  All test files must be placed in the `/tests` folder. This ensures a clean project structure and makes it easy to discover and run all tests.
specs/specs.md CHANGED
@@ -1,14 +1,15 @@
1
  # Wrdler Game Specifications (specs.md)
2
- **Version:** 0.2.0
3
  **Status:** Production Ready - Leaderboards Implemented
4
  **Last Updated:** 2025-12-08
5
 
6
  ## Overview
7
- Wrdler is a simplified vocabulary puzzle game based on BattleWords, but with key differences. The objective is to discover hidden words on a grid by making strategic guesses and using free letter reveals at the game start.
8
 
9
  **Current Status:** All 7 sprints complete, 100% tested, fully documented
10
 
11
  ## Key Differences from BattleWords
 
12
  - **8x6 grid** (instead of 12x12)
13
  - **One word per row** (instead of 6 words placed anywhere)
14
  - **Horizontal words only** (no vertical placement)
@@ -60,7 +61,7 @@ Wrdler is a simplified vocabulary puzzle game based on BattleWords, but with key
60
  - ✅ 10 incorrect guess limit per game
61
  - ✅ Two game modes: Classic (chain guesses) and Too Easy (single guess per reveal)
62
 
63
- ## Implemented Features (v0.1.1)
64
 
65
  ### AI Word Generation (v0.1.0+)
66
  - ✅ **Topic-Based Generation:** Create custom word lists for any theme using AI
@@ -85,7 +86,7 @@ Wrdler is a simplified vocabulary puzzle game based on BattleWords, but with key
85
  - ✅ **Top 5 Display:** Leaderboard banner shows top 5 players
86
  - ✅ **Optional Sharing:** "Show Challenge Share Links" toggle (default OFF) controls URL visibility
87
 
88
- ### Leaderboard System (v0.2.0) ✅
89
  Wrdler features a comprehensive daily and weekly leaderboard system:
90
 
91
  **Core Features:**
@@ -144,18 +145,21 @@ HF_REPO_ID/games/
144
  - INSTALL_GUIDE.md added with platform-specific install steps
145
  - No gameplay logic changes
146
 
 
 
 
147
  ## Storage
148
 
149
- ### Current (v0.0.2)
150
  - ✅ Challenge Mode uses remote storage via Hugging Face datasets
151
  - ✅ Game ID is generated from the word list for replay/sharing
152
 
153
- ### Planned (v0.3.0)
154
- - 📋 Local persistent storage for game results and high scores (JSON files)
155
- - 📋 Local storage location: `~/.wrdler/data/`
156
- - 📋 Privacy-first offline access
157
 
158
- ## UI Elements (v0.0.2 - Implemented)
159
  - ✅ 8x6 grid (48 cells total)
160
  - ✅ Free letter guess buttons (2 at game start) - circular green gradient design
161
  - ✅ Text box for word guesses
@@ -253,10 +257,10 @@ HF_REPO_ID/
253
 
254
  ## Development Status
255
 
256
- **Current Version:** 0.2.0 (Production Ready - Leaderboards Implemented)
257
 
258
  ### Completed ✅
259
- - **v0.2.0:** Daily and Weekly Leaderboards
260
  - Settings-based leaderboard separation
261
  - Folder-based discovery (no index.json)
262
  - Top 20 displayed entries per leaderboard
@@ -264,6 +268,7 @@ HF_REPO_ID/
264
  - Automatic score qualification and submission
265
  - Integration with challenge mode
266
  - Query parameter filtering for direct links
 
267
 
268
  - **v0.1.1:** Enhanced AI word generation
269
  - Intelligent word saving with duplicate prevention
@@ -283,8 +288,9 @@ HF_REPO_ID/
283
  - 📚 Complete documentation
284
 
285
  ### Future Roadmap
286
- - **v0.3.0:** Local persistent storage, high score tracking, player statistics, leaderboard caching
287
- - **v1.0.0:** Enhanced UX, multiple difficulty levels, daily puzzle mode
 
288
 
289
  ## Copyright
290
  Wrdler is based on BattleWords. BattlewordsTM. All Rights Reserved. All content, trademarks and logos are copyrighted by the owner.
 
1
  # Wrdler Game Specifications (specs.md)
2
+ **Version:** 0.2.1
3
  **Status:** Production Ready - Leaderboards Implemented
4
  **Last Updated:** 2025-12-08
5
 
6
  ## Overview
7
+ Wrdler is a Python/Streamlit vocabulary puzzle game based on BattleWords, but with key differences. The objective is to discover hidden words on a grid by making strategic guesses and using free letter reveals at the game start.
8
 
9
  **Current Status:** All 7 sprints complete, 100% tested, fully documented
10
 
11
  ## Key Differences from BattleWords
12
+ - **Python project** (Streamlit, Python 3.12.8)
13
  - **8x6 grid** (instead of 12x12)
14
  - **One word per row** (instead of 6 words placed anywhere)
15
  - **Horizontal words only** (no vertical placement)
 
61
  - ✅ 10 incorrect guess limit per game
62
  - ✅ Two game modes: Classic (chain guesses) and Too Easy (single guess per reveal)
63
 
64
+ ## Implemented Features (v0.2.1)
65
 
66
  ### AI Word Generation (v0.1.0+)
67
  - ✅ **Topic-Based Generation:** Create custom word lists for any theme using AI
 
86
  - ✅ **Top 5 Display:** Leaderboard banner shows top 5 players
87
  - ✅ **Optional Sharing:** "Show Challenge Share Links" toggle (default OFF) controls URL visibility
88
 
89
+ ### Leaderboard System (v0.2.1) ✅
90
  Wrdler features a comprehensive daily and weekly leaderboard system:
91
 
92
  **Core Features:**
 
145
  - INSTALL_GUIDE.md added with platform-specific install steps
146
  - No gameplay logic changes
147
 
148
+ ### Settings Page (Planned/Upcoming)
149
+ - Move all game settings from sidebar to a dedicated settings page (`?page=settings`) requiring a logged in user (OAuth)
150
+
151
  ## Storage
152
 
153
+ ### Current (v0.2.1)
154
  - ✅ Challenge Mode uses remote storage via Hugging Face datasets
155
  - ✅ Game ID is generated from the word list for replay/sharing
156
 
157
+ ### Planned (Future)
158
+ - Local persistent storage for game results and high scores (JSON files)
159
+ - Local storage location: `~/.wrdler/data/`
160
+ - Privacy-first offline access
161
 
162
+ ## UI Elements (v0.2.1 - Implemented)
163
  - ✅ 8x6 grid (48 cells total)
164
  - ✅ Free letter guess buttons (2 at game start) - circular green gradient design
165
  - ✅ Text box for word guesses
 
257
 
258
  ## Development Status
259
 
260
+ **Current Version:** 0.2.1 (Production Ready - Leaderboards Implemented)
261
 
262
  ### Completed ✅
263
+ - **v0.2.1:** Daily and Weekly Leaderboards
264
  - Settings-based leaderboard separation
265
  - Folder-based discovery (no index.json)
266
  - Top 20 displayed entries per leaderboard
 
268
  - Automatic score qualification and submission
269
  - Integration with challenge mode
270
  - Query parameter filtering for direct links
271
+ - Settings page planned (move from sidebar, OAuth login required)
272
 
273
  - **v0.1.1:** Enhanced AI word generation
274
  - Intelligent word saving with duplicate prevention
 
288
  - 📚 Complete documentation
289
 
290
  ### Future Roadmap
291
+ - Local persistent storage, high score tracking, player statistics, leaderboard caching (future)
292
+ - Enhanced UI animations, retry logic, rate limiting, archival script (future)
293
+ - AI difficulty tuning, multi-language word generation, i18n (future)
294
 
295
  ## Copyright
296
  Wrdler is based on BattleWords. BattlewordsTM. All Rights Reserved. All content, trademarks and logos are copyrighted by the owner.
wrdler/oauth.py ADDED
@@ -0,0 +1,229 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ OAuth utilities for HuggingFace Spaces authentication.
3
+
4
+ This module provides helper functions to work with HuggingFace OAuth
5
+ in Streamlit apps deployed on HF Spaces.
6
+
7
+ Required README.md configuration:
8
+ ```yaml
9
+ hf_oauth: true
10
+ hf_oauth_scopes:
11
+ - openid
12
+ - profile
13
+ ```
14
+
15
+ HF OAuth documentation:
16
+ https://huggingface.co/docs/hub/en/spaces-oauth
17
+ """
18
+
19
+ import streamlit as st
20
+ import os
21
+ from typing import Optional, Dict, Any
22
+
23
+
24
+ def get_user_info() -> Optional[Dict[str, Any]]:
25
+ """
26
+ Get the authenticated user's information from HF OAuth.
27
+
28
+ HF Spaces automatically injects user info into st.session_state["oauth_user"]
29
+ when OAuth is enabled in README.md
30
+
31
+ Returns:
32
+ Dictionary with user info (username, email, etc.) or None if not authenticated.
33
+ """
34
+ try:
35
+ # HF OAuth stores the user info in session state
36
+ oauth_user = st.session_state.get("oauth_user")
37
+ if oauth_user:
38
+ return oauth_user
39
+ except Exception:
40
+ pass
41
+
42
+ return None
43
+
44
+
45
+ def get_username() -> Optional[str]:
46
+ """
47
+ Get the authenticated user's username (preferred_username).
48
+
49
+ Returns:
50
+ Username string or None if not authenticated.
51
+ """
52
+ user_info = get_user_info()
53
+ if user_info:
54
+ # HF OAuth provides 'preferred_username' in the user info dict
55
+ return user_info.get("preferred_username") or user_info.get("name")
56
+ return None
57
+
58
+
59
+ def get_email() -> Optional[str]:
60
+ """
61
+ Get the authenticated user's email.
62
+
63
+ Returns:
64
+ Email string or None if not authenticated.
65
+ """
66
+ user_info = get_user_info()
67
+ if user_info:
68
+ return user_info.get("email")
69
+ return None
70
+
71
+
72
+ def is_authenticated() -> bool:
73
+ """
74
+ Check if user is currently authenticated via HF OAuth.
75
+
76
+ Returns:
77
+ True if user is authenticated, False otherwise.
78
+ """
79
+ return get_user_info() is not None
80
+
81
+
82
+ def get_admin_users() -> set:
83
+ """
84
+ Get the set of allowed admin usernames from environment variable.
85
+
86
+ Returns:
87
+ Set of admin usernames (empty set if not configured)
88
+ """
89
+ admin_users_str = os.getenv("ADMIN_USERS", "")
90
+ return {u.strip().lower() for u in admin_users_str.split(",") if u.strip()}
91
+
92
+
93
+ def is_admin(allowed_users: Optional[set] = None) -> bool:
94
+ """
95
+ Check if authenticated user is in the admin list.
96
+
97
+ Args:
98
+ allowed_users: Set of allowed admin usernames. If None, uses
99
+ environment variable ADMIN_USERS (comma-separated).
100
+
101
+ Returns:
102
+ True if user is an admin, False otherwise.
103
+ """
104
+ username = get_username()
105
+ if not username:
106
+ return False
107
+
108
+ # Get allowed users from parameter or environment
109
+ if allowed_users is None:
110
+ allowed_users = get_admin_users()
111
+
112
+ # Case-insensitive comparison
113
+ return username.lower() in {u.lower() for u in allowed_users}
114
+
115
+
116
+ def require_login(page_name: str = "this page") -> bool:
117
+ """
118
+ Display login prompt if user is not authenticated.
119
+ Returns True if user is authenticated and can proceed.
120
+
121
+ Args:
122
+ page_name: Name of page to display in prompt
123
+
124
+ Returns:
125
+ True if authenticated, False if login required
126
+ """
127
+ if is_authenticated():
128
+ return True
129
+
130
+ st.warning(f"?? You need to be logged in to access {page_name}.")
131
+ st.info(
132
+ "Sign in with your HuggingFace account using the login button in the top-right corner."
133
+ )
134
+ st.markdown(
135
+ """
136
+ <style>
137
+ .login-prompt {
138
+ background: rgba(29, 100, 200, 0.1);
139
+ border: 1px solid rgba(29, 100, 200, 0.3);
140
+ padding: 1rem;
141
+ border-radius: 0.5rem;
142
+ margin-top: 1rem;
143
+ }
144
+ </style>
145
+ <div class="login-prompt">
146
+ <p><strong>How to login:</strong></p>
147
+ <ol>
148
+ <li>Click the <strong>Login</strong> button at the top-right of the page</li>
149
+ <li>Authorize with your HuggingFace account</li>
150
+ <li>You'll be redirected back to this page</li>
151
+ </ol>
152
+ </div>
153
+ """,
154
+ unsafe_allow_html=True
155
+ )
156
+ return False
157
+
158
+
159
+ def require_admin(allowed_users: Optional[set] = None, page_name: str = "this page") -> bool:
160
+ """
161
+ Check if user is admin, displaying appropriate message if not.
162
+ Returns True if user is admin and can proceed.
163
+
164
+ Args:
165
+ allowed_users: Set of allowed admin usernames
166
+ page_name: Name of page to display in prompt
167
+
168
+ Returns:
169
+ True if admin, False otherwise
170
+ """
171
+ username = get_username()
172
+
173
+ if not username:
174
+ st.error("? You must be logged in to access the settings page.")
175
+ st.info(
176
+ "Sign in with your HuggingFace account using the login button in the top-right corner."
177
+ )
178
+ return False
179
+
180
+ if not is_admin(allowed_users):
181
+ admin_users = allowed_users if allowed_users else get_admin_users()
182
+ st.error(f"? Access denied. Only administrators can access {page_name}.")
183
+ st.info(f"Logged in as: **{username}**")
184
+
185
+ # Show admin list if in debug mode
186
+ if os.getenv("DEBUG") == "true":
187
+ st.caption(f"Configured admins: {', '.join(admin_users) if admin_users else '(none)'}")
188
+
189
+ return False
190
+
191
+ return True
192
+
193
+
194
+ def display_user_info(in_sidebar: bool = False):
195
+ """
196
+ Display current authenticated user's information.
197
+
198
+ Args:
199
+ in_sidebar: If True, renders in sidebar; otherwise in main area
200
+ """
201
+ container = st.sidebar if in_sidebar else st
202
+
203
+ if is_authenticated():
204
+ username = get_username()
205
+ email = get_email()
206
+
207
+ container.markdown(
208
+ f"""
209
+ <style>
210
+ .user-info-box {{
211
+ background: rgba(32, 212, 108, 0.1);
212
+ border: 1px solid rgba(32, 212, 108, 0.3);
213
+ padding: 0.75rem;
214
+ border-radius: 0.5rem;
215
+ margin-bottom: 1rem;
216
+ }}
217
+ </style>
218
+ <div class="user-info-box">
219
+ <strong>?? {username}</strong><br/>
220
+ <small>{email if email else 'No email'}</small>
221
+ </div>
222
+ """,
223
+ unsafe_allow_html=True
224
+ )
225
+
226
+ if is_admin():
227
+ container.success("? Administrator")
228
+ else:
229
+ container.info("?? Not logged in")