Surn commited on
Commit
9ecb8cf
Β·
1 Parent(s): fd702c9

0.2.17 (claude code)

Browse files

- documentation updates and corrections
- updated CLAUDE.md with accurate feature status and project structure
- clarified v0.3.0 planned features vs current implementation

Files changed (6) hide show
  1. README.md +53 -0
  2. battlewords/__init__.py +1 -1
  3. battlewords/storage.py +182 -0
  4. claude.md +146 -30
  5. specs/requirements.md +45 -84
  6. specs/specs.md +13 -0
README.md CHANGED
@@ -93,6 +93,7 @@ docker run -p 8501:8501 battlewords
93
  - `generator.py` – word placement logic
94
  - `logic.py` – game mechanics (reveal, guess, scoring)
95
  - `ui.py` – Streamlit UI composition
 
96
  - `words/wordlist.txt` – candidate words
97
  - `specs/` – documentation (`specs.md`, `requirements.md`)
98
  - `tests/` – unit tests
@@ -106,6 +107,11 @@ docker run -p 8501:8501 battlewords
106
 
107
  ## Changelog
108
 
 
 
 
 
 
109
  -0.2.16
110
  - replace question marks in score panel with underscores
111
  - add option to toggle incorrect guess history display in settings (enabled by default)
@@ -358,3 +364,50 @@ For any issues or enhancements, please refer to the project documentation or con
358
 
359
  Happy gaming and sound designing!
360
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  - `generator.py` – word placement logic
94
  - `logic.py` – game mechanics (reveal, guess, scoring)
95
  - `ui.py` – Streamlit UI composition
96
+ - `storage.py` – **NEW**: persistent storage and high scores
97
  - `words/wordlist.txt` – candidate words
98
  - `specs/` – documentation (`specs.md`, `requirements.md`)
99
  - `tests/` – unit tests
 
107
 
108
  ## Changelog
109
 
110
+ -0.2.17
111
+ - documentation updates and corrections
112
+ - updated CLAUDE.md with accurate feature status and project structure
113
+ - clarified v0.3.0 planned features vs current implementation
114
+
115
  -0.2.16
116
  - replace question marks in score panel with underscores
117
  - add option to toggle incorrect guess history display in settings (enabled by default)
 
364
 
365
  Happy gaming and sound designing!
366
 
367
+ ## What's New in v0.3.0
368
+
369
+ ### Game Sharing πŸ”—
370
+ - Each game gets a unique ID based on the word combination
371
+ - Share challenges with friends via URL (`?game_id=ABC123`)
372
+ - Friends get the same words but different positions
373
+ - Compare scores on the same puzzle!
374
+
375
+ ### High Scores πŸ†
376
+ - Local high score tracking (stored in `~/.battlewords/data/`)
377
+ - Filter high scores by wordlist and game mode
378
+ - Top 10 leaderboard display
379
+ - Player names supported (optional)
380
+
381
+ ### Persistent Storage πŸ’Ύ
382
+ - All game results automatically saved locally
383
+ - View your personal statistics
384
+ - No account needed - everything stored on your device
385
+
386
+ ## Folder Structure
387
+
388
+ - `app.py` – Streamlit entry point
389
+ - `battlewords/` – Python package
390
+ - `models.py` – data models and types
391
+ - `word_loader.py` – word list loading and validation
392
+ - `generator.py` – word placement logic
393
+ - `logic.py` – game mechanics (reveal, guess, scoring)
394
+ - `ui.py` – Streamlit UI composition
395
+ - `storage.py` – **NEW**: persistent storage and high scores
396
+ - `words/wordlist.txt` – candidate words
397
+ - `specs/` – documentation (`specs.md`, `requirements.md`)
398
+ - `tests/` – unit tests
399
+
400
+ ## Data Storage
401
+
402
+ **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".
403
+
404
+ **Storage Location**:
405
+ - Windows: `C:\Users\<username>\.battlewords\data\`
406
+ - Linux/Mac: `~/.battlewords/data/`
407
+
408
+ **Files**:
409
+ - `game_results.json` - All completed games
410
+ - `highscores.json` - Top 100 scores
411
+
412
+ You can delete these files at any time to reset your data.
413
+
battlewords/__init__.py CHANGED
@@ -1,2 +1,2 @@
1
- __version__ = "0.2.16"
2
  __all__ = ["models", "generator", "logic", "ui"]
 
1
+ __version__ = "0.2.17"
2
  __all__ = ["models", "generator", "logic", "ui"]
battlewords/storage.py ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // file: battlewords/storage.py
2
+ """
3
+ Storage module for BattleWords game.
4
+
5
+ Provides functionality for:
6
+ 1. Saving/loading game results to local JSON files
7
+ 2. Managing high scores and leaderboards
8
+ 3. Sharing game IDs via query strings
9
+ """
10
+
11
+ from __future__ import annotations
12
+ from dataclasses import dataclass, field, asdict
13
+ from typing import List, Dict, Optional, Any
14
+ from datetime import datetime
15
+ import json
16
+ import os
17
+ from pathlib import Path
18
+
19
+ @dataclass
20
+ class GameResult:
21
+ game_id: str
22
+ wordlist: str
23
+ game_mode: str
24
+ score: int
25
+ tier: str
26
+ elapsed_seconds: int
27
+ words_found: List[str]
28
+ completed_at: str
29
+ player_name: Optional[str] = None
30
+
31
+ def to_dict(self) -> Dict[str, Any]:
32
+ return asdict(self)
33
+
34
+ @classmethod
35
+ def from_dict(cls, data: Dict[str, Any]) -> "GameResult":
36
+ return cls(**data)
37
+
38
+ @dataclass
39
+ class HighScoreEntry:
40
+ player_name: str
41
+ score: int
42
+ tier: str
43
+ wordlist: str
44
+ game_mode: str
45
+ elapsed_seconds: int
46
+ completed_at: str
47
+ game_id: str
48
+
49
+ def to_dict(self) -> Dict[str, Any]:
50
+ return asdict(self)
51
+
52
+ @classmethod
53
+ def from_dict(cls, data: Dict[str, Any]) -> "HighScoreEntry":
54
+ return cls(**data)
55
+
56
+ class GameStorage:
57
+ def __init__(self, storage_dir: Optional[str] = None):
58
+ if storage_dir is None:
59
+ storage_dir = os.path.join(
60
+ os.path.expanduser("~"),
61
+ ".battlewords",
62
+ "data"
63
+ )
64
+ self.storage_dir = Path(storage_dir)
65
+ self.storage_dir.mkdir(parents=True, exist_ok=True)
66
+ self.results_file = self.storage_dir / "game_results.json"
67
+ self.highscores_file = self.storage_dir / "highscores.json"
68
+
69
+ def save_result(self, result: GameResult) -> bool:
70
+ try:
71
+ results = self.load_all_results()
72
+ results.append(result.to_dict())
73
+ with open(self.results_file, 'w', encoding='utf-8') as f:
74
+ json.dump(results, f, indent=2, ensure_ascii=False)
75
+ self._update_highscores(result)
76
+ return True
77
+ except Exception as e:
78
+ print(f"Error saving result: {e}")
79
+ return False
80
+
81
+ def load_all_results(self) -> List[Dict[str, Any]]:
82
+ if not self.results_file.exists():
83
+ return []
84
+ try:
85
+ with open(self.results_file, 'r', encoding='utf-8') as f:
86
+ return json.load(f)
87
+ except Exception as e:
88
+ print(f"Error loading results: {e}")
89
+ return []
90
+
91
+ def get_results_by_game_id(self, game_id: str) -> List[GameResult]:
92
+ all_results = self.load_all_results()
93
+ matching = [
94
+ GameResult.from_dict(r)
95
+ for r in all_results
96
+ if r.get("game_id") == game_id
97
+ ]
98
+ return sorted(matching, key=lambda x: x.score, reverse=True)
99
+
100
+ def _update_highscores(self, result: GameResult) -> None:
101
+ highscores = self.load_highscores()
102
+ entry = HighScoreEntry(
103
+ player_name=result.player_name or "Anonymous",
104
+ score=result.score,
105
+ tier=result.tier,
106
+ wordlist=result.wordlist,
107
+ game_mode=result.game_mode,
108
+ elapsed_seconds=result.elapsed_seconds,
109
+ completed_at=result.completed_at,
110
+ game_id=result.game_id
111
+ )
112
+ highscores.append(entry.to_dict())
113
+ highscores.sort(key=lambda x: x["score"], reverse=True)
114
+ highscores = highscores[:100]
115
+ with open(self.highscores_file, 'w', encoding='utf-8') as f:
116
+ json.dump(highscores, f, indent=2, ensure_ascii=False)
117
+
118
+ def load_highscores(
119
+ self,
120
+ wordlist: Optional[str] = None,
121
+ game_mode: Optional[str] = None,
122
+ limit: int = 10
123
+ ) -> List[HighScoreEntry]:
124
+ if not self.highscores_file.exists():
125
+ return []
126
+ try:
127
+ with open(self.highscores_file, 'r', encoding='utf-8') as f:
128
+ scores = json.load(f)
129
+ if wordlist:
130
+ scores = [s for s in scores if s.get("wordlist") == wordlist]
131
+ if game_mode:
132
+ scores = [s for s in scores if s.get("game_mode") == game_mode]
133
+ scores.sort(key=lambda x: x["score"], reverse=True)
134
+ return [HighScoreEntry.from_dict(s) for s in scores[:limit]]
135
+ except Exception as e:
136
+ print(f"Error loading highscores: {e}")
137
+ return []
138
+
139
+ def get_player_stats(self, player_name: str) -> Dict[str, Any]:
140
+ all_results = self.load_all_results()
141
+ player_results = [
142
+ GameResult.from_dict(r)
143
+ for r in all_results
144
+ if r.get("player_name") == player_name
145
+ ]
146
+ if not player_results:
147
+ return {
148
+ "games_played": 0,
149
+ "total_score": 0,
150
+ "average_score": 0,
151
+ "best_score": 0,
152
+ "best_tier": None
153
+ }
154
+ scores = [r.score for r in player_results]
155
+ return {
156
+ "games_played": len(player_results),
157
+ "total_score": sum(scores),
158
+ "average_score": sum(scores) / len(scores),
159
+ "best_score": max(scores),
160
+ "best_tier": max(player_results, key=lambda x: x.score).tier,
161
+ "fastest_time": min(r.elapsed_seconds for r in player_results)
162
+ }
163
+
164
+ def generate_game_id_from_words(words: List[str]) -> str:
165
+ import hashlib
166
+ sorted_words = sorted([w.upper() for w in words])
167
+ word_string = "".join(sorted_words)
168
+ hash_obj = hashlib.sha256(word_string.encode('utf-8'))
169
+ return hash_obj.hexdigest()[:8].upper()
170
+
171
+ def parse_game_id_from_url() -> Optional[str]:
172
+ try:
173
+ import streamlit as st
174
+ params = st.query_params
175
+ return params.get("game_id")
176
+ except Exception:
177
+ return None
178
+
179
+ def create_shareable_url(game_id: str, base_url: Optional[str] = None) -> str:
180
+ if base_url is None:
181
+ base_url = "https://huggingface.co/spaces/Surn/BattleWords"
182
+ return f"{base_url}?game_id={game_id}"
claude.md CHANGED
@@ -3,7 +3,8 @@
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.6
 
7
  **Repository:** https://github.com/Oncorporation/BattleWords.git
8
  **Live Demo:** https://huggingface.co/spaces/Surn/BattleWords
9
 
@@ -14,6 +15,10 @@ BattleWords is a vocabulary learning game inspired by Battleship mechanics, buil
14
  - After revealing a letter, players can guess words
15
  - Scoring: word length + bonus for unrevealed letters
16
  - Game ends when all words are guessed or all word letters revealed
 
 
 
 
17
 
18
  ### Scoring Tiers
19
  - **Fantastic:** 42+ points
@@ -28,6 +33,7 @@ BattleWords is a vocabulary learning game inspired by Battleship mechanics, buil
28
  - **Language:** Python 3.12.8
29
  - **Visualization:** Matplotlib, NumPy
30
  - **Data Processing:** Pandas, Altair
 
31
  - **Testing:** Pytest
32
  - **Code Quality:** Flake8, MyPy
33
  - **Package Manager:** UV (modern Python package manager)
@@ -37,24 +43,32 @@ BattleWords is a vocabulary learning game inspired by Battleship mechanics, buil
37
  battlewords/
38
  β”œβ”€β”€ app.py # Streamlit entry point
39
  β”œβ”€β”€ battlewords/ # Main package
40
- β”‚ β”œβ”€β”€ __init__.py # Version: 0.2.6
41
  β”‚ β”œβ”€β”€ models.py # Data models (Coord, Word, Puzzle, GameState)
42
  β”‚ β”œβ”€β”€ generator.py # Puzzle generation with deterministic seeding
43
  β”‚ β”œβ”€β”€ logic.py # Game mechanics (reveal, guess, scoring)
44
- β”‚ β”œβ”€β”€ ui.py # Streamlit UI (~1170 lines)
45
  β”‚ β”œβ”€β”€ word_loader.py # Word list management
46
  β”‚ β”œβ”€β”€ audio.py # Background music system
 
 
 
47
  β”‚ β”œβ”€β”€ version_info.py # Version display
48
  β”‚ └── words/ # Word list files
49
  β”‚ β”œβ”€β”€ classic.txt # Default word list
50
- β”‚ └── wordlist.txt # Additional words
 
51
  β”œβ”€β”€ tests/ # Unit tests
52
  β”œβ”€β”€ specs/ # Documentation
 
 
 
53
  β”œβ”€β”€ .env # Environment variables
54
  β”œβ”€β”€ pyproject.toml # Project metadata
55
  β”œβ”€β”€ requirements.txt # Dependencies
56
  β”œβ”€β”€ uv.lock # UV lock file
57
- └── Dockerfile # Container deployment
 
58
  ```
59
 
60
  ## Key Features
@@ -63,6 +77,31 @@ battlewords/
63
  1. **Classic Mode:** Allows consecutive guessing after correct answers
64
  2. **Too Easy Mode:** Single guess per reveal
65
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  ### Puzzle Generation
67
  - Deterministic seeding support for reproducible puzzles
68
  - Configurable word spacing (spacer: 0-2)
@@ -70,25 +109,55 @@ battlewords/
70
  - 1: At least 1 blank cell between words (default)
71
  - 2: At least 2 blank cells between words
72
  - Validation ensures no overlaps, proper bounds, correct word distribution
 
73
 
74
- ### UI Components
75
  - **Radar Visualization:** Animated matplotlib GIF showing word boundaries
76
  - Displays pulsing rings at last letter of each word
77
  - Hides rings for guessed words
78
  - Three-layer composition: gradient background, scope image, animated rings
79
  - Cached per-puzzle with signature matching
80
- - **Game Grid:** Interactive 12x12 button grid
81
- - **Score Panel:** Real-time scoring with client-side timer
82
- - **Settings Sidebar:** Word list picker, game mode, spacing, audio controls
83
- - **Theme System:** Ocean gradient background with animations
84
- - **Audio System:** Background music with volume control
85
-
86
- ### Recent Fixes (cc-01 branch)
87
- - **Radar Alignment Issue:** Fixed inconsistent sizing of animated rings layer
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  - Added `fig.subplots_adjust(left=0, right=0.9, top=0.9, bottom=0)`
89
  - Set `fig.patch.set_alpha(0.0)` for transparent background
90
  - Maintains 2% margin for tick visibility while ensuring consistent layer alignment
91
 
 
 
 
 
 
92
  ## Data Models
93
 
94
  ### Core Classes
@@ -152,39 +221,83 @@ docker run -p 8501:8501 battlewords
152
  pytest tests/
153
  ```
154
 
155
- ## Current Development Branch
156
- **Branch:** cc-01
157
- **Purpose:** Bug fixes and improvements, specifically radar graphic alignment
 
158
 
159
- ### Git Configuration
160
- - **Remote ONCORP:** https://github.com/Oncorporation/BattleWords.git (main remote)
161
- - **Remote Hugging:** https://huggingface.co/spaces/Surn/BattleWords (deployment)
162
 
163
  ## Known Issues
164
  - Word list loading bug: App may not select proper word lists in some environments
165
  - Investigation needed in `word_loader.get_wordlist_files()` and `load_word_list()`
166
  - Sidebar selection persistence needs verification
167
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168
  ## Future Roadmap
169
 
170
- ### Beta (0.5.0)
171
- - Word overlaps on shared letters (crossword-style)
172
- - Enhanced responsive layout
 
 
 
 
 
 
 
 
173
  - Keyboard navigation and guessing
174
- - Deterministic seed UI
 
175
 
176
- ### Full (1.0.0)
177
- - Daily puzzle mode
178
- - Practice mode
179
- - Leaderboards
180
- - Persistent storage
181
- - Enhanced UX features
 
182
 
183
  ## Deployment Targets
184
  - **Hugging Face Spaces:** Primary deployment platform
185
  - **Docker:** Containerized deployment for any platform
186
  - **Local:** Development and testing
187
 
 
 
 
 
 
 
188
  ## Notes for Claude
189
  - Project uses modern Python features (3.12+)
190
  - Heavy use of Streamlit session state for game state management
@@ -193,3 +306,6 @@ pytest tests/
193
  - CSS heavily customized for game aesthetics
194
  - All file paths should be absolute when working in WSL environment
195
  - Current working directory: `/mnt/d/Projects/Battlewords`
 
 
 
 
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
 
 
15
  - After revealing a letter, players can guess words
16
  - Scoring: word length + bonus for unrevealed letters
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
 
33
  - **Language:** Python 3.12.8
34
  - **Visualization:** Matplotlib, NumPy
35
  - **Data Processing:** Pandas, Altair
36
+ - **Storage:** JSON-based local persistence
37
  - **Testing:** Pytest
38
  - **Code Quality:** Flake8, MyPy
39
  - **Package Manager:** UV (modern Python package manager)
 
43
  battlewords/
44
  β”œβ”€β”€ app.py # Streamlit entry point
45
  β”œβ”€β”€ battlewords/ # Main package
46
+ β”‚ β”œβ”€β”€ __init__.py # Version: 0.2.17
47
  β”‚ β”œβ”€β”€ models.py # Data models (Coord, Word, Puzzle, GameState)
48
  β”‚ β”œβ”€β”€ generator.py # Puzzle generation with deterministic seeding
49
  β”‚ β”œβ”€β”€ logic.py # Game mechanics (reveal, guess, scoring)
50
+ β”‚ β”œβ”€β”€ ui.py # Streamlit UI (~1800 lines)
51
  β”‚ β”œβ”€β”€ word_loader.py # Word list management
52
  β”‚ β”œβ”€β”€ audio.py # Background music system
53
+ β”‚ β”œβ”€β”€ sounds.py # Sound effects management
54
+ β”‚ β”œβ”€β”€ generate_sounds.py # Sound generation utilities
55
+ β”‚ β”œβ”€β”€ storage.py # Storage module (v0.3.0 - IN DEVELOPMENT)
56
  β”‚ β”œβ”€β”€ version_info.py # Version display
57
  β”‚ └── words/ # Word list files
58
  β”‚ β”œβ”€β”€ classic.txt # Default word list
59
+ β”‚ β”œβ”€β”€ fourth_grade.txt # Elementary word list
60
+ β”‚ └── wordlist.txt # Full word list
61
  β”œβ”€β”€ tests/ # Unit tests
62
  β”œβ”€β”€ specs/ # Documentation
63
+ β”‚ β”œβ”€β”€ specs.md # Game specifications
64
+ β”‚ β”œβ”€β”€ requirements.md # Implementation requirements
65
+ β”‚ └── history.md # Game history
66
  β”œβ”€β”€ .env # Environment variables
67
  β”œβ”€β”€ pyproject.toml # Project metadata
68
  β”œβ”€β”€ requirements.txt # Dependencies
69
  β”œβ”€β”€ uv.lock # UV lock file
70
+ β”œβ”€β”€ Dockerfile # Container deployment
71
+ └── CLAUDE.md # This file - project context for Claude
72
  ```
73
 
74
  ## Key Features
 
77
  1. **Classic Mode:** Allows consecutive guessing after correct answers
78
  2. **Too Easy Mode:** Single guess per reveal
79
 
80
+ ### Audio & Visual Effects
81
+ - **Background Music:** Toggleable ocean-themed background music with volume control
82
+ - **Sound Effects:** Hit/miss/correct/incorrect guess sounds with volume control
83
+ - **Animated Radar:** Pulsing rings showing word boundaries (last letter locations)
84
+ - **Ocean Theme:** Gradient animated background with wave effects
85
+ - **Incorrect Guess History:** Visual display of wrong guesses (toggleable in settings)
86
+
87
+ ### PLANNED: Storage & Sharing (v0.3.0)
88
+ - **Game ID System:** 8-character deterministic IDs based on word combinations
89
+ - Format: `?game_id=ABC123` in URL
90
+ - Same words, different positions for each replay
91
+ - Enables fair challenges between players
92
+ - **Local Storage:**
93
+ - Location: `~/.battlewords/data/`
94
+ - Files: `game_results.json`, `highscores.json`
95
+ - Privacy-first: no cloud dependency
96
+ - **High Scores:**
97
+ - Top 100 scores tracked automatically
98
+ - Filterable by wordlist and game mode
99
+ - Optional player names (defaults to "Anonymous")
100
+ - **Player Statistics:**
101
+ - Games played, average score, best score
102
+ - Fastest completion time
103
+ - Per-player history
104
+
105
  ### Puzzle Generation
106
  - Deterministic seeding support for reproducible puzzles
107
  - Configurable word spacing (spacer: 0-2)
 
109
  - 1: At least 1 blank cell between words (default)
110
  - 2: At least 2 blank cells between words
111
  - Validation ensures no overlaps, proper bounds, correct word distribution
112
+ - **v0.3.0:** Game ID system planned (not yet integrated into Puzzle model)
113
 
114
+ ### UI Components (Current)
115
  - **Radar Visualization:** Animated matplotlib GIF showing word boundaries
116
  - Displays pulsing rings at last letter of each word
117
  - Hides rings for guessed words
118
  - Three-layer composition: gradient background, scope image, animated rings
119
  - Cached per-puzzle with signature matching
120
+ - **Game Grid:** Interactive 12x12 button grid with responsive layout
121
+ - **Score Panel:** Real-time scoring with client-side JavaScript timer
122
+ - **Settings Sidebar:**
123
+ - Word list picker (classic, fourth_grade, wordlist)
124
+ - Game mode selector
125
+ - Word spacing configuration (0-2)
126
+ - Audio volume controls (music and effects separate)
127
+ - Toggle for incorrect guess history display
128
+ - **Theme System:** Ocean gradient background with CSS animations
129
+ - **Game Over Dialog:** Final score display with tier ranking
130
+ - **Incorrect Guess Display:** Shows history of wrong guesses with count
131
+ - **PLANNED (v0.3.0):** High scores expander in sidebar
132
+ - **PLANNED (v0.3.0):** Share challenge button in game over dialog
133
+ - **PLANNED (v0.3.0):** Game ID display during gameplay
134
+
135
+ ### Recent Changes & Branch Status
136
+ **Branch:** cc-01 (Storage and sharing features - v0.3.0 development)
137
+
138
+ **Latest (v0.2.17):**
139
+ - Documentation updates and corrections
140
+ - Updated CLAUDE.md with accurate feature status
141
+ - Clarified v0.3.0 planned features vs current implementation
142
+ - Added comprehensive project structure details
143
+ - Improved version tracking and roadmap clarity
144
+
145
+ **Previously Fixed (v0.2.16):**
146
+ - Replace question marks with underscores in score panel
147
+ - Add toggle for incorrect guess history display (enabled by default)
148
+ - Game over popup positioning improvements
149
+ - Music playback after game end
150
+ - Sound effect and music volume issues
151
+ - Radar alignment inconsistencies
152
  - Added `fig.subplots_adjust(left=0, right=0.9, top=0.9, bottom=0)`
153
  - Set `fig.patch.set_alpha(0.0)` for transparent background
154
  - Maintains 2% margin for tick visibility while ensuring consistent layer alignment
155
 
156
+ **In Progress (v0.3.0 on cc-01):**
157
+ - Storage module implementation (storage.py created)
158
+ - Game ID system for sharing
159
+ - High score tracking infrastructure
160
+
161
  ## Data Models
162
 
163
  ### Core Classes
 
221
  pytest tests/
222
  ```
223
 
224
+ ## Git Configuration & Deployment
225
+ **Current Branch:** cc-01
226
+ **Purpose:** Storage and sharing features (v0.3.0 development)
227
+ **Main Branch:** main (not specified in git config, but typical convention)
228
 
229
+ ### Remotes
230
+ - **ONCORP (origin):** https://github.com/Oncorporation/BattleWords.git (main repository)
231
+ - **Hugging:** https://huggingface.co/spaces/Surn/BattleWords (live deployment)
232
 
233
  ## Known Issues
234
  - Word list loading bug: App may not select proper word lists in some environments
235
  - Investigation needed in `word_loader.get_wordlist_files()` and `load_word_list()`
236
  - Sidebar selection persistence needs verification
237
 
238
+ ## v0.3.0 Development Status (cc-01 branch)
239
+
240
+ ### Completed βœ…
241
+ - `battlewords/storage.py` module created with:
242
+ - `GameStorage` class for JSON-based local storage
243
+ - `GameResult` and `HighScoreEntry` dataclasses
244
+ - Functions: `generate_game_id_from_words()`, `parse_game_id_from_url()`, `create_shareable_url()`
245
+ - Storage location: `~/.battlewords/data/` (game_results.json, highscores.json)
246
+ - Documentation updated in specs/ folder
247
+
248
+ ### In Progress ⏳
249
+ - Puzzle model integration for game_id field
250
+ - Generator updates for `target_words` parameter (replay from game_id)
251
+ - UI integration:
252
+ - Storage calls on game completion
253
+ - High score display in sidebar
254
+ - Share button in game over dialog
255
+ - Query parameter parsing for game_id
256
+ - Player name input in sidebar
257
+
258
+ ### Planned πŸ“‹
259
+ - Unit tests for storage module
260
+ - Integration tests for complete storage flow
261
+ - Game replay from shared ID functionality
262
+ - Player statistics display
263
+ - Share results text generation
264
+
265
  ## Future Roadmap
266
 
267
+ ### Phase 1.5 (v0.3.0) - Current Focus ⏳
268
+ - βœ… Storage module with local JSON persistence (backend complete)
269
+ - βœ… Game ID generation system (backend complete)
270
+ - ⏳ High score tracking and display (backend complete, UI pending)
271
+ - ⏳ Share challenge functionality (UI integration pending)
272
+ - ⏳ Game replay from shared IDs (generator updates needed)
273
+ - ⏳ Player name input and statistics
274
+
275
+ ### Beta (v0.5.0)
276
+ - Word overlaps on shared letters (crossword-style gameplay)
277
+ - Enhanced responsive layout for mobile/tablet
278
  - Keyboard navigation and guessing
279
+ - Deterministic seed UI for custom puzzles
280
+ - Improved accessibility features
281
 
282
+ ### Full (v1.0.0)
283
+ - Optional cloud storage backend (FastAPI)
284
+ - Daily puzzle mode with global leaderboards
285
+ - Practice mode with hints
286
+ - Enhanced UX features (animations, themes)
287
+ - Multiple difficulty levels
288
+ - Internationalization (i18n) support
289
 
290
  ## Deployment Targets
291
  - **Hugging Face Spaces:** Primary deployment platform
292
  - **Docker:** Containerized deployment for any platform
293
  - **Local:** Development and testing
294
 
295
+ ### Privacy & Data
296
+ - All storage is local (no telemetry)
297
+ - Player names optional
298
+ - No data leaves user's machine
299
+ - Easy to delete: just remove `~/.battlewords/data/`
300
+
301
  ## Notes for Claude
302
  - Project uses modern Python features (3.12+)
303
  - Heavy use of Streamlit session state for game state management
 
306
  - CSS heavily customized for game aesthetics
307
  - All file paths should be absolute when working in WSL environment
308
  - Current working directory: `/mnt/d/Projects/Battlewords`
309
+ - Storage features are backward-compatible (game works without storage)
310
+ - Game IDs are deterministic for consistent sharing
311
+ - JSON storage chosen for simplicity and privacy
specs/requirements.md CHANGED
@@ -136,94 +136,55 @@ Known Issues / TODO
136
  - Ensure `load_word_list(selected_wordlist)` loads the chosen file and matches `generate_puzzle(words_by_len=...)` expected shape.
137
  - Add tests for selection, sorting, and fallback behavior.
138
 
139
- Beta (0.5.0) – Planned
140
- - Allow shared-letter overlaps when letters match; deterministic seeding; keyboard navigation.
141
-
142
- B) UI and Interaction
143
- - Cell rendering with consistent sizing and responsive layout (desktop/mobile).
144
- - Keyboard support for grid navigation and guessing (custom JS via `st.html` or component).
145
- - Maintain radar behavior and scoring rules.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
146
  Acceptance:
147
- - Grid scales cleanly across typical desktop and mobile widths.
148
- - Users can enter guesses and move focus via keyboard.
149
-
150
- C) Tests
151
- - Property checks for overlap validity (only same letters may share a cell).
152
- - Seed reproducibility tests (same seed β†’ identical placements).
153
- - Optional validation tests for adjacency curation (when enabled).
154
-
155
- Phase 2: Full Version (1.0.0)
156
- Goal: Robust app with polish, persistence, test coverage, and optional advanced features.
157
-
158
- A) UX and Visual Polish
159
- - Cell rendering with consistent sizing and responsive layout (desktop/mobile).
160
- - Keyboard support for navigation and guessing (custom JS via `st.html` or a component if needed).
161
- - Animations for reveals and correct guesses (CSS/JS via `st.html` or component).
162
- - Color-blind friendly palette and accessible contrast.
163
- - Configurable themes (light/dark) via Streamlit theme config.
164
- - Streamlit: `st.tabs` for modes/help; `st.popover`/`st.expander` for tips; `st.toast`/`st.warning` for feedback.
165
-
166
- B) Game Content and Generation
167
- - Curated word lists by difficulty; exclude obscure/abusive words.
168
- - Deterministic seed support to reproduce puzzles (e.g., daily seed based on date).
169
- - Validation pass to ensure no unintended partial words formed adjacently (content curation rule, optional).
170
- - Optional generator diagnostics panel for QA using `st.expander` and `st.dataframe`.
171
- - Streamlit: `st.cache_data` for word lists; `st.cache_resource` if needed for heavier resources.
172
-
173
- C) Game Modes and Settings
174
- - Daily Puzzle mode (same seed for all players per day).
175
- - Practice mode (random puzzles).
176
- - Difficulty presets that tweak word selection (common vs. rare) but still keep 2Γ—4, 2Γ—5, 2Γ—6.
177
- - Optional hint system with limited uses (e.g., reveal a random letter in an unguessed word) with score penalty.
178
- - Streamlit: mode selection via `st.radio`/`st.segmented_control`; settings via `st.sidebar` with `st.toggle`/`st.slider`.
179
-
180
- D) Persistence and Profiles
181
- - Save/Load local game state (browser cookie or Streamlit session + query params).
182
- - Cloud persistence via lightweight backend API (FastAPI) or Streamlit secrets + hosted storage for:
183
- - User profiles (username only), completed puzzles, scores.
184
- - Leaderboards for Daily mode.
185
- - Privacy notice and opt-in for storing data.
186
- - Streamlit: `st.text_input` for username; `st.button` to save; call backend via `requests`.
187
-
188
- E) Leaderboards and Sharing
189
- - Global and friends leaderboards (score and completion time if captured; note: game is strategy-first, time is optional).
190
- - Share result string with spoiler-free grid (similar to popular word games).
191
- - Streamlit: `st.table`/`st.dataframe` for leaderboards; `st.download_button` or copy-to-clipboard via `st.text_area` + `st.button`.
192
-
193
- F) Observability and Quality
194
- - Logging for generator failures and gameplay events (anonymized).
195
- - Error boundary UI with recover/retry.
196
- - Test suite:
197
- - Unit: generator, logic, scoring, gating, radar.
198
- - Property-based tests for generator (e.g., Hypothesis) to stress placement constraints.
199
- - Integration tests that simulate a full game and validate scoring.
200
- - Visual regression snapshots of grid/radar (optional).
201
- - CI/CD with linting (flake8/ruff), type checks (mypy/pyright), tests, and build.
202
- - Streamlit: developer toggles in an `st.expander` to simulate states; optional `st.fragment` to limit rerenders in hotspots.
203
-
204
- G) Performance
205
- - Optimize generator to avoid excessive retries (precompute candidate slots, shuffle deterministically).
206
- - Memoize derived UI state.
207
- - Efficient grid rendering (batch updates or delta rendering where possible in Streamlit).
208
- - Streamlit: consider `st.fragment` for the grid/radar to avoid full-page rerenders.
209
-
210
- H) Internationalization (Optional)
211
- - i18n-ready strings; language toggle.
212
- - Locale-specific word lists.
213
- - Streamlit: language toggle via `st.selectbox`.
214
-
215
- I) Security and Abuse Prevention
216
- - Validate all inputs (guess strings A–Z only).
217
- - Rate-limit backend endpoints (if any) and sanitize stored data.
218
- - Streamlit: enforce validation in the submit handler and sanitize displayed content with strict formatting.
219
-
220
- J) Deployment
221
- - Streamlit Community Cloud or containerized deployment (Dockerfile) to any cloud.
222
- - Environment configuration via `.env` or Streamlit secrets.
223
- - Streamlit: use `st.secrets` for API keys.
224
 
225
  Milestones and Estimates (High-level)
226
  - Phase 1 (POC): 2–4 days
 
227
  - Beta (0.5.0): 3–5 days (overlaps, responsive UI, keyboard, deterministic seed)
228
  - Phase 2 (Full): 1–2 weeks depending on features selected
229
 
 
136
  - Ensure `load_word_list(selected_wordlist)` loads the chosen file and matches `generate_puzzle(words_by_len=...)` expected shape.
137
  - Add tests for selection, sorting, and fallback behavior.
138
 
139
+ Phase 1.5: Storage and Sharing (0.3.0) - NEW
140
+ Goal: Add persistent storage, high scores, and game sharing capabilities.
141
+
142
+ A) Storage Module
143
+ - Create `battlewords/storage.py` with:
144
+ - `GameStorage` class for saving/loading game results and high scores
145
+ - `GameResult` and `HighScoreEntry` dataclasses
146
+ - JSON-based local storage in `~/.battlewords/data/`
147
+ - Update `models.py` to include `game_id` in Puzzle (based on word list)
148
+ - Update `generator.py` to:
149
+ - Generate game_id from sorted word list
150
+ - Accept optional `target_words` parameter for replay
151
+ - Integrate storage into game flow:
152
+ - Save result on game completion
153
+ - Display high scores in sidebar
154
+ Acceptance:
155
+ - Game results saved to local JSON files
156
+ - High scores correctly filtered and sorted
157
+ - Game IDs generated deterministically
158
+
159
+ B) Game Sharing
160
+ - Parse `game_id` from query params (`?game_id=ABC123`)
161
+ - Generate puzzle from game_id (same words, different positions)
162
+ - "Share Challenge" button creates shareable URL
163
+ - Display game_id in UI to show shared challenges
164
  Acceptance:
165
+ - URL with game_id loads same word set
166
+ - Share button generates correct URL
167
+ - Game ID visible to players
168
+
169
+ C) High Score Display
170
+ - Sidebar expander for high scores
171
+ - Filter by: All-time, Current Wordlist, Current Mode
172
+ - Top 10 entries with: Rank, Player, Score, Tier, Time
173
+ - Player name input (optional, defaults to "Anonymous")
174
+ Acceptance:
175
+ - High scores display correctly
176
+ - Filters work as expected
177
+ - Player names saved with results
178
+
179
+ D) Tests
180
+ - Unit tests for storage operations
181
+ - Test game ID generation consistency
182
+ - Test save/load result flow
183
+ - Test high score filtering and ranking
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
 
185
  Milestones and Estimates (High-level)
186
  - Phase 1 (POC): 2–4 days
187
+ - Phase 1.5 (Storage & Sharing): 2–3 days ⏳ IN PROGRESS
188
  - Beta (0.5.0): 3–5 days (overlaps, responsive UI, keyboard, deterministic seed)
189
  - Phase 2 (Full): 1–2 weeks depending on features selected
190
 
specs/specs.md CHANGED
@@ -53,12 +53,25 @@ Battlewords is inspired by the classic Battleship game, but uses words instead o
53
  - Persistence, leaderboards, and additional modes as specified in requirements.
54
  - Deterministic daily mode and practice mode supported.
55
 
 
 
 
 
 
 
 
 
 
 
56
  ## UI Elements
57
  - 12x12 grid
58
  - Radar screen (shows last letter locations); y-axis inverted so (0,0) is top-left
59
  - Text box for word guesses
60
  - Score display (shows word, base points, bonus points, total score)
61
  - Guess status indicator (Correct/Try Again)
 
 
 
62
 
63
  ## Word List
64
  - External list at `battlewords/words/wordlist.txt`.
 
53
  - Persistence, leaderboards, and additional modes as specified in requirements.
54
  - Deterministic daily mode and practice mode supported.
55
 
56
+ ## New Features (v0.3.0)
57
+ - **Game ID Sharing:** Each puzzle generates a deterministic game ID based on the word list. Players can share URLs with `?game_id=ABC123` to challenge others with the same words.
58
+ - **Persistent Storage:** Game results and high scores are saved locally in `~/.battlewords/data/`.
59
+ - **High Scores:** Top scores are tracked and displayed in the sidebar, filterable by wordlist and game mode.
60
+ - **Player Name:** Optional player name is saved with results.
61
+
62
+ ## Storage
63
+ - Game results and high scores are stored in JSON files for privacy and offline access.
64
+ - Game ID is generated from the sorted word list for replay/sharing.
65
+
66
  ## UI Elements
67
  - 12x12 grid
68
  - Radar screen (shows last letter locations); y-axis inverted so (0,0) is top-left
69
  - Text box for word guesses
70
  - Score display (shows word, base points, bonus points, total score)
71
  - Guess status indicator (Correct/Try Again)
72
+ - Game ID display and share button in game over dialog.
73
+ - High score expander in sidebar.
74
+ - Player name input in sidebar.
75
 
76
  ## Word List
77
  - External list at `battlewords/words/wordlist.txt`.