puzzle-trainer/levels.ts
2026-05-23 01:05:21 +00:00

80 lines
3.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 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 (1100) 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 }> = {
queens: { name: "Queens", accent: "#d97706", desc: "Une couronne par ligne, colonne et zone colorée." },
tango: { name: "Tango", accent: "#ea580c", desc: "Équilibrez soleils et lunes selon les contraintes." },
zip: { name: "Zip", accent: "#2563eb", desc: "Reliez les chiffres dans l'ordre en couvrant tout." },
sudoku: { name: "Sudoku", accent: "#16a34a", desc: "Chiffres 16 dans chaque ligne, colonne et bloc." },
patches: { name: "Patches", accent: "#7c3aed", desc: "Remplissez chaque zone selon sa forme et sa taille." },
};