File size: 21,602 Bytes
1dcc163
 
 
 
 
2de3d96
0b81bf2
1dcc163
 
 
0b81bf2
 
2de3d96
 
 
 
 
 
 
 
0b81bf2
 
 
 
 
 
1dcc163
 
 
 
 
 
2de3d96
9ecb8cf
 
0b81bf2
 
2de3d96
0b81bf2
1dcc163
 
 
 
 
 
 
 
 
 
1b1b6cc
1dcc163
 
 
9ecb8cf
1dcc163
 
 
 
 
 
 
 
 
9ecb8cf
1dcc163
 
 
9ecb8cf
1dcc163
 
9ecb8cf
 
507acad
1dcc163
507acad
 
 
 
 
 
1dcc163
 
9ecb8cf
 
1dcc163
 
9ecb8cf
 
 
1dcc163
 
 
 
9ecb8cf
 
1dcc163
 
 
 
 
 
 
 
9ecb8cf
 
 
 
 
 
 
0b81bf2
 
 
 
9ecb8cf
0b81bf2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9ecb8cf
 
0b81bf2
 
 
9ecb8cf
0b81bf2
9ecb8cf
 
 
0b81bf2
9ecb8cf
1dcc163
 
 
 
 
 
 
 
9ecb8cf
1dcc163
 
 
 
 
9ecb8cf
 
 
 
 
 
 
 
 
 
 
0b81bf2
 
 
 
 
 
 
 
9ecb8cf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1dcc163
 
 
 
0b81bf2
507acad
 
 
 
 
 
 
 
 
 
 
0b81bf2
507acad
 
 
0b81bf2
 
 
507acad
0b81bf2
507acad
 
 
0b81bf2
 
 
 
 
 
 
9ecb8cf
1dcc163
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0b81bf2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9ecb8cf
 
 
 
1dcc163
9ecb8cf
 
 
1dcc163
 
 
 
 
 
9ecb8cf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1dcc163
 
9ecb8cf
 
 
 
 
 
 
 
 
 
 
1dcc163
9ecb8cf
 
1dcc163
9ecb8cf
 
 
 
 
 
 
1dcc163
 
 
 
 
 
9ecb8cf
 
 
 
 
 
1dcc163
 
 
 
 
 
 
 
9ecb8cf
 
 
f969c4d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
507acad
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
# BattleWords - Project Context

## Project Overview
BattleWords is a vocabulary learning game inspired by Battleship mechanics, built with Streamlit and Python 3.12. Players reveal cells on a 12x12 grid to discover hidden words and earn points for strategic guessing.

**Current Version:** 0.2.28 (Stable - PWA, Challenge Mode & Remote Storage)
**Next Version:** 0.3.0 (In Development - Local Player History & Statistics)
**Repository:** https://github.com/Oncorporation/BattleWords.git
**Live Demo:** https://huggingface.co/spaces/Surn/BattleWords

## Recent Changes & Branch Status

**Latest (v0.2.28):**
- Progressive Web App (PWA) support
  - Added `service worker` and `manifest.json`
  - Basic offline caching of static assets
  - INSTALL_GUIDE.md added with install steps
  - No gameplay logic changes

**Previous (v0.2.27):**
- New setting: "Show Challenge Share Links" (default OFF)
  - Header Challenge Mode banner hides the "Share this challenge" link when disabled
  - Game Over dialog still lets you submit or create a challenge, but hides the generated share URL when disabled (shows a success message instead)
  - Setting is stored in Streamlit session state and preserved across "New Game"
  - No changes to `logic.py` or `game_storage.py`; this is a UI-only visibility feature

## Core Gameplay
- 12x12 grid with 6 hidden words (2Γ—4-letter, 2Γ—5-letter, 2Γ—6-letter)
- Words placed horizontally or vertically, no overlaps
- Players click cells to reveal letters or empty spaces
- After revealing a letter, players can guess words
- Scoring: word length + bonus for unrevealed letters
- Game ends when all words are guessed or all word letters are revealed
- Incorrect guess history with optional display (enabled by default)
- 10 incorrect guess limit per game
- **βœ… IMPLEMENTED (v0.2.20+):** Challenge Mode with game sharing via short URLs
- **βœ… IMPLEMENTED (v0.2.20+):** Remote storage via Hugging Face datasets for challenges and leaderboards
- **βœ… IMPLEMENTED (v0.2.28):** PWA install support
- **PLANNED (v0.3.0):** Local persistent storage for individual player results and high scores

### Scoring Tiers
- **Fantastic:** 42+ points
- **Great:** 38-41 points
- **Good:** 34-37 points
- **Keep practicing:** < 34 points

## Technical Architecture

### Technology Stack
- **Framework:** Streamlit 1.51.0
- **Language:** Python 3.12.8
- **Visualization:** Matplotlib, NumPy
- **Data Processing:** Pandas, Altair
- **Storage:** JSON-based local persistence
- **Testing:** Pytest
- **Code Quality:** Flake8, MyPy
- **Package Manager:** UV (modern Python package manager)

### Project Structure
```
battlewords/
β”œβ”€β”€ app.py                    # Streamlit entry point
β”œβ”€β”€ battlewords/              # Main package
β”‚   β”œβ”€β”€ __init__.py          # Version: 0.2.17
β”‚   β”œβ”€β”€ models.py            # Data models (Coord, Word, Puzzle, GameState)
β”‚   β”œβ”€β”€ generator.py         # Puzzle generation with deterministic seeding
β”‚   β”œβ”€β”€ logic.py             # Game mechanics (reveal, guess, scoring)
β”‚   β”œβ”€β”€ ui.py                # Streamlit UI (~1800 lines)
β”‚   β”œβ”€β”€ word_loader.py       # Word list management
β”‚   β”œβ”€β”€ audio.py             # Background music system
β”‚   β”œβ”€β”€ sounds.py            # Sound effects management
β”‚   β”œβ”€β”€ generate_sounds.py   # Sound generation utilities
β”‚   β”œβ”€β”€ game_storage.py      # HF game storage wrapper (v0.1.0)
β”‚   β”œβ”€β”€ version_info.py      # Version display
β”‚   β”œβ”€β”€ modules/             # Shared utility modules (from OpenBadge)
β”‚   β”‚   β”œβ”€β”€ __init__.py      # Module exports
β”‚   β”‚   β”œβ”€β”€ storage.py       # HuggingFace storage & URL shortener (v0.1.5)
β”‚   β”‚   β”œβ”€β”€ storage.md       # Storage module documentation
β”‚   β”‚   β”œβ”€β”€ constants.py     # Storage-related constants (trimmed)
β”‚   β”‚   └── file_utils.py    # File utility functions
β”‚   └── words/               # Word list files
β”‚       β”œβ”€β”€ classic.txt      # Default word list
β”‚       β”œβ”€β”€ fourth_grade.txt # Elementary word list
β”‚       └── wordlist.txt     # Full word list
β”œβ”€β”€ tests/                   # Unit tests
β”œβ”€β”€ specs/                   # Documentation
β”‚   β”œβ”€β”€ specs.md             # Game specifications
β”‚   β”œβ”€β”€ requirements.md      # Implementation requirements
β”‚   └── history.md           # Game history
β”œβ”€β”€ .env                     # Environment variables
β”œβ”€β”€ pyproject.toml          # Project metadata
β”œβ”€β”€ requirements.txt        # Dependencies
β”œβ”€β”€ uv.lock                 # UV lock file
β”œβ”€β”€ Dockerfile              # Container deployment
└── CLAUDE.md               # This file - project context for Claude
```

## Key Features

### Game Modes
1. **Classic Mode:** Allows consecutive guessing after correct answers
2. **Too Easy Mode:** Single guess per reveal

### Audio & Visual Effects
- **Background Music:** Toggleable ocean-themed background music with volume control
- **Sound Effects:** Hit/miss/correct/incorrect guess sounds with volume control
- **Animated Radar:** Pulsing rings showing word boundaries (last letter locations)
- **Ocean Theme:** Gradient animated background with wave effects
- **Incorrect Guess History:** Visual display of wrong guesses (toggleable in settings)

### βœ… Challenge Mode & Remote Storage (v0.2.20+)
- **Game ID System:** Short URL-based challenge sharing
  - Format: `?game_id=<sid>` in URL (shortened URL reference)
  - Each player gets different random words from the same wordlist
  - Enables fair challenges between players
  - Stored in Hugging Face dataset repository
- **Remote Storage via HuggingFace Hub:**
  - Per-game settings JSON in `games/{uid}/settings.json`
  - Shortened URL mapping in `shortener.json`
  - Multi-user leaderboards with score, time, and difficulty tracking
  - Results sorted by: highest score β†’ fastest time β†’ highest difficulty
- **Challenge Features:**
  - Submit results to existing challenges
  - Create new challenges from any completed game
  - Top 5 leaderboard display in Challenge Mode banner
  - Optional player names (defaults to "Anonymous")
  - Word list difficulty calculation and display
  - "Show Challenge Share Links" toggle (default OFF) to control URL visibility

### PLANNED: Local Player Storage (v0.3.0)
- **Local Storage:**
  - Location: `~/.battlewords/data/`
  - Files: `game_results.json`, `highscores.json`
  - Privacy-first: no cloud dependency, offline-capable
- **Personal High Scores:**
  - Top 100 scores tracked automatically on local machine
  - Filterable by wordlist and game mode
  - High score sidebar expander display
- **Player Statistics:**
  - Games played, average score, best score
  - Fastest completion time
  - Per-player history on local device

### Puzzle Generation
- Deterministic seeding support for reproducible puzzles
- Configurable word spacing (spacer: 0-2)
  - 0: Words may touch
  - 1: At least 1 blank cell between words (default)
  - 2: At least 2 blank cells between words
- Validation ensures no overlaps, proper bounds, correct word distribution

### UI Components (Current)
- **Radar Visualization:** Animated matplotlib GIF showing word boundaries
  - Displays pulsing rings at last letter of each word
  - Hides rings for guessed words
  - Three-layer composition: gradient background, scope image, animated rings
  - Cached per-puzzle with signature matching
- **Game Grid:** Interactive 12x12 button grid with responsive layout
- **Score Panel:** Real-time scoring with client-side JavaScript timer
- **Settings Sidebar:**
  - Word list picker (classic, fourth_grade, wordlist)
  - Game mode selector
  - Word spacing configuration (0-2)
  - Audio volume controls (music and effects separate)
  - Toggle for incorrect guess history display
- **Theme System:** Ocean gradient background with CSS animations
- **Game Over Dialog:** Final score display with tier ranking
- **Incorrect Guess Display:** Shows history of wrong guesses with count
- **βœ… Challenge Mode UI (v0.2.20+):**
  - Challenge Mode banner with leaderboard (top 5 players)
  - Share challenge button in game over dialog
  - Submit result or create new challenge options
  - Word list difficulty display
  - Conditional share URL visibility toggle
- **PLANNED (v0.3.0):** Local high scores expander in sidebar
- **PLANNED (v0.3.0):** Personal statistics display

### Recent Changes & Branch Status
**Branch:** cc-01 (Storage and sharing features - v0.3.0 development)

**Latest (v0.2.17):**
- Documentation updates and corrections
  - Updated CLAUDE.md with accurate feature status
  - Clarified v0.3.0 planned features vs current implementation
  - Added comprehensive project structure details
  - Improved version tracking and roadmap clarity

**Previously Fixed (v0.2.16):**
- Replace question marks with underscores in score panel
- Add toggle for incorrect guess history display (enabled by default)
- Game over popup positioning improvements
- Music playback after game end
- Sound effect and music volume issues
- Radar alignment inconsistencies
  - Added `fig.subplots_adjust(left=0, right=0.9, top=0.9, bottom=0)`
  - Set `fig.patch.set_alpha(0.0)` for transparent background
  - Maintains 2% margin for tick visibility while ensuring consistent layer alignment

**Completed (v0.2.20-0.2.27 - Challenge Mode):**
- βœ… Imported storage modules from OpenBadge project:
  - `battlewords/modules/storage.py` (v0.1.5) - HuggingFace storage & URL shortener
  - `battlewords/modules/constants.py` (trimmed) - Storage-related constants
  - `battlewords/modules/file_utils.py` - File utility functions
  - `battlewords/modules/storage.md` - Documentation
- βœ… Created `battlewords/game_storage.py` (v0.1.0) - BattleWords storage wrapper:
  - `save_game_to_hf()` - Save game to HF repo and generate short URL
  - `load_game_from_sid()` - Load game from short ID
  - `generate_uid()` - Generate unique game identifiers
  - `serialize_game_settings()` - Convert game data to JSON
  - `get_shareable_url()` - Generate shareable URLs
  - `add_user_result_to_game()` - Append results to existing challenges
- βœ… UI integration complete (`battlewords/ui.py`):
  - Query parameter parsing for `?game_id=<sid>` on app load
  - Load shared game settings into session state
  - Challenge Mode banner with leaderboard (top 5)
  - Share button in game over dialog with "Generate Share Link" or "Submit Result"
  - Conditional share URL display based on settings toggle
  - Automatic save to HuggingFace on game completion
  - Word list difficulty calculation and display
- βœ… Generator updates (`battlewords/generator.py`):
  - Added `target_words` parameter for loading specific words
  - Added `may_overlap` parameter (for future crossword mode)
  - Support for shared game replay with randomized word positions

**In Progress (v0.3.0 - Local Player History):**
- ⏳ Local storage module (`battlewords/local_storage.py`)
- ⏳ Personal high score tracking (local JSON files)
- ⏳ High score sidebar UI display
- ⏳ Player statistics tracking and display

## Data Models

### Core Classes
```python
@dataclass
class Coord:
    x: int  # row, 0-based
    y: int  # col, 0-based

@dataclass
class Word:
    text: str
    start: Coord
    direction: Direction  # "H" or "V"
    cells: List[Coord]

@dataclass
class Puzzle:
    words: List[Word]
    radar: List[Coord]
    may_overlap: bool
    spacer: int
    uid: str  # Unique identifier for caching

@dataclass
class GameState:
    grid_size: int
    puzzle: Puzzle
    revealed: Set[Coord]
    guessed: Set[str]
    score: int
    last_action: str
    can_guess: bool
    game_mode: str
    points_by_word: Dict[str, int]
    start_time: Optional[datetime]
    end_time: Optional[datetime]
```

## Development Workflow

### Running Locally
```bash
# Install dependencies
uv pip install -r requirements.txt --link-mode=copy

# Run app
uv run streamlit run app.py
# or
streamlit run app.py
```

### Docker Deployment
```bash
docker build -t battlewords .
docker run -p 8501:8501 battlewords
```

### Testing
```bash
pytest tests/
```

### Environment Variables (for Challenge Mode)
Challenge Mode requires HuggingFace Hub access for remote storage. Create a `.env` file in the project root:

```bash
# Required for Challenge Mode
HF_API_TOKEN=hf_xxxxxxxxxxxxxxxxxxxxx  # or HF_TOKEN
HF_REPO_ID=YourUsername/YourRepo       # Target HF dataset repo
SPACE_NAME=YourUsername/BattleWords    # Your HF Space name

# Optional
CRYPTO_PK=                             # Reserved for future signing
```

**How to get your HF_API_TOKEN:**
1. Go to https://huggingface.co/settings/tokens
2. Create a new token with `write` access
3. Add to `.env` file as `HF_API_TOKEN=hf_...`

**HF_REPO_ID Structure:**
The dataset repository will contain:
- `shortener.json` - Short URL mappings
- `games/{uid}/settings.json` - Per-game challenge data
- `games/{uid}/result.json` - Optional detailed results

**Note:** The app will work without these variables but Challenge Mode features (sharing, leaderboards) will be disabled.

## Git Configuration & Deployment
**Current Branch:** cc-01
**Purpose:** Storage and sharing features (v0.3.0 development)
**Main Branch:** main (not specified in git config, but typical convention)

### Remotes
- **ONCORP (origin):** https://github.com/Oncorporation/BattleWords.git (main repository)
- **Hugging:** https://huggingface.co/spaces/Surn/BattleWords (live deployment)

## Known Issues
- Word list loading bug: App may not select proper word lists in some environments
  - Investigation needed in `word_loader.get_wordlist_files()` and `load_word_list()`
  - Sidebar selection persistence needs verification

## v0.3.0 Development Status (cc-01 branch)

### Completed βœ…
- `battlewords/storage.py` module created with:
  - `GameStorage` class for JSON-based local storage
  - `GameResult` and `HighScoreEntry` dataclasses
  - Functions: `generate_game_id_from_words()`, `parse_game_id_from_url()`, `create_shareable_url()`
  - Storage location: `~/.battlewords/data/` (game_results.json, highscores.json)
- Documentation updated in specs/ folder

### In Progress ⏳
- Puzzle model integration for game_id field
- Generator updates for `target_words` parameter (replay from game_id)
- UI integration:
  - Storage calls on game completion
  - High score display in sidebar
  - Share button in game over dialog
  - Query parameter parsing for game_id
  - Player name input in sidebar

### Planned πŸ“‹
- Unit tests for storage module
- Integration tests for complete storage flow
- Game replay from shared ID functionality
- Player statistics display
- Share results text generation

## Future Roadmap

### Phase 1.5 (v0.3.0) - Current Focus ⏳
- βœ… Storage module with local JSON persistence (backend complete)
- βœ… Game ID generation system (backend complete)
- ⏳ High score tracking and display (backend complete, UI pending)
- ⏳ Share challenge functionality (UI integration pending)
- ⏳ Game replay from shared IDs (generator updates needed)
- ⏳ Player name input and statistics

### Beta (v0.5.0)
- Word overlaps on shared letters (crossword-style gameplay)
- Enhanced responsive layout for mobile/tablet
- Keyboard navigation and guessing
- Deterministic seed UI for custom puzzles
- Improved accessibility features

### Full (v1.0.0)
- Optional cloud storage backend (FastAPI)
- Daily puzzle mode with global leaderboards
- Practice mode with hints
- Enhanced UX features (animations, themes)
- Multiple difficulty levels
- Internationalization (i18n) support

## Deployment Targets
- **Hugging Face Spaces:** Primary deployment platform
- **Docker:** Containerized deployment for any platform
- **Local:** Development and testing

### Privacy & Data
- All storage is local (no telemetry)
- Player names optional
- No data leaves user's machine
- Easy to delete: just remove `~/.battlewords/data/`

## Notes for Claude
- Project uses modern Python features (3.12+)
- Heavy use of Streamlit session state for game state management
- Matplotlib figures are converted to PIL images and animated GIFs
- Client-side JavaScript for timer updates without page refresh
- CSS heavily customized for game aesthetics
- All file paths should be absolute when working in WSL environment
- Current working directory: `/mnt/d/Projects/Battlewords`
- Storage features are backward-compatible (game works without storage)
- Game IDs are deterministic for consistent sharing
- JSON storage chosen for simplicity and privacy

### WSL Environment Python Versions
The development environment is WSL (Windows Subsystem for Linux) with access to both native Linux and Windows Python installations:

**Native WSL (Linux):**
- `python3` β†’ Python 3.10.12 (`/usr/bin/python3`)
- `python3.10` β†’ Python 3.10.12

**Windows Python (accessible via WSL):**
- `python311.exe` β†’ Python 3.11.9 (`/mnt/c/Users/cfettinger/AppData/Local/Programs/Python/Python311/`)
- `python3.13.exe` β†’ Python 3.13.1 (`/mnt/c/ProgramData/chocolatey/bin/`)

**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+ but can run on 3.10+.

## v0.2.20: Remote Storage game_id via Shortened URL

Overview
- Use a storage server (Hugging Face Hub repo) to persist:
  - Per-game settings JSON (word_list, score, time, game_mode, grid_size, puzzle options)
  - High scores JSON (top scores)
- Each completed game writes JSON to repo under a unique `uid` folder.
- A shortened URL (sid) referencing the settings JSON is generated and used as `game_id` in the query string.
- On load, if `?game_id=<sid>` exists, resolve sid to the full JSON URL, fetch it, and apply settings for the session.

Modules to leverage (from OpenBadge/modules)
- `modules/storage.py`
  - `upload_files_to_repo(files, repo_id, folder_name, repo_type="dataset")`
  - `gen_full_url(short_url=None, full_url=None, repo_id=None, json_file="shortener.json")`
- `modules/constants.py` (env + defaults)
  - Uses HF token from env (expects `HF_TOKEN`, we will also document `HF_API_TOKEN`)
  - `HF_REPO_ID`, `SPACE_NAME`, `SHORTENER_JSON_FILE`
- `modules/file_utils.py` (helpers)

Environment variables (.env)
- HF_API_TOKEN or HF_TOKEN: Hugging Face access token (Bearer)
- CRYPTO_PK: optional, reserved for future signing
- HF_REPO_ID: target repo, e.g., Surn/Storage
- SPACE_NAME: e.g., Surn/BattleWords

Repository structure (dataset repo)
- shortener.json                # sid -> full URL mapping
- games/{uid}/settings.json     # per-game settings payload (primary)
- games/{uid}/result.json       # finalized game result (optional)
- highscores/highscores.json    # global/top scores JSON

Game settings JSON (example)
{
  "uid": "20250101T120001Z-ABC123",
  "word_list": ["APPLE","TRAIN","..."],
  "score": 40,
  "time": 173,
  "game_mode": "classic",
  "grid_size": 12,
  "puzzle_options": { "spacer": 1, "may_overlap": false }
}

Flow
1) On game completion
   - Build unique `uid` (timestamp + random suffix).
   - Write settings.json (and optional result.json) to games/{uid}/
   - Create full URL to settings.json, call `gen_full_url(full_url=...)` to obtain `sid`
   - Share link: https://<space>/?game_id=<sid>
2) On load with `?game_id=<sid>`
   - Call `gen_full_url(short_url=sid)` to get full URL
   - Fetch settings.json, apply session: word_list, game_mode, grid_size, puzzle options; ignore score/time for gameplay
3) Highscores
   - Maintain highscores/highscores.json in repo; append/update with best entries

Security/Privacy
- Only game configuration and scores are stored; no PII required
- sid is a reference; shortener.json can be in the same repo
- Consider private repo for write access; dataset can be public for read

Notes
- We will keep `battlewords/storage.py` as local-only storage (JSON on disk) and introduce a new integration wrapper in a later PR (e.g., `battlewords/hf_storage.py`) to avoid confusion with the generic modules. If needed, rename `battlewords/storage.py` to `local_storage.py` in a future pass.
- Add dependencies: `huggingface_hub`, `python-dotenv`

## Documentation Structure

This file (CLAUDE.md) serves as a **living context document** for AI-assisted development. It complements the formal specification documents:

- **[specs/specs.md](specs/specs.md)** - Game rules, requirements, and feature specifications
- **[specs/requirements.md](specs/requirements.md)** - Implementation phases, acceptance criteria, and technical tasks
- **[README.md](README.md)** - User-facing documentation, installation guide, and changelog

**When to use each:**
- **specs.md** - Understanding game rules, scoring, and player experience
- **requirements.md** - Planning implementation work, tracking phases, and defining done criteria
- **CLAUDE.md** - Quick reference for codebase structure, recent changes, and development context
- **README.md** - Public-facing information, setup instructions, and feature announcements

**Synchronization:**
Changes to game mechanics should update specs.md β†’ requirements.md β†’ CLAUDE.md β†’ README.md in that order

## Challenge Mode & Remote Storage

- The app supports a Challenge Mode where games can be shared via a short link (`?game_id=<sid>`).
- Results are stored in a Hugging Face dataset repo using `game_storage.py`.
- The leaderboard for a challenge is sorted by highest score (descending), then by fastest time (ascending).
- Each user result is appended to the challenge's `users` array in the remote JSON.