Une implémentation en SPA React du jeu de plateau Linkx, jouable à deux sur le même écran ou contre l'ordinateur. L'application est installable et jouable hors ligne.
Règle officielle : https://www.jeux-abstraits.fr/wp-content/uploads/2026/07/lynkx.pdf · fiche éditeur : https://blueorangegames.eu/fr/jeux/linkx/
npm install
npm run dev| Commande | Rôle |
|---|---|
npm run dev |
serveur de développement Vite |
npm test |
suite Vitest en une passe |
npm run test:watch |
Vitest en mode veille |
npm run lint |
oxlint |
npm run build |
tsc -b puis build de production |
npm run preview |
sert le build de production |
node scripts/generate-icons.mjs |
régénère les icônes PNG de public/ |
npx supabase start |
pile Supabase locale, dans Docker |
npx supabase functions serve <nom> --no-verify-jwt |
sert une fonction edge en local |
Les outils de mesure du moteur (scripts/*.ts) sont décrits dans .claude/rules/moteur.md ; le déploiement dans docs/deploiement.md.
plan.mdest la spécification produit : règles du jeu, topologie des pièces, algorithmes de domaine, décisions UX, critères d'acceptation et checklists de validation visuelle. Le lire entièrement avant toute modification fonctionnelle ou visuelle importante.- Ce README est la spécification technique globale. Il sert aussi de
CLAUDE.mdet deAGENTS.md, donc il est lu en entier à chaque session : 1 500 mots au plus. N'y entre que ce qui vaut pour tout le dépôt, en une ligne. .claude/rules/porte le savoir propre à un périmètre ; chaque règle déclare les chemins qui la chargent. Y écrire tout ce qui ne sert pas à chaque session.domaine.md—src/game/: sources uniques, modèle, notation, tests de domainemoteur.md— moteur du maître, livre d'ouverture, scripts et discipline de mesureinterface.md—src/components/,App.tsx, CSS : pièges de rendu et de mise en pagehors-ligne.md—public/, service worker, worker de recherche, sous-chemintournoi-ecrans.md—src/tournament/tournoi-serveur.md—supabase/
docs/porte la documentation destinée aux humains :deploiement.md(secrets, réglages manuels, amorçage de l'IA de la maison) etprotocole-ia.md(ce qu'une IA du tournoi doit respecter).- Deux documents ne se recopient jamais l'un dans l'autre. Une décision produit se reporte dans
plan.mddans le même changement, en supprimant l'ancienne recommandation contradictoire ; une modification de stack, d'arborescence ou de contrat entre modules se documente ici ou dans la règle concernée.
- React 19, TypeScript, Vite 8 (Node
20.19+), CSS classique danssrc/index.cssetsrc/App.css. Vitest et oxlint.package-lock.jsonfait foi : utilisernpm. scripts/*.tssont vérifiés au build viatsconfig.scripts.jsonet se lancent avecvite-node; les.mjsrestent du JS pur.- Aucune dépendance d'état ou de rendu graphique : le jeu tient dans un reducer React et des fonctions TypeScript pures. Ne pas en ajouter sans nécessité démontrée.
- Trois dépendances, et trois seulement, servent la plateforme de tournoi :
react-router,@supabase/supabase-jset@tanstack/react-query. Elles ne sont jamais chargées par le jeu. - Les imports relatifs de
src/game/portent leur extension.ts, pour Deno. Ne pas la retirer ni l'étendre au reste desrc/. - Le backend vit dans
supabase/, en Deno. L'interface en ligne de commande Supabase s'appelle parnpx supabase ….
src/game/ logique pure, sans React
src/components/ affichage React du jeu
src/tournament/ écrans de la plateforme, morceau chargé paresseusement
src/aiWorker.ts tour de l'ordinateur hors du fil principal
src/App.tsx câblage : reducer, tour de l'ordinateur, raccourcis clavier
src/Root.tsx aiguillage jeu / tournoi sur le fragment, avant tout routeur
public/ manifeste, service worker, icônes
scripts/ outils de mesure du moteur, génération du livre et des icônes
fixtures/urls.md positions prêtes à coller pour les vérifications navigateur
supabase/functions/ fonctions edge ; `_shared/` porte leur logique pure
supabase/migrations/ schéma, vues publiques, file, cron
supabase/tests/ pgTAP
docs/ déploiement, protocole des IA du tournoi
Les tests vivent à côté de leur module, en *.test.ts / *.test.tsx.
- Toute la logique de règles reste dans
src/game/, tout l'affichage danssrc/components/. Les fonctions de domaine sont pures, déterministes et testables sans React.src/game/types.tsfait foi pour le modèle. - Une action de dépôt transmet seulement la colonne ; le reducer recalcule toujours l'atterrissage.
- Sources uniques, à ne jamais recréer à côté :
placement.tspour la chute et le support,aimedColumnpour la conversion pointeur → ancre,pieceGeometry.tspour les silhouettes,PlexiDefs.tsxpour la matière,bitboard.tspour le plateau de bits. connectivity.tsrelie les zones sur la couleur des cases, jamais sur lepieceId.chooseMoveForDifficultyest l'unique entrée de l'ordinateur ; l'interface transmet un niveau, jamais une profondeur ni un budget.- Le moteur du maître ne redéfinit aucune règle ;
engineBoard.test.tsle prouve contresrc/game/. Toucher aux règles doit faire échouer ce test avant tout le reste. - Aucune modification de la recherche ou de l'évaluation ne se garde sans mesure (
.claude/rules/moteur.md).openingBook.data.tsest généré, jamais édité à la main. - L'état de survol, les délais et les animations restent dans l'UI tant qu'ils n'affectent pas les règles.
App.tsxne fait que câbler.
C'est la règle la plus importante du dépôt.
- Le jeu reste jouable intégralement hors ligne, sans compte et sans le moindre appel réseau, adversaire maître compris : aucune de ses fonctions ne dépend de
supabase/. Comptes, persistance et service distant n'existent que pour la plateforme (plan.md, histoires 14 à 16). - Sans
VITE_SUPABASE_URLetVITE_SUPABASE_ANON_KEY, l'entrée du tournoi disparaît et le fragment ne charge rien. C'est le cas par défaut d'un dépôt cloné, et il doit le rester. - Rien du jeu n'importe
src/tournament/, saufRoot.tsx(parReact.lazy) etSetupPanel.tsx(config.tsetroutes.ts). - Les règles ne sont jamais réécrites côté serveur : les fonctions edge importent
src/game/*.tspar chemin relatif. Une fonction edge n'importe jamaissrc/tournament/. src/ne litsupabase/qu'à deux endroits :_shared/openings.tset_shared/tournamentPaths.ts.- Côté navigateur, le seul stockage du jeu est le niveau de l'ordinateur (
useStoredDifficulty.ts) ; l'état d'une partie n'est jamais stocké. Tout ce qui est relu du stockage se valide avant emploi. - Pas de jeu en réseau entre humains ni d'effets sonores sans demande explicite.
- Ajouter ou adapter des tests de domaine pour toute modification de règles, de chute, de passe ou de connexion, et toujours couvrir le cas de refus.
- Les scripts
testrelèvent--testTimeoutà 20 s danspackage.json, pas dansvite.config.ts: Vitest 3 embarque sa propre copie de Vite, dont les types ne se mêlent pas à ceux de Vite 8. Un test franchement plus long garde son délai explicite. - Pour toute modification d'interface, faire le passage navigateur décrit dans
plan.mdsur un viewport bureau et un viewport mobile : aucun débordement horizontal, aucune erreur console.fixtures/urls.mdfournit des positions prêtes à coller.
Vérification finale obligatoire :
npm test
npm run lint
npm run build.github/workflows/ci.yml fait foi : lint, tests et build sur push et pull_request vers main, publication de dist/ sur gh-pages et déploiement du backend au seul push vers main. Le site est servi sous un sous-chemin et s'installe hors ligne : les contraintes qui en découlent sont dans .claude/rules/hors-ligne.md, les secrets et réglages manuels dans docs/deploiement.md.
- Préserver les changements existants de l'utilisateur et éviter les réécritures sans rapport avec la tâche.
- Préférer de petits composants et des fonctions nommées aux duplications de logique.
- Commenter peu. Un commentaire n'explique que ce qui n'est pas clair à la lecture du code. Ne rien redire de ce que
plan.mdou une règle spécifie déjà : le doublon se périme. Souvent, un meilleur nom suffit. - Ne pas modifier les matrices des pièces, les règles de support ou la connectivité pour résoudre un problème purement visuel.
- Avant de terminer, examiner le diff, exécuter
git diff --checket résumer les vérifications effectuées.
