Spaces:
Running
# Battlewords: Implementation Requirements
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).
Assumptions
- Tech stack: Python 3.10+, Streamlit for UI, matplotlib for radar, numpy for tick helpers.
- Single-player, local state stored in Streamlit session state for POC.
- 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.
- Entry point is
app.py.
Streamlit key components (API usage plan)
- State & caching
st.session_stateforpuzzle,revealed,guessed,score,can_guess,last_action.st.cache_datato load and filter the word list.
- Layout & structure
st.title,st.subheader,st.markdownfor headers/instructions.st.columns(12)to render the 12×12 grid;st.containerfor grouping;st.sidebarfor secondary controls/help.st.expanderfor inline help/intel tips.
- Widgets (interaction)
st.buttonfor each grid cell (144 total) with uniquekeyto handle reveals.st.form+st.text_input+st.form_submit_button("OK")for controlled word guessing.st.button("New Game")orst.link_buttonto reset state.st.metricto show score;st.checkbox/st.togglefor optional settings (e.g., show radar).
- Visualization
st.pyplotfor the radar mini-grid (scatter on a 12×12 axes) orst.plotly_chartif interactive.- Radar plot uses
ax.set_ylim(size, 0)to invert Y so (0,0) is top-left.
- Control flow
- App reruns on interaction; uses
st.rerun()after state changes (reveal, guess);st.stop()after game over summary to freeze UI.
- App reruns on interaction; uses
Folder Structure
app.py– Streamlit entry pointbattlewords/– Python package__init__.pymodels.py– data models and typesword_loader.py– load/validate/cached word lists (usesbattlewords/words/wordlist.txtwith fallback)generator.py– word placement; imports fromword_loader; avoids duplicate wordslogic.py– game mechanics (reveal, guess, scoring, tiers)ui.py– Streamlit UI composition; immediate rerender on reveal/guess viast.rerun(); inverted radar Ywords/wordlist.txt– candidate words
specs/– documentation (this file andspecs.md)tests/– unit tests (optional for now)
Phase 1: Proof of Concept (0.1.0) Goal: A playable, single-session game demonstrating core rules, scoring, and radar without persistence or advanced UX.
- Data Models
- Define
Coord(x:int, y:int). - Define
Word(text:str, start:Coord, direction:str{"H","V"}, cells:list[Coord]). - Define
Puzzle(words:list[Word], radar:list[Coord])– radar holds last-letter coordinates. - Define
GameState(grid_size:int=12, puzzle:Puzzle, revealed:set[Coord], guessed:set[str], score:int, last_action:str, can_guess:bool).
Acceptance: Types exist and are consumed by generator/logic; simple constructors and validators.
- Word List
- Add an English word list filtered to alphabetic uppercase, lengths in {4,5,6}.
- Ensure words contain no special characters; maintain reasonable difficulty.
- Streamlit:
st.cache_datato memoize loading/filtering. - Loader is centralized in
word_loader.pyand used by generator and UI.
Acceptance: Loading function returns lists by length with >= 500 words per length or fallback minimal lists.
- Puzzle Generation (Placement)
- Randomly place 2×4, 2×5, 2×6 letter words on a 12×12 grid.
- Constraints (POC):
- Horizontal (left→right) or Vertical (top→down) only.
- No overlapping letters between different words (cells must be unique).
- Compute radar pulses as the last cell of each word.
- Retry strategy with max attempts; raise a controlled error if generation fails.
Acceptance: Generator returns a valid Puzzle passing validation checks (no collisions, in-bounds, correct counts, no duplicates).
- Game Mechanics
- Reveal:
- Click a covered cell to reveal; if the cell is part of a word, show the letter; else mark empty (CSS class
empty). - After a reveal action, set
can_guess=True. - Streamlit: 12×12
st.columns+st.button(label, key=f"cell_{r}_{c}")per cell; on click, updatest.session_stateand callst.rerun().
- Click a covered cell to reveal; if the cell is part of a word, show the letter; else mark empty (CSS class
- Guess:
- Accept a guess only if
can_guessis True and input length ∈ {4,5,6}. - Match guess case-insensitively against unguessed words in puzzle.
- 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. - If incorrect: no points awarded.
- After any guess, set
can_guess=Falseand require another reveal before next guess. - Streamlit:
with st.form("guess"):+st.text_input("Your guess", key="guess_text")+st.form_submit_button("OK", disabled=not st.session_state.can_guess); after guess, callst.rerun().
- Accept a guess only if
- End of game when all 6 words are guessed or all word letters are revealed; display summary and tier, then
st.stop().
Acceptance: Unit tests cover scoring, guess gating, and reveal behavior.
- UI (Streamlit)
- Layout:
- Title and brief instructions via
st.title,st.subheader,st.markdown. - Left: 12×12 grid using
st.columns(12)ofst.buttons. - Right: Radar mini-grid via
st.pyplot(matplotlib scatter) orst.plotly_chart. - Bottom/right: Guess form using
st.form,st.text_input,st.form_submit_button. - Score panel showing current score using
st.metricandst.markdownfor last action. - Optional
st.sidebarto host reset/new game and settings; shows word list source/counts.
- Title and brief instructions via
- Visuals:
- Covered cell vs revealed styles: use button labels/emojis and background color hints; revealed empty cells use CSS class
emptyfor background.
- Covered cell vs revealed styles: use button labels/emojis and background color hints; revealed empty cells use CSS class
Acceptance: Users can play end-to-end in one session; UI updates consistently; radar shows exactly 6 pulses; single-click reveal and guess update via rerun.
- Scoring Tiers
- After game ends, compute tier:
- Good: 34–37
- Great: 38–41
- Fantastic: 42+
- Display final summary with found words, per-word points, total.
- Streamlit: show results in a
st.containerorst.expander("Game summary").
Acceptance: Tier text shown at game end; manual test with mocked states.
- Basic Tests
- Unit tests for:
- Placement validity (bounds, overlap, counts, no duplicate words).
- Scoring logic and bonus calculation.
- Guess gating (must reveal before next guess).
Acceptance: Tests run and pass locally.
Beta Version (0.5.0) Goal: Introduce overlapping words on shared letters, improve UX responsiveness and input options, and add deterministic seeding.
A) Generator and Validation
- Allow shared-letter overlaps: words may cross on the same letter; still disallow conflicting letters on the same cell.
- Optional validation pass to detect and avoid unintended adjacent partial words (content curation rule).
- Deterministic seed support to reproduce puzzles (e.g., daily seed derived from date). Acceptance:
- Placement permits shared-letter crossings only when letters match.
- With a fixed seed/date, the same puzzle is produced.
B) UI and Interaction
- Cell rendering with consistent sizing and responsive layout (desktop/mobile).
- Keyboard support for grid navigation and guessing (custom JS via
st.htmlor component). - Maintain radar behavior and scoring rules. Acceptance:
- Grid scales cleanly across typical desktop and mobile widths.
- Users can enter guesses and move focus via keyboard.
C) Tests
- Property checks for overlap validity (only same letters may share a cell).
- Seed reproducibility tests (same seed → identical placements).
- Optional validation tests for adjacency curation (when enabled).
Phase 2: Full Version (1.0.0) Goal: Robust app with polish, persistence, test coverage, and optional advanced features.
A) UX and Visual Polish
- Cell rendering with consistent sizing and responsive layout (desktop/mobile).
- Keyboard support for navigation and guessing (custom JS via
st.htmlor a component if needed). - Animations for reveals and correct guesses (CSS/JS via
st.htmlor component). - Color-blind friendly palette and accessible contrast.
- Configurable themes (light/dark) via Streamlit theme config.
- Streamlit:
st.tabsfor modes/help;st.popover/st.expanderfor tips;st.toast/st.warningfor feedback.
B) Game Content and Generation
- Curated word lists by difficulty; exclude obscure/abusive words.
- Deterministic seed support to reproduce puzzles (e.g., daily seed based on date).
- Validation pass to ensure no unintended partial words formed adjacently (content curation rule, optional).
- Optional generator diagnostics panel for QA using
st.expanderandst.dataframe. - Streamlit:
st.cache_datafor word lists;st.cache_resourceif needed for heavier resources.
C) Game Modes and Settings
- Daily Puzzle mode (same seed for all players per day).
- Practice mode (random puzzles).
- Difficulty presets that tweak word selection (common vs. rare) but still keep 2×4, 2×5, 2×6.
- Optional hint system with limited uses (e.g., reveal a random letter in an unguessed word) with score penalty.
- Streamlit: mode selection via
st.radio/st.segmented_control; settings viast.sidebarwithst.toggle/st.slider.
D) Persistence and Profiles
- Save/Load local game state (browser cookie or Streamlit session + query params).
- Cloud persistence via lightweight backend API (FastAPI) or Streamlit secrets + hosted storage for:
- User profiles (username only), completed puzzles, scores.
- Leaderboards for Daily mode.
- Privacy notice and opt-in for storing data.
- Streamlit:
st.text_inputfor username;st.buttonto save; call backend viarequests.
E) Leaderboards and Sharing
- Global and friends leaderboards (score and completion time if captured; note: game is strategy-first, time is optional).
- Share result string with spoiler-free grid (similar to popular word games).
- Streamlit:
st.table/st.dataframefor leaderboards;st.download_buttonor copy-to-clipboard viast.text_area+st.button.
F) Observability and Quality
- Logging for generator failures and gameplay events (anonymized).
- Error boundary UI with recover/retry.
- Test suite:
- Unit: generator, logic, scoring, gating, radar.
- Property-based tests for generator (e.g., Hypothesis) to stress placement constraints.
- Integration tests that simulate a full game and validate scoring.
- Visual regression snapshots of grid/radar (optional).
- CI/CD with linting (flake8/ruff), type checks (mypy/pyright), tests, and build.
- Streamlit: developer toggles in an
st.expanderto simulate states; optionalst.fragmentto limit rerenders in hotspots.
G) Performance
- Optimize generator to avoid excessive retries (precompute candidate slots, shuffle deterministically).
- Memoize derived UI state.
- Efficient grid rendering (batch updates or delta rendering where possible in Streamlit).
- Streamlit: consider
st.fragmentfor the grid/radar to avoid full-page rerenders.
H) Internationalization (Optional)
- i18n-ready strings; language toggle.
- Locale-specific word lists.
- Streamlit: language toggle via
st.selectbox.
I) Security and Abuse Prevention
- Validate all inputs (guess strings A–Z only).
- Rate-limit backend endpoints (if any) and sanitize stored data.
- Streamlit: enforce validation in the submit handler and sanitize displayed content with strict formatting.
J) Deployment
- Streamlit Community Cloud or containerized deployment (Dockerfile) to any cloud.
- Environment configuration via
.envor Streamlit secrets. - Streamlit: use
st.secretsfor API keys.
Milestones and Estimates (High-level)
- Phase 1 (POC): 2–4 days
- Beta (0.5.0): 3–5 days (overlaps, responsive UI, keyboard, deterministic seed)
- Phase 2 (Full): 1–2 weeks depending on features selected
Definitions of Done (per task)
- Code merged with tests and docs updated.
- No regressions in existing tests; coverage maintained or improved for core logic.
- Manual playthrough validates rules: reveal/guess gating, scoring, radar pulses, end state and tiers.