L'engine-kit (optionnel)
Cette page est optionnelle. Tout ici est une façon commode de produire un run conforme ; tu peux ignorer le kit entièrement et livrer un run nu que tu as écrit à la main. Tends la main vers @caputchin/engine-kit quand tu préfères écrire une logique de jeu ordinaire et avoir le déterminisme, la boucle de rejeu et l'encodage de trace gérés pour toi.
Le kit réexporte toute la surface du contrat de rejeu, donc les utilisateurs du kit ont un seul site d'import.
npm install @caputchin/engine-kitL'idée : écris un reducer, obtiens un run
Le geste central du kit est de te laisser exprimer ton jeu comme un reducer pur : une fonction qui prend l'état actuel et l'entrée d'un tick et renvoie l'état suivant. À partir de ce reducer plus des primitives déterministes, l'adaptateur toRun du kit produit un run(seed, config, trace) qui se conforme au contrat par construction.
import { defineEngine, toRun, cap } from "@caputchin/engine-kit";
const engine = defineEngine({
setup(seed) {
const rng = cap.rng(seed); // deterministic, seeded RNG
return { score: 0, targets: spawn(rng), rng };
},
tick(state, input) {
// pure: same (state, input) always yields the same next state
return applyInput(state, input);
},
result(state) {
return { passed: state.score >= 3, score: state.score, durationMs: state.elapsed };
},
});
export const run = toRun(engine); // a conforming RunFnPrimitives déterministes
Le kit te donne les deux choses qui cassent le plus souvent le déterminisme inter-runtime, toutes deux ensemencées et reproductibles :
cap.rng(seed)(etcap.rngFromState) : un PRNG ensemençable. Utilise-le pour chaque choix aléatoire au lieu deMath.random().cap.math: des transcendantales déterministes (sin,cos, ...) qui s'accordent entre runtimes, au lieu de celles de la plateformeMathqui peuvent ne pas le faire.
Shims optionnels
applyShim(): neutralise les globaux non déterministes (l'horloge murale,Math.random) pour qu'un appel accidentel échoue bruyamment en développement au lieu de diverger silencieusement au rejeu.applyDomShim(): un DOM headless minimal pour le chemin framework, pour qu'un reducer qui touche une petite surface DOM se rejoue quand même dans l'isolat sans DOM.
Exécute-le en local avant de publier
Le harnais replay du kit passe une trace à travers ton moteur de la même façon que le serveur le fera, et selfCheck exécute une batterie de cas et rapporte tout non-déterminisme avant que tu publies :
import { selfCheck } from "@caputchin/engine-kit";
const report = selfCheck(engine, { cases: myCases });
if (!report.ok) console.error(report.violations);Un selfCheck local propre est le meilleur prédicteur que l'auto-vérification de publication du marketplace réussira (c'est le même standard de déterminisme). Le kit livre aussi une CLI pour exécuter ça depuis les scripts package.json ou la CI.
Encodage de trace
encodeTrace / decodeTrace te donnent un format de trace compact et versionné pour que le flux d'entrées que ton jeu en direct émet soit exactement ce que ton run décode au rejeu. Les utiliser des deux côtés garde les deux au pas.
Quand sauter le kit
Saute-le quand tu as déjà une logique déterministe (une simulation à virgule fixe, un module WASM compilé pour les flottants-spec) ou quand ton jeu est assez simple pour qu'écrire run à la main soit trivial. La plateforme ne sait ni ne se soucie jamais si le kit a produit ton run ; elle ne fait que charger et rejouer le contrat.
Voir aussi
- Le contrat de rejeu : le
run/ la graine / le verdict que ce kit produit. - Bâtir un jeu pour le marketplace : le jeu dont ce kit peut adosser la logique.
- Le manifeste caputchin.json : livrer le
runproduit comme artefact.