Files
Ustagammon/app/tests/obvious-move.test.ts

83 lines
3.5 KiB
TypeScript

// Le coup évident du double toucher (charte, ecrans-etape-1.md, 1i), sur le
// vrai moteur WebAssembly : un seul coup légal complet commence par le pion
// touché, ou le moteur ne propose qu'un coup.
//
// 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 type { Engine } from "../src/domain/game/engine.js";
import { Game } from "../src/domain/game/game.js";
import { formatHalfMoves, type HalfMove } from "../src/domain/game/position.js";
import { fixedDice } from "../src/domain/game/roll.js";
import { loadNodeEngine } from "./support/node-engine.js";
// Le joueur, au trait : un pion sur sa case 6, un sur sa case 1.
const SIX_AND_ONE = "BQAACAIAAAAAAA";
// Le joueur, au trait : deux pions, sur ses cases 6 et 5.
const PLAYER_ALMOST_DONE = "/38AAABQAAAAAA";
let engine: Engine | null = null;
async function loaded(): Promise<Engine> {
engine ??= await loadNodeEngine();
return engine;
}
/** Une partie à partir d'une position, joueur au trait, dés lancés. */
async function from(position: string, dice: [number, number]): Promise<Game> {
const game = Game.start(await loaded(), {
setup: { position, firstTurn: "player" },
dice: fixedDice(dice),
});
await game.roll();
return game;
}
function text(halfMoves: readonly HalfMove[] | null): string | null {
return halfMoves === null ? null : formatHalfMoves(halfMoves);
}
test("au départ avec 3-1 : plusieurs coups commencent par chaque pion, rien d'évident", async () => {
const game = Game.start(await loaded(), { dice: fixedDice([3, 1]) });
for (const point of [24, 13, 8, 6]) {
assert.equal(await game.obviousMove(point), null, `pion de la ${point}`);
}
// Rien n'a été saisi.
assert.deepEqual(game.pendingHalfMoves, []);
});
test("6-1 avec des pions sur 6 et 1 : seul le pion de la 1 a un coup évident", async () => {
const game = await from(SIX_AND_ONE, [6, 1]);
// 1/off, puis le 6 sort l'autre pion : un seul coup.
assert.equal(text(await game.obviousMove(1)), "1/off 6/off");
// Depuis la 6 : 6/off 1/off, ou 6/5/off (le pion de la 1 reste).
assert.equal(await game.obviousMove(6), null);
// Une case sans pion n'a pas de coup.
assert.equal(await game.obviousMove(3), null);
});
test("un seul coup légal : il est évident depuis chaque pion, même à moitié saisi", async () => {
const game = await from(PLAYER_ALMOST_DONE, [6, 6]);
assert.equal((await (await loaded()).legalMoves(game.state.position, [6, 6])).length, 1);
const fromFive = await game.obviousMove(5);
const fromSix = await game.obviousMove(6);
assert.ok(fromFive !== null && fromSix !== null);
assert.equal(fromFive.length, 2);
assert.equal(fromSix.length, 2);
// Une fois 6/off saisi, il ne reste que 5/off.
assert.equal(await game.playHalfMove({ from: 6, to: 0 }), true);
assert.equal(text(await game.obviousMove(5)), "5/off");
// Le coup évident se joue tel quel, demi-coup par demi-coup.
for (const halfMove of (await game.obviousMove(5)) ?? []) {
assert.equal(await game.playHalfMove(halfMove), true);
}
assert.equal(game.moveComplete, true);
});
test("hors du tour de jeu du joueur, aucun coup évident", async () => {
const game = Game.start(await loaded(), { dice: fixedDice([1, 3]) });
assert.equal(game.state.turn, "computer");
assert.equal(await game.obviousMove(13), null);
});