Files
website/src/lib/language-learner/random.ts
Stan Runge 438623d857
Some checks failed
Test and deploy staging / staging (push) Has been cancelled
add language learner project
Dutch and Korean (explained in English) and English (explained in Dutch)
from A1 to C1, with a Hangul level for Korean. Lessons with notes, words
and sentences generate multiple-choice, typing, sentence building, cloze,
listening and matching exercises. Endless challenges: a seeded daily
challenge, sprint, survival, mixed practice, level tests, spaced-repetition
reviews and generated drills (numbers, clock times, de/het, plurals, Dutch
and Korean conjugation, particles, Hangul reading, irregular verbs).
Progress is stored in localStorage with export/import.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 17:34:56 +00:00

51 lines
1.5 KiB
TypeScript

export type Rng = () => number;
/** Small deterministic generator, so a daily challenge is the same all day. */
export function seeded(seed: number | string): Rng {
let h = typeof seed === 'number' ? seed >>> 0 : 2166136261;
if (typeof seed === 'string') {
for (let i = 0; i < seed.length; i++) h = Math.imul(h ^ seed.charCodeAt(i), 16777619);
}
return () => {
h = (h + 0x6d2b79f5) >>> 0;
let t = h;
t = Math.imul(t ^ (t >>> 15), t | 1);
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
};
}
export const int = (rng: Rng, min: number, max: number) =>
min + Math.floor(rng() * (max - min + 1));
export const pick = <T>(rng: Rng, list: readonly T[]): T => list[Math.floor(rng() * list.length)];
export function shuffle<T>(rng: Rng, list: readonly T[]): T[] {
const copy = [...list];
for (let i = copy.length - 1; i > 0; i--) {
const j = Math.floor(rng() * (i + 1));
[copy[i], copy[j]] = [copy[j], copy[i]];
}
return copy;
}
/** Up to `count` distinct values from `list` that differ from `exclude` once normalized. */
export function sample<T>(
rng: Rng,
list: readonly T[],
count: number,
key: (value: T) => string = String,
exclude: string[] = []
): T[] {
const seen = new Set(exclude.map((value) => value.toLowerCase()));
const out: T[] = [];
for (const value of shuffle(rng, list)) {
if (out.length >= count) break;
const k = key(value).toLowerCase();
if (seen.has(k)) continue;
seen.add(k);
out.push(value);
}
return out;
}