Files
artlegend/SPRINT.md
bill d4c48ed393 Add artistic curriculum and Sprint 2 plan
CURRICULUM.md: full 14-group skill tree drawn from the artistic learning
journey — line control through creative systems, with skill dependency
graph and notes on how exercises translate into module designs.

SPRINT.md: Sprint 2 tickets (AL-010 through AL-015) replacing the Sprint 1
static-stroke prototype with the real scrolling corridor from MODULES.md,
including score model migration, difficulty tiers, and session summary screen.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 22:51:48 -06:00

162 lines
6.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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<Int>`
**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