Surn commited on
Commit
0b81bf2
·
1 Parent(s): 96b8e12

- Add "Show Challenge Share Links" setting (default: off)
- When disabled:
- Header Challenge Mode: hides the Share Challenge link
- Game Over: allows submitting results but suppresses displaying the generated share URL
- The setting is saved in session state and preserved across "New Game"
- No changes to game logic or storage; only UI visibility behavior

Files changed (6) hide show
  1. README.md +110 -63
  2. battlewords/__init__.py +1 -1
  3. battlewords/ui.py +55 -95
  4. claude.md +86 -24
  5. specs/requirements.md +84 -48
  6. specs/specs.md +63 -2
README.md CHANGED
@@ -23,22 +23,60 @@ BattleWords is a vocabulary learning game inspired by classic Battleship mechani
23
 
24
  ## Features
25
 
 
26
  - 12x12 grid with six hidden words (2x4-letter, 2x5-letter, 2x6-letter)
27
  - Words placed horizontally or vertically
28
- - Radar visualization to help locate word boundaries
29
  - Reveal grid cells and guess words for points
30
  - Scoring tiers: Good (34–37), Great (38–41), Fantastic (42+)
 
 
 
 
 
 
 
 
 
31
  - Responsive UI built with Streamlit
32
- - Customizable word lists
 
 
33
  - Wordlist sidebar controls (picker + one-click sort)
34
- - Deterministic seed support (Beta/Full)
35
- - Keyboard navigation and guessing (Beta/Full)
36
- - Daily and practice modes (Full)
37
- - Leaderboards, persistence, and advanced features (Full)
38
- - **Dockerfile-based deployment supported for Hugging Face Spaces and other container platforms**
39
- - **Game ends when all words are guessed or all word letters are revealed**
40
- - **Incorrect guess history with tooltip and optional display (enabled by default)**
41
- - **Challenge Mode:** Shareable game links with leaderboard, remote storage, and multi-user results
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
 
43
  ## Challenge Mode & Leaderboard
44
 
@@ -89,6 +127,27 @@ docker build -t battlewords .
89
  docker run -p8501:8501 battlewords
90
  ```
91
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
  ## Folder Structure
93
 
94
  - `app.py` – Streamlit entry point
@@ -116,12 +175,19 @@ docker run -p8501:8501 battlewords
116
  ## Changelog
117
 
118
  ### v0.3.0 (planned)
119
- - Game Sharing: share unique game URLs with friends to challenge them on the same word set.
120
- - High Scores: local leaderboard tracking top scores by wordlist and game mode.
121
- - Persistent Storage: all game results saved locally for personal statistics without accounts.
122
- - Challenge Mode: remote storage of challenge results, multi-user leaderboard, and shareable links.
 
 
 
 
 
 
 
123
 
124
- -02.26
125
  - fix copy/share link button
126
 
127
  -0.2.25
@@ -438,52 +504,33 @@ For any issues or enhancements, please refer to the project documentation or con
438
 
439
  Happy gaming and sound designing!
440
 
441
- ## What's New in v0.3.0
442
-
443
- ### Game Sharing 🔗
444
- - Each game gets a unique ID based on the word combination
445
- - Share challenges with friends via URL (`?game_id=ABC123`)
446
- - Friends get the same words but different positions
447
- - Compare scores on the same puzzle!
448
-
449
- ### High Scores 🏆
450
- - Local high score tracking (stored in `~/.battlewords/data/`)
451
- - Filter high scores by wordlist and game mode
452
- - Top10 leaderboard display
453
- - Player names supported (optional)
454
-
455
- ### Persistent Storage 💾
456
- - All game results automatically saved locally
457
- - View your personal statistics
458
- - No account needed - everything stored on your device
459
-
460
- ## Folder Structure
461
-
462
- - `app.py` Streamlit entry point
463
- - `battlewords/` – Python package
464
- - `models.py` data models and types
465
- - `word_loader.py` word list loading and validation
466
- - `generator.py` word placement logic
467
- - `logic.py` game mechanics (reveal, guess, scoring)
468
- - `ui.py` Streamlit UI composition
469
- - `game_storage.py` Hugging Face remote storage integration and challenge sharing
470
- - `local_storage.py` – local JSON storage for results and high scores
471
- - `storage.py` – (legacy) local storage and high scores
472
- - `words/wordlist.txt` – candidate words
473
- - `specs/` – documentation (`specs.md`, `requirements.md`)
474
- - `tests/` – unit tests
475
-
476
- ## Data Storage
477
-
478
- **Privacy First**: All game data is stored locally on your device in `~/.battlewords/data/`. No data is sent to external servers. Player names are optional and default to "Anonymous".
479
-
480
- **Storage Location**:
481
- - Windows: `C:\Users\<username>\.battlewords\data\`
482
- - Linux/Mac: `~/.battlewords/data/`
483
-
484
- **Files**:
485
- - `game_results.json` - All completed games
486
- - `highscores.json` - Top100 scores
487
-
488
- You can delete these files at any time to reset your data.
489
 
 
23
 
24
  ## Features
25
 
26
+ ### Core Gameplay
27
  - 12x12 grid with six hidden words (2x4-letter, 2x5-letter, 2x6-letter)
28
  - Words placed horizontally or vertically
29
+ - Animated radar visualization showing word boundaries
30
  - Reveal grid cells and guess words for points
31
  - Scoring tiers: Good (34–37), Great (38–41), Fantastic (42+)
32
+ - Game ends when all words are guessed or all word letters are revealed
33
+ - Incorrect guess history with tooltip and optional display (enabled by default)
34
+ - 10 incorrect guess limit per game
35
+ - Two game modes: Classic (chain guesses) and Too Easy (single guess per reveal)
36
+
37
+ ### Audio & Visuals
38
+ - Ocean-themed gradient background with wave animations
39
+ - Background music system (toggleable with volume control)
40
+ - Sound effects for hits, misses, correct/incorrect guesses
41
  - Responsive UI built with Streamlit
42
+
43
+ ### Customization
44
+ - Multiple word lists (classic, fourth_grade, wordlist)
45
  - Wordlist sidebar controls (picker + one-click sort)
46
+ - Configurable word spacing (0-2 cells between words)
47
+ - Audio volume controls (music and effects separate)
48
+
49
+ ### Challenge Mode (v0.2.20+)
50
+ - **Shareable challenge links** via short URLs (`?game_id=<sid>`)
51
+ - **Multi-user leaderboards** sorted by score and time
52
+ - **Remote storage** via Hugging Face datasets
53
+ - **Word list difficulty calculation** and display
54
+ - **Submit results** to existing challenges or create new ones
55
+ - **Top 5 leaderboard** display in Challenge Mode banner
56
+ - **"Show Challenge Share Links" toggle** (default OFF) to control URL visibility
57
+ - Each player gets different random words from the same wordlist
58
+
59
+ ### Deployment & Technical
60
+ - **Dockerfile-based deployment** supported for Hugging Face Spaces and other container platforms
61
+ - **Environment variables** for Challenge Mode (HF_API_TOKEN, HF_REPO_ID, SPACE_NAME)
62
+ - Works offline without HF credentials (Challenge Mode features disabled gracefully)
63
+
64
+ ### Planned (v0.3.0)
65
+ - Local persistent storage for personal game history
66
+ - Personal high scores sidebar (offline-capable)
67
+ - Player statistics tracking
68
+ - Deterministic seed UI for custom puzzles
69
+
70
+ ### Beta (v0.5.0)
71
+ - Word overlaps on shared letters (crossword-style)
72
+ - Enhanced responsive layout for mobile/tablet
73
+ - Keyboard navigation and guessing
74
+
75
+ ### Full (v1.0.0)
76
+ - Daily puzzle mode with global leaderboards
77
+ - Practice mode with hints
78
+ - Multiple difficulty levels
79
+ - Internationalization (i18n) support
80
 
81
  ## Challenge Mode & Leaderboard
82
 
 
127
  docker run -p8501:8501 battlewords
128
  ```
129
 
130
+ ### Environment Variables (for Challenge Mode)
131
+
132
+ Challenge Mode requires a `.env` file in the project root with HuggingFace Hub credentials:
133
+
134
+ ```bash
135
+ # Required for Challenge Mode
136
+ HF_API_TOKEN=hf_xxxxxxxxxxxxxxxxxxxxx # or HF_TOKEN
137
+ HF_REPO_ID=YourUsername/YourRepo # Target HF dataset repo
138
+ SPACE_NAME=YourUsername/BattleWords # Your HF Space name
139
+
140
+ # Optional
141
+ CRYPTO_PK= # Reserved for future signing
142
+ ```
143
+
144
+ **How to get your HF_API_TOKEN:**
145
+ 1. Go to https://huggingface.co/settings/tokens
146
+ 2. Create a new token with `write` access
147
+ 3. Add to `.env` file as `HF_API_TOKEN=hf_...`
148
+
149
+ **Note:** The app works without these variables, but Challenge Mode features (sharing, leaderboards) will be disabled.
150
+
151
  ## Folder Structure
152
 
153
  - `app.py` – Streamlit entry point
 
175
  ## Changelog
176
 
177
  ### v0.3.0 (planned)
178
+ - Local persistent storage for personal game history (offline-capable)
179
+ - Personal high scores sidebar with filtering
180
+ - Player statistics tracking (games played, averages, bests)
181
+
182
+ -0.2.27
183
+ - Add "Show Challenge Share Links" setting (default: off)
184
+ - When disabled:
185
+ - Header Challenge Mode: hides the Share Challenge link
186
+ - Game Over: allows submitting results but suppresses displaying the generated share URL
187
+ - The setting is saved in session state and preserved across "New Game"
188
+ - No changes to game logic or storage; only UI visibility behavior
189
 
190
+ -0.2.26
191
  - fix copy/share link button
192
 
193
  -0.2.25
 
504
 
505
  Happy gaming and sound designing!
506
 
507
+ ## What's New in v0.2.20-0.2.27: Challenge Mode 🎯
508
+
509
+ ### Remote Challenge Sharing 🔗
510
+ - Share challenges with friends via short URLs (`?game_id=<sid>`)
511
+ - Each player gets different random words from the same wordlist
512
+ - Multi-user leaderboards sorted by score and time
513
+ - Word list difficulty calculation and display
514
+ - Compare your performance against others!
515
+
516
+ ### Leaderboards 🏆
517
+ - Top 5 players displayed in Challenge Mode banner
518
+ - Results sorted by: highest score → fastest time → highest difficulty
519
+ - Submit results to existing challenges or create new ones
520
+ - Player names supported (optional, defaults to "Anonymous")
521
+
522
+ ### Remote Storage 💾
523
+ - Challenge data stored in Hugging Face dataset repositories
524
+ - Automatic save on game completion (with user consent)
525
+ - "Show Challenge Share Links" toggle for privacy control (default OFF)
526
+ - Works offline when HF credentials not configured
527
+
528
+ ## What's Planned for v0.3.0
529
+
530
+ ### Local Player History (Coming Soon)
531
+ - Personal game results saved locally in `~/.battlewords/data/`
532
+ - Offline-capable high score tracking
533
+ - Player statistics (games played, averages, bests)
534
+ - Privacy-first: no cloud dependency for personal data
535
+ - Easy data management (delete `~/.battlewords/data/` to reset)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
536
 
battlewords/__init__.py CHANGED
@@ -1,2 +1,2 @@
1
- __version__ = "0.2.26"
2
  __all__ = ["models", "generator", "logic", "ui", "game_storage"]
 
1
+ __version__ = "0.2.27"
2
  __all__ = ["models", "generator", "logic", "ui", "game_storage"]
battlewords/ui.py CHANGED
@@ -430,6 +430,9 @@ def _init_session() -> None:
430
  if "show_incorrect_guesses" not in st.session_state:
431
  st.session_state.show_incorrect_guesses = True
432
 
 
 
 
433
 
434
  def _new_game() -> None:
435
  selected = st.session_state.get("selected_wordlist")
@@ -443,6 +446,9 @@ def _new_game() -> None:
443
  music_volume = st.session_state.get("music_volume",15)
444
  effects_volume = st.session_state.get("effects_volume",25)
445
  enable_sound_effects = st.session_state.get("enable_sound_effects", True)
 
 
 
446
  st.session_state.clear()
447
  if selected:
448
  st.session_state.selected_wordlist = selected
@@ -458,6 +464,9 @@ def _new_game() -> None:
458
  st.session_state.music_volume = music_volume
459
  st.session_state.effects_volume = effects_volume
460
  st.session_state.enable_sound_effects = enable_sound_effects
 
 
 
461
  st.session_state.radar_gif_path = None
462
  st.session_state.radar_gif_signature = None
463
  st.session_state.start_time = datetime.now() # Reset timer on new game
@@ -541,7 +550,8 @@ def _render_header():
541
  # Get the challenge SID from session state
542
  sid = st.session_state.get("loaded_game_sid")
543
  share_html = ""
544
- if sid:
 
545
  share_url = get_shareable_url(sid)
546
  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>"
547
 
@@ -657,6 +667,11 @@ def _render_sidebar():
657
  st.session_state.show_incorrect_guesses = True
658
  st.checkbox("Show incorrect guesses", value=st.session_state.show_incorrect_guesses, key="show_incorrect_guesses")
659
 
 
 
 
 
 
660
  # Audio settings
661
  st.header("Audio")
662
  tracks = get_audio_tracks()
@@ -1644,100 +1659,45 @@ def _game_over_content(state: GameState) -> None:
1644
  except Exception as e:
1645
  st.error(f"Failed to save game: {e}")
1646
  else:
1647
- # Display generated share URL
1648
- share_url = st.session_state["share_url"]
1649
- st.success("✅ Share link generated!")
1650
- st.code(share_url, language=None)
1651
-
1652
- # More robust copy-to-clipboard implementation with fallbacks
1653
- import json as _json
1654
- import html as _html
1655
-
1656
- _share_url_json = _json.dumps(share_url) # safe for JS
1657
- _share_url_attr = _html.escape(share_url, quote=True) # safe for HTML attribute
1658
- _share_url_text = _html.escape(share_url)
1659
-
1660
- components.html(
1661
- f"""
1662
- <div id="bw-copy-container" style="
1663
- display:flex;
1664
- gap:8px;
1665
- width:100%;
1666
- align-items:center;
1667
- margin-top:6px;
1668
- justify-content:center;
1669
- ">
1670
-
1671
- <strong><a href="{_share_url_attr}"
1672
- target="_blank"
1673
- rel="noopener noreferrer"
1674
- style="text-decoration: underline; color: #fff; word-break: break-all; filter: drop-shadow(1px 1px 2px #003);">
1675
- {_share_url_text}
1676
- </a></strong>
1677
-
1678
-
1679
- </div>
1680
- <script>
1681
- (function() {{
1682
- const SHARE_URL = {_share_url_json};
1683
- const input = document.getElementById('bw-share-url');
1684
-
1685
- async function modernCopy(text) {{
1686
- if (!navigator.clipboard || !window.isSecureContext) {{
1687
- throw new Error('Clipboard API unavailable in this context');
1688
- }}
1689
- await navigator.clipboard.writeText(text);
1690
- }}
1691
-
1692
- function legacyCopy(text) {{
1693
- const ta = document.createElement('textarea');
1694
- ta.value = text;
1695
- ta.setAttribute('readonly', '');
1696
- ta.style.position = 'fixed';
1697
- ta.style.top = '-1000px';
1698
- ta.style.left = '-1000px';
1699
- document.body.appendChild(ta);
1700
- ta.focus();
1701
- ta.select();
1702
- let ok = false;
1703
- try {{
1704
- ok = document.execCommand('copy');
1705
- }} finally {{
1706
- document.body.removeChild(ta);
1707
- }}
1708
- if (!ok) throw new Error('execCommand failed');
1709
- }}
1710
-
1711
- btn.addEventListener('click', async () => {{
1712
- try {{
1713
- try {{
1714
- await modernCopy(SHARE_URL);
1715
- }} catch (e) {{
1716
- legacyCopy(SHARE_URL);
1717
- }}
1718
- setStatus('Copied!');
1719
- }} catch (e) {{
1720
- // Final fallback: select input text and ask user to copy
1721
- try {{
1722
- input.removeAttribute('readonly');
1723
- input.focus();
1724
- input.select();
1725
- input.setSelectionRange(0, input.value.length);
1726
- document.execCommand('copy'); // may still work on some browsers
1727
- input.setAttribute('readonly', '');
1728
- setStatus('Copied!');
1729
- }} catch (err) {{
1730
- input.focus();
1731
- input.select();
1732
- setStatus('Press Ctrl+C');
1733
- }}
1734
- }}
1735
- }});
1736
- }})();
1737
- </script>
1738
- """,
1739
- height=80
1740
- )
1741
 
1742
  st.markdown("---")
1743
 
 
430
  if "show_incorrect_guesses" not in st.session_state:
431
  st.session_state.show_incorrect_guesses = True
432
 
433
+ # NEW: Initialize Show Challenge Share Links (default OFF)
434
+ if "show_challenge_share_links" not in st.session_state:
435
+ st.session_state.show_challenge_share_links = False
436
 
437
  def _new_game() -> None:
438
  selected = st.session_state.get("selected_wordlist")
 
446
  music_volume = st.session_state.get("music_volume",15)
447
  effects_volume = st.session_state.get("effects_volume",25)
448
  enable_sound_effects = st.session_state.get("enable_sound_effects", True)
449
+ # NEW: Preserve Show Challenge Share Links
450
+ show_challenge_share_links = st.session_state.get("show_challenge_share_links", False)
451
+
452
  st.session_state.clear()
453
  if selected:
454
  st.session_state.selected_wordlist = selected
 
464
  st.session_state.music_volume = music_volume
465
  st.session_state.effects_volume = effects_volume
466
  st.session_state.enable_sound_effects = enable_sound_effects
467
+ # NEW: Restore Show Challenge Share Links
468
+ st.session_state.show_challenge_share_links = show_challenge_share_links
469
+
470
  st.session_state.radar_gif_path = None
471
  st.session_state.radar_gif_signature = None
472
  st.session_state.start_time = datetime.now() # Reset timer on new game
 
550
  # Get the challenge SID from session state
551
  sid = st.session_state.get("loaded_game_sid")
552
  share_html = ""
553
+ # NEW: Only render share link when setting enabled
554
+ if sid and st.session_state.get("show_challenge_share_links", False):
555
  share_url = get_shareable_url(sid)
556
  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>"
557
 
 
667
  st.session_state.show_incorrect_guesses = True
668
  st.checkbox("Show incorrect guesses", value=st.session_state.show_incorrect_guesses, key="show_incorrect_guesses")
669
 
670
+ # NEW: Add Show Challenge Share Links option - default OFF
671
+ if "show_challenge_share_links" not in st.session_state:
672
+ st.session_state.show_challenge_share_links = False
673
+ st.checkbox("Show Challenge Share Links", value=st.session_state.show_challenge_share_links, key="show_challenge_share_links")
674
+
675
  # Audio settings
676
  st.header("Audio")
677
  tracks = get_audio_tracks()
 
1659
  except Exception as e:
1660
  st.error(f"Failed to save game: {e}")
1661
  else:
1662
+ # Conditionally display the generated share URL
1663
+ if st.session_state.get("show_challenge_share_links", False):
1664
+ # Display generated share URL
1665
+ share_url = st.session_state["share_url"]
1666
+ st.success("✅ Share link generated!")
1667
+ st.code(share_url, language=None)
1668
+
1669
+ # More robust copy-to-clipboard implementation with fallbacks
1670
+ import json as _json
1671
+ import html as _html
1672
+
1673
+ _share_url_json = _json.dumps(share_url) # safe for JS
1674
+ _share_url_attr = _html.escape(share_url, quote=True) # safe for HTML attribute
1675
+ _share_url_text = _html.escape(share_url)
1676
+
1677
+ components.html(
1678
+ f"""
1679
+ <div id="bw-copy-container" style="
1680
+ display:flex;
1681
+ gap:8px;
1682
+ width:100%;
1683
+ align-items:center;
1684
+ margin-top:6px;
1685
+ justify-content:center;
1686
+ ">
1687
+
1688
+ <strong><a href="{_share_url_attr}"
1689
+ target="_blank"
1690
+ rel="noopener noreferrer"
1691
+ style="text-decoration: underline; color: #fff; word-break: break-all; filter: drop-shadow(1px 1px 2px #003);">
1692
+ {_share_url_text}
1693
+ </a></strong>
1694
+ </div>
1695
+ """,
1696
+ height=80
1697
+ )
1698
+ else:
1699
+ # Do not display the share URL, but confirm it’s saved/submitted
1700
+ st.success("✅ Your result has been saved.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1701
 
1702
  st.markdown("---")
1703
 
claude.md CHANGED
@@ -3,11 +3,20 @@
3
  ## Project Overview
4
  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.
5
 
6
- **Current Version:** 0.2.17 (Stable)
7
- **Next Version:** 0.3.0 (In Development - Storage & Sharing Features)
8
  **Repository:** https://github.com/Oncorporation/BattleWords.git
9
  **Live Demo:** https://huggingface.co/spaces/Surn/BattleWords
10
 
 
 
 
 
 
 
 
 
 
11
  ## Core Gameplay
12
  - 12x12 grid with 6 hidden words (2×4-letter, 2×5-letter, 2×6-letter)
13
  - Words placed horizontally or vertically, no overlaps
@@ -17,8 +26,9 @@ BattleWords is a vocabulary learning game inspired by Battleship mechanics, buil
17
  - Game ends when all words are guessed or all word letters revealed
18
  - Incorrect guess history with optional display (enabled by default)
19
  - 10 incorrect guess limit per game
20
- - **PLANNED (v0.3.0):** Game sharing via unique game IDs
21
- - **PLANNED (v0.3.0):** Local persistent storage for results and high scores
 
22
 
23
  ### Scoring Tiers
24
  - **Fantastic:** 42+ points
@@ -90,23 +100,38 @@ battlewords/
90
  - **Ocean Theme:** Gradient animated background with wave effects
91
  - **Incorrect Guess History:** Visual display of wrong guesses (toggleable in settings)
92
 
93
- ### PLANNED: Storage & Sharing (v0.3.0)
94
- - **Game ID System:** 8-character deterministic IDs based on word combinations
95
- - Format: `?game_id=ABC123` in URL
96
- - Same words, different positions for each replay
97
  - Enables fair challenges between players
98
- - **Local Storage:**
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
  - Location: `~/.battlewords/data/`
100
  - Files: `game_results.json`, `highscores.json`
101
- - Privacy-first: no cloud dependency
102
- - **High Scores:**
103
- - Top 100 scores tracked automatically
104
  - Filterable by wordlist and game mode
105
- - Optional player names (defaults to "Anonymous")
106
  - **Player Statistics:**
107
  - Games played, average score, best score
108
  - Fastest completion time
109
- - Per-player history
110
 
111
  ### Puzzle Generation
112
  - Deterministic seeding support for reproducible puzzles
@@ -115,7 +140,6 @@ battlewords/
115
  - 1: At least 1 blank cell between words (default)
116
  - 2: At least 2 blank cells between words
117
  - Validation ensures no overlaps, proper bounds, correct word distribution
118
- - **v0.3.0:** Game ID system planned (not yet integrated into Puzzle model)
119
 
120
  ### UI Components (Current)
121
  - **Radar Visualization:** Animated matplotlib GIF showing word boundaries
@@ -134,9 +158,14 @@ battlewords/
134
  - **Theme System:** Ocean gradient background with CSS animations
135
  - **Game Over Dialog:** Final score display with tier ranking
136
  - **Incorrect Guess Display:** Shows history of wrong guesses with count
137
- - **PLANNED (v0.3.0):** High scores expander in sidebar
138
- - **PLANNED (v0.3.0):** Share challenge button in game over dialog
139
- - **PLANNED (v0.3.0):** Game ID display during gameplay
 
 
 
 
 
140
 
141
  ### Recent Changes & Branch Status
142
  **Branch:** cc-01 (Storage and sharing features - v0.3.0 development)
@@ -159,7 +188,7 @@ battlewords/
159
  - Set `fig.patch.set_alpha(0.0)` for transparent background
160
  - Maintains 2% margin for tick visibility while ensuring consistent layer alignment
161
 
162
- **In Progress (v0.3.0 on cc-01):**
163
  - ✅ Imported storage modules from OpenBadge project:
164
  - `battlewords/modules/storage.py` (v0.1.5) - HuggingFace storage & URL shortener
165
  - `battlewords/modules/constants.py` (trimmed) - Storage-related constants
@@ -171,18 +200,25 @@ battlewords/
171
  - `generate_uid()` - Generate unique game identifiers
172
  - `serialize_game_settings()` - Convert game data to JSON
173
  - `get_shareable_url()` - Generate shareable URLs
 
174
  - ✅ UI integration complete (`battlewords/ui.py`):
175
  - Query parameter parsing for `?game_id=<sid>` on app load
176
  - Load shared game settings into session state
177
- - Challenge Mode banner showing target score and time
178
- - Share button in game over dialog with "Generate Share Link"
179
- - Copy-to-clipboard functionality for share URLs
180
  - Automatic save to HuggingFace on game completion
 
181
  - ✅ Generator updates (`battlewords/generator.py`):
182
  - Added `target_words` parameter for loading specific words
183
  - Added `may_overlap` parameter (for future crossword mode)
184
- - Support for shared game replay with same words, different positions
185
- - ⏳ High score tracking infrastructure (backend ready, UI pending)
 
 
 
 
 
186
 
187
  ## Data Models
188
 
@@ -247,6 +283,32 @@ docker run -p 8501:8501 battlewords
247
  pytest tests/
248
  ```
249
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
250
  ## Git Configuration & Deployment
251
  **Current Branch:** cc-01
252
  **Purpose:** Storage and sharing features (v0.3.0 development)
 
3
  ## Project Overview
4
  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.
5
 
6
+ **Current Version:** 0.2.27 (Stable - Challenge Mode & Remote Storage)
7
+ **Next Version:** 0.3.0 (In Development - Local Player History & Statistics)
8
  **Repository:** https://github.com/Oncorporation/BattleWords.git
9
  **Live Demo:** https://huggingface.co/spaces/Surn/BattleWords
10
 
11
+ ## Recent Changes & Branch Status
12
+
13
+ **Latest (v0.2.27):**
14
+ - New setting: "Show Challenge Share Links" (default OFF)
15
+ - Header Challenge Mode banner hides the "Share this challenge" link when disabled
16
+ - Game Over dialog still lets you submit or create a challenge, but hides the generated share URL when disabled (shows a success message instead)
17
+ - Setting is stored in Streamlit session state and preserved across "New Game"
18
+ - No changes to `logic.py` or `game_storage.py`; this is a UI-only visibility feature
19
+
20
  ## Core Gameplay
21
  - 12x12 grid with 6 hidden words (2×4-letter, 2×5-letter, 2×6-letter)
22
  - Words placed horizontally or vertically, no overlaps
 
26
  - Game ends when all words are guessed or all word letters revealed
27
  - Incorrect guess history with optional display (enabled by default)
28
  - 10 incorrect guess limit per game
29
+ - **✅ IMPLEMENTED (v0.2.20+):** Challenge Mode with game sharing via short URLs
30
+ - **✅ IMPLEMENTED (v0.2.20+):** Remote storage via Hugging Face datasets for challenges and leaderboards
31
+ - **PLANNED (v0.3.0):** Local persistent storage for individual player results and high scores
32
 
33
  ### Scoring Tiers
34
  - **Fantastic:** 42+ points
 
100
  - **Ocean Theme:** Gradient animated background with wave effects
101
  - **Incorrect Guess History:** Visual display of wrong guesses (toggleable in settings)
102
 
103
+ ### Challenge Mode & Remote Storage (v0.2.20+)
104
+ - **Game ID System:** Short URL-based challenge sharing
105
+ - Format: `?game_id=<sid>` in URL (shortened URL reference)
106
+ - Each player gets different random words from the same wordlist
107
  - Enables fair challenges between players
108
+ - Stored in Hugging Face dataset repository
109
+ - **Remote Storage via HuggingFace Hub:**
110
+ - Per-game settings JSON in `games/{uid}/settings.json`
111
+ - Shortened URL mapping in `shortener.json`
112
+ - Multi-user leaderboards with score, time, and difficulty tracking
113
+ - Results sorted by: highest score → fastest time → highest difficulty
114
+ - **Challenge Features:**
115
+ - Submit results to existing challenges
116
+ - Create new challenges from any completed game
117
+ - Top 5 leaderboard display in Challenge Mode banner
118
+ - Optional player names (defaults to "Anonymous")
119
+ - Word list difficulty calculation and display
120
+ - "Show Challenge Share Links" toggle (default OFF) to control URL visibility
121
+
122
+ ### PLANNED: Local Player Storage (v0.3.0)
123
+ - **Local Storage:**
124
  - Location: `~/.battlewords/data/`
125
  - Files: `game_results.json`, `highscores.json`
126
+ - Privacy-first: no cloud dependency, offline-capable
127
+ - **Personal High Scores:**
128
+ - Top 100 scores tracked automatically on local machine
129
  - Filterable by wordlist and game mode
130
+ - High score sidebar expander display
131
  - **Player Statistics:**
132
  - Games played, average score, best score
133
  - Fastest completion time
134
+ - Per-player history on local device
135
 
136
  ### Puzzle Generation
137
  - Deterministic seeding support for reproducible puzzles
 
140
  - 1: At least 1 blank cell between words (default)
141
  - 2: At least 2 blank cells between words
142
  - Validation ensures no overlaps, proper bounds, correct word distribution
 
143
 
144
  ### UI Components (Current)
145
  - **Radar Visualization:** Animated matplotlib GIF showing word boundaries
 
158
  - **Theme System:** Ocean gradient background with CSS animations
159
  - **Game Over Dialog:** Final score display with tier ranking
160
  - **Incorrect Guess Display:** Shows history of wrong guesses with count
161
+ - **✅ Challenge Mode UI (v0.2.20+):**
162
+ - Challenge Mode banner with leaderboard (top 5 players)
163
+ - Share challenge button in game over dialog
164
+ - Submit result or create new challenge options
165
+ - Word list difficulty display
166
+ - Conditional share URL visibility toggle
167
+ - **PLANNED (v0.3.0):** Local high scores expander in sidebar
168
+ - **PLANNED (v0.3.0):** Personal statistics display
169
 
170
  ### Recent Changes & Branch Status
171
  **Branch:** cc-01 (Storage and sharing features - v0.3.0 development)
 
188
  - Set `fig.patch.set_alpha(0.0)` for transparent background
189
  - Maintains 2% margin for tick visibility while ensuring consistent layer alignment
190
 
191
+ **Completed (v0.2.20-0.2.27 - Challenge Mode):**
192
  - ✅ Imported storage modules from OpenBadge project:
193
  - `battlewords/modules/storage.py` (v0.1.5) - HuggingFace storage & URL shortener
194
  - `battlewords/modules/constants.py` (trimmed) - Storage-related constants
 
200
  - `generate_uid()` - Generate unique game identifiers
201
  - `serialize_game_settings()` - Convert game data to JSON
202
  - `get_shareable_url()` - Generate shareable URLs
203
+ - `add_user_result_to_game()` - Append results to existing challenges
204
  - ✅ UI integration complete (`battlewords/ui.py`):
205
  - Query parameter parsing for `?game_id=<sid>` on app load
206
  - Load shared game settings into session state
207
+ - Challenge Mode banner with leaderboard (top 5)
208
+ - Share button in game over dialog with "Generate Share Link" or "Submit Result"
209
+ - Conditional share URL display based on settings toggle
210
  - Automatic save to HuggingFace on game completion
211
+ - Word list difficulty calculation and display
212
  - ✅ Generator updates (`battlewords/generator.py`):
213
  - Added `target_words` parameter for loading specific words
214
  - Added `may_overlap` parameter (for future crossword mode)
215
+ - Support for shared game replay with randomized word positions
216
+
217
+ **In Progress (v0.3.0 - Local Player History):**
218
+ - ⏳ Local storage module (`battlewords/local_storage.py`)
219
+ - ⏳ Personal high score tracking (local JSON files)
220
+ - ⏳ High score sidebar UI display
221
+ - ⏳ Player statistics tracking and display
222
 
223
  ## Data Models
224
 
 
283
  pytest tests/
284
  ```
285
 
286
+ ### Environment Variables (for Challenge Mode)
287
+ Challenge Mode requires HuggingFace Hub access for remote storage. Create a `.env` file in the project root:
288
+
289
+ ```bash
290
+ # Required for Challenge Mode
291
+ HF_API_TOKEN=hf_xxxxxxxxxxxxxxxxxxxxx # or HF_TOKEN
292
+ HF_REPO_ID=YourUsername/YourRepo # Target HF dataset repo
293
+ SPACE_NAME=YourUsername/BattleWords # Your HF Space name
294
+
295
+ # Optional
296
+ CRYPTO_PK= # Reserved for future signing
297
+ ```
298
+
299
+ **How to get your HF_API_TOKEN:**
300
+ 1. Go to https://huggingface.co/settings/tokens
301
+ 2. Create a new token with `write` access
302
+ 3. Add to `.env` file as `HF_API_TOKEN=hf_...`
303
+
304
+ **HF_REPO_ID Structure:**
305
+ The dataset repository will contain:
306
+ - `shortener.json` - Short URL mappings
307
+ - `games/{uid}/settings.json` - Per-game challenge data
308
+ - `games/{uid}/result.json` - Optional detailed results
309
+
310
+ **Note:** The app will work without these variables but Challenge Mode features (sharing, leaderboards) will be disabled.
311
+
312
  ## Git Configuration & Deployment
313
  **Current Branch:** cc-01
314
  **Purpose:** Storage and sharing features (v0.3.0 development)
specs/requirements.md CHANGED
@@ -15,7 +15,9 @@ Streamlit key components (API usage plan)
15
  - `st.session_state.letter_map` derived from puzzle.
16
  - `st.session_state.selected_wordlist` for sidebar picker.
17
  - `st.session_state.radar_gif_path` for session-persistent radar animation.
18
- - `st.cache_data` to load and filter the word list.
 
 
19
  - Layout & structure
20
  - `st.title`, `st.subheader`, `st.markdown` for headers/instructions.
21
  - `st.columns(12)` to render the 12×12 grid; `st.container` for grouping; `st.sidebar` for secondary controls/help.
@@ -189,8 +191,10 @@ D) Tests
189
  - Test high score filtering and ranking
190
 
191
  Milestones and Estimates (High-level)
192
- - Phase 1 (POC): 2–4 days
193
- - Phase 1.5 (Storage & Sharing): 2–3 days ⏳ IN PROGRESS
 
 
194
  - Beta (0.5.0): 3–5 days (overlaps, responsive UI, keyboard, deterministic seed)
195
  - Phase 2 (Full): 1–2 weeks depending on features selected
196
 
@@ -212,52 +216,84 @@ Definitions of Done (per task)
212
  - The app applies all settings from the file for a true replay.
213
  - No direct encoding of game data in the query string; only the reference is shared.
214
 
215
- ## Phase 1.6: Remote Storage & Sharing (0.2.20)
216
 
217
- Goal
218
- - Persist per-game settings and highscores on a storage server (Hugging Face Hub repo).
219
- - Use a shortened `game_id` (sid) that resolves to settings.json for replay/sharing.
220
 
221
- A) Storage Server Integration
222
- - Use modules from OpenBadge `modules/storage.py`:
223
  - `upload_files_to_repo(...)` to write JSON to `HF_REPO_ID`
224
  - `gen_full_url(...)` for shortener lookups/creation backed by `shortener.json`
225
- - Create per-game folder `games/{uid}/` and write:
226
- - `settings.json` with: word_list, score, time, game_mode, grid_size, puzzle options
227
- - (optional) `result.json` with additional details for analytics
228
- - Maintain `highscores/highscores.json` for top scores
229
- - Env vars (.env): `HF_API_TOKEN` (or `HF_TOKEN`), `CRYPTO_PK`, `HF_REPO_ID`, `SPACE_NAME`
230
-
231
- B) Sharing Link (game_id)
232
- - After upload of `settings.json`, call `gen_full_url(full_url=...)` to obtain a short id (sid)
233
- - Shareable link: `https://<SPACE_NAME>/?game_id=<sid>`
234
- - On app load with `game_id`, call `gen_full_url(short_url=sid)`, fetch JSON, apply settings
235
-
236
- C) App Behavior
237
- - When `game_id` exists:
238
- - Apply `game_mode`, `grid_size`, `puzzle_options` directly
239
- - Load `word_list` as the target set; positions remain randomized unless later extended
240
- - Ignore `score`/`time` for gameplay; those are metadata only
241
- - Continue to support local JSON storage for users without HF credentials
242
-
243
- D) Dependencies
244
- - Add `huggingface_hub` and `python-dotenv` to requirements
245
- - Ensure `requests` remains present for HTTP fetch fallback if needed
246
-
247
- E) Acceptance
248
- - A completed game produces a working share link with `game_id` sid
249
- - Visiting the link reconstructs the session settings from storage server JSON
250
- - Highscores JSON updates in the repo
251
- - Documentation updated with env vars, repo structure, and flows
252
-
253
- F) Implementation Notes
254
- - Do not replace `battlewords/storage.py` now; introduce a separate integration wrapper (e.g., `battlewords/hf_storage.py`) in the next PR
255
- - Consider a private repo for write access; shortener JSON and settings JSONs can be public read
256
-
257
- ## Challenge Mode & Remote Storage
258
-
259
- - The app must support remote storage of challenge results using Hugging Face datasets.
260
- - The leaderboard for a shared challenge (`game_id`) must display all user results.
261
- - **Sorting:** Results are sorted by highest score (descending), then by fastest time (ascending).
262
- - Each user result is appended to the challenge's `users` array in the remote JSON.
263
- - The UI must allow submitting results to an existing challenge or creating a new one.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  - `st.session_state.letter_map` derived from puzzle.
16
  - `st.session_state.selected_wordlist` for sidebar picker.
17
  - `st.session_state.radar_gif_path` for session-persistent radar animation.
18
+ - `st.session_state.show_incorrect_guesses` toggle.
19
+ - `st.session_state.show_challenge_share_links` toggle (v0.2.27, default OFF) to control visibility of challenge share links in the header and Game Over dialog.
20
+
21
  - Layout & structure
22
  - `st.title`, `st.subheader`, `st.markdown` for headers/instructions.
23
  - `st.columns(12)` to render the 12×12 grid; `st.container` for grouping; `st.sidebar` for secondary controls/help.
 
191
  - Test high score filtering and ranking
192
 
193
  Milestones and Estimates (High-level)
194
+ - Phase 1 (POC): 2–4 days ✅ COMPLETE
195
+ - Phase 1.5 (Local Storage & Sharing - planned): 2–3 days ⏳ PENDING (deferred to v0.3.0)
196
+ - Phase 1.6 (Remote Storage & Challenge Mode): 3–4 days ✅ COMPLETE (v0.2.20-0.2.27)
197
+ - Phase 1.7 (Local Player History): 2–3 days ⏳ IN PROGRESS (v0.3.0)
198
  - Beta (0.5.0): 3–5 days (overlaps, responsive UI, keyboard, deterministic seed)
199
  - Phase 2 (Full): 1–2 weeks depending on features selected
200
 
 
216
  - The app applies all settings from the file for a true replay.
217
  - No direct encoding of game data in the query string; only the reference is shared.
218
 
219
+ ## Phase 1.6: Remote Storage & Challenge Mode (v0.2.20-0.2.27) ✅ COMPLETE
220
 
221
+ ### Goal
222
+ Persist per-game settings and leaderboards on a storage server (Hugging Face Hub repo) with shortened URLs for challenge sharing.
 
223
 
224
+ ### A) Storage Server Integration
225
+ - Imported modules from OpenBadge `modules/storage.py`:
226
  - `upload_files_to_repo(...)` to write JSON to `HF_REPO_ID`
227
  - `gen_full_url(...)` for shortener lookups/creation backed by `shortener.json`
228
+ - Created `battlewords/game_storage.py` wrapper with functions:
229
+ - `save_game_to_hf()` - Save challenge and get short URL
230
+ - `load_game_from_sid()` - Load challenge from short ID
231
+ - `add_user_result_to_game()` - Append user result to existing challenge
232
+ - `get_shareable_url()` - Generate shareable URLs
233
+ - Repository structure in HF dataset:
234
+ - `shortener.json` - Short URL mappings
235
+ - `games/{uid}/settings.json` - Per-game challenge data with users array
236
+ - Required env vars (.env): `HF_API_TOKEN` (or `HF_TOKEN`), `HF_REPO_ID`, `SPACE_NAME`
237
+
238
+ ### B) Sharing Link (game_id) ✅
239
+ - Shortened URL flow: `gen_full_url(full_url=...)` returns short id (sid)
240
+ - Shareable link format: `https://<SPACE_NAME>/?game_id=<sid>`
241
+ - On app load with `game_id`: fetch JSON, apply settings, show Challenge Mode banner
242
+
243
+ ### C) Challenge Mode Features
244
+ - Multi-user leaderboards with score, time, and difficulty tracking
245
+ - Results sorted by: highest score → fastest time → highest difficulty
246
+ - Challenge Mode UI banner showing top 5 players
247
+ - Submit result to existing challenge or create new challenge
248
+ - Word list difficulty calculation and display
249
+ - "Show Challenge Share Links" toggle (default OFF) for URL visibility control
250
+ - Each player gets different random words from the same wordlist source
251
+
252
+ ### D) Dependencies
253
+ - Added `huggingface_hub` and `python-dotenv` to requirements
254
+ - Module imports in `ui.py:30`
255
+
256
+ ### E) Acceptance Criteria ✅
257
+ - Completed game produces working share link with `game_id` sid
258
+ - Visiting link reconstructs challenge with leaderboard
259
+ - ✅ Multiple users can submit results to same challenge
260
+ - Leaderboard displays and sorts correctly
261
+ - ✅ Documentation updated with env vars and flows
262
+ - App works without HF credentials (Challenge Mode features disabled gracefully)
263
+
264
+ ### F) Implementation Files
265
+ - `battlewords/game_storage.py` - HF storage wrapper (v0.1.0)
266
+ - `battlewords/modules/storage.py` - Generic HF storage (v0.1.5)
267
+ - `battlewords/ui.py` - Challenge Mode UI integration (lines 508-601, 1588-1701)
268
+ - `battlewords/generator.py` - Support for target_words parameter
269
+
270
+ ## Phase 1.7: Local Player History (v0.3.0) ⏳ IN PROGRESS
271
+
272
+ ### Goal
273
+ Add local persistent storage for individual player game results and personal high scores (offline-capable, privacy-first).
274
+
275
+ ### A) Local Storage Module
276
+ - Create `battlewords/local_storage.py` with:
277
+ - `GameResult` and `HighScoreEntry` dataclasses
278
+ - JSON-based storage in `~/.battlewords/data/`
279
+ - Functions: `save_game_result()`, `load_high_scores()`, `get_player_stats()`
280
+ - Storage location: `~/.battlewords/data/` (game_results.json, highscores.json)
281
+
282
+ ### B) High Score Display
283
+ - Sidebar expander for personal high scores
284
+ - Filter by: All-time, Current Wordlist, Current Mode
285
+ - Top 10 entries with: Rank, Player, Score, Tier, Time
286
+ - Player name input (optional, defaults to "Anonymous")
287
+
288
+ ### C) Player Statistics
289
+ - Games played, average score, best score
290
+ - Fastest completion time
291
+ - Per-player history tracking
292
+
293
+ ### D) Acceptance Criteria
294
+ - Local JSON files created and updated on game completion
295
+ - High scores display correctly in sidebar
296
+ - Filters work as expected
297
+ - Player names saved with results
298
+ - No cloud dependency required
299
+ - Easy data deletion (remove ~/.battlewords/data/)
specs/specs.md CHANGED
@@ -78,11 +78,20 @@ Battlewords is inspired by the classic Battleship game, but uses words instead o
78
  - Game ID display and share button in game over dialog.
79
  - High score expander in sidebar.
80
  - Player name input in sidebar.
 
 
 
 
 
 
 
 
 
81
 
82
  ## Word List
83
  - External list at `battlewords/words/wordlist.txt`.
84
  - Loaded by `battlewords.word_loader.load_word_list()` with caching.
85
- - Filtered to uppercase A�Z, lengths in {4,5,6}; falls back if < 25 per length.
86
 
87
  ## Generator
88
  - Centralized word loader.
@@ -92,6 +101,58 @@ Battlewords is inspired by the classic Battleship game, but uses words instead o
92
  - The Streamlit entry point is `app.py`.
93
  - **A `Dockerfile` can be used for containerized deployment (recommended for Hugging Face Spaces).**
94
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
  ## Copyright
96
  BattlewordsTM. All Rights Reserved. All content, trademarks and logos are copyrighted by the owner.
97
 
@@ -113,7 +174,7 @@ High Scores
113
  - Local highscores remain supported for offline use
114
 
115
  UI/UX
116
- - Show the current `game_id` (sid) and a �Share Challenge� link
117
  - When loading with a `game_id`, indicate the puzzle is a shared challenge
118
 
119
  Security/Privacy
 
78
  - Game ID display and share button in game over dialog.
79
  - High score expander in sidebar.
80
  - Player name input in sidebar.
81
+ - Checkbox: "Show Challenge Share Links" (v0.2.27, default OFF)
82
+ - When OFF:
83
+ - Challenge Mode header hides the Share Challenge link
84
+ - Game Over dialog still supports submitting/creating challenges, but does not display the generated share URL
85
+ - Persisted in session state and preserved across "New Game"
86
+
87
+ ## New Features (v0.2.27)
88
+ - Added "Show Challenge Share Links" visibility toggle for Challenge Mode sharing UI
89
+ - Purely a UI change; gameplay logic and storage behavior unchanged
90
 
91
  ## Word List
92
  - External list at `battlewords/words/wordlist.txt`.
93
  - Loaded by `battlewords.word_loader.load_word_list()` with caching.
94
+ - Filtered to uppercase A�Z, lengths in {4,5,6}; falls back if < 25 per length.
95
 
96
  ## Generator
97
  - Centralized word loader.
 
101
  - The Streamlit entry point is `app.py`.
102
  - **A `Dockerfile` can be used for containerized deployment (recommended for Hugging Face Spaces).**
103
 
104
+ ## Deployment Requirements
105
+
106
+ ### Basic Deployment (Offline Mode)
107
+ No special configuration needed. The app will run with all core gameplay features.
108
+
109
+ ### Challenge Mode Deployment (Remote Storage)
110
+ Requires HuggingFace Hub integration for challenge sharing and leaderboards.
111
+
112
+ **Required Environment Variables:**
113
+ ```bash
114
+ HF_API_TOKEN=hf_xxxxxxxxxxxxxxxxxxxxx # or HF_TOKEN (write access required)
115
+ HF_REPO_ID=YourUsername/YourRepo # Target HF dataset repository
116
+ SPACE_NAME=YourUsername/BattleWords # Your HF Space name for URL generation
117
+ ```
118
+
119
+ **Optional Environment Variables:**
120
+ ```bash
121
+ CRYPTO_PK= # Reserved for future challenge signing
122
+ ```
123
+
124
+ **Setup Steps:**
125
+ 1. Create a HuggingFace account at https://huggingface.co
126
+ 2. Create a dataset repository (e.g., `YourUsername/BattleWordsStorage`)
127
+ 3. Generate an access token with `write` permissions:
128
+ - Go to https://huggingface.co/settings/tokens
129
+ - Click "New token"
130
+ - Select "Write" access
131
+ - Copy the token (starts with `hf_`)
132
+ 4. Create a `.env` file in project root with the variables above
133
+ 5. For Hugging Face Spaces deployment, add these as Space secrets
134
+
135
+ **Repository Structure (automatically created):**
136
+ ```
137
+ HF_REPO_ID/
138
+ ├── shortener.json # Short URL mappings (sid -> full URL)
139
+ └── games/
140
+ └── {uid}/
141
+ └── settings.json # Challenge data with users array
142
+ ```
143
+
144
+ **Data Privacy:**
145
+ - Challenge Mode stores: word lists, scores, times, game modes, player names
146
+ - No PII beyond optional player name (defaults to "Anonymous")
147
+ - Players control URL visibility via "Show Challenge Share Links" setting
148
+ - App functions fully offline when HF credentials not configured
149
+
150
+ **Deployment Platforms:**
151
+ - Local development: Run with `streamlit run app.py`
152
+ - Docker: Use provided `Dockerfile`
153
+ - Hugging Face Spaces: Dockerfile deployment (recommended)
154
+ - Any Python 3.10+ hosting with Streamlit support
155
+
156
  ## Copyright
157
  BattlewordsTM. All Rights Reserved. All content, trademarks and logos are copyrighted by the owner.
158
 
 
174
  - Local highscores remain supported for offline use
175
 
176
  UI/UX
177
+ - Show the current `game_id` (sid) and a �Share Challenge� link
178
  - When loading with a `game_id`, indicate the puzzle is a shared challenge
179
 
180
  Security/Privacy