Files
gurixandClaude Opus 4.6 5819ade457 docs(03): create phase plan — komplettes spielerlebnis
5 plans across 3 waves covering all 17 requirements:
- Plan 01 (W1): Word lists, Progress extension, rate limits
- Plan 02 (W2): Lesson 3-phase state machine, review/repeat
- Plan 03 (W1): Forest visual scene with SVG + grid
- Plan 04 (W3): Reward screen, pre-generation, full flow wiring
- Plan 05 (W2): Parent area with stats and settings

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 13:37:08 +02:00

341 lines
14 KiB
Markdown

---
phase: 03-komplettes-spielerlebnis
plan: 02
type: execute
wave: 2
depends_on: [03-01]
files_modified:
- src/game/typing.ts
- src/game/typing.test.ts
- src/ui/screens.ts
- index.html
autonomous: true
requirements: [TYPE-05, TYPE-06, TYPE-07, LEVL-05, LEVL-06]
must_haves:
truths:
- "Entdecken phase shows companion text and highlights the new key on keyboard"
- "Woerter phase displays whole words with active letter highlighted"
- "Lesson flows through discover -> practice -> words automatically"
- "Every 3rd session is a review mixing letters from last 2-3 levels"
- "After 2x same level, companion shows encouragement with curiosity trigger"
artifacts:
- path: "src/game/typing.ts"
provides: "Extended createExerciseState with mode='letters'|'words', generateWordLetters()"
exports: ["createExerciseState", "generateWordLetters"]
- path: "src/ui/screens.ts"
provides: "Lesson sub-phase state machine: discover -> practice -> words"
contains: "LessonPhase"
key_links:
- from: "src/ui/screens.ts"
to: "src/game/typing.ts"
via: "createExerciseState for practice and word phases"
pattern: "createExerciseState"
- from: "src/ui/screens.ts"
to: "src/companion/companion.ts"
via: "getLetterIntro for discover phase"
pattern: "getLetterIntro"
- from: "src/ui/screens.ts"
to: "src/game/words.ts"
via: "getWordsForLevel for word phase"
pattern: "getWordsForLevel"
---
<objective>
Build the complete lesson flow with 3 sub-phases (Entdecken -> Ueben -> Woerter) as an internal state machine, plus review mechanic and repetition protection.
Purpose: This is the core game loop — the child experiences discover, practice, then words in one fluid session.
Output: Extended typing engine with word mode, lesson screen with 3-phase state machine, review and repetition logic.
</objective>
<execution_context>
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
@$HOME/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/03-komplettes-spielerlebnis/03-01-SUMMARY.md
@src/game/typing.ts
@src/ui/screens.ts
@src/companion/companion.ts
@src/game/words.ts
@src/game/levels.ts
@src/types.ts
@index.html
<interfaces>
<!-- From src/types.ts (after Plan 01): -->
export type LessonPhase = 'discover' | 'practice' | 'words';
export interface Progress {
id: 1;
currentLevel: number;
completedLevels: number[];
totalSessions: number;
sessionDates: string[];
selectedCharacter: CompanionType;
selectedLayout: KeyboardLayout;
totalCorrect: number;
totalErrors: number;
errorKeyCounts: Record<string, number>;
lastPlayedLevels: number[];
}
<!-- From src/game/words.ts (after Plan 01): -->
export const wordsByLevel: Record<number, string[]>;
export function getWordsForLevel(level: number, count: number): string[];
<!-- From src/companion/companion.ts (existing): -->
export async function getLetterIntro(characterType: CompanionType, letter: string, fingerDescription: string, db: IDBDatabase): Promise<string>;
<!-- From src/game/typing.ts (existing): -->
export function createExerciseState(level: number): TypingExerciseState;
export function handleKeyPress(state: TypingExerciseState, pressedKey: string): KeyPressResult;
export function getCurrentLetter(state: TypingExerciseState): string | null;
<!-- From src/game/levels.ts (existing): -->
export function getLevelByNumber(n: number): Level | undefined;
export function getKeysUpToLevel(n: number): string[];
</interfaces>
</context>
<tasks>
<task type="auto" tdd="true">
<name>Task 1: Extend typing engine with word mode</name>
<files>src/game/typing.ts, src/game/typing.test.ts</files>
<read_first>src/game/typing.ts, src/game/words.ts, src/types.ts</read_first>
<behavior>
- Test: createExerciseState(level) still works as before (letters mode, backwards compatible)
- Test: createWordExerciseState(level, words) creates state with letters from the words joined, e.g. words=["eis","die"] -> letters=["e","i","s","d","i","e"]
- Test: createWordExerciseState stores words and wordBoundaries in state for UI rendering
- Test: handleKeyPress works the same way for word-mode state
- Test: getWordAtIndex(state, letterIndex) returns which word the letter belongs to
</behavior>
<action>
Extend `src/game/typing.ts` (per D-03):
1. Add `ExerciseMode` type: `'letters' | 'words'`
2. Extend `TypingExerciseState` interface (in types.ts) with optional word fields:
```typescript
export interface TypingExerciseState {
currentLetterIndex: number;
letters: string[];
intervalMs: number;
correctStreak: number;
totalCorrect: number;
totalErrors: number;
mode: 'letters' | 'words';
words?: string[]; // original words for display
wordBoundaries?: number[]; // indices where each word starts in letters[]
}
```
3. Keep existing `createExerciseState(level)` unchanged but set `mode: 'letters'`, `words: undefined`, `wordBoundaries: undefined`.
4. Add new function:
```typescript
export function createWordExerciseState(words: string[]): TypingExerciseState {
const letters: string[] = [];
const wordBoundaries: number[] = [];
for (const word of words) {
wordBoundaries.push(letters.length);
for (const ch of word) {
letters.push(ch);
}
}
return {
currentLetterIndex: 0,
letters,
intervalMs: 0, // no falling in word mode
correctStreak: 0,
totalCorrect: 0,
totalErrors: 0,
mode: 'words',
words,
wordBoundaries,
};
}
```
5. Add helper: `getCurrentWordIndex(state: TypingExerciseState): number` — returns which word index (0-based) the currentLetterIndex is in, using wordBoundaries.
6. `handleKeyPress` already works generically — no changes needed.
Create `src/game/typing.test.ts` (or extend if exists) with vitest tests for the new functions plus backwards compatibility.
</action>
<verify>
<automated>npx vitest run src/game/typing.test.ts</automated>
</verify>
<acceptance_criteria>
- grep "createWordExerciseState" src/game/typing.ts returns export
- grep "getCurrentWordIndex" src/game/typing.ts returns export
- grep "mode.*letters.*words" src/types.ts returns match showing mode field
- npx vitest run src/game/typing.test.ts exits 0
</acceptance_criteria>
<done>Typing engine supports word mode with word boundaries tracking. Backwards compatible with letter mode. Tests pass.</done>
</task>
<task type="auto">
<name>Task 2: Lesson screen 3-phase state machine with review and repetition</name>
<files>src/ui/screens.ts, index.html</files>
<read_first>src/ui/screens.ts, src/game/typing.ts, src/companion/companion.ts, src/game/words.ts, src/game/levels.ts, src/types.ts, index.html</read_first>
<action>
Rewrite `initLessonScreen` in `src/ui/screens.ts` as a 3-phase state machine (per D-01, D-02, D-03):
**Add to index.html** inside `#screen-lesson .lesson`:
```html
<div class="lesson__phase-indicator" id="lesson-phase-indicator"></div>
```
This shows which phase is active (Entdecken / Ueben / Woerter). Add above the letter-area.
**Signature change:**
```typescript
export function initLessonScreen(
db: IDBDatabase,
level: number,
layout: KeyboardLayout,
onComplete: (level: number, stats: { totalCorrect: number; totalErrors: number; errorKeys: Record<string, number> }) => void,
onCancel?: () => void,
): () => void
```
The `onComplete` callback now receives exercise stats (per D-16).
**State machine flow:**
1. **DISCOVER phase** (per D-02, TYPE-05):
- Show phase indicator: "Entdecken"
- Get the first new key of this level via `getLevelByNumber(level).newKeys[0]`
- Fetch companion letter intro via `getLetterIntro(characterType, letter, fingerDescription, db)`
- Display intro text in `#lesson-companion-text`
- Highlight the target key on keyboard (already have `highlightKey()`)
- Make key pulse (add class `keyboard__key--discover-pulse` to the key)
- Wait for child to press the correct key ONCE to proceed
- If level has a second newKey, repeat discover for that key too
- Then transition to PRACTICE
2. **PRACTICE phase** (existing behavior, TYPE-01-04):
- Show phase indicator: "Ueben"
- Run existing letter exercise with `createExerciseState(level)`
- On complete, accumulate stats, transition to WORDS
3. **WORDS phase** (per D-03, TYPE-06):
- Show phase indicator: "Woerter"
- Get 3-4 random words via `getWordsForLevel(level, 4)` (fallback to 3 if < 4 available)
- Create word exercise via `createWordExerciseState(words)`
- Display current word fully, highlight active letter:
```html
<div class="word-display">
<span class="word-display__letter word-display__letter--done">e</span>
<span class="word-display__letter word-display__letter--active">i</span>
<span class="word-display__letter">s</span>
</div>
```
- When word complete, show next word. After all words done, call `onComplete(level, stats)`.
**Review mechanic (per D-05, LEVL-05):**
Add `isReviewSession` parameter to `initLessonScreen`. When `true`:
- Discover phase: skip (reviews don't introduce new letters)
- Practice phase: use mixed letters from last 2-3 levels (combine allKeys from current and previous 1-2 levels)
- Words phase: pick words from last 2-3 levels randomly
The review trigger logic (`totalSessions % 3 === 0`) is handled in `app.ts` (Plan 04), not here. This function just needs to accept the `isReview: boolean` flag.
**Repetition protection (per D-06, LEVL-06):**
Add `showEncouragement` parameter. When `true`:
- Before discover phase, show companion encouragement text: a hardcoded array of 3-4 messages like "Magst du sehen, was als Naechstes kommt? Im Wald gibt es noch so viel zu entdecken!"
- Show for 3 seconds, then proceed to discover (or practice if review).
The trigger logic (checking `lastPlayedLevels` for 2x same) is handled in `app.ts` (Plan 04).
**Cumulative stats across phases:**
Track `totalCorrect`, `totalErrors`, `errorKeys: Record<string, number>` across all 3 phases. In handleKeyPress wrapper, if `!result.correct`, increment `errorKeys[expectedKey]`. Pass accumulated stats to `onComplete`.
**CSS for phase indicator** — add styles in action:
```css
.lesson__phase-indicator {
text-align: center;
font-family: var(--font-heading);
font-size: 1rem;
color: var(--text-warm);
margin-bottom: 0.5rem;
opacity: 0.7;
}
```
**CSS for word display:**
```css
.word-display {
display: flex;
justify-content: center;
gap: 0.25rem;
font-family: var(--font-heading);
font-size: 2.5rem;
margin: 2rem 0;
}
.word-display__letter {
color: var(--text-light);
transition: color 0.3s;
}
.word-display__letter--active {
color: var(--accent-gold);
font-weight: 700;
}
.word-display__letter--done {
color: var(--accent-green);
}
```
Add CSS for discover pulse:
```css
.keyboard__key--discover-pulse {
animation: discover-pulse 1.5s ease-in-out infinite;
}
@keyframes discover-pulse {
0%, 100% { transform: scale(1); box-shadow: 0 0 0 0 var(--accent-gold); }
50% { transform: scale(1.1); box-shadow: 0 0 12px 4px var(--accent-gold); }
}
```
</action>
<verify>
<automated>npx tsc --noEmit && npx vitest run</automated>
</verify>
<acceptance_criteria>
- grep "lesson-phase-indicator" index.html returns a match
- grep "discover.*practice.*words" src/ui/screens.ts shows state machine phases
- grep "getLetterIntro" src/ui/screens.ts shows companion integration in discover
- grep "createWordExerciseState" src/ui/screens.ts shows word mode usage
- grep "getWordsForLevel" src/ui/screens.ts shows word list integration
- grep "isReview" src/ui/screens.ts shows review parameter
- grep "showEncouragement" src/ui/screens.ts shows repetition protection parameter
- grep "word-display" src/styles/main.css returns CSS matches
- grep "discover-pulse" src/styles/main.css returns CSS matches
- npx tsc --noEmit exits 0
</acceptance_criteria>
<done>Lesson screen runs 3 phases (discover->practice->words) with companion letter intro, word display UI, review flag support, and repetition encouragement. Stats accumulated across phases and passed to onComplete callback.</done>
</task>
</tasks>
<verification>
- `npx tsc --noEmit` — no type errors
- `npx vitest run` — all tests pass
- Manual: lesson screen shows phase indicator, discover phase displays companion text and highlights key, words phase shows word with active letter
</verification>
<success_criteria>
1. Typing engine has createWordExerciseState() and getCurrentWordIndex()
2. Lesson screen flows: discover -> practice -> words -> onComplete(level, stats)
3. Discover phase: companion introduces letter, key pulses, child presses once to proceed
4. Words phase: 3-4 words displayed with active letter highlighted, typed letter by letter
5. Review flag skips discover and mixes letters/words from recent levels
6. Encouragement shown when showEncouragement=true
7. Stats (correct, errors, errorKeys) accumulated and passed to onComplete
</success_criteria>
<output>
After completion, create `.planning/phases/03-komplettes-spielerlebnis/03-02-SUMMARY.md`
</output>