0.1.2 Minor updates for challenges
Browse files- CLAUDE.md +51 -107
- wrdler/__init__.py +1 -1
- wrdler/ui.py +68 -19
- wrdler/words/english.txt +81 -7
- wrdler/words/sports.txt +136 -8
CLAUDE.md
CHANGED
|
@@ -307,31 +307,67 @@ docker run -p 8501:8501 wrdler
|
|
| 307 |
pytest tests/
|
| 308 |
```
|
| 309 |
|
| 310 |
-
### Environment Variables
|
| 311 |
-
|
|
|
|
| 312 |
|
| 313 |
```bash
|
| 314 |
-
#
|
| 315 |
-
|
| 316 |
-
|
| 317 |
-
|
|
|
|
|
|
|
|
|
|
| 318 |
|
| 319 |
# Optional
|
| 320 |
-
CRYPTO_PK= # Reserved for future signing
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 321 |
```
|
| 322 |
|
| 323 |
-
|
| 324 |
1. Go to https://huggingface.co/settings/tokens
|
| 325 |
2. Create a new token with `write` access
|
| 326 |
3. Add to `.env` file as `HF_API_TOKEN=hf_...`
|
| 327 |
|
| 328 |
-
|
| 329 |
The dataset repository will contain:
|
| 330 |
- `shortener.json` - Short URL mappings
|
| 331 |
- `games/{uid}/settings.json` - Per-game challenge data
|
| 332 |
- `games/{uid}/result.json` - Optional detailed results
|
| 333 |
|
| 334 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 335 |
|
| 336 |
## Git Configuration & Deployment
|
| 337 |
**Current Branch:** main
|
|
@@ -438,100 +474,8 @@ The dataset repository will contain:
|
|
| 438 |
- ✅ PWA injection via Docker build script (`inject-pwa-head.sh`)
|
| 439 |
- ✅ AI word generation via `word_loader_ai.py` with Hugging Face integration
|
| 440 |
- ✅ Utility modules provide reusable functions for storage and file ops
|
| 441 |
-
|
| 442 |
-
|
| 443 |
-
-
|
| 444 |
-
-
|
| 445 |
-
-
|
| 446 |
-
- **One word per row** - exactly 6 words total
|
| 447 |
-
- **Word length requirement** - exactly 2 four-letter words, 2 five-letter words, and 2 six-letter words per puzzle
|
| 448 |
-
- **Free letters tracked** - `free_letters` set and `free_letters_used` counter
|
| 449 |
-
- **Auto-completion** - words auto-marked when all letters revealed
|
| 450 |
-
- **Incorrect guess limit** - maximum 10 per game
|
| 451 |
-
- **AI word generation** - generates 75 words per topic, saves to local files
|
| 452 |
-
- **Utility modules** - shared functions from OpenBadge project
|
| 453 |
-
|
| 454 |
-
### WSL Environment Python Versions
|
| 455 |
-
The development environment is WSL (Windows Subsystem for Linux) with access to both native Linux and Windows Python installations:
|
| 456 |
-
|
| 457 |
-
**Native WSL (Linux):**
|
| 458 |
-
- `python3` → Python 3.10.12 (`/usr/bin/python3`)
|
| 459 |
-
- `python3.10` → Python 3.10.12
|
| 460 |
-
|
| 461 |
-
**Windows Python (accessible via WSL):**
|
| 462 |
-
- `python311.exe` → Python 3.11.9 (`/mnt/c/Users/cfettinger/AppData/Local/Programs/Python/Python311/`)
|
| 463 |
-
- `python3.13.exe` → Python 3.13.1 (`/mnt/c/ProgramData/chocolatey/bin/`)
|
| 464 |
-
|
| 465 |
-
**Note:** Windows Python executables (`.exe`) can be invoked directly from WSL and are useful for testing compatibility across Python versions. The project targets Python 3.12.8 specifically but requires >=3.12, <3.13 per pyproject.toml.
|
| 466 |
-
|
| 467 |
-
## Documentation Structure
|
| 468 |
-
|
| 469 |
-
This file (CLAUDE.md) serves as a **living context document** for AI-assisted development:
|
| 470 |
-
|
| 471 |
-
- **[specs/specs.md](specs/specs.md)** - Game rules, requirements, and feature specifications
|
| 472 |
-
- **[specs/requirements.md](specs/requirements.md)** - Implementation requirements and acceptance criteria
|
| 473 |
-
- **[GAMEPLAY_GUIDE.md](GAMEPLAY_GUIDE.md)** - User guide with tips and strategies
|
| 474 |
-
- **[INSTALL_GUIDE.md](INSTALL_GUIDE.md)** - PWA installation instructions
|
| 475 |
-
- **[README.md](README.md)** - User-facing documentation, installation guide, and complete changelog
|
| 476 |
-
- **[Dockerfile](Dockerfile)** - Container deployment configuration with PWA injection
|
| 477 |
-
- **[pyproject.toml](pyproject.toml)** - Python project metadata and dependencies
|
| 478 |
-
|
| 479 |
-
**When to use each:**
|
| 480 |
-
- **specs.md** - Understanding game rules and scoring system
|
| 481 |
-
- **requirements.md** - Implementation status and acceptance criteria
|
| 482 |
-
- **CLAUDE.md** - Quick reference for codebase and development context
|
| 483 |
-
- **GAMEPLAY_GUIDE.md** - How to play the game
|
| 484 |
-
- **README.md** - Public-facing info, setup instructions, and complete changelog
|
| 485 |
-
- **INSTALL_GUIDE.md** - Installing Wrdler as a PWA
|
| 486 |
-
- **Dockerfile** - Deployment configuration and container setup
|
| 487 |
-
|
| 488 |
-
## Challenge Mode & Remote Storage
|
| 489 |
-
|
| 490 |
-
- ✅ Challenge Mode allows sharing games via short links (`?game_id=<sid>`)
|
| 491 |
-
- ✅ Results stored in Hugging Face dataset repos via `game_storage.py`
|
| 492 |
-
- ✅ Leaderboard sorted by: highest score → fastest time → highest difficulty
|
| 493 |
-
- ✅ Multi-user challenges with top 5 display
|
| 494 |
-
- ✅ Optional sharing (controlled by "Show Challenge Share Links" toggle, default OFF)
|
| 495 |
-
- ✅ Word list difficulty calculation (v0.2.29)
|
| 496 |
-
- ✅ iframe embedding support with `&iframe_host=` parameter (v0.2.23)
|
| 497 |
-
|
| 498 |
-
## Known Issues
|
| 499 |
-
|
| 500 |
-
### Active
|
| 501 |
-
- ❓ 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.
|
| 502 |
-
|
| 503 |
-
### Resolved
|
| 504 |
-
- ✅ Duplicate rendering call bug (fixed in v0.0.2)
|
| 505 |
-
- ✅ Music looping on congratulations screen (fixed in v0.2.12)
|
| 506 |
-
- ✅ Sound effect and music volume wiring (fixed in v0.2.18, v0.2.19)
|
| 507 |
-
- ✅ Sonar grid alignment (removed in Sprint 3)
|
| 508 |
-
- ✅ Challenge mode link issues (fixed in v0.2.22)
|
| 509 |
-
|
| 510 |
-
## Dependencies
|
| 511 |
-
|
| 512 |
-
From `requirements.txt`:
|
| 513 |
-
- streamlit>=1.51.0 (primary framework)
|
| 514 |
-
- matplotlib>=3.8 (visualization)
|
| 515 |
-
- requests>=2.31.0 (HTTP requests)
|
| 516 |
-
- huggingface_hub>=0.20.0 (remote storage)
|
| 517 |
-
- python-dotenv>=1.0.0 (environment variables)
|
| 518 |
-
- transformers (AI word generation)
|
| 519 |
-
- gradio_client (HF Space API)
|
| 520 |
-
|
| 521 |
-
From `pyproject.toml`:
|
| 522 |
-
- Python >=3.12, <3.13 (strict version requirement)
|
| 523 |
-
|
| 524 |
-
## Version History Summary
|
| 525 |
-
|
| 526 |
-
- **v0.1.1** (Current) - Enhanced AI word generation with intelligent saving, retry logic, file size limits
|
| 527 |
-
- **v0.1.0** (Previous) - AI word generation, utility modules, version bump
|
| 528 |
-
- **v0.0.4** - Documentation sync, version update
|
| 529 |
-
- **v0.0.2-0.0.3** - All 7 sprints complete, core Wrdler features
|
| 530 |
-
- **v0.2.20-0.2.29** - Challenge Mode, PWA, remote storage (inherited from BattleWords)
|
| 531 |
-
- **v0.1.x** - Initial BattleWords releases before Wrdler fork
|
| 532 |
-
|
| 533 |
-
---
|
| 534 |
-
|
| 535 |
-
**Last Updated:** 2025-01-31
|
| 536 |
-
**Current Version:** 0.1.1
|
| 537 |
-
**Status:** Production Ready - AI Enhanced ✅
|
|
|
|
| 307 |
pytest tests/
|
| 308 |
```
|
| 309 |
|
| 310 |
+
### Environment Variables
|
| 311 |
+
|
| 312 |
+
Wrdler uses environment variables for optional features. Create a `.env` file in the project root:
|
| 313 |
|
| 314 |
```bash
|
| 315 |
+
# ============================================
|
| 316 |
+
# Challenge Mode (Remote Storage)
|
| 317 |
+
# ============================================
|
| 318 |
+
# Required for Challenge Mode features (game sharing, leaderboards)
|
| 319 |
+
HF_API_TOKEN=hf_xxxxxxxxxxxxxxxxxxxxx # or HF_TOKEN - HuggingFace API token with write access
|
| 320 |
+
HF_REPO_ID=YourUsername/YourRepo # Target HF dataset repo for challenge storage
|
| 321 |
+
SPACE_NAME=YourUsername/Wrdler # Your HF Space name (for deployment)
|
| 322 |
|
| 323 |
# Optional
|
| 324 |
+
CRYPTO_PK= # Reserved for future challenge signing features
|
| 325 |
+
|
| 326 |
+
# ============================================
|
| 327 |
+
# AI Word Generation
|
| 328 |
+
# ============================================
|
| 329 |
+
# Controls AI-powered word list generation
|
| 330 |
+
USE_HF_WORDS=false # Enable HF Space API for word generation
|
| 331 |
+
# - true: Use Hugging Face Space API (primary)
|
| 332 |
+
# - false: Use local transformers models (fallback)
|
| 333 |
+
|
| 334 |
+
HF_WORD_LIST_REPO_ID=YourUsername/WordRepo # HF dataset repo for AI-generated word lists
|
| 335 |
+
# (Required if USE_HF_WORDS=true)
|
| 336 |
+
|
| 337 |
+
IS_LOCAL= # Override environment detection
|
| 338 |
+
# - true: Force local development mode
|
| 339 |
+
# - false: Force production mode
|
| 340 |
+
# - (empty): Auto-detect based on environment
|
| 341 |
```
|
| 342 |
|
| 343 |
+
#### Getting Your HF_API_TOKEN
|
| 344 |
1. Go to https://huggingface.co/settings/tokens
|
| 345 |
2. Create a new token with `write` access
|
| 346 |
3. Add to `.env` file as `HF_API_TOKEN=hf_...`
|
| 347 |
|
| 348 |
+
#### HF_REPO_ID Structure (Challenge Mode)
|
| 349 |
The dataset repository will contain:
|
| 350 |
- `shortener.json` - Short URL mappings
|
| 351 |
- `games/{uid}/settings.json` - Per-game challenge data
|
| 352 |
- `games/{uid}/result.json` - Optional detailed results
|
| 353 |
|
| 354 |
+
#### HF_WORD_LIST_REPO_ID Structure (AI Word Generation)
|
| 355 |
+
The dataset repository will contain:
|
| 356 |
+
- `words/{topic}.txt` - AI-generated word lists by topic
|
| 357 |
+
- Auto-managed by `word_loader_ai.py`
|
| 358 |
+
- Maximum 1000 words per file
|
| 359 |
+
- Sorted by length then alphabetically
|
| 360 |
+
|
| 361 |
+
#### Environment Variable Behavior
|
| 362 |
+
**Without these variables:**
|
| 363 |
+
- Challenge Mode features (sharing, leaderboards) will be disabled
|
| 364 |
+
- AI word generation will fall back to local transformers models
|
| 365 |
+
- Core game functionality remains fully operational
|
| 366 |
+
|
| 367 |
+
**With these variables:**
|
| 368 |
+
- Challenge Mode enables game sharing via short URLs
|
| 369 |
+
- AI generation can use HF Space API (faster, more reliable)
|
| 370 |
+
- Remote storage for multi-user leaderboards
|
| 371 |
|
| 372 |
## Git Configuration & Deployment
|
| 373 |
**Current Branch:** main
|
|
|
|
| 474 |
- ✅ PWA injection via Docker build script (`inject-pwa-head.sh`)
|
| 475 |
- ✅ AI word generation via `word_loader_ai.py` with Hugging Face integration
|
| 476 |
- ✅ Utility modules provide reusable functions for storage and file ops
|
| 477 |
+
- ⚠️ **IMPORTANT: Use Python syntax (colons `:`) NOT JavaScript syntax (curly braces `{}`)**
|
| 478 |
+
- Python: `if condition:` followed by indented block
|
| 479 |
+
- NOT: `if condition {` - this is JavaScript/C-style syntax
|
| 480 |
+
- Python: `try:` / `except Exception:` / `else:`
|
| 481 |
+
- NOT: `try {` / `} catch (Exception) {` / `} else {`
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
wrdler/__init__.py
CHANGED
|
@@ -8,5 +8,5 @@ Key differences from BattleWords:
|
|
| 8 |
- 2 free letter guesses at game start
|
| 9 |
"""
|
| 10 |
|
| 11 |
-
__version__ = "0.1.
|
| 12 |
__all__ = ["models", "generator", "logic", "ui", "word_loader"]
|
|
|
|
| 8 |
- 2 free letter guesses at game start
|
| 9 |
"""
|
| 10 |
|
| 11 |
+
__version__ = "0.1.2"
|
| 12 |
__all__ = ["models", "generator", "logic", "ui", "word_loader"]
|
wrdler/ui.py
CHANGED
|
@@ -482,7 +482,7 @@ border-radius: 50% !important;
|
|
| 482 |
text-align: center;
|
| 483 |
}
|
| 484 |
div[data-testid="stButton"] button { max-width: 100%; aspect-ratio: 16 / 11; border-radius: 0; background: #1d64c8; color: #ffffff; font-weight: 700; padding: 0.5rem 0.75rem; min-height: 2.5rem; min-width: 1.75rem;}
|
| 485 |
-
.st-key-new_game_btn, .st-key-sort_wordlist_btn { margin: 0 auto; aspect-ratio: unset; }
|
| 486 |
.st-key-new_game_btn > div[data-testid="stButton"] button, .st-key-sort_wordlist_btn > div[data-testid="stButton"] button { aspect-ratio: unset; text-align:center; height: auto;}
|
| 487 |
|
| 488 |
div[data-testid="column"], .st-emotion-cache-zh2fnc { width: auto !important; flex: 1 1 auto !important; min-width: 100% !important; max-width: 100% !important; }
|
|
@@ -671,9 +671,9 @@ def _init_session() -> None:
|
|
| 671 |
if "show_incorrect_guesses" not in st.session_state:
|
| 672 |
st.session_state.show_incorrect_guesses = True
|
| 673 |
|
| 674 |
-
# NEW: Initialize Show Challenge Share Links (default
|
| 675 |
if "show_challenge_share_links" not in st.session_state:
|
| 676 |
-
st.session_state.show_challenge_share_links =
|
| 677 |
|
| 678 |
# NEW: Initialize free letters tracking
|
| 679 |
if "free_letters" not in st.session_state:
|
|
@@ -695,7 +695,7 @@ def _new_game() -> None:
|
|
| 695 |
mode = st.session_state.get("game_mode", "classic")
|
| 696 |
show_grid_ticks = st.session_state.get("show_grid_ticks", False)
|
| 697 |
show_incorrect = st.session_state.get("show_incorrect_guesses", True)
|
| 698 |
-
show_challenge_share_links = st.session_state.get("show_challenge_share_links",
|
| 699 |
|
| 700 |
shared_settings = st.session_state.get("shared_game_settings")
|
| 701 |
|
|
@@ -737,8 +737,8 @@ def _new_game() -> None:
|
|
| 737 |
st.session_state.free_letters = set()
|
| 738 |
st.session_state.free_letters_used = 0
|
| 739 |
|
| 740 |
-
# Preserve preferences
|
| 741 |
-
|
| 742 |
st.session_state.show_grid_ticks = show_grid_ticks
|
| 743 |
st.session_state.show_incorrect_guesses = show_incorrect
|
| 744 |
st.session_state.initialized = True # Prevent _init_session from overwriting
|
|
@@ -789,6 +789,37 @@ def _render_header():
|
|
| 789 |
is_challenge_mode = ( "shared_game_settings" in st.session_state and "game_id" in params ) or st.session_state.get("share_sid")
|
| 790 |
|
| 791 |
if is_challenge_mode:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 792 |
with st.expander("🎯 Challenge Mode (click to expand/collapse)", expanded=True):
|
| 793 |
shared_settings = st.session_state.get("shared_game_settings")
|
| 794 |
if shared_settings:
|
|
@@ -811,8 +842,8 @@ def _render_header():
|
|
| 811 |
# Build leaderboard HTML
|
| 812 |
leaderboard_rows = []
|
| 813 |
for i, user in enumerate(sorted_users[:5], 1): # Top 5
|
| 814 |
-
u_mins,
|
| 815 |
-
u_time_str = f"{u_mins:02d}:{
|
| 816 |
medal = ["🥇", "🥈", "🥉"][i-1] if i <= 3 else f"{i}."
|
| 817 |
# show optional difficulty if present
|
| 818 |
diff_str = ""
|
|
@@ -832,10 +863,11 @@ def _render_header():
|
|
| 832 |
# NEW: Only render share link when setting enabled
|
| 833 |
if sid and st.session_state.get("show_challenge_share_links", False):
|
| 834 |
share_url = get_shareable_url(sid)
|
| 835 |
-
share_html = f"<div style='margin-top:1rem;margin-bottom:0.5rem;font-size: 0.9rem;'><a href='{share_url}' target='_blank' style='color:#FFF;text-decoration:underline;'><strong>🔗 Share this challenge</a></strong
|
| 836 |
|
| 837 |
st.markdown(
|
| 838 |
f"""
|
|
|
|
| 839 |
<div style="
|
| 840 |
background: linear-gradient(90deg, #1d64c8 0%, #165ba8 100%);
|
| 841 |
color: white;
|
|
@@ -845,6 +877,10 @@ def _render_header():
|
|
| 845 |
text-align: center;
|
| 846 |
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
| 847 |
">
|
|
|
|
|
|
|
|
|
|
|
|
|
| 848 |
🎯 <strong>CHALLENGE MODE</strong> 🎯<br/>
|
| 849 |
<span style="font-size: 0.9rem;">
|
| 850 |
Beat the best: <strong>{best_score} points</strong> in <strong>{best_time_str}</strong> by <strong>{best_user['username']}</strong>
|
|
@@ -861,6 +897,7 @@ def _render_header():
|
|
| 861 |
else:
|
| 862 |
st.markdown(
|
| 863 |
"""
|
|
|
|
| 864 |
<div style="
|
| 865 |
background: linear-gradient(90deg, #1d64c8 0%, #165ba8 100%);
|
| 866 |
color: white;
|
|
@@ -870,6 +907,10 @@ def _render_header():
|
|
| 870 |
text-align: center;
|
| 871 |
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
| 872 |
">
|
|
|
|
|
|
|
|
|
|
|
|
|
| 873 |
🎯 <strong>CHALLENGE MODE</strong> 🎯<br/>
|
| 874 |
<span style="font-size: 0.9rem;">
|
| 875 |
Be the first to complete this challenge!
|
|
@@ -989,9 +1030,9 @@ def _render_sidebar():
|
|
| 989 |
st.session_state.show_incorrect_guesses = True
|
| 990 |
st.checkbox("Show incorrect guesses", value=st.session_state.show_incorrect_guesses, key="show_incorrect_guesses")
|
| 991 |
|
| 992 |
-
# NEW: Add Show Challenge Share Links option - default
|
| 993 |
if "show_challenge_share_links" not in st.session_state:
|
| 994 |
-
st.session_state.show_challenge_share_links =
|
| 995 |
st.checkbox("Show Challenge Share Links", value=st.session_state.show_challenge_share_links, key="show_challenge_share_links")
|
| 996 |
|
| 997 |
# NEW: Initialize Enable Free Letters (default OFF)
|
|
@@ -1677,7 +1718,7 @@ def _game_over_content(state: GameState) -> None:
|
|
| 1677 |
color: #fff;
|
| 1678 |
padding: 16px;
|
| 1679 |
}
|
| 1680 |
-
.bw-dialog-header { display:flex; justify-content: space-between; align-items:center; }
|
| 1681 |
.bw-dialog-title, .st-emotion-cache-11elpad p { margin: 0; font-weight: bold;font-size: 1.5rem;text-align: center;flex: auto;filter:drop-shadow(1px 1px 2px #003);}
|
| 1682 |
.text-success { color: #20d46c;font-weight: bold;font-size: 1.5rem;text-align: center;flex: auto;filter:drop-shadow(1px 1px 2px #003); }
|
| 1683 |
.st-key-new_game_btn_dialog, .st-key-close_game_over { width: 50% !important;
|
|
@@ -1689,6 +1730,9 @@ def _game_over_content(state: GameState) -> None:
|
|
| 1689 |
.st-key-new_game_btn_dialog button, .st-key-close_game_over button {
|
| 1690 |
height: 50px !important;
|
| 1691 |
}
|
|
|
|
|
|
|
|
|
|
| 1692 |
.st-key-new_game_btn_dialog:hover, .st-key-close_game_over:hover{
|
| 1693 |
/*background: linear-gradient(-45deg, #1d64c8, #ffffff, #1d64c8, #666666);*/
|
| 1694 |
background: #1d64c8 !important;
|
|
@@ -1904,8 +1948,8 @@ def _game_over_content(state: GameState) -> None:
|
|
| 1904 |
|
| 1905 |
# Dialog actions
|
| 1906 |
if st.button("Close", key="close_game_over"):
|
| 1907 |
-
st.session_state["
|
| 1908 |
-
st.session_state["remount_background_audio"] = True
|
| 1909 |
st.rerun()
|
| 1910 |
|
| 1911 |
# Prefer st.dialog/experimental_dialog; fallback to st.modal if unavailable
|
|
@@ -1926,7 +1970,8 @@ else:
|
|
| 1926 |
_game_over_content(state)
|
| 1927 |
|
| 1928 |
def _render_game_over(state: GameState):
|
| 1929 |
-
|
|
|
|
| 1930 |
music_dir = _get_music_dir()
|
| 1931 |
if visible:
|
| 1932 |
# Mount congratulations music (play once, do not loop) only if music is enabled
|
|
@@ -2000,7 +2045,7 @@ def run_app():
|
|
| 2000 |
st.session_state["shared_game_loaded"] = True # Prevent repeated attempts
|
| 2001 |
except Exception as e:
|
| 2002 |
st.error(f"❌ Error loading shared game: {e}")
|
| 2003 |
-
st.session_state["shared_game_loaded"] = True # Prevent repeated attempts
|
| 2004 |
|
| 2005 |
_init_session()
|
| 2006 |
st.markdown(ocean_background_css, unsafe_allow_html=True)
|
|
@@ -2024,12 +2069,15 @@ def run_app():
|
|
| 2024 |
#st.divider()
|
| 2025 |
_render_score_panel(state)
|
| 2026 |
with left:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2027 |
# Show free letter selection if enabled and not complete
|
| 2028 |
if st.session_state.get("enable_free_letters", False) and state.free_letters_used < 2:
|
| 2029 |
_render_free_letters(state)
|
| 2030 |
|
| 2031 |
_render_grid(state, st.session_state.letter_map, show_grid_ticks=st.session_state.get("show_grid_ticks", True))
|
| 2032 |
-
st.button("New Game", width=125, on_click=_new_game, key="new_game_btn")
|
| 2033 |
|
| 2034 |
# End condition (only show overlay if dismissed)
|
| 2035 |
state = _to_state()
|
|
@@ -2039,7 +2087,7 @@ def run_app():
|
|
| 2039 |
def _on_game_option_change() -> None:
|
| 2040 |
"""
|
| 2041 |
Unified callback for game option changes.
|
| 2042 |
-
If currently in a loaded challenge, break the link by resetting challenge
|
| 2043 |
and removing the game_id query param. Then start a new game with the updated options.
|
| 2044 |
"""
|
| 2045 |
try:
|
|
@@ -2049,7 +2097,7 @@ def _on_game_option_change() -> None:
|
|
| 2049 |
# st.query_params may be a Mapping; pop safely if supported
|
| 2050 |
try:
|
| 2051 |
if "game_id" in qp:
|
| 2052 |
-
qp.pop("game_id")
|
| 2053 |
except Exception:
|
| 2054 |
# Fallback: clear all params if pop not supported
|
| 2055 |
try:
|
|
@@ -2059,6 +2107,7 @@ def _on_game_option_change() -> None:
|
|
| 2059 |
except Exception:
|
| 2060 |
pass
|
| 2061 |
|
|
|
|
| 2062 |
# Clear challenge session flags and links
|
| 2063 |
if st.session_state.get("loaded_game_sid") is not None:
|
| 2064 |
st.session_state.loaded_game_sid = None
|
|
|
|
| 482 |
text-align: center;
|
| 483 |
}
|
| 484 |
div[data-testid="stButton"] button { max-width: 100%; aspect-ratio: 16 / 11; border-radius: 0; background: #1d64c8; color: #ffffff; font-weight: 700; padding: 0.5rem 0.75rem; min-height: 2.5rem; min-width: 1.75rem;}
|
| 485 |
+
.st-key-new_game_btn, .st-key-sort_wordlist_btn { margin: 0 auto; aspect-ratio: unset; z-index:9999;}
|
| 486 |
.st-key-new_game_btn > div[data-testid="stButton"] button, .st-key-sort_wordlist_btn > div[data-testid="stButton"] button { aspect-ratio: unset; text-align:center; height: auto;}
|
| 487 |
|
| 488 |
div[data-testid="column"], .st-emotion-cache-zh2fnc { width: auto !important; flex: 1 1 auto !important; min-width: 100% !important; max-width: 100% !important; }
|
|
|
|
| 671 |
if "show_incorrect_guesses" not in st.session_state:
|
| 672 |
st.session_state.show_incorrect_guesses = True
|
| 673 |
|
| 674 |
+
# NEW: Initialize Show Challenge Share Links (default ON)
|
| 675 |
if "show_challenge_share_links" not in st.session_state:
|
| 676 |
+
st.session_state.show_challenge_share_links = True
|
| 677 |
|
| 678 |
# NEW: Initialize free letters tracking
|
| 679 |
if "free_letters" not in st.session_state:
|
|
|
|
| 695 |
mode = st.session_state.get("game_mode", "classic")
|
| 696 |
show_grid_ticks = st.session_state.get("show_grid_ticks", False)
|
| 697 |
show_incorrect = st.session_state.get("show_incorrect_guesses", True)
|
| 698 |
+
show_challenge_share_links = st.session_state.get("show_challenge_share_links", True) # preserve (default True)
|
| 699 |
|
| 700 |
shared_settings = st.session_state.get("shared_game_settings")
|
| 701 |
|
|
|
|
| 737 |
st.session_state.free_letters = set()
|
| 738 |
st.session_state.free_letters_used = 0
|
| 739 |
|
| 740 |
+
# Preserve preferences - but do NOT set widget-bound keys like game_mode
|
| 741 |
+
# game_mode is managed by the selectbox widget in _render_sidebar()
|
| 742 |
st.session_state.show_grid_ticks = show_grid_ticks
|
| 743 |
st.session_state.show_incorrect_guesses = show_incorrect
|
| 744 |
st.session_state.initialized = True # Prevent _init_session from overwriting
|
|
|
|
| 789 |
is_challenge_mode = ( "shared_game_settings" in st.session_state and "game_id" in params ) or st.session_state.get("share_sid")
|
| 790 |
|
| 791 |
if is_challenge_mode:
|
| 792 |
+
# Add exit challenge mode button/link styling
|
| 793 |
+
st.markdown(
|
| 794 |
+
"""
|
| 795 |
+
<style>
|
| 796 |
+
.bw-challenge-header {
|
| 797 |
+
display: flex;
|
| 798 |
+
justify-content: space-between;
|
| 799 |
+
align-items: center;
|
| 800 |
+
margin-bottom: 0.5rem;
|
| 801 |
+
}
|
| 802 |
+
.bw-exit-challenge {
|
| 803 |
+
# background: rgba(255, 75, 75, 0.2);
|
| 804 |
+
# border: 1px solid rgba(255, 75, 75, 0.5);
|
| 805 |
+
color: #fff;
|
| 806 |
+
padding: 0.25rem 0.75rem;
|
| 807 |
+
border-radius: 0.25rem;
|
| 808 |
+
text-decoration: none !important;
|
| 809 |
+
font-size: 0.85rem;
|
| 810 |
+
cursor: pointer;
|
| 811 |
+
transition: all 0.2s ease;
|
| 812 |
+
}
|
| 813 |
+
.bw-exit-challenge:hover {
|
| 814 |
+
background: rgba(255, 75, 75, 0.4);
|
| 815 |
+
border-color: rgba(255, 75, 75, 0.8);
|
| 816 |
+
text-decoration: none;
|
| 817 |
+
}
|
| 818 |
+
</style>
|
| 819 |
+
""",
|
| 820 |
+
unsafe_allow_html=True
|
| 821 |
+
)
|
| 822 |
+
|
| 823 |
with st.expander("🎯 Challenge Mode (click to expand/collapse)", expanded=True):
|
| 824 |
shared_settings = st.session_state.get("shared_game_settings")
|
| 825 |
if shared_settings:
|
|
|
|
| 842 |
# Build leaderboard HTML
|
| 843 |
leaderboard_rows = []
|
| 844 |
for i, user in enumerate(sorted_users[:5], 1): # Top 5
|
| 845 |
+
u_mins, uSecs = divmod(user["time"], 60)
|
| 846 |
+
u_time_str = f"{u_mins:02d}:{uSecs:02d}"
|
| 847 |
medal = ["🥇", "🥈", "🥉"][i-1] if i <= 3 else f"{i}."
|
| 848 |
# show optional difficulty if present
|
| 849 |
diff_str = ""
|
|
|
|
| 863 |
# NEW: Only render share link when setting enabled
|
| 864 |
if sid and st.session_state.get("show_challenge_share_links", False):
|
| 865 |
share_url = get_shareable_url(sid)
|
| 866 |
+
share_html = f"<div style='margin-top:1rem;margin-bottom:0.5rem;font-size: 0.9rem;'><a href='{share_url}' target='_blank' style='color:#FFF;text-decoration:underline;'><strong>🔗 Share this challenge</a></strong><br/><br/><span style='font-size:0.85em;color:#ddd;'>{share_url}</span></div>"
|
| 867 |
|
| 868 |
st.markdown(
|
| 869 |
f"""
|
| 870 |
+
|
| 871 |
<div style="
|
| 872 |
background: linear-gradient(90deg, #1d64c8 0%, #165ba8 100%);
|
| 873 |
color: white;
|
|
|
|
| 877 |
text-align: center;
|
| 878 |
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
| 879 |
">
|
| 880 |
+
<div class="bw-challenge-header">
|
| 881 |
+
<div style="flex: 1;"></div>
|
| 882 |
+
<a href="/" target="_self" class="bw-exit-challenge" title="Exit challenge mode and start a new game">❎ Exit</a>
|
| 883 |
+
</div>
|
| 884 |
🎯 <strong>CHALLENGE MODE</strong> 🎯<br/>
|
| 885 |
<span style="font-size: 0.9rem;">
|
| 886 |
Beat the best: <strong>{best_score} points</strong> in <strong>{best_time_str}</strong> by <strong>{best_user['username']}</strong>
|
|
|
|
| 897 |
else:
|
| 898 |
st.markdown(
|
| 899 |
"""
|
| 900 |
+
|
| 901 |
<div style="
|
| 902 |
background: linear-gradient(90deg, #1d64c8 0%, #165ba8 100%);
|
| 903 |
color: white;
|
|
|
|
| 907 |
text-align: center;
|
| 908 |
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
| 909 |
">
|
| 910 |
+
<div class="bw-challenge-header">
|
| 911 |
+
<div style="flex: 1;"></div>
|
| 912 |
+
<a href="/" target="_self" class="bw-exit-challenge" title="Exit challenge mode and start a new game">❎ Exit</a>
|
| 913 |
+
</div>
|
| 914 |
🎯 <strong>CHALLENGE MODE</strong> 🎯<br/>
|
| 915 |
<span style="font-size: 0.9rem;">
|
| 916 |
Be the first to complete this challenge!
|
|
|
|
| 1030 |
st.session_state.show_incorrect_guesses = True
|
| 1031 |
st.checkbox("Show incorrect guesses", value=st.session_state.show_incorrect_guesses, key="show_incorrect_guesses")
|
| 1032 |
|
| 1033 |
+
# NEW: Add Show Challenge Share Links option - default ON
|
| 1034 |
if "show_challenge_share_links" not in st.session_state:
|
| 1035 |
+
st.session_state.show_challenge_share_links = True
|
| 1036 |
st.checkbox("Show Challenge Share Links", value=st.session_state.show_challenge_share_links, key="show_challenge_share_links")
|
| 1037 |
|
| 1038 |
# NEW: Initialize Enable Free Letters (default OFF)
|
|
|
|
| 1718 |
color: #fff;
|
| 1719 |
padding: 16px;
|
| 1720 |
}
|
| 1721 |
+
.bw-dialog-header { display:flex; justify-content: space-between; align-items: center; }
|
| 1722 |
.bw-dialog-title, .st-emotion-cache-11elpad p { margin: 0; font-weight: bold;font-size: 1.5rem;text-align: center;flex: auto;filter:drop-shadow(1px 1px 2px #003);}
|
| 1723 |
.text-success { color: #20d46c;font-weight: bold;font-size: 1.5rem;text-align: center;flex: auto;filter:drop-shadow(1px 1px 2px #003); }
|
| 1724 |
.st-key-new_game_btn_dialog, .st-key-close_game_over { width: 50% !important;
|
|
|
|
| 1730 |
.st-key-new_game_btn_dialog button, .st-key-close_game_over button {
|
| 1731 |
height: 50px !important;
|
| 1732 |
}
|
| 1733 |
+
.st-key-new_game_btn button {
|
| 1734 |
+
transition: none !important;
|
| 1735 |
+
}
|
| 1736 |
.st-key-new_game_btn_dialog:hover, .st-key-close_game_over:hover{
|
| 1737 |
/*background: linear-gradient(-45deg, #1d64c8, #ffffff, #1d64c8, #666666);*/
|
| 1738 |
background: #1d64c8 !important;
|
|
|
|
| 1948 |
|
| 1949 |
# Dialog actions
|
| 1950 |
if st.button("Close", key="close_game_over"):
|
| 1951 |
+
st.session_state["hide_gameover_overlay"] = True # Changed from show_gameover_overlay = False
|
| 1952 |
+
st.session_state["remount_background_audio"] = True
|
| 1953 |
st.rerun()
|
| 1954 |
|
| 1955 |
# Prefer st.dialog/experimental_dialog; fallback to st.modal if unavailable
|
|
|
|
| 1970 |
_game_over_content(state)
|
| 1971 |
|
| 1972 |
def _render_game_over(state: GameState):
|
| 1973 |
+
# Use hide_gameover_overlay (same key as run_app) with inverted logic
|
| 1974 |
+
visible = not st.session_state.get("hide_gameover_overlay", False) and is_game_over(state)
|
| 1975 |
music_dir = _get_music_dir()
|
| 1976 |
if visible:
|
| 1977 |
# Mount congratulations music (play once, do not loop) only if music is enabled
|
|
|
|
| 2045 |
st.session_state["shared_game_loaded"] = True # Prevent repeated attempts
|
| 2046 |
except Exception as e:
|
| 2047 |
st.error(f"❌ Error loading shared game: {e}")
|
| 2048 |
+
st.session_state["shared_game_loaded"] = True # Prevent repeated attempts
|
| 2049 |
|
| 2050 |
_init_session()
|
| 2051 |
st.markdown(ocean_background_css, unsafe_allow_html=True)
|
|
|
|
| 2069 |
#st.divider()
|
| 2070 |
_render_score_panel(state)
|
| 2071 |
with left:
|
| 2072 |
+
# New Game button moved above grid to prevent unnecessary rerenders on grid clicks
|
| 2073 |
+
# Using on_click callback ensures _new_game runs before widgets are instantiated
|
| 2074 |
+
st.button("New Game", width=125, on_click=_new_game, key="new_game_btn")
|
| 2075 |
+
|
| 2076 |
# Show free letter selection if enabled and not complete
|
| 2077 |
if st.session_state.get("enable_free_letters", False) and state.free_letters_used < 2:
|
| 2078 |
_render_free_letters(state)
|
| 2079 |
|
| 2080 |
_render_grid(state, st.session_state.letter_map, show_grid_ticks=st.session_state.get("show_grid_ticks", True))
|
|
|
|
| 2081 |
|
| 2082 |
# End condition (only show overlay if dismissed)
|
| 2083 |
state = _to_state()
|
|
|
|
| 2087 |
def _on_game_option_change() -> None:
|
| 2088 |
"""
|
| 2089 |
Unified callback for game option changes.
|
| 2090 |
+
If currently in a loaded challenge, break the link by resetting challenge mode
|
| 2091 |
and removing the game_id query param. Then start a new game with the updated options.
|
| 2092 |
"""
|
| 2093 |
try:
|
|
|
|
| 2097 |
# st.query_params may be a Mapping; pop safely if supported
|
| 2098 |
try:
|
| 2099 |
if "game_id" in qp:
|
| 2100 |
+
qp.pop("game_id")
|
| 2101 |
except Exception:
|
| 2102 |
# Fallback: clear all params if pop not supported
|
| 2103 |
try:
|
|
|
|
| 2107 |
except Exception:
|
| 2108 |
pass
|
| 2109 |
|
| 2110 |
+
|
| 2111 |
# Clear challenge session flags and links
|
| 2112 |
if st.session_state.get("loaded_game_sid") is not None:
|
| 2113 |
st.session_state.loaded_game_sid = None
|
wrdler/words/english.txt
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
# AI-generated word list
|
| 2 |
# Topic: English
|
| 3 |
-
# Last updated: 2025-
|
| 4 |
-
# Total words:
|
| 5 |
# Format: one word per line, sorted by length then alphabetically
|
| 6 |
#
|
| 7 |
ALES
|
|
@@ -13,21 +13,30 @@ BALE
|
|
| 13 |
BANE
|
| 14 |
BANK
|
| 15 |
BARN
|
|
|
|
| 16 |
BASE
|
|
|
|
| 17 |
BEAN
|
| 18 |
BELL
|
| 19 |
BEND
|
|
|
|
| 20 |
BOOK
|
| 21 |
BORE
|
| 22 |
BORN
|
|
|
|
| 23 |
BUNK
|
| 24 |
BURN
|
|
|
|
| 25 |
CAKE
|
| 26 |
CAME
|
| 27 |
CARE
|
|
|
|
|
|
|
| 28 |
CLIP
|
| 29 |
CODE
|
| 30 |
COPY
|
|
|
|
|
|
|
| 31 |
DARE
|
| 32 |
DEAR
|
| 33 |
DENT
|
|
@@ -42,6 +51,9 @@ EELS
|
|
| 42 |
ELMY
|
| 43 |
EMIT
|
| 44 |
ENDS
|
|
|
|
|
|
|
|
|
|
| 45 |
FELL
|
| 46 |
FILE
|
| 47 |
FINE
|
|
@@ -49,8 +61,11 @@ FIRE
|
|
| 49 |
FISH
|
| 50 |
FLAP
|
| 51 |
FLAT
|
|
|
|
|
|
|
| 52 |
FUEL
|
| 53 |
GAIN
|
|
|
|
| 54 |
GAME
|
| 55 |
GEMS
|
| 56 |
GENT
|
|
@@ -58,18 +73,23 @@ GIRD
|
|
| 58 |
GIVE
|
| 59 |
GLOB
|
| 60 |
HALE
|
|
|
|
| 61 |
HELP
|
| 62 |
HONE
|
|
|
|
| 63 |
HOSE
|
|
|
|
| 64 |
HUES
|
| 65 |
IRON
|
| 66 |
JUMP
|
| 67 |
LAKE
|
|
|
|
| 68 |
LAND
|
| 69 |
LANE
|
| 70 |
LARK
|
| 71 |
LASH
|
| 72 |
LATE
|
|
|
|
| 73 |
LEAD
|
| 74 |
LEAN
|
| 75 |
LEAP
|
|
@@ -77,6 +97,7 @@ LEET
|
|
| 77 |
LEFT
|
| 78 |
LEND
|
| 79 |
LENS
|
|
|
|
| 80 |
LIFE
|
| 81 |
LINE
|
| 82 |
LION
|
|
@@ -86,6 +107,7 @@ LOBE
|
|
| 86 |
LOCO
|
| 87 |
LODE
|
| 88 |
LOON
|
|
|
|
| 89 |
LOST
|
| 90 |
LURE
|
| 91 |
MAIN
|
|
@@ -109,7 +131,10 @@ OREO
|
|
| 109 |
PACE
|
| 110 |
PAGE
|
| 111 |
PAIN
|
|
|
|
|
|
|
| 112 |
PENS
|
|
|
|
| 113 |
POET
|
| 114 |
POLY
|
| 115 |
POPE
|
|
@@ -143,30 +168,41 @@ REPS
|
|
| 143 |
REST
|
| 144 |
RIDE
|
| 145 |
RILE
|
| 146 |
-
RHYME
|
| 147 |
RINE
|
| 148 |
RINK
|
| 149 |
RISE
|
| 150 |
RITE
|
| 151 |
ROAM
|
| 152 |
ROPS
|
|
|
|
| 153 |
SEAL
|
| 154 |
SEAM
|
|
|
|
| 155 |
SINE
|
|
|
|
| 156 |
SIRE
|
| 157 |
SLAB
|
| 158 |
SLIT
|
| 159 |
SLUG
|
| 160 |
SORE
|
|
|
|
|
|
|
|
|
|
| 161 |
STEM
|
| 162 |
STEP
|
| 163 |
TAGS
|
| 164 |
TAIL
|
| 165 |
TALE
|
|
|
|
|
|
|
|
|
|
| 166 |
TEXT
|
| 167 |
TIME
|
|
|
|
| 168 |
TYPE
|
|
|
|
| 169 |
WALK
|
|
|
|
| 170 |
WINE
|
| 171 |
WIRE
|
| 172 |
WISH
|
|
@@ -253,6 +289,7 @@ DODGE
|
|
| 253 |
DOLLS
|
| 254 |
DOZEN
|
| 255 |
DRAWS
|
|
|
|
| 256 |
DRILL
|
| 257 |
DROPS
|
| 258 |
DUELS
|
|
@@ -266,15 +303,12 @@ EDGES
|
|
| 266 |
EJECT
|
| 267 |
ELITE
|
| 268 |
ELOPE
|
| 269 |
-
EMIT
|
| 270 |
ENEMY
|
| 271 |
ENSUE
|
| 272 |
ENVOI
|
| 273 |
-
EPIC
|
| 274 |
EPOCH
|
| 275 |
ERASE
|
| 276 |
ERUPT
|
| 277 |
-
EULOGY
|
| 278 |
EVOKE
|
| 279 |
EXACT
|
| 280 |
EXALT
|
|
@@ -317,7 +351,6 @@ FLING
|
|
| 317 |
FLOES
|
| 318 |
FLOOD
|
| 319 |
FLOOR
|
| 320 |
-
FLOP
|
| 321 |
FLOWE
|
| 322 |
FLOWS
|
| 323 |
FLUNG
|
|
@@ -506,28 +539,69 @@ WRITE
|
|
| 506 |
ASSIST
|
| 507 |
AUTHOR
|
| 508 |
BEACON
|
|
|
|
|
|
|
|
|
|
| 509 |
BRIDGE
|
| 510 |
BRIGHT
|
| 511 |
CASTLE
|
| 512 |
DINNER
|
| 513 |
EMPIRE
|
|
|
|
|
|
|
|
|
|
| 514 |
FANOUT
|
| 515 |
FAXING
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 516 |
FLIGHT
|
| 517 |
FLOWER
|
| 518 |
FOREST
|
| 519 |
FRAMES
|
| 520 |
GENTLE
|
|
|
|
| 521 |
HAMMER
|
| 522 |
ISLAND
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 523 |
LENGTH
|
| 524 |
LETTER
|
| 525 |
PHRASE
|
| 526 |
PRAISE
|
|
|
|
|
|
|
| 527 |
REASON
|
| 528 |
REFORM
|
| 529 |
REYLES
|
|
|
|
| 530 |
SCRIPT
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 531 |
SPRINT
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 532 |
SURVEY
|
| 533 |
SYMBOL
|
|
|
|
| 1 |
# AI-generated word list
|
| 2 |
# Topic: English
|
| 3 |
+
# Last updated: 2025-12-04 10:32:04
|
| 4 |
+
# Total words: 601
|
| 5 |
# Format: one word per line, sorted by length then alphabetically
|
| 6 |
#
|
| 7 |
ALES
|
|
|
|
| 13 |
BANE
|
| 14 |
BANK
|
| 15 |
BARN
|
| 16 |
+
BARS
|
| 17 |
BASE
|
| 18 |
+
BEAD
|
| 19 |
BEAN
|
| 20 |
BELL
|
| 21 |
BEND
|
| 22 |
+
BIRD
|
| 23 |
BOOK
|
| 24 |
BORE
|
| 25 |
BORN
|
| 26 |
+
BROW
|
| 27 |
BUNK
|
| 28 |
BURN
|
| 29 |
+
CAGE
|
| 30 |
CAKE
|
| 31 |
CAME
|
| 32 |
CARE
|
| 33 |
+
CASE
|
| 34 |
+
CITE
|
| 35 |
CLIP
|
| 36 |
CODE
|
| 37 |
COPY
|
| 38 |
+
CORD
|
| 39 |
+
DANS
|
| 40 |
DARE
|
| 41 |
DEAR
|
| 42 |
DENT
|
|
|
|
| 51 |
ELMY
|
| 52 |
EMIT
|
| 53 |
ENDS
|
| 54 |
+
EONS
|
| 55 |
+
EPIC
|
| 56 |
+
FANE
|
| 57 |
FELL
|
| 58 |
FILE
|
| 59 |
FINE
|
|
|
|
| 61 |
FISH
|
| 62 |
FLAP
|
| 63 |
FLAT
|
| 64 |
+
FLAY
|
| 65 |
+
FLOP
|
| 66 |
FUEL
|
| 67 |
GAIN
|
| 68 |
+
GALE
|
| 69 |
GAME
|
| 70 |
GEMS
|
| 71 |
GENT
|
|
|
|
| 73 |
GIVE
|
| 74 |
GLOB
|
| 75 |
HALE
|
| 76 |
+
HANG
|
| 77 |
HELP
|
| 78 |
HONE
|
| 79 |
+
HORN
|
| 80 |
HOSE
|
| 81 |
+
HUED
|
| 82 |
HUES
|
| 83 |
IRON
|
| 84 |
JUMP
|
| 85 |
LAKE
|
| 86 |
+
LAMB
|
| 87 |
LAND
|
| 88 |
LANE
|
| 89 |
LARK
|
| 90 |
LASH
|
| 91 |
LATE
|
| 92 |
+
LAZE
|
| 93 |
LEAD
|
| 94 |
LEAN
|
| 95 |
LEAP
|
|
|
|
| 97 |
LEFT
|
| 98 |
LEND
|
| 99 |
LENS
|
| 100 |
+
LENT
|
| 101 |
LIFE
|
| 102 |
LINE
|
| 103 |
LION
|
|
|
|
| 107 |
LOCO
|
| 108 |
LODE
|
| 109 |
LOON
|
| 110 |
+
LORE
|
| 111 |
LOST
|
| 112 |
LURE
|
| 113 |
MAIN
|
|
|
|
| 131 |
PACE
|
| 132 |
PAGE
|
| 133 |
PAIN
|
| 134 |
+
PANS
|
| 135 |
+
PATE
|
| 136 |
PENS
|
| 137 |
+
PLED
|
| 138 |
POET
|
| 139 |
POLY
|
| 140 |
POPE
|
|
|
|
| 168 |
REST
|
| 169 |
RIDE
|
| 170 |
RILE
|
|
|
|
| 171 |
RINE
|
| 172 |
RINK
|
| 173 |
RISE
|
| 174 |
RITE
|
| 175 |
ROAM
|
| 176 |
ROPS
|
| 177 |
+
SAGE
|
| 178 |
SEAL
|
| 179 |
SEAM
|
| 180 |
+
SHOE
|
| 181 |
SINE
|
| 182 |
+
SING
|
| 183 |
SIRE
|
| 184 |
SLAB
|
| 185 |
SLIT
|
| 186 |
SLUG
|
| 187 |
SORE
|
| 188 |
+
SPAN
|
| 189 |
+
SPIN
|
| 190 |
+
STAM
|
| 191 |
STEM
|
| 192 |
STEP
|
| 193 |
TAGS
|
| 194 |
TAIL
|
| 195 |
TALE
|
| 196 |
+
TEAL
|
| 197 |
+
TEEN
|
| 198 |
+
TEND
|
| 199 |
TEXT
|
| 200 |
TIME
|
| 201 |
+
TONS
|
| 202 |
TYPE
|
| 203 |
+
WAGE
|
| 204 |
WALK
|
| 205 |
+
WAYS
|
| 206 |
WINE
|
| 207 |
WIRE
|
| 208 |
WISH
|
|
|
|
| 289 |
DOLLS
|
| 290 |
DOZEN
|
| 291 |
DRAWS
|
| 292 |
+
DREAM
|
| 293 |
DRILL
|
| 294 |
DROPS
|
| 295 |
DUELS
|
|
|
|
| 303 |
EJECT
|
| 304 |
ELITE
|
| 305 |
ELOPE
|
|
|
|
| 306 |
ENEMY
|
| 307 |
ENSUE
|
| 308 |
ENVOI
|
|
|
|
| 309 |
EPOCH
|
| 310 |
ERASE
|
| 311 |
ERUPT
|
|
|
|
| 312 |
EVOKE
|
| 313 |
EXACT
|
| 314 |
EXALT
|
|
|
|
| 351 |
FLOES
|
| 352 |
FLOOD
|
| 353 |
FLOOR
|
|
|
|
| 354 |
FLOWE
|
| 355 |
FLOWS
|
| 356 |
FLUNG
|
|
|
|
| 539 |
ASSIST
|
| 540 |
AUTHOR
|
| 541 |
BEACON
|
| 542 |
+
BLEATS
|
| 543 |
+
BRAINS
|
| 544 |
+
BREACH
|
| 545 |
BRIDGE
|
| 546 |
BRIGHT
|
| 547 |
CASTLE
|
| 548 |
DINNER
|
| 549 |
EMPIRE
|
| 550 |
+
EULOGY
|
| 551 |
+
FALTER
|
| 552 |
+
FANATE
|
| 553 |
FANOUT
|
| 554 |
FAXING
|
| 555 |
+
FLAKED
|
| 556 |
+
FLAKES
|
| 557 |
+
FLAMES
|
| 558 |
+
FLANGE
|
| 559 |
+
FLARED
|
| 560 |
FLIGHT
|
| 561 |
FLOWER
|
| 562 |
FOREST
|
| 563 |
FRAMES
|
| 564 |
GENTLE
|
| 565 |
+
GRATES
|
| 566 |
HAMMER
|
| 567 |
ISLAND
|
| 568 |
+
KNIGHT
|
| 569 |
+
KNITWE
|
| 570 |
+
KNOWED
|
| 571 |
+
KNOWER
|
| 572 |
+
LANDER
|
| 573 |
LENGTH
|
| 574 |
LETTER
|
| 575 |
PHRASE
|
| 576 |
PRAISE
|
| 577 |
+
RAISED
|
| 578 |
+
RAISES
|
| 579 |
REASON
|
| 580 |
REFORM
|
| 581 |
REYLES
|
| 582 |
+
SCREEN
|
| 583 |
SCRIPT
|
| 584 |
+
SHADED
|
| 585 |
+
SHAPES
|
| 586 |
+
SHELVE
|
| 587 |
+
SHOUTS
|
| 588 |
+
SLATED
|
| 589 |
+
SLATES
|
| 590 |
+
SLAYER
|
| 591 |
+
SLICES
|
| 592 |
+
SLIDES
|
| 593 |
+
SLINGS
|
| 594 |
+
SLIVER
|
| 595 |
+
SPACES
|
| 596 |
+
SPRAYS
|
| 597 |
SPRINT
|
| 598 |
+
STABLE
|
| 599 |
+
STAGED
|
| 600 |
+
STAINS
|
| 601 |
+
STATED
|
| 602 |
+
STEADS
|
| 603 |
+
STEALS
|
| 604 |
+
STEELS
|
| 605 |
+
STUNED
|
| 606 |
SURVEY
|
| 607 |
SYMBOL
|
wrdler/words/sports.txt
CHANGED
|
@@ -1,42 +1,170 @@
|
|
| 1 |
# AI-generated word list
|
| 2 |
-
# Topic:
|
| 3 |
-
# Last updated: 2025-12-
|
| 4 |
-
# Total words:
|
| 5 |
# Format: one word per line, sorted by length then alphabetically
|
| 6 |
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
BALLS
|
| 8 |
BENCH
|
|
|
|
|
|
|
|
|
|
| 9 |
CLUBS
|
|
|
|
|
|
|
| 10 |
DARTS
|
| 11 |
DENIM
|
| 12 |
-
FENCY
|
| 13 |
FILMS
|
| 14 |
FINES
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
GAMES
|
|
|
|
|
|
|
| 16 |
GOLFS
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 17 |
HEATS
|
|
|
|
| 18 |
HOOPS
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
LANCE
|
| 20 |
LINES
|
|
|
|
| 21 |
LOOPS
|
|
|
|
| 22 |
POLES
|
|
|
|
|
|
|
|
|
|
| 23 |
RALLY
|
| 24 |
REELS
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
SCORE
|
| 26 |
SHANK
|
|
|
|
|
|
|
|
|
|
| 27 |
SHOOT
|
| 28 |
SKATE
|
|
|
|
| 29 |
SLICE
|
|
|
|
| 30 |
SLIPS
|
|
|
|
| 31 |
SOLES
|
| 32 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
SPURN
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
TEAMS
|
| 38 |
TRACK
|
| 39 |
TRAMP
|
| 40 |
TURNS
|
| 41 |
WREST
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 42 |
SOCCER
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
# AI-generated word list
|
| 2 |
+
# Topic: sporTS
|
| 3 |
+
# Last updated: 2025-12-04 10:41:42
|
| 4 |
+
# Total words: 164
|
| 5 |
# Format: one word per line, sorted by length then alphabetically
|
| 6 |
#
|
| 7 |
+
ARMS
|
| 8 |
+
BALL
|
| 9 |
+
BASK
|
| 10 |
+
BOWS
|
| 11 |
+
BULL
|
| 12 |
+
DART
|
| 13 |
+
DASH
|
| 14 |
+
DUEL
|
| 15 |
+
FAME
|
| 16 |
+
FINE
|
| 17 |
+
FLAP
|
| 18 |
+
FLIT
|
| 19 |
+
GAGE
|
| 20 |
+
GAIN
|
| 21 |
+
GALE
|
| 22 |
+
GENT
|
| 23 |
+
GILD
|
| 24 |
+
HALE
|
| 25 |
+
HARE
|
| 26 |
+
HINT
|
| 27 |
+
HOOK
|
| 28 |
+
HOOP
|
| 29 |
+
HOPS
|
| 30 |
+
HUES
|
| 31 |
+
JAWS
|
| 32 |
+
JUMP
|
| 33 |
+
KICK
|
| 34 |
+
LANE
|
| 35 |
+
LODE
|
| 36 |
+
LURE
|
| 37 |
+
PINT
|
| 38 |
+
RACE
|
| 39 |
+
RACK
|
| 40 |
+
RAIL
|
| 41 |
+
RIDE
|
| 42 |
+
RIME
|
| 43 |
+
RISE
|
| 44 |
+
RODE
|
| 45 |
+
RUSH
|
| 46 |
+
SHIP
|
| 47 |
+
SHOE
|
| 48 |
+
SOAR
|
| 49 |
+
SOFA
|
| 50 |
+
SPAN
|
| 51 |
+
SWIM
|
| 52 |
+
TALK
|
| 53 |
+
WING
|
| 54 |
+
BAILS
|
| 55 |
BALLS
|
| 56 |
BENCH
|
| 57 |
+
BENDS
|
| 58 |
+
BOWL
|
| 59 |
+
CHAMP
|
| 60 |
CLUBS
|
| 61 |
+
CURLS
|
| 62 |
+
DANCE
|
| 63 |
DARTS
|
| 64 |
DENIM
|
|
|
|
| 65 |
FILMS
|
| 66 |
FINES
|
| 67 |
+
FLAIL
|
| 68 |
+
FLAKE
|
| 69 |
+
FLAPS
|
| 70 |
+
FLARE
|
| 71 |
+
FLAWS
|
| 72 |
+
FLICK
|
| 73 |
+
FLINT
|
| 74 |
+
FLUTE
|
| 75 |
+
FLUKE
|
| 76 |
+
GAINS
|
| 77 |
+
GAMER
|
| 78 |
GAMES
|
| 79 |
+
GATES
|
| 80 |
+
GLOBE
|
| 81 |
GOLFS
|
| 82 |
+
GOUTS
|
| 83 |
+
GRAFT
|
| 84 |
+
GROAN
|
| 85 |
+
GUNS
|
| 86 |
+
HAILS
|
| 87 |
HEATS
|
| 88 |
+
HEELS
|
| 89 |
HOOPS
|
| 90 |
+
HORNS
|
| 91 |
+
HORSE
|
| 92 |
+
JINX
|
| 93 |
+
JOLTS
|
| 94 |
+
KNITS
|
| 95 |
LANCE
|
| 96 |
LINES
|
| 97 |
+
LIONS
|
| 98 |
LOOPS
|
| 99 |
+
PITCH
|
| 100 |
POLES
|
| 101 |
+
PUNCH
|
| 102 |
+
RACER
|
| 103 |
+
RACES
|
| 104 |
RALLY
|
| 105 |
REELS
|
| 106 |
+
ROLES
|
| 107 |
+
ROWER
|
| 108 |
+
RUGBY
|
| 109 |
+
SAILS
|
| 110 |
SCORE
|
| 111 |
SHANK
|
| 112 |
+
SHARE
|
| 113 |
+
SHARP
|
| 114 |
+
SHOES
|
| 115 |
SHOOT
|
| 116 |
SKATE
|
| 117 |
+
SKIER
|
| 118 |
SLICE
|
| 119 |
+
SLIDE
|
| 120 |
SLIPS
|
| 121 |
+
SLOPE
|
| 122 |
SOLES
|
| 123 |
+
SOLO
|
| 124 |
+
SPITE
|
| 125 |
+
SPOKE
|
| 126 |
+
SPOON
|
| 127 |
+
SPORT
|
| 128 |
SPURN
|
| 129 |
+
STAGE
|
| 130 |
+
STARS
|
| 131 |
+
STEPS
|
| 132 |
+
STERN
|
| 133 |
+
STICK
|
| 134 |
+
STORM
|
| 135 |
+
SWIMS
|
| 136 |
+
TABLE
|
| 137 |
TEAMS
|
| 138 |
TRACK
|
| 139 |
TRAMP
|
| 140 |
TURNS
|
| 141 |
WREST
|
| 142 |
+
BASKET
|
| 143 |
+
BIKERS
|
| 144 |
+
BOWLER
|
| 145 |
+
BOXING
|
| 146 |
+
DAZZLE
|
| 147 |
+
FENCER
|
| 148 |
+
GOLFER
|
| 149 |
+
HITTER
|
| 150 |
+
HORSES
|
| 151 |
+
JUDGES
|
| 152 |
+
JUMPER
|
| 153 |
+
KICKER
|
| 154 |
+
PISTON
|
| 155 |
+
POCKET
|
| 156 |
+
ROLLER
|
| 157 |
+
RUNNER
|
| 158 |
+
SCORES
|
| 159 |
+
SLALOM
|
| 160 |
SOCCER
|
| 161 |
+
SPIKES
|
| 162 |
+
SPORTS
|
| 163 |
+
SPRINT
|
| 164 |
+
STABLE
|
| 165 |
+
STANCE
|
| 166 |
+
STITCH
|
| 167 |
+
SURFER
|
| 168 |
+
SURVEY
|
| 169 |
+
TAILOR
|
| 170 |
+
WINNER
|