wrdler / specs /specs.md
Surn's picture
v0.2.5
082223e
# Wrdler Game Specifications (specs.md)
**Version:** 0.2.4
**Status:** Production Ready - Leaderboards Implemented
**Last Updated:** 2025-12-08
## 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-Www}/{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 (Planned/Upcoming)
- Move all game settings from sidebar to a dedicated settings page (`?page=settings`) requiring a logged in user (OAuth)
## Storage
### Current (v0.2.1)
- βœ… Challenge Mode uses remote storage via Hugging Face datasets
- βœ… Game ID is generated from the word list for replay/sharing
### Planned (Future)
- Local persistent storage for game results and high scores (JSON files)
- Local storage location: `~/.wrdler/data/`
- Privacy-first offline access
## UI Elements (v0.2.1 - Implemented)
- βœ… 8x6 grid (48 cells total)
- βœ… Free letter guess buttons (2 at game start) - circular green gradient design
- βœ… Text box for word guesses
- βœ… Score display (shows word, base points, bonus points, total score)
- βœ… Guess status indicator (Correct/Try Again)
- βœ… Incorrect guess history display (toggleable)
- βœ… Game ID display and share button in game over dialog
- βœ… Challenge Mode banner with leaderboard (top 5)
- βœ… High score expander in sidebar
- βœ… Player name input in sidebar
- βœ… Checkbox: "Show Challenge Share Links" (default OFF)
- When OFF:
- Challenge Mode header hides the Share Challenge link
- Game Over dialog still supports submitting/creating challenges, but does not display the generated share URL
- Persisted in session state and preserved across "New Game"
## Word List
- External list at `wrdler/words/wordlist.txt`
- Loaded by `wrdler.word_loader.load_word_list()` with caching
- Filtered to uppercase A-Z, lengths in {4,5,6}; falls back if < 25 per length
## Generator
- Centralized word loader
- No duplicate word texts are selected
- Horizontal-only word placement
- One word per row in 8x6 grid
- **Word length distribution:** Each puzzle must contain exactly 2 four-letter words, 2 five-letter words, and 2 six-letter words
- No word spacing configuration (fixed one word per row)
## Entry Point
- The Streamlit entry point is `app.py`
- **A `Dockerfile` can be used for containerized deployment (recommended for Hugging Face Spaces)**
## Deployment Requirements
### Basic Deployment (Offline Mode)
No special configuration needed. The app will run with all core gameplay features.
Optional: Install as PWA from the browser menu (Add to Home Screen/Install app).
### Challenge Mode Deployment (Remote Storage)
Requires HuggingFace Hub integration for challenge sharing and leaderboards.
**Required Environment Variables:**
```bash
HF_API_TOKEN=hf_xxxxxxxxxxxxxxxxxxxxx # or HF_TOKEN (write access required)
HF_REPO_ID=YourUsername/YourRepo # Target HF dataset repository
SPACE_NAME=YourUsername/Wrdler # Your HF Space name for URL generation
```
**Optional Environment Variables:**
```bash
CRYPTO_PK= # Reserved for future challenge signing
```
**Setup Steps:**
1. Create a HuggingFace account at https://huggingface.co
2. Create a dataset repository (e.g., `YourUsername/WrdlerStorage`)
3. Generate an access token with `write` permissions:
- Go to https://huggingface.co/settings/tokens
- Click "New token"
- Select "Write" access
- Copy the token (starts with `hf_`)
4. Create a `.env` file in project root with the variables above
5. For Hugging Face Spaces deployment, add these as Space secrets
**Repository Structure (automatically created):**
```
HF_REPO_ID/
β”œβ”€β”€ shortener.json # Short URL mappings (sid -> full URL)
└── games/
β”œβ”€β”€ leaderboards/
β”‚ β”œβ”€β”€ daily/
β”‚ β”‚ └── {YYYY-MM-DD}/ # Daily period folders
β”‚ β”‚ └── {file_id}/ # Settings-specific leaderboard
β”‚ β”‚ └── settings.json # entry_type: "daily"
β”‚ └── weekly/
β”‚ └── {YYYY-Www}/ # Weekly period folders (ISO week)
β”‚ └── {file_id}/ # Settings-specific leaderboard
β”‚ └── settings.json # entry_type: "weekly"
└── {challenge_id}/
└── settings.json # Challenge data (entry_type: "challenge")
```
**Data Privacy:**
- Challenge Mode stores: word lists, scores, times, game modes, player names
- No PII beyond optional player name (defaults to "Anonymous")
- Players control URL visibility via "Show Challenge Share Links" setting
- App functions fully offline when HF credentials not configured
**Deployment Platforms:**
- Local development: Run with `streamlit run app.py`
- Docker: Use provided `Dockerfile`
- Hugging Face Spaces: Dockerfile deployment (recommended)
- Any Python 3.10+ hosting with Streamlit support
## Development Status
**Current Version:** 0.2.4 (Production Ready - Leaderboards Implemented)
### Completed βœ…
- **v0.2.4:** Word List Filtering
- Added "Filter Wordlist" button to sidebar
- Implemented blocklist filtering via `assets/filter.txt`
- Added results dialog showing removed words
- **v0.2.1:** Daily and Weekly Leaderboards
- Settings-based leaderboard separation
- Folder-based discovery (no index.json)
- Top 20 displayed entries per leaderboard
- Four-tab leaderboard page (Today, Daily, Weekly, History)
- Automatic score qualification and submission
- Integration with challenge mode
- Query parameter filtering for direct links
- Settings page planned (move from sidebar, OAuth login required)
- **v0.1.1:** Enhanced AI word generation
- Intelligent word saving with duplicate prevention
- Automatic retry mechanism (up to 3 attempts)
- 1000-word file size limit
- Improved HF Space API integration
- Enhanced logging and error handling
- **v0.1.0:** AI word generation foundation
- Topic-based word list creation
- Dual generation modes (HF Space + local)
- Utility modules integration
- **v0.0.2:** All 7 sprints complete
- βœ… 100% test coverage (25/25 tests)
- πŸ“Š Development time: ~12.75 hours (sprints 1-7)
- πŸ“š Complete documentation
### Future Roadmap
- Local persistent storage, high score tracking, player statistics, leaderboard caching (future)
- Enhanced UI animations, retry logic, rate limiting, archival script (future)
- AI difficulty tuning, multi-language word generation, i18n (future)
## Copyright
Wrdler is based on BattleWords. BattlewordsTM. All Rights Reserved. All content, trademarks and logos are copyrighted by the owner.
## Test File Location
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.