// Tests du choix de la partie et du match, sans navigateur : réglages de // l'écran « Nouvelle partie », correspondance des niveaux avec ceux de // GNU Backgammon, résumé d'une partie de match enregistrée, adaptateur du // moteur (Match ID d'un match) sur le vrai moteur. // // Copyright (C) 2026 les auteurs d'Ustagammon. // Licence : GPLv3 ou ultérieure (voir LICENSE). import assert from "node:assert/strict"; import { test } from "node:test"; import { Game } from "../src/domain/game/game.js"; import { COMPUTER_LEVELS, computerLevelOf, ENGINE_LEVELS } from "../src/domain/game/level.js"; import { firstGameOf, MATCH_LENGTHS, nextGameOf } from "../src/domain/game/match.js"; import { START_POSITION } from "../src/domain/game/position.js"; import { fixedDice } from "../src/domain/game/roll.js"; import { levelOf, summarizeGame } from "../src/domain/game/saved-game.js"; import { chosenGame, DEFAULT_SETTINGS, parseSettings } from "../src/ui/settings.js"; import { loadNodeEngine } from "./support/node-engine.js"; test("les six niveaux montrés au joueur, du plus faible au plus fort, et leur niveau de gnubg", () => { assert.deepEqual(COMPUTER_LEVELS, [ "beginner", "amateur", "intermediate", "advanced", "expert", "champion", ]); assert.deepEqual( COMPUTER_LEVELS.map((level) => ENGINE_LEVELS[level]), ["beginner", "casual", "intermediate", "advanced", "0ply", "2ply"], ); assert.equal(computerLevelOf("casual"), "amateur"); assert.equal(computerLevelOf("3ply"), null); }); test("réglages de la nouvelle partie : partie libre, Intermédiaire, conseil activé par défaut ; valeurs illisibles ignorées", () => { assert.equal(DEFAULT_SETTINGS.gameKind, "free"); assert.equal(DEFAULT_SETTINGS.level, "intermediate"); assert.equal(DEFAULT_SETTINGS.masterAdvice, true); // Réglages d'avant l'étape 1c : les valeurs par défaut. const old = parseSettings('{"vibrations": false, "gamesStarted": 4}'); assert.equal(old.gameKind, "free"); assert.equal(old.matchLength, 5); assert.equal(old.level, "intermediate"); const saved = parseSettings( '{"gameKind": "match", "matchLength": 7, "level": "champion", "masterAdvice": false}', ); assert.deepEqual( [saved.gameKind, saved.matchLength, saved.level, saved.masterAdvice], ["match", 7, "champion", false], ); const wrong = parseSettings('{"gameKind": "x", "matchLength": 4, "level": "4ply"}'); assert.deepEqual([wrong.gameKind, wrong.matchLength, wrong.level], ["free", 5, "intermediate"]); assert.deepEqual([...MATCH_LENGTHS], [3, 5, 7, 11]); }); test("la partie choisie : le niveau de gnubg, et la première partie d'un match à 0-0", () => { assert.deepEqual(chosenGame(DEFAULT_SETTINGS), { level: "intermediate" }); assert.deepEqual(chosenGame({ ...DEFAULT_SETTINGS, gameKind: "match", level: "expert" }), { level: "0ply", match: { length: 5, score: { player: 0, computer: 0 }, crawford: false }, }); assert.throws(() => firstGameOf(0), /longueur/); const match = firstGameOf(3); assert.equal( nextGameOf(match, { score: { player: 3, computer: 0 }, over: true, crawford: false }), null, ); assert.deepEqual( nextGameOf(match, { score: { player: 2, computer: 0 }, over: false, crawford: true }), { length: 3, score: { player: 2, computer: 0 }, crawford: true }, ); }); test("le résumé d'une partie de match enregistrée porte le match ; celui d'une partie libre n'en a pas", async () => { const engine = await loadNodeEngine(); const times = { startedAt: 1, updatedAt: 2 }; const free = Game.start(engine, { dice: fixedDice([3, 1]), level: "beginner" }); assert.equal(levelOf(free.journal), "beginner"); const freeSummary = summarizeGame("a", free.journal, { level: "beginner" }, times); assert.equal(freeSummary.match, undefined); assert.equal(freeSummary.level, "beginner"); const match = Game.start(engine, { dice: fixedDice([3, 1]), level: "2ply", match: { length: 5, score: { player: 4, computer: 2 }, crawford: true }, }); const summary = summarizeGame("b", match.journal, { level: levelOf(match.journal) }, times); assert.deepEqual(summary.match, { length: 5, score: { player: 4, computer: 2 }, crawford: true, over: false, }); }); test("adaptateur : le Match ID d'un match code la longueur, le score vu du joueur au trait et Crawford", async () => { const engine = await loadNodeEngine(); const id = await engine.encodeMatch({ value: 1, owner: "centered", match: { length: 5, score: { onRoll: 4, opponent: 2 }, crawford: true }, }); const decoded = await engine.decodeMatch(id); assert.deepEqual( [decoded.matchLength, decoded.score, decoded.crawford, decoded.jacoby], [5, [4, 2], true, false], ); // En partie Crawford, le moteur refuse le videau au joueur au trait. const crawford = await engine.cubeDecision( START_POSITION, { value: 1, owner: "centered", match: { length: 5, score: { onRoll: 2, opponent: 4 }, crawford: true }, }, 0, ); assert.equal(crawford.available, false); const after = await engine.matchAfterGame( { length: 5, score: { onRoll: 3, opponent: 2 }, crawford: false }, "onRoll", 1, ); assert.deepEqual(after, { score: { onRoll: 4, opponent: 2 }, over: false, crawford: true }); });