Domaine : reprise d'une partie, plateau affiché, demi-coups possibles, droit de doubler
Ce dont l'interface a besoin, sans règle réimplémentée : le plateau vu de chaque camp, les demi-coups que le moteur accepte à la suite de ceux déjà saisis, le droit de doubler selon le moteur, et la reprise d'une partie depuis son journal enregistré. Les types du navigateur sont ajoutés à l'application ; le domaine reste vérifié sans eux. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
fea94324af
commit
613ff38254
@@ -218,3 +218,57 @@ test("lecture et écriture des demi-coups", () => {
|
||||
assert.throws(() => parseHalfMoves("8-5"), /mal formé/);
|
||||
assert.throws(() => parseHalfMoves("30/5"), /case invalide/);
|
||||
});
|
||||
|
||||
test("une partie se reprend depuis son journal ; un journal d'une autre version est refusé", async () => {
|
||||
const game = Game.start(fakeEngine(), { dice: fixedDice([3, 1]) });
|
||||
await game.playHalfMove({ from: 8, to: 5 });
|
||||
await game.playHalfMove({ from: 6, to: 5 });
|
||||
const saved = JSON.parse(JSON.stringify(game.journal));
|
||||
|
||||
const resumed = Game.resume(fakeEngine(), saved);
|
||||
assert.deepEqual(resumed.state, game.state);
|
||||
assert.deepEqual(resumed.events, game.events);
|
||||
|
||||
const other = { ...saved, events: saved.events.map((e: object) => ({ ...e, version: 99 })) };
|
||||
assert.throws(() => Game.resume(fakeEngine(), other), /version/);
|
||||
assert.throws(() => Game.resume(fakeEngine(), EMPTY_JOURNAL), /mal formé/);
|
||||
assert.throws(() => Game.resume(fakeEngine(), { ...saved, head: "inconnu" }), /introuvable/);
|
||||
});
|
||||
|
||||
test("le droit de doubler vient du moteur, seulement avant de lancer les dés", async () => {
|
||||
let available = true;
|
||||
const engine = fakeEngine({
|
||||
cubeDecision: async () => ({ available, double: false, take: true }),
|
||||
});
|
||||
const game = Game.start(engine, {
|
||||
dice: fixedDice([3, 1]),
|
||||
setup: { position: "X", firstTurn: "player" },
|
||||
});
|
||||
assert.equal(await game.canDouble(), true);
|
||||
available = false;
|
||||
assert.equal(await game.canDouble(), false);
|
||||
available = true;
|
||||
await game.roll();
|
||||
assert.equal(game.state.phase, "moving");
|
||||
assert.equal(await game.canDouble(), false);
|
||||
});
|
||||
|
||||
test("le plateau affiché est donné du point de vue de chaque camp", async () => {
|
||||
const side = (pipCount: number) => ({ points: [], bar: 0, off: 0, pipCount });
|
||||
const engine = fakeEngine({
|
||||
board: async () => ({
|
||||
onRoll: side(1),
|
||||
opponent: side(2),
|
||||
swappedPosition: "SWAPPED",
|
||||
result: null,
|
||||
}),
|
||||
});
|
||||
const playerTurn = Game.start(engine, { setup: { position: "X", firstTurn: "player" } });
|
||||
const sides = await playerTurn.displayedBoard();
|
||||
assert.equal(sides.player.pipCount, 1);
|
||||
assert.equal(sides.computer.pipCount, 2);
|
||||
const computerTurn = Game.start(engine, { setup: { position: "X", firstTurn: "computer" } });
|
||||
const swapped = await computerTurn.displayedBoard();
|
||||
assert.equal(swapped.player.pipCount, 2);
|
||||
assert.equal(swapped.computer.pipCount, 1);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
// Ce que l'interface propose au joueur, sur le vrai moteur WebAssembly : les
|
||||
// demi-coups possibles (mis en évidence quand il touche un pion) et le
|
||||
// plateau affiché pendant la saisie d'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 { 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, n'a plus que deux pions, sur ses cases 6 et 5
|
||||
// (même position que dans les étapes du scénario).
|
||||
const PLAYER_ALMOST_DONE = "/38AAABQAAAAAA";
|
||||
|
||||
/** Les demi-coups en notation, triés : « 13/10 13/12… ». */
|
||||
function text(halfMoves: readonly HalfMove[]): string {
|
||||
return halfMoves
|
||||
.map((h) => formatHalfMoves([h]))
|
||||
.sort()
|
||||
.join(" ");
|
||||
}
|
||||
|
||||
test("au départ avec 3-1 : chaque pion jouable, avec chaque dé", async () => {
|
||||
const engine = await loadNodeEngine();
|
||||
const game = Game.start(engine, { dice: fixedDice([3, 1]) });
|
||||
assert.equal(text(await game.nextHalfMoves()), "13/10 24/21 24/23 6/3 6/5 8/5 8/7");
|
||||
// 13/12 manque : votre case 12 est tenue par l'ordinateur. Après 8/5,
|
||||
// il ne reste que le 1.
|
||||
assert.equal(await game.playHalfMove({ from: 8, to: 5 }), true);
|
||||
assert.equal(text(await game.nextHalfMoves()), "24/23 5/4 6/5 8/7");
|
||||
});
|
||||
|
||||
test("en fin de partie, les demi-coups vers la sortie viennent du moteur", async () => {
|
||||
const engine = await loadNodeEngine();
|
||||
const game = Game.start(engine, {
|
||||
dice: fixedDice([6, 5]),
|
||||
setup: { position: PLAYER_ALMOST_DONE, firstTurn: "player" },
|
||||
});
|
||||
await game.roll();
|
||||
assert.equal(text(await game.nextHalfMoves()), "5/off 6/1 6/off");
|
||||
});
|
||||
|
||||
test("hors du tour de jeu du joueur, rien n'est proposé", async () => {
|
||||
const engine = await loadNodeEngine();
|
||||
const game = Game.start(engine, { dice: fixedDice([1, 3]) });
|
||||
assert.equal(game.state.turn, "computer");
|
||||
assert.deepEqual(await game.nextHalfMoves(), []);
|
||||
});
|
||||
|
||||
test("le plateau affiché suit les demi-coups saisis", async () => {
|
||||
const engine = await loadNodeEngine();
|
||||
const game = Game.start(engine, { dice: fixedDice([3, 1]) });
|
||||
const before = await game.displayedBoard();
|
||||
assert.equal(before.player.points[7], 3);
|
||||
assert.equal(before.computer.points[23], 2);
|
||||
await game.playHalfMove({ from: 8, to: 5 });
|
||||
const after = await game.displayedBoard();
|
||||
assert.equal(after.player.points[7], 2);
|
||||
assert.equal(after.player.points[4], 1);
|
||||
assert.equal(after.player.pipCount, before.player.pipCount - 3);
|
||||
});
|
||||
Reference in New Issue
Block a user