Spaces:
Running
Running
Documentation Update
Browse files- README.md +22 -0
- requirements.txt +1 -0
- specs/requirements.md +47 -40
- specs/specs.md +0 -1
README.md
CHANGED
|
@@ -28,6 +28,7 @@ BattleWords is a vocabulary learning game inspired by classic Battleship mechani
|
|
| 28 |
- Reveal grid cells and guess words for points
|
| 29 |
- Scoring tiers: Good (34β37), Great (38β41), Fantastic (42+)
|
| 30 |
- Responsive UI built with Streamlit
|
|
|
|
| 31 |
- Deterministic seed support (Beta/Full)
|
| 32 |
- Keyboard navigation and guessing (Beta/Full)
|
| 33 |
- Overlapping words on shared letters (Beta)
|
|
@@ -85,6 +86,27 @@ streamlit run app.py
|
|
| 85 |
3. Earn points for correct guesses and bonus points for unrevealed letters.
|
| 86 |
4. **The game ends when all six words are found or all word letters are revealed. Your score tier is displayed.**
|
| 87 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
## Development Phases
|
| 89 |
|
| 90 |
- **Proof of Concept (0.1.0):** No overlaps, basic UI, single session.
|
|
|
|
| 28 |
- Reveal grid cells and guess words for points
|
| 29 |
- Scoring tiers: Good (34β37), Great (38β41), Fantastic (42+)
|
| 30 |
- Responsive UI built with Streamlit
|
| 31 |
+
- Wordlist sidebar controls (picker + one-click sort)
|
| 32 |
- Deterministic seed support (Beta/Full)
|
| 33 |
- Keyboard navigation and guessing (Beta/Full)
|
| 34 |
- Overlapping words on shared letters (Beta)
|
|
|
|
| 86 |
3. Earn points for correct guesses and bonus points for unrevealed letters.
|
| 87 |
4. **The game ends when all six words are found or all word letters are revealed. Your score tier is displayed.**
|
| 88 |
|
| 89 |
+
## Changelog
|
| 90 |
+
|
| 91 |
+
- 0.1.5
|
| 92 |
+
- Hit/Miss circular indicator.
|
| 93 |
+
- Completed words render as non-interactive styled cells.
|
| 94 |
+
- Tooltips show cell coordinates for buttons and revealed cells.
|
| 95 |
+
- Stable letter map rebuild after reveals.
|
| 96 |
+
|
| 97 |
+
- 0.1.4
|
| 98 |
+
- Radar rewritten as an animated GIF with metallic gradient background and scope overlay.
|
| 99 |
+
- Session-level caching of the generated radar GIF to avoid regeneration during a session.
|
| 100 |
+
- Mobile layout improvements: radar above grid on small screens; tighter grid gaps and horizontal scrolling per row.
|
| 101 |
+
|
| 102 |
+
- 0.1.3
|
| 103 |
+
- Sidebar wordlist picker and `Sort Wordlist` action (length-first then alphabetic) with a 5-second feedback delay before restarting a new game.
|
| 104 |
+
- Score panel improvements, per-word points, and emphasized final score styling.
|
| 105 |
+
|
| 106 |
+
## Known Issues / TODO
|
| 107 |
+
|
| 108 |
+
- Word list loading bug: the app may not select the proper word lists in some environments. Investigate `word_loader.get_wordlist_files()` / `load_word_list()` and sidebar selection persistence to ensure the chosen file is correctly used by the generator.
|
| 109 |
+
|
| 110 |
## Development Phases
|
| 111 |
|
| 112 |
- **Proof of Concept (0.1.0):** No overlaps, basic UI, single session.
|
requirements.txt
CHANGED
|
@@ -3,6 +3,7 @@ pandas
|
|
| 3 |
streamlit
|
| 4 |
matplotlib
|
| 5 |
numpy
|
|
|
|
| 6 |
pytest
|
| 7 |
flake8
|
| 8 |
mypy
|
|
|
|
| 3 |
streamlit
|
| 4 |
matplotlib
|
| 5 |
numpy
|
| 6 |
+
Pillow
|
| 7 |
pytest
|
| 8 |
flake8
|
| 9 |
mypy
|
specs/requirements.md
CHANGED
|
@@ -3,14 +3,18 @@
|
|
| 3 |
This document breaks down the tasks to build Battlewords using the game rules described in `specs.md`. It is organized in phases: a minimal Proof of Concept (POC), a Beta Version (0.5.0), and a Full Version (1.0.0).
|
| 4 |
|
| 5 |
Assumptions
|
| 6 |
-
- Tech stack: Python 3.10+, Streamlit for UI, matplotlib for radar, numpy for tick helpers.
|
| 7 |
- Single-player, local state stored in Streamlit session state for POC.
|
| 8 |
- Grid is always 12x12 with exactly six words: two 4-letter, two 5-letter, two 6-letter words; horizontal/vertical only; no shared letters or overlaps in POC; shared-letter overlaps allowed in Beta; no overlaps in Full.
|
| 9 |
- Entry point is `app.py`.
|
| 10 |
|
| 11 |
Streamlit key components (API usage plan)
|
| 12 |
- State & caching
|
| 13 |
-
- `st.session_state` for `puzzle`, `revealed`, `guessed`, `score`, `
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
- `st.cache_data` to load and filter the word list.
|
| 15 |
- Layout & structure
|
| 16 |
- `st.title`, `st.subheader`, `st.markdown` for headers/instructions.
|
|
@@ -18,12 +22,13 @@ Streamlit key components (API usage plan)
|
|
| 18 |
- `st.expander` for inline help/intel tips.
|
| 19 |
- Widgets (interaction)
|
| 20 |
- `st.button` for each grid cell (144 total) with unique `key` to handle reveals.
|
| 21 |
-
- `st.form` + `st.text_input` + `st.form_submit_button("OK")` for controlled word guessing.
|
| 22 |
-
- `st.button("New Game")`
|
| 23 |
-
- `st.metric` to show score
|
| 24 |
- Visualization
|
| 25 |
-
-
|
| 26 |
-
-
|
|
|
|
| 27 |
- Control flow
|
| 28 |
- App reruns on interaction; uses `st.rerun()` after state changes (reveal, guess); `st.stop()` after game over summary to freeze UI.
|
| 29 |
|
|
@@ -35,10 +40,10 @@ Folder Structure
|
|
| 35 |
- `word_loader.py` β load/validate/cached word lists (uses `battlewords/words/wordlist.txt` with fallback)
|
| 36 |
- `generator.py` β word placement; imports from `word_loader`; avoids duplicate words
|
| 37 |
- `logic.py` β game mechanics (reveal, guess, scoring, tiers)
|
| 38 |
-
- `ui.py` β Streamlit UI composition; immediate rerender on reveal/guess via `st.rerun()`; inverted radar Y
|
| 39 |
- `words/wordlist.txt` β candidate words
|
| 40 |
- `specs/` β documentation (this file and `specs.md`)
|
| 41 |
-
- `tests/` β unit tests
|
| 42 |
|
| 43 |
Phase 1: Proof of Concept (0.1.0)
|
| 44 |
Goal: A playable, single-session game demonstrating core rules, scoring, and radar without persistence or advanced UX.
|
|
@@ -73,59 +78,61 @@ Acceptance: Generator returns a valid `Puzzle` passing validation checks (no col
|
|
| 73 |
- Reveal:
|
| 74 |
- Click a covered cell to reveal; if the cell is part of a word, show the letter; else mark empty (CSS class `empty`).
|
| 75 |
- After a reveal action, set `can_guess=True`.
|
| 76 |
-
- Streamlit: 12Γ12 `st.columns` + `st.button(label, key=f"cell_{r}_{c}")` per cell; on click, update `st.session_state` and call `st.rerun()`.
|
| 77 |
- Guess:
|
| 78 |
- Accept a guess only if `can_guess` is True and input length β {4,5,6}.
|
| 79 |
- Match guess case-insensitively against unguessed words in puzzle.
|
| 80 |
- If correct: add base points = word length; bonus points = count of unrevealed cells in that word at guess time; mark all cells of the word as revealed; add to `guessed`.
|
| 81 |
- If incorrect: no points awarded.
|
| 82 |
- After any guess, set `can_guess=False` and require another reveal before next guess.
|
| 83 |
-
-
|
| 84 |
-
-
|
|
|
|
| 85 |
|
| 86 |
Acceptance: Unit tests cover scoring, guess gating, and reveal behavior.
|
| 87 |
|
| 88 |
5) UI (Streamlit)
|
| 89 |
- Layout:
|
| 90 |
- Title and brief instructions via `st.title`, `st.subheader`, `st.markdown`.
|
| 91 |
-
- Left: 12Γ12 grid using `st.columns(12)
|
| 92 |
-
- Right:
|
| 93 |
-
-
|
| 94 |
-
- Score panel showing current score using `st.metric` and `st.markdown` for last action.
|
| 95 |
-
- Optional `st.sidebar` to host reset/new game and settings; shows word list source/counts.
|
| 96 |
- Visuals:
|
| 97 |
-
- Covered
|
|
|
|
| 98 |
|
| 99 |
-
Acceptance: Users can play end-to-end
|
| 100 |
|
| 101 |
6) Scoring Tiers
|
| 102 |
- After game ends, compute tier:
|
| 103 |
- Good: 34β37
|
| 104 |
- Great: 38β41
|
| 105 |
- Fantastic: 42+
|
| 106 |
-
- Display final summary with found words, per-word points, total.
|
| 107 |
-
- Streamlit: show results in a `st.container` or `st.expander("Game summary")`.
|
| 108 |
-
|
| 109 |
-
Acceptance: Tier text shown at game end; manual test with mocked states.
|
| 110 |
|
| 111 |
7) Basic Tests
|
| 112 |
-
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
-
|
| 124 |
-
-
|
| 125 |
-
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 129 |
|
| 130 |
B) UI and Interaction
|
| 131 |
- Cell rendering with consistent sizing and responsive layout (desktop/mobile).
|
|
|
|
| 3 |
This document breaks down the tasks to build Battlewords using the game rules described in `specs.md`. It is organized in phases: a minimal Proof of Concept (POC), a Beta Version (0.5.0), and a Full Version (1.0.0).
|
| 4 |
|
| 5 |
Assumptions
|
| 6 |
+
- Tech stack: Python 3.10+, Streamlit for UI, matplotlib for radar, numpy for tick helpers, Pillow for animated GIFs.
|
| 7 |
- Single-player, local state stored in Streamlit session state for POC.
|
| 8 |
- Grid is always 12x12 with exactly six words: two 4-letter, two 5-letter, two 6-letter words; horizontal/vertical only; no shared letters or overlaps in POC; shared-letter overlaps allowed in Beta; no overlaps in Full.
|
| 9 |
- Entry point is `app.py`.
|
| 10 |
|
| 11 |
Streamlit key components (API usage plan)
|
| 12 |
- State & caching
|
| 13 |
+
- `st.session_state` for `puzzle`, `grid_size`, `revealed`, `guessed`, `score`, `last_action`, `can_guess`.
|
| 14 |
+
- `st.session_state.points_by_word` for per-word score breakdown.
|
| 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.
|
|
|
|
| 22 |
- `st.expander` for inline help/intel tips.
|
| 23 |
- Widgets (interaction)
|
| 24 |
- `st.button` for each grid cell (144 total) with unique `key` to handle reveals.
|
| 25 |
+
- `st.form` + `st.text_input` + `st.form_submit_button("OK")` for controlled word guessing (disabled until a reveal).
|
| 26 |
+
- `st.button("New Game")` to reset state; sidebar `selectbox` for wordlist selection and `Sort Wordlist` button (length+alpha).
|
| 27 |
+
- `st.metric` to show score.
|
| 28 |
- Visualization
|
| 29 |
+
- Animated radar using matplotlib `FuncAnimation` + `PillowWriter` saved to GIF.
|
| 30 |
+
- Scope overlay image generated once and reused; metallic gradient background.
|
| 31 |
+
- Radar plot uses inverted Y so (0,0) is top-left.
|
| 32 |
- Control flow
|
| 33 |
- App reruns on interaction; uses `st.rerun()` after state changes (reveal, guess); `st.stop()` after game over summary to freeze UI.
|
| 34 |
|
|
|
|
| 40 |
- `word_loader.py` β load/validate/cached word lists (uses `battlewords/words/wordlist.txt` with fallback)
|
| 41 |
- `generator.py` β word placement; imports from `word_loader`; avoids duplicate words
|
| 42 |
- `logic.py` β game mechanics (reveal, guess, scoring, tiers)
|
| 43 |
+
- `ui.py` β Streamlit UI composition; animated radar; immediate rerender on reveal/guess via `st.rerun()`; inverted radar Y
|
| 44 |
- `words/wordlist.txt` β candidate words
|
| 45 |
- `specs/` β documentation (this file and `specs.md`)
|
| 46 |
+
- `tests/` β unit tests
|
| 47 |
|
| 48 |
Phase 1: Proof of Concept (0.1.0)
|
| 49 |
Goal: A playable, single-session game demonstrating core rules, scoring, and radar without persistence or advanced UX.
|
|
|
|
| 78 |
- Reveal:
|
| 79 |
- Click a covered cell to reveal; if the cell is part of a word, show the letter; else mark empty (CSS class `empty`).
|
| 80 |
- After a reveal action, set `can_guess=True`.
|
|
|
|
| 81 |
- Guess:
|
| 82 |
- Accept a guess only if `can_guess` is True and input length β {4,5,6}.
|
| 83 |
- Match guess case-insensitively against unguessed words in puzzle.
|
| 84 |
- If correct: add base points = word length; bonus points = count of unrevealed cells in that word at guess time; mark all cells of the word as revealed; add to `guessed`.
|
| 85 |
- If incorrect: no points awarded.
|
| 86 |
- After any guess, set `can_guess=False` and require another reveal before next guess.
|
| 87 |
+
- Exception in 0.1.5: after a correct guess, another guess is allowed without a reveal (`can_guess=True`).
|
| 88 |
+
- Streamlit: `with st.form("guess"):` + `st.text_input("Your guess")` + `st.form_submit_button("OK", disabled=not can_guess)`; after guess, call `st.rerun()`.
|
| 89 |
+
- End of game when all 6 words are guessed or all word letters are revealed; display summary and tier, then `st.stop()`.
|
| 90 |
|
| 91 |
Acceptance: Unit tests cover scoring, guess gating, and reveal behavior.
|
| 92 |
|
| 93 |
5) UI (Streamlit)
|
| 94 |
- Layout:
|
| 95 |
- Title and brief instructions via `st.title`, `st.subheader`, `st.markdown`.
|
| 96 |
+
- Left: 12Γ12 grid using `st.columns(12)`.
|
| 97 |
+
- Right: Animated radar, Hit/Miss indicator, guess form, and score panel.
|
| 98 |
+
- Sidebar: New Game, wordlist selectbox, Sort Wordlist action.
|
|
|
|
|
|
|
| 99 |
- Visuals:
|
| 100 |
+
- Covered vs revealed styles; revealed empty cells use CSS class `empty`.
|
| 101 |
+
- Completed word cells styled with `bw-cell-complete`; cell tooltips show coordinates.
|
| 102 |
|
| 103 |
+
Acceptance: Users can play end-to-end; radar shows exactly 6 pulses; reveal and guess update via rerun; completed words are visually distinct.
|
| 104 |
|
| 105 |
6) Scoring Tiers
|
| 106 |
- After game ends, compute tier:
|
| 107 |
- Good: 34β37
|
| 108 |
- Great: 38β41
|
| 109 |
- Fantastic: 42+
|
|
|
|
|
|
|
|
|
|
|
|
|
| 110 |
|
| 111 |
7) Basic Tests
|
| 112 |
+
- Placement validity (bounds, overlap, counts, no duplicate words).
|
| 113 |
+
- Scoring logic and bonus calculation.
|
| 114 |
+
- Guess gating (reveal required except chaining after correct guess).
|
| 115 |
+
|
| 116 |
+
Current Deltas (0.1.3 β 0.1.5)
|
| 117 |
+
- 0.1.3
|
| 118 |
+
- Sidebar wordlist select; sorting persists length-then-alpha ordering; auto new-game after 5s notice.
|
| 119 |
+
- Score panel improvements; per-word points; final score styling.
|
| 120 |
+
- 0.1.4
|
| 121 |
+
- Animated radar GIF with metallic gradient and scope overlay; session reuse via `radar_gif_path`.
|
| 122 |
+
- Mobile layout improvements; tighter grid spacing and horizontal scroll per row.
|
| 123 |
+
- 0.1.5
|
| 124 |
+
- Hit/Miss indicator derived from `last_action`.
|
| 125 |
+
- Completed word cells render as non-buttons with tooltips.
|
| 126 |
+
- Helper functions for scope image and stable letter map rebuild.
|
| 127 |
+
|
| 128 |
+
Known Issues / TODO
|
| 129 |
+
- Word list selection bug: improper list fetched/propagated in some runs.
|
| 130 |
+
- Verify `get_wordlist_files()` returns correct filenames and `selected_wordlist` persists across `_new_game()`.
|
| 131 |
+
- Ensure `load_word_list(selected_wordlist)` loads the chosen file and matches `generate_puzzle(words_by_len=...)` expected shape.
|
| 132 |
+
- Add tests for selection, sorting, and fallback behavior.
|
| 133 |
+
|
| 134 |
+
Beta (0.5.0) β Planned
|
| 135 |
+
- Allow shared-letter overlaps when letters match; deterministic seeding; keyboard navigation.
|
| 136 |
|
| 137 |
B) UI and Interaction
|
| 138 |
- Cell rendering with consistent sizing and responsive layout (desktop/mobile).
|
specs/specs.md
CHANGED
|
@@ -39,7 +39,6 @@ Battlewords is inspired by the classic Battleship game, but uses words instead o
|
|
| 39 |
- Seed is optional and not standardized.
|
| 40 |
|
| 41 |
## Beta (0.5.0) Additions
|
| 42 |
-
- Overlaps allowed on shared letters: words may cross only where letters match; still forbid conflicting letters in the same cell.
|
| 43 |
- Optional validation pass to avoid unintended adjacent partial words (content curation rule).
|
| 44 |
- Cell rendering with consistent sizing and responsive layout (desktop/mobile).
|
| 45 |
- Keyboard support for navigation and guessing (custom JS via `st.html` or a component).
|
|
|
|
| 39 |
- Seed is optional and not standardized.
|
| 40 |
|
| 41 |
## Beta (0.5.0) Additions
|
|
|
|
| 42 |
- Optional validation pass to avoid unintended adjacent partial words (content curation rule).
|
| 43 |
- Cell rendering with consistent sizing and responsive layout (desktop/mobile).
|
| 44 |
- Keyboard support for navigation and guessing (custom JS via `st.html` or a component).
|