Files
Zauberwald/.planning/phases/03-komplettes-spielerlebnis/03-02-PLAN.md
T
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

14 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
phase plan type wave depends_on files_modified autonomous requirements must_haves
03-komplettes-spielerlebnis 02 execute 2
03-01
src/game/typing.ts
src/game/typing.test.ts
src/ui/screens.ts
index.html
true
TYPE-05
TYPE-06
TYPE-07
LEVL-05
LEVL-06
truths artifacts key_links
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
path provides exports
src/game/typing.ts Extended createExerciseState with mode='letters'|'words', generateWordLetters()
createExerciseState
generateWordLetters
path provides contains
src/ui/screens.ts Lesson sub-phase state machine: discover -> practice -> words LessonPhase
from to via pattern
src/ui/screens.ts src/game/typing.ts createExerciseState for practice and word phases createExerciseState
from to via pattern
src/ui/screens.ts src/companion/companion.ts getLetterIntro for discover phase getLetterIntro
from to via pattern
src/ui/screens.ts src/game/words.ts getWordsForLevel for word phase getWordsForLevel
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.

<execution_context> @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md </execution_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 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; lastPlayedLevels: number[]; }

export const wordsByLevel: Record<number, string[]>; export function getWordsForLevel(level: number, count: number): string[];

export async function getLetterIntro(characterType: CompanionType, letter: string, fingerDescription: string, db: IDBDatabase): Promise;

export function createExerciseState(level: number): TypingExerciseState; export function handleKeyPress(state: TypingExerciseState, pressedKey: string): KeyPressResult; export function getCurrentLetter(state: TypingExerciseState): string | null;

export function getLevelByNumber(n: number): Level | undefined; export function getKeysUpToLevel(n: number): string[];

Task 1: Extend typing engine with word mode src/game/typing.ts, src/game/typing.test.ts src/game/typing.ts, src/game/words.ts, src/types.ts - 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 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.
npx vitest run src/game/typing.test.ts - 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 Typing engine supports word mode with word boundaries tracking. Backwards compatible with letter mode. Tests pass. Task 2: Lesson screen 3-phase state machine with review and repetition src/ui/screens.ts, index.html 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 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); }
}
```
npx tsc --noEmit && npx vitest run - 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 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. - `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

<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>
After completion, create `.planning/phases/03-komplettes-spielerlebnis/03-02-SUMMARY.md`