80 lines
3.6 KiB
TypeScript
80 lines
3.6 KiB
TypeScript
/**
|
||
* Level system — 100 levels per game, deterministic seeds, graduated difficulty.
|
||
*
|
||
* Seed strategy: level N → synthetic date "LEVEL-{game}-{N:03d}"
|
||
* Each generator uses date.split("-").reduce(…) to derive a numeric seed,
|
||
* so we pass a date-shaped string that yields a unique seed per (game, level).
|
||
*
|
||
* Level N → date string: `${1900 + N}-01-01`
|
||
* This stays far from real daily dates (2024+) and is unique per level.
|
||
*/
|
||
|
||
export const GAMES = ["queens", "tango", "zip", "sudoku", "patches"] as const;
|
||
export type GameId = typeof GAMES[number];
|
||
|
||
export const TOTAL_LEVELS = 100;
|
||
|
||
/** Convert level number (1–100) to the synthetic date fed to the generator. */
|
||
export function levelToDate(level: number): string {
|
||
const n = Math.max(1, Math.min(TOTAL_LEVELS, level));
|
||
// e.g. level 1 → "1901-01-01", level 42 → "1942-01-01", level 100 → "2000-01-01"
|
||
return `${1900 + n}-01-01`;
|
||
}
|
||
|
||
/** Queens: N scales 6→10 across 100 levels. */
|
||
export function queensSizeForLevel(level: number): number {
|
||
if (level <= 15) return 6;
|
||
if (level <= 35) return 7;
|
||
if (level <= 60) return 8;
|
||
if (level <= 80) return 9;
|
||
return 10;
|
||
}
|
||
|
||
/** Zip: grid size scales with level. */
|
||
export function zipSizeForLevel(level: number): number {
|
||
if (level <= 20) return 5;
|
||
if (level <= 50) return 6;
|
||
if (level <= 75) return 7;
|
||
return 8;
|
||
}
|
||
|
||
export interface LevelMeta {
|
||
level: number;
|
||
difficulty: 1 | 2 | 3 | 4 | 5; // 1=easy … 5=expert
|
||
difficultyLabel: string;
|
||
}
|
||
|
||
const DIFFICULTY_LABELS = ["", "Facile", "Normal", "Intermédiaire", "Difficile", "Expert"];
|
||
|
||
export function levelMeta(game: GameId, level: number): LevelMeta {
|
||
let difficulty: 1 | 2 | 3 | 4 | 5;
|
||
if (game === "queens") {
|
||
if (level <= 15) difficulty = 1;
|
||
else if (level <= 35) difficulty = 2;
|
||
else if (level <= 60) difficulty = 3;
|
||
else if (level <= 80) difficulty = 4;
|
||
else difficulty = 5;
|
||
} else if (game === "zip") {
|
||
if (level <= 20) difficulty = 1;
|
||
else if (level <= 50) difficulty = 2;
|
||
else if (level <= 75) difficulty = 3;
|
||
else if (level <= 90) difficulty = 4;
|
||
else difficulty = 5;
|
||
} else {
|
||
// Tango, Sudoku, Patches: fixed size, difficulty is nominal
|
||
if (level <= 20) difficulty = 1;
|
||
else if (level <= 45) difficulty = 2;
|
||
else if (level <= 65) difficulty = 3;
|
||
else if (level <= 85) difficulty = 4;
|
||
else difficulty = 5;
|
||
}
|
||
return { level, difficulty, difficultyLabel: DIFFICULTY_LABELS[difficulty] };
|
||
}
|
||
|
||
export const GAME_META: Record<GameId, { name: string; accent: string; desc: string; subtitle: string; duration: string; symbol: string }> = {
|
||
queens: { name: "Queens", accent: "#d97706", desc: "Une couronne par ligne, colonne et zone colorée.", subtitle: "1 couronne par ligne, colonne et zone", duration: "≈ 3 min", symbol: "♛" },
|
||
tango: { name: "Tango", accent: "#ea580c", desc: "Équilibrez soleils et lunes selon les contraintes.", subtitle: "Équilibre soleils ☀ et lunes ◐ sur la grille", duration: "≈ 2 min", symbol: "☀" },
|
||
zip: { name: "Zip", accent: "#2563eb", desc: "Reliez les chiffres dans l'ordre en couvrant tout.", subtitle: "Relie les chiffres dans l'ordre en couvrant tout", duration: "≈ 2 min", symbol: "∞" },
|
||
sudoku: { name: "Sudoku", accent: "#16a34a", desc: "Chiffres 1–6 dans chaque ligne, colonne et bloc.", subtitle: "Chiffres 1–6 dans chaque ligne, colonne et bloc", duration: "≈ 4 min", symbol: "#" },
|
||
patches: { name: "Patches", accent: "#7c3aed", desc: "Remplissez chaque zone selon sa forme et sa taille.", subtitle: "Remplis chaque zone avec les bonnes pièces", duration: "≈ 3 min", symbol: "▦" },
|
||
};
|