wrdler / specs /specs.md
Surn's picture
v0.2.8 Settings Page fixed
f0df64e
# Wrdler Specifications
**Version:** 0.2.8
**Status:** Production Ready - Leaderboards & Settings Page Implemented
**Last Updated:** 2025-12-09
## Overview
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.
**Current Status:** All 7 sprints complete, 100% tested, fully documented
## Key Differences from BattleWords
- **Python project** (Streamlit, Python 3.12.8)
- **8x6 grid** (instead of 12x12)
- **One word per row** (instead of 6 words placed anywhere)
- **Horizontal words only** (no vertical placement)
- **No scope/radar visualization**
- **2 free letter guesses at game start** (all instances of chosen letters are revealed)
## Game Board
- 8 x 6 grid
- Six hidden words:
- One word per row (row 0-5)
- **Word composition:** Exactly 2 four-letter words, 2 five-letter words, and 2 six-letter words
- All placed horizontally (left-right)
- No vertical placement
- No diagonal placement
- Words do not overlap
- Entry point is `app.py`
- **Supports Dockerfile-based deployment for Hugging Face Spaces and other container platforms**
## Gameplay (Core)
- Players start by choosing 2 letters; all instances of those letters are revealed in the grid
- Players click grid squares to reveal letters or empty spaces
- Empty revealed squares are styled with CSS class `empty`
- After any reveal, the app immediately reruns (`st.rerun`) to show the change
- After revealing a letter, players may guess a word by entering it in a text box
- Guess submission triggers an immediate rerun to reflect results
- Only one guess per letter reveal; must uncover another letter before guessing again
- In the default mode, a correct guess allows chaining an additional guess without another reveal
- **The game ends when all six words are guessed or all word letters are revealed**
## Scoring
- Each correct word guess awards points:
- 1 point per letter in the word
- Bonus points for each hidden letter at the time of guessing
- Score tiers:
- **Legendary:** 45+ points
- **Fantastic:** 42-44 points
- **Great:** 39-41 points
- **Good:** 35-38 points
- **Keep practicing:** <35 points
- **Game over is triggered by either all words being guessed or all word letters being revealed**
## Core Rules (v0.0.2 - Implemented)
- βœ… 8x6 grid with one word per row
- βœ… Horizontal words only; no vertical placement
- βœ… No overlaps: words do not overlap or share letters
- βœ… No radar/scope visualization (removed in Sprint 3)
- βœ… 2 free letter guesses at game start (implemented in Sprint 4)
- βœ… Incorrect guess history with optional display
- βœ… 10 incorrect guess limit per game
- βœ… Two game modes: Classic (chain guesses) and Too Easy (single guess per reveal)
## Implemented Features (v0.2.4)
### Word List Management (v0.2.4)
- βœ… **Filter Wordlist:** Remove words found in `assets/filter.txt` from the selected word list
- βœ… **Sort Wordlist:** Sort words by length and alphabetically
- βœ… **Feedback:** Dialog showing count and list of removed words
### AI Word Generation (v0.1.0+)
- βœ… **Topic-Based Generation:** Create custom word lists for any theme using AI
- βœ… **Dual Generation Modes:**
- HF Space API (primary): Uses Hugging Face Space when `USE_HF_WORDS=true`
- Local transformers (fallback): Falls back to local models if HF unavailable
- βœ… **Intelligent Word Management:**
- Smart detection separates existing dictionary words from new AI-generated words
- Only saves new words to prevent duplicates in word files
- Automatic retry mechanism (up to 3 attempts) if insufficient words generated
- 1000-word file size limit prevents dictionary bloat
- Auto-sorted by length then alphabetically
- βœ… **Guaranteed Distribution:** Ensures exactly 25 words each of lengths 4, 5, and 6
- βœ… **Graceful Fallback:** Uses dictionary words if AI generation fails
- βœ… **Enhanced Logging:** Detailed pipeline visibility for debugging
### Challenge Mode
- βœ… **Game ID Sharing:** Each puzzle generates a shareable link with `?game_id=<sid>` to challenge others with the same word list
- βœ… **Remote Storage:** Game results and leaderboards stored in Hugging Face dataset repos
- βœ… **Leaderboards:** Multi-user leaderboards sorted by score (descending) then time (ascending)
- βœ… **Word List Difficulty:** Calculated and displayed for each challenge
- βœ… **Top 5 Display:** Leaderboard banner shows top 5 players
- βœ… **Optional Sharing:** "Show Challenge Share Links" toggle (default OFF) controls URL visibility
### Leaderboard System (v0.2.1) βœ… IMPLEMENTED
Wrdler features a comprehensive daily and weekly leaderboard system:
**Core Features:**
- **Daily Leaderboards:** Top 20 scores for each day (resets UTC midnight)
- **Weekly Leaderboards:** Top 20 scores for each ISO week (resets Monday UTC 00:00)
- **Settings-Based Separation:** Each unique combination of game-affecting settings creates a separate leaderboard:
- `game_mode` (classic, easy, too easy)
- `wordlist_source` (classic.txt, fourth_grade.txt, etc.)
- `show_incorrect_guesses` (boolean)
- `enable_free_letters` (boolean)
- `puzzle_options` (spacer, may_overlap)
- **Sorting:** Scores sorted by: score (desc) β†’ time (asc) β†’ difficulty (desc)
- **Qualification:** Only top 25 (configurable) scores displayed per leaderboard (more can be stored)
**Storage Structure:**
```
HF_REPO_ID/games/
β”œβ”€β”€ leaderboards/
β”‚ β”œβ”€β”€ daily/{YYYY-MM-DD}/{file_id}/settings.json
β”‚ └── weekly/{YYYY-Wwww}/{file_id}/settings.json
└── {challenge_id}/settings.json
```
**File ID Format:** `{wordlist_source}-{game_mode}-{sequence}`
- Example: `classic-classic-0`, `easy-too_easy-1`
- Sanitized (no .txt, lowercase, underscores for spaces)
**Leaderboard Page UI:**
- **Today Tab:** Current daily and weekly leaderboards
- Query params: `?gidd={file_id}` and `?gidw={file_id}` for filtering
- Side-by-side display in two columns
- **Daily Tab:** Last 7 days of daily leaderboards
- Expandable groups per date
- All settings combinations shown
- **Weekly Tab:** Current ISO week leaderboard
- All settings combinations displayed
- **History Tab:** Historical leaderboard browser
- Dropdown selectors for period and settings
- Separate daily and weekly columns
**Integration:**
- Automatic submission after game completion (opt-in)
- 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)
**Discovery:** Folder-based (no index.json)
- Scans period folders for date/week IDs
- Filters by file_id prefix for matching settings
- Loads and verifies full settings match
**Date Display Updates:**
- All leaderboard files use UTC for 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]`
**Access:** 'Leaderboard' link in the footer navigation at the bottom of the page
### PWA Support
- βœ… **PWA Installation:** App is installable as a Progressive Web App on desktop and mobile
- Added `service worker` and `manifest.json`
- Basic offline caching of static assets
- INSTALL_GUIDE.md added with platform-specific install steps
- No gameplay logic changes
### Settings Page (v0.2.8)
- All game settings moved from sidebar to a dedicated settings page (`?page=settings`)
- Accessible via the footer navigation (`βš™οΈ Settings` link)
- Controls game mode, word list selection, grid options (spacer, grid ticks), and audio (music and sound effects)
- Settings are persisted to JSON files in `wrdler/settings/` and the latest settings are loaded on app startup