# Wrdler Requirements **Version:** 0.2.8 **Status:** Production Ready - Leaderboards Implemented **Last Updated:** 2025-12-09 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. **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, dedicated Settings page and local settings persistence implemented in v0.2.8 ## Key Differences from BattleWords - Python project (Streamlit, Python 3.12.8) - 8x6 grid instead of 12x12 - One word per row (6 total) instead of flexible placement - Horizontal words only (no vertical) - No radar/scope visualization - 2 free letter guesses at game start ## Implementation Details (v0.2.1) - **Tech Stack:** Python 3.12.8, Streamlit 1.51.0, numpy, matplotlib, transformers, gradio_client - **Architecture:** Single-player, local state in Streamlit session state - **Grid:** 8 columns × 6 rows (48 cells) with exactly six words - **Word Placement:** Horizontal-only, one word per row, no overlaps - **AI Generation:** Topic-based word lists with intelligent saving and retry logic - **Entry Point:** `app.py` - **Testing:** pytest with 25/25 tests passing (100%) - **Development Time:** ~12.75 hours across 7 sprints (Phase 1) + AI enhancements ## Streamlit Components (Implemented in v0.0.2) - State & caching ✅ - `st.session_state` for `puzzle`, `grid_rows`, `grid_cols`, `revealed`, `guessed`, `score`, `last_action`, `can_guess` - `st.session_state.points_by_word` for per-word score breakdown - `st.session_state.letter_map` derived from puzzle - `st.session_state.selected_wordlist` for sidebar picker - `st.session_state.show_incorrect_guesses` toggle - `st.session_state.show_challenge_share_links` toggle (default OFF) - `st.session_state.free_letters` and `free_letters_used` for free letter tracking - Layout & structure ✅ - `st.title`, `st.subheader`, `st.markdown` for headers - `st.columns(8)` to render the 8×6 grid (6 rows) - Footer navigation for Play, Leaderboard, and Settings pages - Widgets (interaction) ✅ - `st.button` for each grid cell (48 total) with unique `key` - Circular green gradient free letter choice buttons (2 at game start) - `st.form` + `st.text_input` + `st.form_submit_button("OK")` for word guessing - `st.button("New Game")` to reset state - Settings page controls for wordlist selection, game mode, grid options, and audio - Visualization ✅ - Ocean-themed gradient background - No animated radar (removed in Sprint 3) - Responsive 8×6 grid layout - Cell state indicators (unrevealed, letter, empty, completed) - Control flow ✅ - App reruns on interaction using `st.rerun()` - Game over dialog with final score and tier display ## Folder Structure (Implemented) - `app.py` – Streamlit entry point ✅ - `wrdler/` – Python package ✅ - `__init__.py` (version 0.2.7) - `models.py` – data models and types (rectangular grid support) - `word_loader.py` – load/validate/cached word lists - `word_loader_ai.py` – AI word generation with retry logic - `generator.py` – word placement (8x6, horizontal only, one per row) - `logic.py` – game mechanics (reveal, guess, scoring, tiers, free letters) - `ui.py` – Streamlit UI composition (8×6 grid rendering) - `leaderboard.py` – Daily/weekly leaderboard system (v0.2.1) - `leaderboard_page.py` – Leaderboard UI page (v0.2.1) - `audio.py` – background music system - `sounds.py` – sound effects management - `game_storage.py` – HF storage wrapper for Challenge Mode - `oauth.py` – HuggingFace OAuth utilities (NEW) - `settings_page.py` – Settings page UI (complete; replaces sidebar settings) - `modules/` – shared utilities (storage with folder listing, constants, file_utils) - `words/` – word list files (classic.txt, fourth_grade.txt, wordlist.txt) - `specs/` – documentation (specs.md, requirements.md, sprint reports) - `tests/` – unit tests (test_sprint6_integration.py with 7 comprehensive tests) - `static/` – PWA assets (manifest.json, service-worker.js, icons) ## Phase 1: Wrdler v0.0.2 (Complete) ✅ **Goal:** A playable 8x6 grid game with free letter guesses, horizontal-only words, and Challenge Mode support. **Status:** ✅ All requirements complete, 7/7 sprints finished, 100% test pass rate ### 1) Data Models ✅ (Sprint 1) - ✅ `Coord(x:int, y:int)` with `in_bounds_rect()` for rectangular grid validation - ✅ `Word(text:str, start:Coord, direction:str{"H"}, cells:list[Coord])` (H only) - ✅ `Puzzle(words:list[Word], grid_rows:int, grid_cols:int, uid:str)` - ✅ `GameState(grid_rows:int, grid_cols:int, puzzle:Puzzle, revealed:set[Coord], guessed:set[str], score:int, free_letters:set[str], free_letters_used:int, ...)` **Acceptance:** ✅ Types implemented and fully integrated (13/13 tests passing) ### 2) Word List Management ✅ (Sprint 1, Enhanced in v0.1.0-0.1.1, v0.2.4) - ✅ English word list filtered to alphabetic uppercase, lengths in {4,5,6} - ✅ Loader centralized in `word_loader.py` with caching - ✅ Three word lists: classic, fourth_grade, wordlist - ✅ **Filter functionality** (v0.2.4): - Filter word lists against blocklist (`assets/filter.txt`) - Return count and list of removed words - Display results in UI dialog - ✅ **AI word generation** support via `word_loader_ai.py`: - Generates 75 words per topic (25 each of lengths 4, 5, 6) - **Dual generation modes** (v0.1.0+): - 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 saving** (v0.1.1): - Smart detection separates existing dictionary words from new AI-generated words - Only saves new words to prevent duplicates - 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 - **Additional word generation**: Automatically generates more words when MIN_REQUIRED threshold not met - **Enhanced logging**: Detailed pipeline visibility for debugging - ✅ Unified loader (`load_word_list_or_ai`) routes between file-based and AI-generated words - ✅ Saves new AI-generated words to local files for expansion **Acceptance:** ✅ Loading function returns lists by length with >= 25 words per length; AI generation produces valid words with intelligent saving and retry logic ### 3) Puzzle Generation (8x6 Horizontal) ✅ (Sprint 2) - ✅ Randomly place 6 words on 8x6 grid, one per row - ✅ **Word length requirement:** Each puzzle must have exactly 2 four-letter words, 2 five-letter words, and 2 six-letter words - ✅ Constraints: - Horizontal (left→right) only - One word per row (no stacking) - No overlapping letters - ✅ Retry strategy with max attempts - ✅ Deterministic seeding support **Acceptance:** ✅ Generator returns valid `Puzzle` with 6 words, no collisions, in-bounds (5/5 tests passing) ### 4) Free Letter Guesses ✅ (Sprint 4) - ✅ At game start, show 2 circular green gradient buttons for letter selection - ✅ On selection, reveal all instances of that letter in the grid - ✅ Mark as used; disable buttons after 2 uses - ✅ Sound effects play on reveal (hit/miss) - ✅ Set `can_guess=True` after revealing letters **Acceptance:** ✅ Both free letters properly reveal all matching cells; buttons disabled appropriately; UI renders correctly ### 5) Game Mechanics ✅ (Sprint 1, 6) - ✅ Reveal: Click a covered cell to reveal letter or mark empty - ✅ Guess: After revealing, guess word (4-6 letters) - ✅ Scoring: Base + bonus for unrevealed cells - ✅ End: All words guessed or all word letters revealed - ✅ Incorrect guess limit: 10 per game - ✅ Auto-mark completed words when all letters revealed **Acceptance:** ✅ Unit tests cover reveal, guess gating, scoring, tiers, auto-completion (7/7 integration tests passing) ### 6) UI (Streamlit) ✅ (Sprint 5) - ✅ Layout: - Title and instructions - Left: 8×6 grid using `st.columns(8)` with 6 rows - Right: Score panel, guess form, incorrect guess history - Sidebar: New Game, wordlist select, game mode, settings - ✅ Visuals: - Ocean gradient background - Covered vs revealed cell styles (40px buttons) - Completed word highlighting - Free letter selection UI with circular buttons **Acceptance:** ✅ Users can play end-to-end; all features functional; responsive design ### 7) Challenge Mode ✅ (Inherited from BattleWords) - ✅ Parse `game_id` from query params - ✅ Load game settings from HF repo - ✅ Share button generates shareable URL - ✅ Display top 5 leaderboard in Challenge Mode banner - ✅ "Show Challenge Share Links" toggle (default OFF) **Acceptance:** ✅ URL with `game_id` loads correctly; share button works; leaderboard displays properly ### 8) Utility Modules ✅ (Sprint 1) - ✅ Shared utility modules from OpenBadge project - ✅ `modules/__init__.py` exports storage, constants, file_utils - ✅ HuggingFace storage & URL shortener - ✅ File handling and utility functions **Acceptance:** ✅ Modules integrated and functional for Challenge Mode storage ### 9) Comprehensive Tests ✅ (Sprint 6) - ✅ Placement validity (bounds, no overlaps, correct counts) - ✅ Scoring logic and bonuses - ✅ Free letter reveal behavior (2-letter limit) - ✅ Guess gating and word validation - ✅ Game completion detection - ✅ Auto-mark completed words - ✅ State consistency validation **Test Results:** ✅ 25/25 tests passing (100%) ## Leaderboard System (v0.2.1) ✅ IMPLEMENTED - Leaderboard files store UTC dates/times for each period. - 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 ### Core Implementation **Files:** - `wrdler/leaderboard.py` - Core leaderboard logic (v0.2.1) - `wrdler/leaderboard_page.py` - Leaderboard UI page (v0.2.1) - `wrdler/modules/storage.py` - Enhanced with folder listing functions - `wrdler/oauth.py` - HuggingFace OAuth utilities (NEW) - `wrdler/settings_page.py` - Settings page UI (implemented; no OAuth gating yet) **Data Models:** - `GameSettings` - Settings that define a unique leaderboard - `UserEntry` - Individual score entry with uid, username, score, time, difficulty - `LeaderboardSettings` - Unified format with entry_type field **Key Features:** - ✅ **Settings-Based Separation:** Each settings combo creates a separate leaderboard - Settings: game_mode, wordlist_source, show_incorrect_guesses, enable_free_letters, puzzle_options - ✅ **Period Organization:** - Daily: `YYYY-MM-DD` (resets UTC midnight) - Weekly: `YYYY-Www` ISO week (resets Monday UTC 00:00) - ✅ **File ID Format:** `{wordlist_source}-{game_mode}-{sequence}` - Examples: `classic-classic-0`, `easy-too_easy-1` - ✅ **Folder-Based Discovery:** No index.json - Scans folders for period IDs - Filters by file_id prefix - Loads and verifies full settings match - ✅ **Top 25 Display:** Sorted by score → time → difficulty - **Sorting:** Scores sorted by: score (desc) → time (asc) → difficulty (desc) - **Qualification:** Only top 25 (configurable) scores displayed per leaderboard (more can be stored) - ✅ **Automatic Submission:** From game over popup - ✅ **Challenge Integration:** Tracks source_challenge_id **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 ``` **Leaderboard Page UI:** - ✅ **Today Tab:** Current daily and weekly leaderboards - Two-column layout - Query param filtering: `?gidd=` and `?gidw=` - ✅ **Daily Tab:** Last 7 days of daily leaderboards - Expandable date groups - All settings combinations per date - ✅ **Weekly Tab:** Current ISO week leaderboard - All settings combinations displayed - ✅ **History Tab:** Historical browser - Date/week selectors - Settings combo dropdown - Explicit load buttons **Table Features:** - Pandas DataFrame with custom styling - Rank emojis (🥇🥈🥉) - Score highlighting (green #20d46c) - Challenge indicators (🎯) - Last updated timestamps - Entry counts **Functions (leaderboard.py):** - `submit_score_to_all_leaderboards()` - Main entry point - `find_matching_leaderboard()` - Folder scanning with prefix filter - `create_or_get_leaderboard()` - Get or create with sequence management - `check_qualification()` - Top 20 filtering - `load_leaderboard()` - Load by file ID - `list_available_periods()` - List dates/weeks - `list_settings_for_period()` - List settings combos - `get_current_daily_id()` / `get_current_weekly_id()` - Period ID generators **Privacy:** - Only stores: username (required), score, time, word_list, difficulty - No PII beyond required player name - All data in HuggingFace repository **Known Limitations (v0.2.1):** - No caching (future) - No pagination beyond top 25 - Basic error handling - No rate limiting - No archival script **Future Enhancements (planned):** - Local persistent storage, high score tracking, player statistics, leaderboard caching - Enhanced UI animations, retry logic, rate limiting, archival script - AI difficulty tuning, multi-language word generation, i18n ## 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. ### PWA Support - ✅ **PWA Installation:** App is installable as a Progressive Web App on desktop and mobile