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>
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 |
|
|
true |
|
|
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.
**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); }
}
```
<success_criteria>
- Typing engine has createWordExerciseState() and getCurrentWordIndex()
- Lesson screen flows: discover -> practice -> words -> onComplete(level, stats)
- Discover phase: companion introduces letter, key pulses, child presses once to proceed
- Words phase: 3-4 words displayed with active letter highlighted, typed letter by letter
- Review flag skips discover and mixes letters/words from recent levels
- Encouragement shown when showEncouragement=true
- Stats (correct, errors, errorKeys) accumulated and passed to onComplete </success_criteria>