Parties enregistrées : IndexedDB, branches, export SGF relu par gnubg
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
08cb89735f
commit
274fbf6252
@@ -0,0 +1,87 @@
|
||||
# Relit dans GNU Backgammon de référence les parties exportées en SGF par
|
||||
# Ustagammon (app/src/domain/game/sgf.ts) et vérifie qu'il y retrouve les
|
||||
# positions du journal.
|
||||
#
|
||||
# Ce script s'exécute DANS gnubg (scripting Python intégré), dans l'image
|
||||
# Docker ustagammon/gnubg-reference : voir tests/test_web.sh. Le dossier
|
||||
# SGF_DIR contient, pour chaque partie, <nom>.sgf et <nom>.json (écrits par
|
||||
# app/tests/tools/sgf-samples.ts) :
|
||||
# after : après chaque coup du journal, la position vue de celui qui a
|
||||
# alors la main ;
|
||||
# result : le résultat de la partie ({winner, points}) ou null.
|
||||
#
|
||||
# Pour chaque partie, gnubg charge le fichier (load game), puis avance
|
||||
# enregistrement par enregistrement (next) : après chaque coup, sa position
|
||||
# doit être celle du journal. Une position de départ mal posée, un coup mal
|
||||
# écrit ou refusé changent donc la suite des positions. Le résultat (RE)
|
||||
# doit aussi être celui du journal. Code de sortie : 0 si tout concorde.
|
||||
|
||||
import glob
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
import gnubg
|
||||
|
||||
FOLDER = os.environ.get("SGF_DIR", "/sgf")
|
||||
|
||||
|
||||
def check(path):
|
||||
"""Les écarts entre gnubg et le journal pour une partie (liste vide si
|
||||
tout concorde)."""
|
||||
with open(path[:-4] + ".json") as f:
|
||||
expected = json.load(f)
|
||||
gnubg.command("load game " + path)
|
||||
games = gnubg.match(analysis=0, boards=0, statistics=0, verbose=0)["games"]
|
||||
if len(games) != 1:
|
||||
return ["gnubg n'a pas chargé la partie"]
|
||||
game = games[0]
|
||||
records = list(game["game"])
|
||||
|
||||
# Le premier « next » applique le premier enregistrement.
|
||||
gnubg.command("first move")
|
||||
after = []
|
||||
for record in records:
|
||||
gnubg.command("next")
|
||||
if record["action"] == "move":
|
||||
after.append(gnubg.positionid())
|
||||
|
||||
errors = []
|
||||
if len(after) != len(expected["after"]):
|
||||
errors.append("%d coups lus, %d attendus" % (len(after), len(expected["after"])))
|
||||
for i, (got, want) in enumerate(zip(after, expected["after"])):
|
||||
if got != want:
|
||||
errors.append("après le coup %d : gnubg %s, journal %s" % (i + 1, got, want))
|
||||
break
|
||||
|
||||
# Dans gnubg, X est le joueur 0 (W, l'ordinateur) et O le joueur 1 (B).
|
||||
info = game["info"]
|
||||
result = expected["result"]
|
||||
if result is None:
|
||||
if info.get("winner") is not None:
|
||||
errors.append("gnubg voit une partie finie : %s" % info)
|
||||
else:
|
||||
winner = "O" if result["winner"] == "player" else "X"
|
||||
if (info.get("winner"), info.get("points-won")) != (winner, result["points"]):
|
||||
errors.append("résultat : gnubg %s, journal %s" % (info, result))
|
||||
return errors
|
||||
|
||||
|
||||
failures = 0
|
||||
paths = sorted(glob.glob(os.path.join(FOLDER, "*.sgf")))
|
||||
for path in paths:
|
||||
errors = check(path)
|
||||
name = os.path.basename(path)
|
||||
if errors:
|
||||
failures += 1
|
||||
print("ÉCHEC %s : %s" % (name, " ; ".join(errors)))
|
||||
else:
|
||||
print("OK %s" % name)
|
||||
|
||||
if not paths:
|
||||
print("aucun SGF dans %s" % FOLDER)
|
||||
failures += 1
|
||||
sys.stdout.flush()
|
||||
# Un script --python de gnubg ne transmet pas son code de sortie : la
|
||||
# dernière ligne le donne au script shell.
|
||||
print("SGF-RESULTAT %s" % ("OK" if failures == 0 else "ECHEC"))
|
||||
+22
-1
@@ -5,7 +5,10 @@
|
||||
# - scénarios de docs/scenarios/ exécutés par Cucumber sur le domaine branché
|
||||
# au vrai moteur (ADR 0008) ;
|
||||
# - règles d'architecture (dependency-cruiser, ADR 0007), et preuve qu'une
|
||||
# violation volontaire de chaque règle est bien détectée.
|
||||
# violation volontaire de chaque règle est bien détectée ;
|
||||
# - export SGF : des parties jouées sur le vrai moteur, exportées, relues par
|
||||
# le GNU Backgammon de référence, qui doit y retrouver les positions du
|
||||
# journal (tests/sgf/check.py).
|
||||
#
|
||||
# Lance `make engine-wasm` et `make web` (sans effet si tout est à jour).
|
||||
|
||||
@@ -39,6 +42,24 @@ web . node --test --test-reporter=dot 'build/web/tests/**/*.test.js' \
|
||||
web . cucumber-js --format summary --import 'build/web/tests/steps/*.js' docs/scenarios/*.feature \
|
||||
|| report "scénarios en échec (voir ci-dessus)"
|
||||
|
||||
# Export SGF relu par gnubg de bureau. Le script Python de gnubg ne
|
||||
# transmet pas de code de sortie : sa dernière ligne donne le verdict.
|
||||
samples=build/sgf-samples
|
||||
rm -rf "$samples"
|
||||
if ! docker image inspect "${REFERENCE_IMAGE:?lancer via make test}" >/dev/null 2>&1; then
|
||||
report "gnubg de référence absent : lancer d'abord make setup"
|
||||
elif ! web . node build/web/tests/tools/sgf-samples.js "$samples" >/dev/null; then
|
||||
report "parties d'exemple SGF non produites (voir ci-dessus)"
|
||||
else
|
||||
output="$(docker run --rm --user "$(id -u):$(id -g)" -e HOME=/tmp \
|
||||
-v "$PWD/$samples:/sgf:ro" -v "$PWD/tests/sgf:/check:ro" "$REFERENCE_IMAGE" \
|
||||
gnubg --tty --quiet --no-rc --python=/check/check.py 2>&1)" || true
|
||||
if ! grep -q '^SGF-RESULTAT OK$' <<<"$output"; then
|
||||
grep -E '^(ÉCHEC|aucun|Traceback| File|[A-Za-z]*Error)' <<<"$output" || true
|
||||
report "export SGF : gnubg ne retrouve pas les positions du journal"
|
||||
fi
|
||||
fi
|
||||
|
||||
web app depcruise --config .dependency-cruiser.cjs src tests \
|
||||
|| report "règles d'architecture non respectées (voir ci-dessus)"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user