# 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, .sgf et .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"))