# ArtLegend — Current Sprint **Sprint goal:** Line Corridor MVP. Implement the real scrolling-corridor game from MODULES.md, replace the Sprint 1 prototype, add difficulty tiers, and wire real session data into the Progress screen. > Sprint 1 built a static "draw one stroke" prototype to validate the architecture. Sprint 2 replaces the game logic with the actual design: a continuously scrolling corridor the player tracks with their stylus, scored on Fill and Containment. --- ## Tickets ### AL-010 — CorridorScore data model [TODO] Replace the prototype `LineScore` with a score model that matches the Corridor Scroll design from MODULES.md. - Delete `core/modules/LineScore.kt` - Create `core/modules/CorridorScore.kt`: ```kotlin data class CorridorScore( val fill: Float, // 0..1 — avg coverage of corridor width val wallPenalties: Int, // raw count of wall crossings val overall: Float // weighted: fill * 0.7 − penalty * 0.05, clamped 0..1 ) ``` - Update `GameHost.onSessionComplete` signature: `((CorridorScore) -> Unit)?` - Update `GameActivity` to pass `fill`, `wallPenalties`, `overall` via Intent extras - Update `MainActivity.gameResultLauncher` to reconstruct `CorridorScore` from Intent - Update `SessionEntity`: replace `straightness`/`anglePrecision` fields with `fill: Float` and `wallPenalties: Int`; bump Room schema version to 2 with a destructive migration (dev only — data is test data) - Update `SessionRepository.save()` to write new fields - Update `ProgressScreen` `SessionCard` and `SessionBarChart` labels (Overall, Fill, Penalties) - Update `ProgressViewModel` — no logic change needed --- ### AL-011 — Scrolling corridor engine [TODO] Rewrite `LineCorridorScreen` to implement the scrolling corridor from MODULES.md. **Geometry:** - Corridor scrolls across the screen at a configurable pixel/sec speed - Corridor is represented as a spine (centerline) moving left-to-right (or at an angle on higher difficulties) - Player tracks the moving gap by holding their stylus inside it **Rendering:** - Two cyan wall lines scrolling across screen - Visible lookahead: player can see a few screen-widths ahead - Stylus trail rendered in yellow (pressure-modulated alpha, same as prototype) - HUD: session timer (top-right), score preview (fill % running avg, penalty count) **Session lifecycle:** - Session runs for a fixed duration (`sessionDuration` param, default 30s) - When time runs out: compute final score, call `onSessionComplete` - Player must hold stylus the entire time — stylus-up counts as not filling **Constructor params:** ```kotlin class LineCorridorScreen( bridge: StylusInputBridge, onSessionComplete: (CorridorScore) -> Unit, corridorWidth: Float = 80f, scrollSpeed: Float = 180f, sessionDuration: Float = 30f, angleChangeEnabled: Boolean = false ) ``` --- ### AL-012 — Real-time containment + fill scoring [TODO] Implement per-frame scoring logic inside `LineCorridorScreen`. **Per-frame:** - Compute the corridor bounds at the stylus X position (or nearest corridor segment) - If stylus is inside bounds: record perpendicular offset → contributes to running fill average - If stylus crosses a wall: increment `wallPenaltyCount`, enforce a brief visual flash (wall turns red for 0.3s) - If stylus is up: does not contribute to fill average (neutral, not a penalty) **At session end:** - `fill` = mean of per-frame fill samples (each sample = `1 - |offset| / halfWidth`, clamped 0..1) - `wallPenalties` = total crossing count - `overall` = `(fill * 0.7 - wallPenalties * 0.05).coerceIn(0f, 1f)` --- ### AL-013 — Difficulty tiers [TODO] Add selectable difficulty levels that feed into `LineCorridorScreen` params. **In `shared/`:** ```kotlin enum class Difficulty { EASY, MEDIUM, HARD } ``` **Difficulty knobs:** | | EASY | MEDIUM | HARD | |---|---|---|---| | Corridor width | 90f | 60f | 38f | | Scroll speed (px/s) | 140f | 220f | 340f | | Angle changes | None | Gentle (±15° max) | Frequent (±35° max) | | Session duration | 30s | 30s | 30s | **UI — `ModuleDetailScreen`:** - Add a `Difficulty` selector (three segmented chips: Easy / Medium / Hard) between the module description and the "Start Training" button - Default: EASY - Pass selected `Difficulty` as an Intent extra to `GameActivity` **In `GameActivity`:** - Read `Difficulty` from Intent, build `LineCorridorScreen` with corresponding params - Pass `Difficulty.name` through to the score result so it can be stored with the session **Add `difficulty: String` column to `SessionEntity`** (schema version → 3, destructive migration) --- ### AL-014 — Session summary screen [TODO] Add a dedicated post-game summary in Compose shown after the LibGDX screen closes, before the user returns to the hub. **New route in `ArtLegendApp`:** `summary/{moduleType}` **`SummaryScreen` composable:** - Score breakdown: Overall %, Fill %, Wall Penalties - Streak badge: "X-day streak" (see streak logic below) - Buttons: "Train Again" (relaunch same module/difficulty), "View Progress", "Back to Hub" **Streak logic (in `ProgressViewModel`):** - Add `fun streakFor(moduleType: ModuleType): Int` — query sessions ordered by timestamp, count consecutive calendar days ending today that have at least one session - Expose as `StateFlow` **Wire-up:** - After `gameResultLauncher` receives a result, navigate to `summary/{moduleType}` instead of staying on `ModuleDetailScreen` - Pass `CorridorScore` to `SummaryScreen` via `ArtLegendApp` state (same pattern as `lastScore` in Sprint 1) - `ModuleDetailScreen` no longer shows the inline `ScoreCard` — summary screen owns that --- ### AL-015 — Integration smoke test [TODO] - `./gradlew :android:assembleDebug` passes with no errors - Hub → Module Detail → difficulty selector renders (Easy selected by default) - "Start Training" → LibGDX renders scrolling corridor with visible movement - Stylus held inside corridor: fill HUD increments; crossing a wall: wall flashes red - Session timer counts down, auto-returns at 0s - Summary screen shows Fill %, Wall Penalties, Overall %, streak badge - Progress screen bar chart reflects the session just completed - All three difficulty tiers launch without crash --- ## Blocked _Nothing blocked currently._ ## Out of Scope This Sprint - Angle change corridor segments (HARD difficulty — define the knob values but angle changes can ship as a fast-follow if time allows) - Sound effects / haptic feedback - Global XP system - In-app purchases - Cloud sync