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

6.4 KiB
Raw Blame History

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:
    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:

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/:

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