83 lines
3.5 KiB
TypeScript
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);
|
|
});
|