// engine-wasm.mjs -- le moteur WebAssembly vu depuis JavaScript. // // Copyright (C) 2026 les auteurs d'Ustagammon. // Licence : GPLv3 ou ultérieure (voir LICENSE). // // Le WebAssembly (build/wasm/, voir engine-wasm/wasm.mk) contient le moteur // et le programme engine-cli. On charge le module une fois, on initialise le // moteur, puis chaque commande est un appel à main avec les mêmes arguments // qu'engine-cli en ligne de commande : la sortie est donc le même JSON. // // Utilisé par le Web Worker de la page (engine-worker.mjs) et, sous Node, // par engine-wasm/engine-cli.mjs et engine-wasm/bench.mjs. Le moteur calcule // sur le fil qui l'appelle : dans le navigateur, toujours dans un Worker. // Dossier des données dans le système de fichiers virtuel (--preload-file). const DATA_DIR = "/data"; // createEngineModule : la fonction exportée par build/wasm/engine.mjs. // wasmDir : où trouver engine.wasm et engine.data, terminé par « / » (une // adresse dans le navigateur, un chemin de fichier sous Node). // Renvoie le moteur prêt, avec : // - runText(args) : { text, exitCode }, la sortie brute d'engine-cli ; // - run(args) : { result, ms }, le JSON décodé et le temps de calcul // (lève une erreur si le moteur en signale une) ; // - memoryBytes() : taille actuelle de la mémoire WebAssembly. export async function loadEngine(createEngineModule, wasmDir) { let output = []; const module = await createEngineModule({ locateFile: (name) => wasmDir + name, print: (line) => output.push(line), printErr: (line) => output.push(line), }); const status = module.ccall("engine_init", "number", ["string"], [DATA_DIR]); if (status !== 0) { throw new Error("le moteur n'a pas pu charger ses données (code " + status + ")"); } function runText(args) { output = []; const exitCode = module.callMain(args.map(String)); return { text: output.join("\n"), exitCode }; } function run(args) { const start = performance.now(); const { text } = runText(args); const ms = performance.now() - start; const result = JSON.parse(text); if (result.error) { throw new Error(result.error.message); } return { result, ms }; } return { runText, run, memoryBytes: () => module.HEAPU8.length, }; } // Arguments d'engine-cli pour chaque fonction de l'interface. Les dés sont // un tableau [3, 1] ou une chaîne « 31 ». export const commands = { bestMove: (positionId, matchId, dice, ply) => ["--ply", ply, "bestmove", positionId, matchId, diceText(dice)], cubeDecision: (positionId, matchId, ply) => ["--ply", ply, "cube", positionId, matchId], evaluate: (positionId, matchId, ply) => ["--ply", ply, "eval", positionId, matchId], reference: (positionId, matchId, dice, ply) => ["--ply", ply, "reference", positionId, matchId, ...(dice ? [diceText(dice)] : [])], }; function diceText(dice) { return Array.isArray(dice) ? dice.join("") : String(dice); }