feat(03-01): implement word lists per level with getWordsForLevel
- Static word lists for levels 1-6, 8-10 words each - Levels 1-2 use letter combos (no real words possible with F/J/D/K) - Levels 3-6 mix real German words with combos - All words validated: only use learned letters, max 5 chars - getWordsForLevel with/without replacement sampling - All 8 tests pass Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,64 @@
|
|||||||
|
import { getKeysUpToLevel } from "./levels";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Word lists per level.
|
||||||
|
* Levels 1-2: Only F/J/D/K available -- no real German words possible, use letter combos.
|
||||||
|
* Levels 3+: Mix of real German words and combos using only learned letters.
|
||||||
|
* All words max 5 characters, no spaces.
|
||||||
|
*/
|
||||||
|
export const wordsByLevel: Record<number, string[]> = {
|
||||||
|
// Level 1: f, j
|
||||||
|
1: ["ff", "jj", "fj", "jf", "fjf", "jfj", "fjfj", "jfjf", "ffj", "jjf"],
|
||||||
|
// Level 2: f, j, d, k
|
||||||
|
2: ["fdk", "kdf", "djf", "fkd", "dkf", "jdk", "kfj", "dfjk", "kjdf", "fkjd"],
|
||||||
|
// Level 3: f, j, d, k, s, l
|
||||||
|
3: ["sdf", "sdk", "lsd", "flsk", "sldf", "dsl", "kls", "flds", "jskl", "lkds"],
|
||||||
|
// Level 4: f, j, d, k, s, l, a, ö -- "aas", "das", "lass", "als" are real words
|
||||||
|
4: ["als", "das", "aas", "lass", "fad", "dal", "lad", "alfa", "salö", "aöds"],
|
||||||
|
// Level 5: f, j, d, k, s, l, a, ö, g, h -- "lag", "sah", "glas" are real words
|
||||||
|
5: ["glas", "lag", "gah", "hag", "gal", "sah", "hals", "has", "gash", "flags"],
|
||||||
|
// Level 6: f, j, d, k, s, l, a, ö, g, h, e, i -- "eis", "die", "sie", "lied", "fies"
|
||||||
|
6: ["eis", "die", "sie", "lied", "fies", "dies", "sei", "geld", "held", "lieh"],
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns `count` random words from the given level's word list.
|
||||||
|
* If count <= list length: sampling without replacement.
|
||||||
|
* If count > list length: sampling with replacement.
|
||||||
|
* Returns empty array for non-existent levels.
|
||||||
|
*/
|
||||||
|
export function getWordsForLevel(level: number, count: number): string[] {
|
||||||
|
const list = wordsByLevel[level];
|
||||||
|
if (!list || list.length === 0) return [];
|
||||||
|
|
||||||
|
if (count <= list.length) {
|
||||||
|
// Shuffle and take first `count` (without replacement)
|
||||||
|
const shuffled = [...list];
|
||||||
|
for (let i = shuffled.length - 1; i > 0; i--) {
|
||||||
|
const j = Math.floor(Math.random() * (i + 1));
|
||||||
|
[shuffled[i], shuffled[j]] = [shuffled[j]!, shuffled[i]!];
|
||||||
|
}
|
||||||
|
return shuffled.slice(0, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
// With replacement
|
||||||
|
const result: string[] = [];
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
result.push(list[Math.floor(Math.random() * list.length)]!);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validation helper (used in tests)
|
||||||
|
export function validateWordList(): boolean {
|
||||||
|
for (const [levelStr, words] of Object.entries(wordsByLevel)) {
|
||||||
|
const level = Number(levelStr);
|
||||||
|
const allowedKeys = getKeysUpToLevel(level).filter((k) => k !== " ");
|
||||||
|
for (const word of words) {
|
||||||
|
for (const char of word) {
|
||||||
|
if (!allowedKeys.includes(char)) return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user