From 0fa546dc907d4fa78a1a75bceb103f22011d4a6f Mon Sep 17 00:00:00 2001 From: tiennm99 Date: Mon, 13 Apr 2026 19:52:41 +0700 Subject: [PATCH] docs: add epics and stories breakdown for 2048 clone 5 epics, 20 stories covering all 31 functional requirements, 9 NFRs, and 20 UX design requirements. --- bmad/_bmad-output/planning-artifacts/epics.md | 652 ++++++++++++++++++ 1 file changed, 652 insertions(+) create mode 100644 bmad/_bmad-output/planning-artifacts/epics.md diff --git a/bmad/_bmad-output/planning-artifacts/epics.md b/bmad/_bmad-output/planning-artifacts/epics.md new file mode 100644 index 0000000..52a7904 --- /dev/null +++ b/bmad/_bmad-output/planning-artifacts/epics.md @@ -0,0 +1,652 @@ +--- +stepsCompleted: ['step-01-validate-prerequisites', 'step-02-design-epics', 'step-03-create-stories', 'step-04-final-validation'] +inputDocuments: + - '_bmad-output/planning-artifacts/prd.md' + - '_bmad-output/planning-artifacts/architecture.md' + - '_bmad-output/planning-artifacts/ux-design-specification.md' +--- + +# try-bmad - Epic Breakdown + +## Overview + +This document provides the complete epic and story breakdown for try-bmad (2048 Clone), decomposing the requirements from the PRD, UX Design, and Architecture into implementable stories. + +## Requirements Inventory + +### Functional Requirements + +FR1: Render a 4x4 grid with empty cell placeholders using Svelte components +FR2: Spawn 2 random tiles on game initialization +FR3: Spawn 1 random tile after each valid move with 90/10 probability split (value 2 vs value 4) +FR4: Slide all movable tiles in the input direction (up, down, left, right) +FR5: Merge identical adjacent tiles in the movement path using leading-edge merge order +FR6: Enforce once-per-move merge rule (a tile can only merge once per move) +FR7: Track and display current score, incrementing by the value of each merged tile +FR8: Track and display best score, updating when current score exceeds it +FR9: Persist best score across sessions using localStorage +FR10: Detect game over condition (no valid moves remaining on a full board) +FR11: Detect win condition (2048 tile created) and display win overlay (trigger once per game) +FR12: Provide "Keep playing" mode after winning, allowing continued play past 2048 +FR13: Provide New Game button to restart the game (no confirmation dialog) +FR14: Accept keyboard input: arrow keys for directional movement +FR15: Accept keyboard input: WASD keys for directional movement +FR16: Accept keyboard input: Vim hjkl keys for directional movement +FR17: Accept touch/swipe input for mobile with 10px minimum threshold and dominant-axis detection +FR18: Display tile colors matching original 2048 12-tier color system based on tile value +FR19: Animate tile sliding with 100ms CSS transition (ease-in-out) +FR20: Animate new tile spawn with 200ms pop animation (scale 0 to 1.0) +FR21: Animate tile merge with 200ms bounce animation (scale 1.0 to 1.2 to 1.0) +FR22: Animate score addition with "+N" float animation (600ms, rises and fades) +FR23: Persist full game state to localStorage after every valid move (survive page refresh) +FR24: Restore game state from localStorage on page load +FR25: Display game over overlay with "Try again" button +FR26: Display win overlay with "Keep going" and "New Game" buttons +FR27: Fade in win/game-over overlays with 800ms opacity transition +FR28: Render responsive layout: 500px container on desktop, 280px on mobile at 520px breakpoint +FR29: Display tile glow effect (box-shadow) on tiles with value 128 or higher +FR30: Apply dynamic font sizing on tiles based on digit count (55/45/35/25/15px) +FR31: Deploy as static site to GitHub Pages via GitHub Actions workflow + +### NonFunctional Requirements + +NFR1: Initial page load under 1 second (small Svelte + Tailwind bundle) +NFR2: All CSS animations at 60fps framerate +NFR3: Input-to-visual-response latency under 16ms +NFR4: Production bundle size under 50KB gzipped +NFR5: Support modern evergreen browsers: Chrome, Firefox, Safari, Edge (latest versions) +NFR6: WCAG AA accessibility compliance for color contrast and keyboard navigation +NFR7: Respect prefers-reduced-motion media query (disable animations when active) +NFR8: No backend, no server communication — fully client-side application +NFR9: Game logic implemented as pure JavaScript functions, testable independently from UI + +### Additional Requirements + +- Starter template: `npm create vite@latest` with Svelte template + Tailwind CSS v4 via `@tailwindcss/vite` plugin + Vitest for unit testing +- Project structure: `src/lib/` for pure JS game logic (zero Svelte imports), `src/components/` for Svelte UI components +- Game logic module returns immutable new state objects — never mutates input +- Props-down component architecture: App.svelte owns all state, children are presentational +- No Svelte stores — props and callback props only +- localStorage schema: `gameState` key (grid, score, won, keepPlaying) + `bestScore` key +- CSS transitions for all animations — no JS setTimeout for animation sequencing +- Use `transitionend` events for animation coordination +- `isAnimating` flag prevents input processing during slide transition with input queuing +- GitHub Actions workflow for automated build and deploy to gh-pages branch +- Vite `base` config set to repo name for correct GitHub Pages asset paths +- Clear Sans font file (woff2) in `public/fonts/` with fallback chain +- Constants module (`constants.js`) for GRID_SIZE, WIN_VALUE, SPAWN_PROBABILITY, DIRECTIONS, TILE_COLORS + +### UX Design Requirements + +UX-DR1: Implement 12-tier tile color system with exact hex values (2:#eee4da, 4:#ede0c8, 8:#f2b179, 16:#f59563, 32:#f67c5f, 64:#f65e3b, 128:#edcf72, 256:#edcc61, 512:#edc850, 1024:#edc53f, 2048:#edc22e, 4096+:#3c3a32) +UX-DR2: Implement text color switching — dark text (#776e65) for tiles 2 and 4, white text (#f9f6f2) for tiles 8 and above +UX-DR3: Apply page background #faf8ef, grid background #bbada0, empty cell color #cdc1b4 +UX-DR4: Implement Clear Sans font family with fallback chain (Helvetica Neue, Arial, sans-serif) +UX-DR5: Implement dynamic tile font sizing by digit count — 1 digit: 55px, 2 digits: 45px, 3 digits: 35px, 4 digits: 25px, 5+ digits: 15px (bold) +UX-DR6: Implement glow effect on 128+ tiles: box-shadow 0 0 30px 10px rgba(243, 215, 116, 0.4) +UX-DR7: Implement score box styling — background #bbada0, label #eee4da 13px uppercase, value #f9f6f2 25px bold +UX-DR8: Implement button styling — background #8f7a66, text #f9f6f2, 18px bold, hover lighten 10%, active darken 5% +UX-DR9: Implement game title at 80px bold in #776e65 +UX-DR10: Implement responsive breakpoint at 520px — scale container from 500px to 280px, grid gap from 15px to 10px, tile fonts proportionally reduced +UX-DR11: Implement mobile font scaling — title 45px, tile 1-digit 30px, 2-digit 25px, 3-digit 20px, 4-digit 14px, score 16px, overlay 35px +UX-DR12: Implement ARIA roles — role="application" on game container, role="grid" on board, role="gridcell" on tiles, role="alertdialog" on overlays +UX-DR13: Implement aria-live="polite" on score display for screen reader announcements +UX-DR14: Implement focus management — auto-focus action button on overlay appear, trap focus within overlay, restore focus on dismiss +UX-DR15: Implement keyboard navigation — Tab to New Game button, Enter to activate buttons, visible focus indicator +UX-DR16: Implement overlay patterns — game over: rgba(238, 228, 218, 0.73) background, win: rgba(237, 194, 46, 0.5) background +UX-DR17: Implement non-blocking animation system — input queuing during transitions, moves execute after current animation completes +UX-DR18: Implement touch targets minimum 44x44px for New Game button and overlay action buttons +UX-DR19: Implement zero-chrome layout — grid as dominant element, header holds only title + scores + new game button +UX-DR20: Implement semantic HTML — use main, header, button elements (no divs-as-buttons) + +### FR Coverage Map + +| FR | Epic | Description | +|----|------|-------------| +| FR1 | Epic 1 | 4x4 grid rendering | +| FR2 | Epic 1 | Spawn 2 tiles at start | +| FR3 | Epic 1 | Spawn 1 tile per move (90/10) | +| FR4 | Epic 1 | Slide tiles in 4 directions | +| FR5 | Epic 1 | Leading-edge merge order | +| FR6 | Epic 1 | Once-per-move merge rule | +| FR7 | Epic 1 | Current score tracking | +| FR8 | Epic 2 | Best score display | +| FR9 | Epic 2 | Best score localStorage persistence | +| FR10 | Epic 1 | Game over detection | +| FR11 | Epic 1 | Win detection + overlay | +| FR12 | Epic 2 | Keep playing mode | +| FR13 | Epic 1 | New Game button | +| FR14 | Epic 1 | Arrow key input | +| FR15 | Epic 4 | WASD key input | +| FR16 | Epic 4 | Vim hjkl key input | +| FR17 | Epic 4 | Touch/swipe input | +| FR18 | Epic 1 | 12-tier tile colors | +| FR19 | Epic 3 | Slide animation (100ms) | +| FR20 | Epic 3 | Spawn pop animation (200ms) | +| FR21 | Epic 3 | Merge bounce animation (200ms) | +| FR22 | Epic 3 | Score float animation (600ms) | +| FR23 | Epic 2 | Game state persistence | +| FR24 | Epic 2 | Game state restore on load | +| FR25 | Epic 1 | Game over overlay | +| FR26 | Epic 1 | Win overlay | +| FR27 | Epic 3 | Overlay fade animation (800ms) | +| FR28 | Epic 4 | Responsive layout (520px breakpoint) | +| FR29 | Epic 5 | Tile glow on 128+ | +| FR30 | Epic 5 | Dynamic font sizing | +| FR31 | Epic 5 | GitHub Pages deployment | + +## Epic List + +### Epic 1: Play a Complete Game (Desktop) +Player can play 2048 from start to finish — slide tiles, merge numbers, track score, win or lose, and restart. Includes project scaffold as first story. +**FRs covered:** FR1, FR2, FR3, FR4, FR5, FR6, FR7, FR10, FR11, FR13, FR14, FR18, FR25, FR26 + +### Epic 2: Save Progress & Keep Playing +Player's game survives page refresh, best score persists across sessions, and they can continue playing past 2048. +**FRs covered:** FR8, FR9, FR12, FR23, FR24 + +### Epic 3: Smooth Animations & Game Feel +Every interaction feels satisfying with smooth slide, merge, spawn, score, and overlay animations matching original 2048 timings. +**FRs covered:** FR19, FR20, FR21, FR22, FR27 + +### Epic 4: Mobile & Multi-Input Support +Game is fully playable on mobile with touch/swipe and responsive layout, plus WASD and Vim key support on desktop. +**FRs covered:** FR15, FR16, FR17, FR28 + +### Epic 5: Visual Polish & Deployment +Final visual refinements (glow effects, dynamic font sizing) and public deployment to GitHub Pages. +**FRs covered:** FR29, FR30, FR31 + +## Epic 1: Play a Complete Game (Desktop) + +Player can play 2048 from start to finish — slide tiles, merge numbers, track score, win or lose, and restart. Includes project scaffold as first story. + +### Story 1.1: Project Scaffold & Dev Environment + +As a developer, +I want a properly configured Svelte + Tailwind + Vite project with the correct folder structure, +So that all subsequent stories have a solid foundation to build on. + +**Acceptance Criteria:** + +**Given** no project exists +**When** the scaffold is created using `npm create vite@latest` with the Svelte template +**Then** the project builds and runs with `npm run dev` +**And** Tailwind CSS v4 is installed via `@tailwindcss/vite` plugin and `@import "tailwindcss"` works in `app.css` +**And** Vitest is installed as a dev dependency and `npx vitest` runs without error +**And** the folder structure matches Architecture: `src/lib/`, `src/components/`, `public/fonts/` +**And** `src/lib/constants.js` exists with GRID_SIZE (4), WIN_VALUE (2048), SPAWN_PROBABILITY (0.9), DIRECTIONS, and TILE_COLORS (12-tier hex values) +**And** Clear Sans font is placed in `public/fonts/` with `@font-face` declaration in `app.css` +**And** page background is set to `#faf8ef` + +### Story 1.2: Game Logic Module + +As a developer, +I want a pure JavaScript game logic module with complete unit tests, +So that all game mechanics are correct and independently testable before connecting to the UI. + +**Acceptance Criteria:** + +**Given** the constants module exists +**When** `initGame()` is called +**Then** it returns a game state with a 4x4 grid (2D array of zeros) with exactly 2 random tiles placed (90% chance value 2, 10% chance value 4) +**And** score is 0, won is false, keepPlaying is false + +**Given** a valid game state +**When** `move(state, direction)` is called with a direction (up/down/left/right) +**Then** it returns a new state object (never mutates input) with tiles slid to the leading edge +**And** identical adjacent tiles in the movement path merge using leading-edge order +**And** each tile merges at most once per move (once-per-move rule enforced) +**And** score delta equals the sum of all merged tile values +**And** one new random tile (90/10 split) is added to a random empty cell + +**Given** a game state where no tiles can move in the requested direction +**When** `move(state, direction)` is called +**Then** the original state is returned unchanged (no new tile spawned) + +**Given** a game state +**When** `isGameOver(state)` is called +**Then** it returns true only when the board is full AND no adjacent tiles share the same value + +**Given** a game state +**When** a merge creates a tile with value 2048 +**Then** the won flag is set to true in the returned state + +**And** all functions have passing Vitest unit tests covering edge cases (full board, corner merges, chain prevention) + +### Story 1.3: Game Board & Tile Rendering + +As a player, +I want to see a 4x4 game grid with colored numbered tiles, +So that I can visually understand the game state. + +**Acceptance Criteria:** + +**Given** the app loads +**When** the Grid component renders +**Then** a 4x4 grid with `#bbada0` background and 16 empty cell placeholders (`#cdc1b4`) is displayed +**And** the grid has `15px` gap and `15px` padding with `6px` border radius +**And** the grid container is `500px` wide and centered on the page + +**Given** a game state with tiles +**When** the Tile component renders for each tile +**Then** each tile displays its numeric value centered in the cell +**And** tile background color matches the 12-tier color system (UX-DR1) +**And** text color is `#776e65` for values 2 and 4, `#f9f6f2` for values 8+ (UX-DR2) +**And** font family is Clear Sans with fallback chain (UX-DR4) +**And** tiles have `role="gridcell"` and `aria-label` with tile value (UX-DR12) +**And** the grid has `role="grid"` and `aria-label="Game board"` (UX-DR12) + +### Story 1.4: Game Header, Score Display & New Game Button + +As a player, +I want to see the game title, my current score, and a New Game button, +So that I can track my progress and restart anytime. + +**Acceptance Criteria:** + +**Given** the app loads +**When** the header renders +**Then** the game title "2048" is displayed at `80px` bold in `#776e65` (UX-DR9) +**And** a subtitle row shows "Join the numbers and get to the 2048 tile!" with the New Game button right-aligned +**And** the layout follows zero-chrome design — header holds only title + scores + new game (UX-DR19) +**And** semantic HTML is used: `
`, `
`, `