Plateau jouable en PWA : interface, Web Worker, stockage, service worker
L'application est à la racine du site : plateau en SVG, saisie au toucher (pion puis case d'arrivée, proposées à partir du moteur), boutons du jet, du videau et de nouvelle partie, coup de l'ordinateur affiché avec son temps, fin de partie, reprise depuis localStorage, aide lue dans les copies de docs/. Le moteur tourne dans le Web Worker existant ; main.ts est le seul assembleur (nouvelles règles dependency-cruiser). Le service worker met tout le site en cache, avec une empreinte du contenu comme version. Les pages de test du moteur passent sous /moteur/. Politique de sécurité du contenu dans le Caddyfile ; make test-browser joue une partie dans Chromium et Firefox sans écran (Playwright, hors de make test). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
613ff38254
commit
a8636b3f83
@@ -0,0 +1,71 @@
|
||||
// page.mjs -- page de test du moteur (index.html) : meilleur coup d'une
|
||||
// position, avec son temps de calcul.
|
||||
//
|
||||
// Copyright (C) 2026 les auteurs d'Ustagammon.
|
||||
// Licence : GPLv3 ou ultérieure (voir LICENSE).
|
||||
|
||||
import { startEngine } from "./engine-client.mjs";
|
||||
|
||||
const $ = (id) => document.getElementById(id);
|
||||
const engine = startEngine();
|
||||
|
||||
engine.ready.then(
|
||||
({ loadMs }) => {
|
||||
$("status").textContent = "Moteur prêt, chargé en " + Math.round(loadMs) + " ms.";
|
||||
$("best-move").disabled = false;
|
||||
},
|
||||
(error) => {
|
||||
$("status").textContent = "Le moteur n'a pas pu se charger : " + error.message;
|
||||
$("status").classList.add("error");
|
||||
},
|
||||
);
|
||||
|
||||
// Exemples : les positions de référence avec des dés.
|
||||
fetch("../wasm/positions.json")
|
||||
.then((response) => response.json())
|
||||
.then(({ positions }) => {
|
||||
for (const p of positions.filter((p) => p.type === "move")) {
|
||||
const option = new Option(p.id + " : " + p.description, p.id);
|
||||
option.position = p;
|
||||
$("example").add(option);
|
||||
}
|
||||
})
|
||||
.catch(() => {}); // la page reste utilisable sans les exemples
|
||||
|
||||
$("example").onchange = () => {
|
||||
const p = $("example").selectedOptions[0].position;
|
||||
if (p) {
|
||||
$("position-id").value = p.position_id;
|
||||
$("match-id").value = p.match_id;
|
||||
[$("die1").value, $("die2").value] = p.dice;
|
||||
}
|
||||
};
|
||||
|
||||
function showResult(move, details, time) {
|
||||
$("move").textContent = move;
|
||||
$("details").textContent = details;
|
||||
$("time").textContent = time;
|
||||
$("result").hidden = false;
|
||||
}
|
||||
|
||||
$("form").onsubmit = async (event) => {
|
||||
event.preventDefault();
|
||||
$("best-move").disabled = true;
|
||||
$("result").hidden = true;
|
||||
try {
|
||||
const { result, ms } = await engine.bestMove($("position-id").value.trim(),
|
||||
$("match-id").value.trim(), [$("die1").value, $("die2").value], $("ply").value);
|
||||
const best = result.best_move;
|
||||
if (best) {
|
||||
showResult(best.move, "Équité " + best.equity.toFixed(3).replace(".", ",")
|
||||
+ ", chances de gain " + (100 * best.probabilities.win).toFixed(1).replace(".", ",")
|
||||
+ " %, évalué à " + best.ply + " pli(s).", Math.round(ms) + " ms");
|
||||
} else {
|
||||
showResult("aucun coup possible", "", Math.round(ms) + " ms");
|
||||
}
|
||||
} catch (error) {
|
||||
showResult("erreur", error.message, "");
|
||||
} finally {
|
||||
$("best-move").disabled = false;
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user