# Story 1.3: Play Again & Skill Tracking Status: ready-for-dev ## Story As a player, I want to play additional rounds after the daily word, with words appropriate to my skill level, So that I can keep playing and the challenge adapts to me. ## Acceptance Criteria 1. **Play Again button:** Given I am on Screen 3 after completing or failing a game, when I view the screen, then a Play Again button is visible. ✅ (button exists from 1.2, just needs wiring) 2. **Play Again request:** Given I tap Play Again, when the client sends POST /api/play-again `{ skillMetric: number }`, then the server selects a word from the target bank using weighted random selection (near skill level more likely). Response: `{ wordId }`. Client navigates to Screen 2 with empty grid. 3. **Skill from difficulty:** Given I complete any game, when the final guess response includes `difficulty`, then the client recalculates skill metric from lifetime statistics (average attempts weighted by word difficulty) and persists it. 4. **Skill sent on play-again:** Given I tap Play Again repeatedly, when I play multiple rounds, then each request sends the latest skill metric. No repeat-word prevention (target set is large enough). 5. **Default difficulty fallback:** Given the server has no solver-ranked word bank (all default 3.0), when Play Again selects a word, then selection is effectively random pending solver data from Epic 2. ## Tasks / Subtasks - [ ] Task 1: Implement POST /api/play-again endpoint (AC: 2, 5) - [ ] Create `server/src/play-again.ts` — weighted random word selection near skill level - [ ] Create `server/src/routes/play-again.ts` — POST /api/play-again, returns `{ wordId }` - [ ] Register route in `server/src/index.ts` - [ ] Task 2: Wire Play Again in client (AC: 1, 2, 4) - [ ] Wire `handlePlayAgain` in App.tsx — call POST /api/play-again, reset game state, navigate to Screen 2 - [ ] Reset useGame state for new round - [ ] Task 3: Implement skill metric calculation (AC: 3) - [ ] Update game-end effect to properly average difficulty into skill metric - [ ] Store updated skill metric in localStorage ## Dev Notes ### Building on Story 1.2 - POST /api/guess returns `difficulty` on final guess ✅ - Screen 3 has Play Again button (non-functional) ✅ - `lastDifficulty` exposed from useGame hook ✅ - `state.skillMetric` persisted in localStorage ✅ ### Architecture Compliance - **AD-8 Weighted random selection:** Words near player skill level more likely. Specific distribution is implementation detail. [Source: ARCHITECTURE-SPINE.md#AD-8] - **AD-2 Deterministic daily word unaffected:** Play Again generates different words per player. [Source: ARCHITECTURE-SPINE.md#AD-2] ### Skill Metric Formula ``` skillMetric = (skillMetric * (gamesPlayed - 1) + difficulty) / gamesPlayed ``` Running average of all word difficulties the player has encountered. This converges to the player's true skill over time. ### Files to Create/Modify **New:** `server/src/play-again.ts`, `server/src/routes/play-again.ts` **Modify:** `server/src/index.ts`, `client/src/App.tsx`, `client/src/hooks/useGame.ts`, `client/src/api.ts` ### References - [Source: _bmad-output/planning-artifacts/prds/prd-wordle-2026-07-07/prd.md#FR-11, FR-12, FR-15] - [Source: _bmad-output/planning-artifacts/architecture/architecture-wordle-2026-07-07/ARCHITECTURE-SPINE.md] - [Source: _bmad-output/planning-artifacts/epics.md#Story 1.3]