Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
217c40a245 | ||
|
|
cfb2e485c3 | ||
|
|
ac0e924e4c | ||
|
|
ceed97e359 | ||
|
|
109655537f | ||
|
|
282a3d688b | ||
|
|
cb42052c80 | ||
|
|
99294900a0 | ||
|
|
1d21ac15d2 | ||
|
|
c42316d9d8 |
@@ -102,10 +102,9 @@ export async function analyseDecision(
|
||||
if (analysis === null) {
|
||||
return null;
|
||||
}
|
||||
const ranking =
|
||||
options.ranking === false
|
||||
? []
|
||||
: await engine.rankMoves(before.position, cube, before.roll, ply);
|
||||
// Le classement est celui de l'analyse même : mêmes coups, mêmes
|
||||
// niveaux et mêmes équités que le coup joué et le meilleur coup.
|
||||
const ranking = options.ranking === false ? [] : analysis.ranking;
|
||||
const start = await engine.board(before.position, cube);
|
||||
const facts = async (move: EvaluatedMove) =>
|
||||
factsOf(start.onRoll, start.opponent, await engine.board(move.position, cube));
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
// Licence : GPLv3 ou ultérieure (voir LICENSE).
|
||||
|
||||
import type { AdvisedMove, MoveAdvice } from "../game/advice.js";
|
||||
import type { Ply } from "../game/engine.js";
|
||||
|
||||
/** Un nombre à la française, signé, à trois décimales : « −0,123 ». */
|
||||
export function formatEquity(value: number): string {
|
||||
@@ -34,18 +35,21 @@ export interface RankingRow {
|
||||
|
||||
/**
|
||||
* Le classement d'un conseil tel que l'affiche la feuille du maître : les
|
||||
* coups du classement du moteur, chacun avec son écart par rapport au
|
||||
* premier ; puis le coup joué, s'il n'y figure pas, avec l'écart de
|
||||
* l'analyse du coup joué.
|
||||
* coups du classement de l'analyse, chacun avec son écart par rapport au
|
||||
* premier ; puis le coup joué, s'il n'y figure pas. Le coup joué a toujours
|
||||
* l'écart de l'analyse (equityLoss), celui que dit le maître.
|
||||
*/
|
||||
export function rankingRows(advice: MoveAdvice): RankingRow[] {
|
||||
const first = advice.ranking[0]?.equity ?? advice.best.equity;
|
||||
const rows = advice.ranking.map((move, i) => ({
|
||||
move,
|
||||
rank: i + 1,
|
||||
loss: move.equity - first,
|
||||
played: move.notation === advice.played.notation,
|
||||
}));
|
||||
const rows = advice.ranking.map((move, i) => {
|
||||
const played = move.notation === advice.played.notation;
|
||||
return {
|
||||
move,
|
||||
rank: i + 1,
|
||||
loss: played ? advice.equityLoss : move.equity - first,
|
||||
played,
|
||||
};
|
||||
});
|
||||
if (!rows.some((row) => row.played)) {
|
||||
rows.push({
|
||||
move: advice.played,
|
||||
@@ -56,3 +60,18 @@ export function rankingRows(advice: MoveAdvice): RankingRow[] {
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Le niveau du classement d'un conseil : celui auquel le moteur a évalué le
|
||||
* meilleur coup. C'est le niveau demandé (advice.ply), sauf quand gnubg
|
||||
* s'arrête avant, parce qu'un seul coup a passé ses filtres : tout le
|
||||
* classement reste alors au niveau inférieur.
|
||||
*/
|
||||
export function rankingPly(advice: MoveAdvice): Ply {
|
||||
return advice.best.ply;
|
||||
}
|
||||
|
||||
/** « 1 pli », « 2 plis » : gnubg compte les plis à partir de 0. */
|
||||
export function pliesText(ply: Ply): string {
|
||||
return `${ply} ${ply > 1 ? "plis" : "pli"}`;
|
||||
}
|
||||
|
||||
@@ -15,8 +15,10 @@ import type { HalfMove, PositionId } from "./position.js";
|
||||
import type { Roll } from "./roll.js";
|
||||
|
||||
/** Version du format d'un conseil enregistré. Un conseil d'une autre
|
||||
* version est ignoré à la reprise (il peut être recalculé). */
|
||||
export const ADVICE_VERSION = 1;
|
||||
* version est ignoré à la reprise (il peut être recalculé). Version 2 : le
|
||||
* classement des coups vient de l'analyse du coup joué, et non plus d'un
|
||||
* second classement comme la commande « hint » de gnubg. */
|
||||
export const ADVICE_VERSION = 2;
|
||||
|
||||
/**
|
||||
* Le conseil du maître sur une décision du joueur : un coup ou une décision
|
||||
@@ -66,8 +68,8 @@ export interface MoveAdvice {
|
||||
readonly best: AdvisedMove;
|
||||
readonly playedFacts: MoveFacts;
|
||||
readonly bestFacts: MoveFacts;
|
||||
/** Les meilleurs coups du classement du moteur, du meilleur au moins
|
||||
* bon (cinq au plus). */
|
||||
/** Les meilleurs coups du classement de l'analyse, du meilleur au
|
||||
* moins bon (cinq au plus), chacun au niveau où le moteur l'a évalué. */
|
||||
readonly ranking: readonly AdvisedMove[];
|
||||
/** Équité du coup joué moins celle du meilleur : 0 ou négative. */
|
||||
readonly equityLoss: number;
|
||||
|
||||
@@ -80,17 +80,6 @@ export interface Engine {
|
||||
ply: Ply,
|
||||
): Promise<MoveAnalysis | null>;
|
||||
|
||||
/** Les coups légaux d'un jet classés du meilleur au moins bon, au
|
||||
* niveau ply, comme la commande « hint » de gnubg (les coups écartés
|
||||
* par ses filtres gardent l'évaluation d'un niveau inférieur, voir leur
|
||||
* ply). */
|
||||
rankMoves(
|
||||
position: PositionId,
|
||||
cube: EngineCube,
|
||||
roll: Roll,
|
||||
ply: Ply,
|
||||
): Promise<EvaluatedMove[]>;
|
||||
|
||||
/** L'analyse d'une décision de videau, position vue du joueur au trait
|
||||
* (celui qui double ou non ; take et pass sont la réponse de son
|
||||
* adversaire). */
|
||||
@@ -222,6 +211,12 @@ export interface MoveAnalysis {
|
||||
/** Équité du coup joué moins celle du meilleur : 0 ou négative. */
|
||||
readonly equityLoss: number;
|
||||
readonly skill: Skill;
|
||||
/** Le classement de l'analyse, du meilleur au moins bon (dix coups au
|
||||
* plus) : la liste de l'analyse de partie de gnubg, chaque coup au
|
||||
* niveau où gnubg l'a évalué (ply). Les coups écartés par ses filtres
|
||||
* gardent l'évaluation d'un niveau inférieur ; le meilleur coup et le
|
||||
* coup joué y ont les évaluations de best et played. */
|
||||
readonly ranking: readonly EvaluatedMove[];
|
||||
}
|
||||
|
||||
/** Décision de videau : doubler ou non (joueur au trait), prendre ou passer
|
||||
|
||||
@@ -20,7 +20,13 @@ import {
|
||||
type ReviewedDecision,
|
||||
summarizeAnalysis,
|
||||
} from "../analysis/game-analysis.js";
|
||||
import { formatEquity, formatPercent, rankingRows } from "../analysis/numbers.js";
|
||||
import {
|
||||
formatEquity,
|
||||
formatPercent,
|
||||
pliesText,
|
||||
rankingPly,
|
||||
rankingRows,
|
||||
} from "../analysis/numbers.js";
|
||||
import { diceWords, type MasterPhrases, masterWords } from "../analysis/phrases.js";
|
||||
import {
|
||||
type Advice,
|
||||
@@ -83,7 +89,7 @@ const plural = (n: number, one: string, many: string) => `${n} ${n > 1 ? many :
|
||||
|
||||
/** « analyse à 2 plis ». */
|
||||
export function plyText(ply: Ply): string {
|
||||
return `analyse à ${ply} ${ply > 1 ? "plis" : "pli"}`;
|
||||
return `analyse à ${pliesText(ply)}`;
|
||||
}
|
||||
|
||||
/** Nombre de décisions analysées à partir duquel le niveau du joueur se
|
||||
@@ -146,8 +152,14 @@ export function decisionPrompt(
|
||||
niveau_eval: plyText(data.advice.ply),
|
||||
appreciation: masterWords(phrases, data.advice).word,
|
||||
};
|
||||
// Le niveau d'un classement de coups : celui de son meilleur coup, que
|
||||
// le moteur a pu évaluer moins profondément que demandé.
|
||||
return data.advice.kind === "move"
|
||||
? fillPrompt(templates.move, { ...common, ...moveValues(data.advice) })
|
||||
? fillPrompt(templates.move, {
|
||||
...common,
|
||||
niveau_eval: plyText(rankingPly(data.advice)),
|
||||
...moveValues(data.advice),
|
||||
})
|
||||
: fillPrompt(templates.cube, { ...common, ...cubeValues(data.advice) });
|
||||
}
|
||||
|
||||
@@ -163,7 +175,10 @@ function moveValues(advice: MoveAdvice): Record<string, string> {
|
||||
// de « Voir les chiffres du moteur », avec les mêmes chiffres.
|
||||
const rows = rankingRows(advice).filter((row) => row.rank <= 3 || row.played);
|
||||
const ranking = rows.map((row) => {
|
||||
const ply = row.move.ply === advice.ply ? "" : `, évalué à ${row.move.ply} pli`;
|
||||
// Un coup écarté par les filtres du moteur garde l'évaluation d'un
|
||||
// niveau inférieur à celui du classement : la ligne le dit.
|
||||
const ply =
|
||||
row.move.ply === rankingPly(advice) ? "" : `, évalué à ${pliesText(row.move.ply)}`;
|
||||
const mine = row.played ? " (mon coup)" : "";
|
||||
return `${row.rank}. ${row.move.notation}${mine} : équité ${formatEquity(row.move.equity)}, écart ${formatEquity(row.loss)}${ply}`;
|
||||
});
|
||||
|
||||
@@ -182,6 +182,7 @@ export function createWasmEngine(run: RunCommand): Engine {
|
||||
best: CliEvaluatedMove | null;
|
||||
equity_loss: number;
|
||||
skill: CliSkill;
|
||||
ranking: CliEvaluatedMove[];
|
||||
};
|
||||
if (answer.rank === null || answer.played === null || answer.best === null) {
|
||||
return null;
|
||||
@@ -193,17 +194,10 @@ export function createWasmEngine(run: RunCommand): Engine {
|
||||
best: toEvaluatedMove(answer.best),
|
||||
equityLoss: answer.equity_loss,
|
||||
skill: toSkill(answer.skill),
|
||||
ranking: answer.ranking.map(toEvaluatedMove),
|
||||
};
|
||||
},
|
||||
|
||||
async rankMoves(position: PositionId, cube: EngineCube, roll: Roll, ply: Ply) {
|
||||
const args = ["--ply", String(ply), "moves", position, await matchId(cube)];
|
||||
const answer = (await run([...args, diceText(roll)])) as {
|
||||
moves: CliEvaluatedMove[];
|
||||
};
|
||||
return answer.moves.map(toEvaluatedMove);
|
||||
},
|
||||
|
||||
async analyseCube(
|
||||
position: PositionId,
|
||||
cube: EngineCube,
|
||||
|
||||
@@ -26,8 +26,8 @@ export const ADVICE_TEXTS = {
|
||||
replay: "Rejouer le coup",
|
||||
numbers: "Voir les chiffres du moteur",
|
||||
you: "(vous)",
|
||||
moveNote: (ply: number) =>
|
||||
`Écart d'équité à ${ply} plis, par rapport au meilleur coup. Un écart de 0,03 ou moins reste un bon coup.`,
|
||||
moveNote: (plies: string) =>
|
||||
`Écart d'équité à ${plies}, par rapport au meilleur coup. Un écart de 0,03 ou moins reste un bon coup.`,
|
||||
cubeNote: (ply: number) =>
|
||||
`Équités de celui qui double, pour un videau de 1, à ${ply} plis. Écart de votre décision : `,
|
||||
cubeRows: {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
// Copyright (C) 2026 les auteurs d'Ustagammon.
|
||||
// Licence : GPLv3 ou ultérieure (voir LICENSE).
|
||||
|
||||
import { rankingRows } from "../domain/analysis/numbers.js";
|
||||
import { pliesText, rankingPly, rankingRows } from "../domain/analysis/numbers.js";
|
||||
import { type MasterPhrases, masterWords } from "../domain/analysis/phrases.js";
|
||||
import {
|
||||
type Advice,
|
||||
@@ -304,7 +304,7 @@ export function createAdviceView(options: AdviceViewOptions): AdviceView {
|
||||
}
|
||||
element(details, "p", {
|
||||
className: "explain-note",
|
||||
text: ADVICE_TEXTS.moveNote(advice.ply),
|
||||
text: ADVICE_TEXTS.moveNote(pliesText(rankingPly(advice))),
|
||||
});
|
||||
} else {
|
||||
// Les lignes de votre décision, en gras : doubler mène à l'une des
|
||||
|
||||
@@ -35,6 +35,35 @@ test("ouverture 3-1 : 24/23 13/10 est une erreur très mauvaise, 8/5 6/5 le meil
|
||||
Math.abs(analysis.equityLoss - (analysis.played.equity - analysis.best.equity)) < 1e-6,
|
||||
);
|
||||
assert.ok(analysis.equityLoss < -SKILL_THRESHOLDS.veryBad);
|
||||
// Le classement de l'analyse, comme gnubg de bureau
|
||||
// (tests/reference/analysis/move-opening-31-error.json) : le meilleur
|
||||
// coup et le coup joué, une erreur, réévalués à 2 plis ; les suivants
|
||||
// restés à 1 pli, puis à 0, écartés par les filtres.
|
||||
assert.deepEqual(analysis.ranking[0], analysis.best);
|
||||
assert.deepEqual(analysis.ranking[1], analysis.played);
|
||||
assert.deepEqual(
|
||||
analysis.ranking.map((move) => move.ply),
|
||||
[2, 2, 1, 1, 1, 0, 0, 0, 0, 0],
|
||||
);
|
||||
});
|
||||
|
||||
test("ouverture 3-1 jouée 8/5 6/5 : un seul coup passe le filtre, tout reste à 1 pli", async () => {
|
||||
const engine = await loadNodeEngine();
|
||||
const analysis = await engine.analyseMove(
|
||||
START_POSITION,
|
||||
centered,
|
||||
[3, 1],
|
||||
"4HPwATCwZ/ABMA",
|
||||
2,
|
||||
);
|
||||
assert.ok(analysis !== null);
|
||||
assert.equal(analysis.rank, 1);
|
||||
assert.deepEqual(analysis.ranking[0], analysis.best);
|
||||
assert.deepEqual(analysis.best, analysis.played);
|
||||
assert.deepEqual(
|
||||
analysis.ranking.slice(0, 5).map((move) => move.ply),
|
||||
[1, 1, 1, 1, 1],
|
||||
);
|
||||
});
|
||||
|
||||
test("sans coup possible, il n'y a rien à analyser", async () => {
|
||||
|
||||
@@ -73,7 +73,6 @@ function fakeEngine(overrides: Partial<Engine> = {}): Engine {
|
||||
};
|
||||
},
|
||||
analyseMove: async () => null,
|
||||
rankMoves: async () => [],
|
||||
analyseCube: async () => ({
|
||||
analysed: false,
|
||||
equities: null,
|
||||
|
||||
@@ -239,7 +239,97 @@ test("prompt d'un coup : la position, les identifiants et le top 3 du moteur, m
|
||||
assert.ok(shown.includes(`${row.move.notation}|${formatEquity(row.loss)}`));
|
||||
}
|
||||
assert.ok(prompt.includes(`1. ${advice.best.notation}`), prompt);
|
||||
assert.ok(prompt.includes("analyse à 2 plis"), prompt);
|
||||
assert.match(prompt, /\(analyse à [12] plis?\)/);
|
||||
}
|
||||
});
|
||||
|
||||
/** Une équité écrite (« −0,212 ») proche d'une équité de gnubg de bureau :
|
||||
* à l'arrondi près, plus la tolérance des positions de référence (0,002 ;
|
||||
* tests/reference/README.md), le moteur WebAssembly ne calculant pas au
|
||||
* bit près comme gnubg de bureau (ADR 0005). */
|
||||
function assertNear(text: string, value: number, message: string): void {
|
||||
const written = Number(text.replace("−", "-").replace("+", "").replace(",", "."));
|
||||
assert.ok(Math.abs(written - value) <= 0.0005 + 0.002, `${text} ≠ ${value}\n${message}`);
|
||||
}
|
||||
|
||||
/** Le niveau du titre du classement, et chaque ligne du classement : coup,
|
||||
* équité, écart, niveau propre (null : celui du titre). */
|
||||
function rankingOfPrompt(prompt: string) {
|
||||
const title = /^Classement de GNU Backgammon \(analyse à (\d) plis?\)/m.exec(prompt);
|
||||
assert.ok(title, prompt);
|
||||
const lines = [
|
||||
...prompt.matchAll(
|
||||
/^(\d+)\. (.+?)( \(mon coup\))? : équité (\S+), écart (\S+?)(?:, évalué à (\d) plis?)?$/gm,
|
||||
),
|
||||
].map((m) => ({
|
||||
rank: Number(m[1]),
|
||||
notation: m[2],
|
||||
mine: m[3] !== undefined,
|
||||
equity: m[4],
|
||||
loss: m[5],
|
||||
ply: m[6] === undefined ? null : Number(m[6]),
|
||||
}));
|
||||
return { ply: Number(title[1]), lines };
|
||||
}
|
||||
|
||||
test("prompt d'un coup : le niveau du titre est celui des lignes, les équités celles de l'analyse", async () => {
|
||||
const engine = await loadNodeEngine();
|
||||
// gnubg de bureau, analyse de partie à 2 plis (tests/reference/analysis/).
|
||||
const reference = (id: string) =>
|
||||
JSON.parse(readFileSync(new URL(`tests/reference/analysis/${id}.json`, root), "utf8"))
|
||||
.plies["2"];
|
||||
for (const [dice, move, id] of [
|
||||
// Erreur : gnubg réévalue à 2 plis le meilleur coup et le coup joué ;
|
||||
// les autres restent au niveau où ses filtres les ont laissés.
|
||||
[[3, 1], "24/23 13/10", "move-opening-31-error"],
|
||||
// Meilleur coup : un seul coup passe le filtre de 1 pli, gnubg
|
||||
// s'arrête là, tout le classement est à 1 pli.
|
||||
[[3, 1], "8/5 6/5", "move-opening-31-best"],
|
||||
[[6, 5], "13/8 13/7", "move-opening-65-error"],
|
||||
] as const) {
|
||||
const played = await playOpening(engine, dice, move);
|
||||
const advice = await adviceOn(engine, played);
|
||||
assert.ok(advice.kind === "move");
|
||||
played.game.recordAdvice(advice);
|
||||
const position = await positionBefore(engine, played.game.journal, played.eventId);
|
||||
const prompt = decisionPrompt(templates, phrases, {
|
||||
journal: played.game.journal,
|
||||
advice,
|
||||
position,
|
||||
});
|
||||
const { ply, lines } = rankingOfPrompt(prompt);
|
||||
const expected = reference(id);
|
||||
|
||||
// Le titre dit le niveau du meilleur coup, celui que gnubg de bureau
|
||||
// donne ; une ligne sans niveau propre est à ce niveau, une ligne
|
||||
// avec un niveau propre, à un niveau inférieur.
|
||||
assert.equal(ply, expected.ranking[0].ply, prompt);
|
||||
assert.equal(ply, advice.best.ply);
|
||||
assert.ok(lines.length >= 3, prompt);
|
||||
for (const line of lines) {
|
||||
const reference = expected.ranking.find(
|
||||
(m: { move: string }) => m.move === line.notation,
|
||||
);
|
||||
assert.ok(reference, `${line.notation} absent du classement de gnubg`);
|
||||
assert.equal(line.ply ?? ply, reference.ply, `${line.notation}\n${prompt}`);
|
||||
assert.ok(line.ply === null || line.ply < ply, prompt);
|
||||
assertNear(line.equity, reference.equity, `${line.notation}\n${prompt}`);
|
||||
}
|
||||
|
||||
// Les équités et l'écart du meilleur coup et du vôtre sont ceux de
|
||||
// l'analyse, que disent la feuille du maître et l'écran d'analyse.
|
||||
const best = lines[0];
|
||||
const mine = lines.find((line) => line.mine);
|
||||
assert.ok(best && mine, prompt);
|
||||
assert.equal(best.notation, advice.best.notation);
|
||||
assert.equal(best.equity, formatEquity(advice.best.equity));
|
||||
assert.equal(best.loss, formatEquity(0));
|
||||
assert.equal(mine.notation, advice.played.notation);
|
||||
assert.equal(mine.rank, advice.rank);
|
||||
assert.equal(mine.equity, formatEquity(advice.played.equity));
|
||||
assert.equal(mine.loss, formatEquity(advice.equityLoss));
|
||||
assertNear(mine.loss, expected.equity_loss, prompt);
|
||||
assert.ok(shownRanking(advice).includes(`${mine.notation}|${mine.loss}`));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -222,6 +222,57 @@ test("les identifiants de partie sont distincts", () => {
|
||||
assert.match(newGameId(), /^[0-9a-z]+-[0-9a-z]{6}$/);
|
||||
});
|
||||
|
||||
// Régression du test navigateur « mes parties » sous Firefox (disque lent) :
|
||||
// la nouvelle partie n'est pas perdue quand son enregistrement attend
|
||||
// derrière ceux, lents, de l'analyse de la partie finie ; seule une lecture
|
||||
// directe du stockage, hors de la file, peut la précéder.
|
||||
test("stockage lent : la nouvelle partie est enregistrée après l'analyse de l'ancienne", async () => {
|
||||
const storage = memoryStorage();
|
||||
const raw = createKeyValueBackend(storage);
|
||||
const pause = () => new Promise((resolve) => setTimeout(resolve, 30));
|
||||
const slow = {
|
||||
...raw,
|
||||
write: async (...args: Parameters<typeof raw.write>) => {
|
||||
await pause();
|
||||
await raw.write(...args);
|
||||
},
|
||||
setCurrentId: async (id: string) => {
|
||||
await pause();
|
||||
await raw.setCurrentId(id);
|
||||
},
|
||||
};
|
||||
const games = createGameLibrary(slow, { now: clock() });
|
||||
const engine = {} as Engine;
|
||||
const session = createGameSession(games, {
|
||||
start: () => Game.start(engine, { dice: fixedDice([5, 2]) }),
|
||||
resume: (j) => Game.resume(engine, j),
|
||||
});
|
||||
const finished = session.newGame();
|
||||
const finishedId = session.shownId as string;
|
||||
session.save(finished.journal);
|
||||
assert.equal((await games.current())?.summary.id, finishedId);
|
||||
|
||||
// L'analyse de la partie finie s'enregistre en arrière-plan, puis
|
||||
// « Rejouer » : nouvelle partie, enregistrée sans attendre.
|
||||
for (let i = 0; i < 5; i++) {
|
||||
void games.saveAnalyses(finishedId, finished.journal, { level: "2ply" });
|
||||
}
|
||||
const next = session.newGame();
|
||||
session.save(next.journal);
|
||||
const nextId = session.shownId as string;
|
||||
assert.notEqual(nextId, finishedId);
|
||||
|
||||
// Lue directement, tout de suite : encore l'ancienne partie.
|
||||
assert.equal(await raw.currentId(), finishedId);
|
||||
// Lue par la bibliothèque, après les écritures en attente : la nouvelle,
|
||||
// avec son journal ; l'ancienne reste là.
|
||||
const current = await games.current();
|
||||
assert.equal(current?.summary.id, nextId);
|
||||
assert.deepEqual(current?.journal, next.journal);
|
||||
assert.equal(await raw.currentId(), nextId);
|
||||
assert.deepEqual((await games.load(finishedId))?.journal, finished.journal);
|
||||
});
|
||||
|
||||
test("session : « Reprendre l'ancienne » par l'identifiant, et reprise d'une branche", async () => {
|
||||
const games = library();
|
||||
// Game.start et Game.resume n'interrogent pas le moteur.
|
||||
|
||||
@@ -184,3 +184,21 @@ a plus besoin (`app/src/ui/review-runner.ts`).
|
||||
La bibliothèque gagne `saveAnalyses`, qui enregistre le journal analysé
|
||||
d'une partie sans en faire la partie en cours ni changer la date de sa
|
||||
dernière action (l'ordre de « Mes parties »).
|
||||
|
||||
### 2026-11-11 — Version 2 des conseils : le classement de l'analyse
|
||||
|
||||
Le classement des coups d'un conseil (`ranking`) venait d'un second appel au
|
||||
moteur, comme la commande « hint » de gnubg, alors que le coup joué et le
|
||||
meilleur coup venaient de l'analyse de partie. Les deux listes diffèrent :
|
||||
quand le coup joué est une erreur, l'analyse réévalue le meilleur coup et le
|
||||
coup joué au niveau demandé, ce que « hint » ne fait pas quand un seul coup
|
||||
passe ses filtres (ouverture 3-1 jouée 24/23 13/10 : 8/5 6/5 à +0,200 à
|
||||
2 plis dans l'analyse, +0,204 à 1 pli dans « hint »). Le classement vient
|
||||
désormais de l'analyse même (`engine_analyse_move`, champ `ranking`), comme
|
||||
le panneau d'analyse de gnubg de bureau.
|
||||
|
||||
- `ADVICE_VERSION` passe à 2 (`app/src/domain/game/advice.ts`). Selon la
|
||||
règle de l'amendement du 2026-11-10, un conseil de la version 1 est oublié
|
||||
à la reprise et recalculé en arrière-plan ; les analyses de l'ordinateur
|
||||
(`review.computer`), du même format, aussi.
|
||||
- Ni les événements (version 2) ni le schéma IndexedDB ne changent.
|
||||
|
||||
@@ -236,8 +236,18 @@ l'ordre, chacune avec une pastille ronde à son numéro :
|
||||
|
||||
Toutes les leçons restent ouvertes : l'ordre est conseillé, jamais imposé.
|
||||
Les écrans d'une leçon suivent la forme du tutoriel (barre de progression,
|
||||
« Passer », « Retour », « Continuer », écrans « à vous » contrôlés par le
|
||||
moteur).
|
||||
« Quitter », « Retour », « Continuer », écrans « à vous » contrôlés par le
|
||||
moteur), aux adresses `#/lecons/<n>/<écran>`, `…/a-vous` et `…/fin`.
|
||||
|
||||
- Une leçon est **terminée** quand tous ses exercices sont réussis.
|
||||
- « Retour » et « Continuer » restent **en bas de l'écran**, sous le pouce,
|
||||
comme dans le tutoriel, quelle que soit la longueur du texte.
|
||||
- « Revoir » (rejouer le coup illustré) se place sur la ligne du titre, à
|
||||
droite, en portrait comme en paysage : le texte garde toute la largeur.
|
||||
- En paysage, la colonne de droite garde la ligne `caption` « Leçon 1 · 1 sur
|
||||
4 · À vous » au-dessus du titre.
|
||||
- Les titres et les textes n'emploient que les mots du glossaire : « point »
|
||||
et « flèche », jamais « case ».
|
||||
|
||||
## 1h. Progression (`#/progression`)
|
||||
|
||||
|
||||
@@ -54,5 +54,5 @@ Repères communs à plusieurs gabarits :
|
||||
| `{diagramme}` | le plateau en texte : vos pions X, ceux de l'ordinateur O, dans votre numérotation |
|
||||
| `{score}` | « partie libre » ou le score du match |
|
||||
| `{videau}` | sa valeur et qui le possède |
|
||||
| `{niveau_eval}` | le niveau d'évaluation de GNU Backgammon, « analyse à 2 plis » |
|
||||
| `{niveau_eval}` | le niveau d'évaluation de GNU Backgammon, « analyse à 2 plis » ; pour un classement de coups, celui auquel le moteur a évalué le meilleur coup (voir [coup.md](coup.md)) |
|
||||
| `{langue}` | la langue de la réponse : « français » |
|
||||
|
||||
@@ -12,7 +12,15 @@ Repères propres à ce gabarit :
|
||||
| `{coup_joue}` | votre coup, dans la notation de GNU Backgammon |
|
||||
| `{appreciation}` | le mot du maître : « Bon coup », « Imprécision », « Erreur » ou « Grosse erreur » |
|
||||
| `{rang}` | « le meilleur coup » ou « le 4e choix sur 15 coups possibles » |
|
||||
| `{classement}` | le classement du moteur : les trois meilleurs coups, et le vôtre s'il n'en est pas, avec leur équité et leur écart au meilleur, comme dans « Voir les chiffres du moteur » |
|
||||
| `{classement}` | le classement de l'analyse de votre coup : les trois meilleurs coups, et le vôtre s'il n'en est pas, avec leur équité et leur écart au meilleur, comme dans « Voir les chiffres du moteur » |
|
||||
|
||||
Le niveau du titre (`{niveau_eval}`) est celui auquel GNU Backgammon a
|
||||
évalué le meilleur coup. C'est d'ordinaire le niveau de l'analyse (2 plis),
|
||||
mais le moteur s'arrête plus tôt quand un seul coup passe ses filtres : à
|
||||
l'ouverture 3-1 jouée 8/5 6/5, tout le classement est à 1 pli. Les coups que
|
||||
ses filtres ont écartés gardent l'évaluation d'un niveau inférieur, et leur
|
||||
ligne le dit (« évalué à 1 pli »). Les équités et l'écart de votre coup sont
|
||||
ceux de l'analyse, que disent la feuille du maître et l'écran d'analyse.
|
||||
|
||||
## Gabarit
|
||||
|
||||
|
||||
+10
-2
@@ -43,7 +43,8 @@
|
||||
* analyse du coup joué (POSITION_JOUÉE :
|
||||
* position après le coup, vue du joueur
|
||||
* qui l'a joué, comme position_id dans
|
||||
* moves et legal)
|
||||
* moves et legal), avec le classement
|
||||
* de l'analyse (dix coups au plus)
|
||||
* analysecube POSITION_ID MATCH_ID DÉCISION
|
||||
* analyse d'une décision de videau :
|
||||
* no_double ou double (joueur au trait),
|
||||
@@ -684,7 +685,7 @@ run_analyse_move_command(const options *opt)
|
||||
const char *played_id = opt->args[3];
|
||||
engine_move_analysis a;
|
||||
engine_status status;
|
||||
int die1, die2;
|
||||
int die1, die2, i;
|
||||
|
||||
if (opt->arg_count != 4)
|
||||
return usage_error("arguments attendus : POSITION_ID MATCH_ID DÉS POSITION_JOUÉE");
|
||||
@@ -705,6 +706,13 @@ run_analyse_move_command(const options *opt)
|
||||
printf(", \"best\": ");
|
||||
print_move(&a.best, 1);
|
||||
}
|
||||
printf(", \"ranking\": [");
|
||||
for (i = 0; i < a.ranking_count; i++) {
|
||||
if (i > 0)
|
||||
printf(", ");
|
||||
print_move(&a.ranking[i], 1);
|
||||
}
|
||||
printf("]");
|
||||
printf(", \"equity_loss\": " NUMBER ", \"skill\": \"%s\"}\n", a.equity_loss,
|
||||
skill_names[a.skill]);
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
+31
-9
@@ -512,6 +512,8 @@ engine_level_best_moves(const char *position_id, const char *match_id, int die1,
|
||||
|
||||
/* ------------------------------------------------- Analyse d'un coup joué */
|
||||
|
||||
static float luck_analysis(const char *position_id, position *pos, int die1, int die2);
|
||||
|
||||
engine_status
|
||||
engine_analyse_move(const char *position_id, const char *match_id, int die1, int die2,
|
||||
const char *played_position_id, int ply, engine_move_analysis *analysis)
|
||||
@@ -546,6 +548,17 @@ engine_analyse_move(const char *position_id, const char *match_id, int die1, int
|
||||
* premier.
|
||||
*/
|
||||
memset(analysis, 0, sizeof *analysis);
|
||||
/*
|
||||
* gnubg analyse la chance du jet juste avant le coup (AnalyzeMove,
|
||||
* l. 679-682), à 0 pli sans élagage, et garde ces évaluations dans son
|
||||
* cache, qui ne distingue pas une évaluation à 0 pli avec ou sans
|
||||
* réseaux d'élagage : le tri à 0 pli de FindnSaveBestMoves les reprend
|
||||
* ensuite, ce qui change l'équité des coups restés à 0 pli, et parfois
|
||||
* ceux que les filtres retiennent. Faire de même, sans garder le
|
||||
* résultat, donne le classement de gnubg de bureau.
|
||||
*/
|
||||
if (luck_analysis(position_id, &pos, die1, die2) == ERR_VAL)
|
||||
return ENGINE_ERROR_INTERNAL;
|
||||
if (FindnSaveBestMoves(&list, die1, die2, (ConstTanBoard) pos.board, &played_key, TRUE,
|
||||
DOUBTFUL_THRESHOLD, &pos.cube, &context,
|
||||
aaamfMoveFilterSettings[0]) < 0)
|
||||
@@ -565,6 +578,10 @@ engine_analyse_move(const char *position_id, const char *match_id, int die1, int
|
||||
status = ENGINE_OK;
|
||||
break;
|
||||
}
|
||||
/* Le classement : le début de la même liste, dans l'ordre de gnubg. */
|
||||
for (i = 0; i < list.cMoves && i < ENGINE_ANALYSIS_RANKING; i++)
|
||||
fill_move(&analysis->ranking[i], &list.amMoves[i], &pos, 1);
|
||||
analysis->ranking_count = (int) i;
|
||||
g_free(list.amMoves);
|
||||
return status;
|
||||
}
|
||||
@@ -1005,13 +1022,25 @@ luck_first(ConstTanBoard board, int i0, int j0, cubeinfo *cube)
|
||||
return equities[i0][j0] - mean / 30.0f;
|
||||
}
|
||||
|
||||
/* LuckAnalysis (analysis.c l. 252-268) : dés rangés du plus fort au plus
|
||||
* faible ; LuckFirst à la position de départ hors doubles. */
|
||||
static float
|
||||
luck_analysis(const char *position_id, position *pos, int die1, int die2)
|
||||
{
|
||||
int high = (die1 > die2 ? die1 : die2) - 1;
|
||||
int low = (die1 > die2 ? die2 : die1) - 1;
|
||||
|
||||
if (strcmp(position_id, START_POSITION_ID) == 0 && high != low)
|
||||
return luck_first((ConstTanBoard) pos->board, high, low, &pos->cube);
|
||||
return luck_normal((ConstTanBoard) pos->board, high, low, &pos->cube);
|
||||
}
|
||||
|
||||
engine_status
|
||||
engine_luck_of(const char *position_id, const char *match_id, int die1, int die2,
|
||||
engine_luck *luck)
|
||||
{
|
||||
position pos;
|
||||
engine_status status;
|
||||
int high, low;
|
||||
float value;
|
||||
|
||||
status = decode_position(position_id, match_id, &pos);
|
||||
@@ -1021,14 +1050,7 @@ engine_luck_of(const char *position_id, const char *match_id, int die1, int die2
|
||||
return status;
|
||||
start_evaluation();
|
||||
|
||||
/* LuckAnalysis (analysis.c l. 252-268) : dés rangés du plus fort au plus
|
||||
* faible ; LuckFirst à la position de départ hors doubles. */
|
||||
high = (die1 > die2 ? die1 : die2) - 1;
|
||||
low = (die1 > die2 ? die2 : die1) - 1;
|
||||
if (strcmp(position_id, START_POSITION_ID) == 0 && high != low)
|
||||
value = luck_first((ConstTanBoard) pos.board, high, low, &pos.cube);
|
||||
else
|
||||
value = luck_normal((ConstTanBoard) pos.board, high, low, &pos.cube);
|
||||
value = luck_analysis(position_id, &pos, die1, die2);
|
||||
if (value == ERR_VAL)
|
||||
return ENGINE_ERROR_INTERNAL;
|
||||
luck->luck = value;
|
||||
|
||||
+21
-1
@@ -207,6 +207,10 @@ typedef struct {
|
||||
* (double_take <= double_pass) */
|
||||
} engine_cube;
|
||||
|
||||
/* Coups gardés dans le classement d'une analyse : les dix premiers, comme
|
||||
* les positions de référence (tests/reference/README.md). */
|
||||
#define ENGINE_ANALYSIS_RANKING 10
|
||||
|
||||
/* Analyse d'un coup joué, comme l'analyse de partie de gnubg. */
|
||||
typedef struct {
|
||||
int move_count; /* nombre de coups légaux (0 : aucun coup
|
||||
@@ -218,6 +222,14 @@ typedef struct {
|
||||
float equity_loss; /* équité du coup joué moins celle du meilleur :
|
||||
* 0 ou négative */
|
||||
engine_skill skill; /* appréciation de gnubg pour cette perte */
|
||||
/* Le classement de l'analyse, du meilleur au moins bon : les premiers
|
||||
* coups de la liste que gnubg garde pour l'analyse de partie (ce que
|
||||
* montre son panneau d'analyse), chacun au niveau où gnubg l'a
|
||||
* évalué. Le meilleur coup et le coup joué y ont les mêmes évaluations
|
||||
* que best et played ; le coup joué n'y figure que s'il est parmi les
|
||||
* premiers. */
|
||||
engine_move ranking[ENGINE_ANALYSIS_RANKING];
|
||||
int ranking_count;
|
||||
} engine_move_analysis;
|
||||
|
||||
/* Analyse d'une décision de videau, comme l'analyse de partie de gnubg. */
|
||||
@@ -452,7 +464,15 @@ engine_luck_rating engine_luck_rating_of(float luck);
|
||||
* Analyse le coup joué par le joueur au trait avec le jet die1, die2, au
|
||||
* niveau ply, comme l'analyse de partie de gnubg (AnalyzeMove dans
|
||||
* analysis.c : FindnSaveBestMoves avec le coup joué, seuil « douteux »,
|
||||
* filtre « Normal », sans RefreshMoveList). played_position_id est la
|
||||
* filtre « Normal », sans RefreshMoveList), classement compris.
|
||||
*
|
||||
* Niveaux : gnubg n'évalue au niveau ply que les coups retenus par ses
|
||||
* filtres ; les autres gardent l'évaluation d'un niveau inférieur (champ
|
||||
* ply de chaque coup, même convention que gnubg de bureau : 0 = statique).
|
||||
* Si un seul coup passe le filtre du niveau ply - 1, gnubg s'arrête là, et
|
||||
* tout le classement reste à ce niveau, sauf quand le coup joué est une
|
||||
* erreur (écart au meilleur supérieur au seuil « douteux ») : le meilleur
|
||||
* coup et le coup joué sont alors réévalués au niveau ply. played_position_id est la
|
||||
* position après le coup, vue du joueur qui vient de le jouer (champ
|
||||
* position_id d'engine_move). ENGINE_ERROR_MOVE si ce n'est pas un coup
|
||||
* légal ; si aucun coup n'est possible, move_count vaut 0.
|
||||
|
||||
+89
-11
@@ -164,6 +164,45 @@ async function readJournal() {
|
||||
);
|
||||
}
|
||||
|
||||
/** L'identifiant de la partie en cours enregistrée dans IndexedDB
|
||||
* (magasin settings, clé « current »), ou null. */
|
||||
async function savedGameId() {
|
||||
return page.evaluate(
|
||||
() =>
|
||||
new Promise((resolve, reject) => {
|
||||
const open = indexedDB.open("ustagammon");
|
||||
open.onerror = () => reject(open.error);
|
||||
open.onsuccess = () => {
|
||||
const db = open.result;
|
||||
const current = db.transaction("settings").objectStore("settings").get("current");
|
||||
current.onsuccess = () => {
|
||||
db.close();
|
||||
resolve(current.result?.value ?? null);
|
||||
};
|
||||
};
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attend que la partie en cours enregistrée ne soit plus oldId, et renvoie
|
||||
* le temps d'attente en ms (null au bout de 30 s). L'application enregistre
|
||||
* sans attendre l'écriture, dans une file (app/src/storage/game-library.ts) :
|
||||
* sous Firefox, sur un disque lent et chargé, l'enregistrement de la
|
||||
* nouvelle partie peut attendre derrière ceux de l'analyse de la partie
|
||||
* finie, et une lecture directe d'IndexedDB trouver encore l'ancienne.
|
||||
*/
|
||||
async function waitForSavedGameOtherThan(oldId) {
|
||||
const start = Date.now();
|
||||
while (Date.now() - start < 30_000) {
|
||||
if ((await savedGameId()) !== oldId) {
|
||||
return Date.now() - start;
|
||||
}
|
||||
await sleep(100);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Le nombre de parties enregistrées dans IndexedDB. */
|
||||
async function savedGameCount() {
|
||||
await savedJournal(); // les enregistrements en cours terminés
|
||||
@@ -587,17 +626,41 @@ async function checkTraces() {
|
||||
* (les dés en ont une de chaque côté du plateau), chacune un bouton
|
||||
* accessible d'au moins 44 px de côté. Renvoie le locator de la zone index. */
|
||||
async function touchTarget(action, label, count = 1, index = 0) {
|
||||
const zones = page.locator(`[data-action="${action}"]`);
|
||||
check((await zones.count()) === count,
|
||||
`zone ${action} : ${await zones.count()} sur le plateau, ${count} attendues`);
|
||||
for (const zone of await zones.all()) {
|
||||
check((await zone.getAttribute("role")) === (action === "menu" ? "link" : "button") &&
|
||||
(await zone.getAttribute("aria-label")) === label, `zone ${action} sans rôle accessible`);
|
||||
const box = await zone.boundingBox();
|
||||
check(box.width >= 44 && box.height >= 44,
|
||||
`zone ${action} trop petite : ${Math.round(box.width)} x ${Math.round(box.height)} px`);
|
||||
// Les zones sont relevées d'un coup dans la page, où le plateau ne peut
|
||||
// pas se redessiner entre deux lectures : mesurées une à une depuis
|
||||
// Playwright (boundingBox), une zone remplacée entre-temps par un
|
||||
// nouveau dessin du plateau n'avait plus de boîte (null, vu sous
|
||||
// Firefox). On attend l'état observable : le nombre de zones attendu,
|
||||
// toutes dessinées (5 s au plus).
|
||||
const read = () =>
|
||||
page.evaluate(
|
||||
(name) =>
|
||||
[...document.querySelectorAll(`[data-action="${name}"]`)].map((zone) => {
|
||||
const box = zone.getBoundingClientRect();
|
||||
return {
|
||||
role: zone.getAttribute("role"),
|
||||
label: zone.getAttribute("aria-label"),
|
||||
width: box.width,
|
||||
height: box.height,
|
||||
};
|
||||
}),
|
||||
action,
|
||||
);
|
||||
let zones = await read();
|
||||
const start = Date.now();
|
||||
while ((zones.length !== count || zones.some((z) => z.width === 0 && z.height === 0)) &&
|
||||
Date.now() - start < 5000) {
|
||||
await sleep(100);
|
||||
zones = await read();
|
||||
}
|
||||
return zones.nth(index);
|
||||
check(zones.length === count, `zone ${action} : ${zones.length} sur le plateau, ${count} attendues`);
|
||||
for (const zone of zones) {
|
||||
check(zone.role === (action === "menu" ? "link" : "button") && zone.label === label,
|
||||
`zone ${action} sans rôle accessible`);
|
||||
check(zone.width >= 44 && zone.height >= 44,
|
||||
`zone ${action} trop petite : ${Math.round(zone.width)} x ${Math.round(zone.height)} px`);
|
||||
}
|
||||
return page.locator(`[data-action="${action}"]`).nth(index);
|
||||
}
|
||||
|
||||
/** Le nombre de dés montrés, et de dés estompés (déjà joués). */
|
||||
@@ -1026,6 +1089,7 @@ async function rematch() {
|
||||
* immédiat, bandeau 10 s. Renvoie le journal de la partie quittée. */
|
||||
async function newGameFromMenu() {
|
||||
const before = await savedJournal();
|
||||
const beforeId = await savedGameId();
|
||||
const started = () =>
|
||||
page.evaluate(() => JSON.parse(localStorage.getItem("ustagammon.settings") ?? "{}").gamesStarted);
|
||||
const startedBefore = await started();
|
||||
@@ -1038,6 +1102,8 @@ async function newGameFromMenu() {
|
||||
`bandeau : « ${await page.textContent("#banner")} »`);
|
||||
check(!(await page.isVisible("#menu")), "le menu reste ouvert après « Nouvelle partie »");
|
||||
await waitForPlayer();
|
||||
check((await waitForSavedGameOtherThan(beforeId)) !== null,
|
||||
"« Nouvelle partie » : la partie n'a pas été enregistrée (30 s)");
|
||||
const after = await savedJournal();
|
||||
// (Les identifiants d'événements se suivent : une partie neuve peut avoir
|
||||
// la même tête que la précédente ; on compte les parties commencées.)
|
||||
@@ -1617,11 +1683,17 @@ step("mes parties");
|
||||
// recharger la page.
|
||||
{
|
||||
const finished = await savedJournal();
|
||||
const finishedId = await savedGameId();
|
||||
await rematch();
|
||||
await waitForPlayer();
|
||||
// L'état observable, et non un délai : la nouvelle partie devient la
|
||||
// partie en cours enregistrée.
|
||||
const waited = await waitForSavedGameOtherThan(finishedId);
|
||||
check(waited !== null, "la nouvelle partie n'a pas été enregistrée (30 s)");
|
||||
log(`nouvelle partie enregistrée ${waited} ms après la main au joueur`);
|
||||
const second = await savedJournal();
|
||||
check(second.head !== finished.head || second.events.length !== finished.events.length,
|
||||
"la nouvelle partie n'a pas été enregistrée");
|
||||
"la nouvelle partie enregistrée a le journal de la partie finie");
|
||||
await openMenu();
|
||||
await page.click("#menu-games");
|
||||
await page.waitForSelector("#games-veil:not([hidden]) .games-item");
|
||||
@@ -1744,6 +1816,12 @@ if (offline === "roll") {
|
||||
await page.click("#roll");
|
||||
await waitForPlayer();
|
||||
}
|
||||
// Si la partie reprise est finie, son bilan se rouvre au rechargement et
|
||||
// couvre l'écran (c'est voulu) : on le ferme avant d'ouvrir l'aide.
|
||||
if (await page.locator("#summary-veil").isVisible()) {
|
||||
await page.click("#summary-close");
|
||||
await page.waitForSelector("#summary-veil", { state: "hidden" });
|
||||
}
|
||||
await page.click("#help-button");
|
||||
await page.waitForSelector(".glossary dt", { state: "attached" });
|
||||
await page.click("#help-back");
|
||||
|
||||
@@ -256,7 +256,11 @@ Un fichier de résultat reprend le cas, avec `type` (`move`, `cube` ou
|
||||
gnubg plus 1), `played_equity`, `best_move`, `best_equity`, `equity_loss`
|
||||
(perte du coup joué, 0 ou négative : statistique « error-skill » de gnubg
|
||||
changée de signe), `skill` (`none`, `doubtful`, `bad`, `very_bad`) et
|
||||
`ranking` (les dix premiers coups, et jusqu'au coup joué) ; plus, une fois
|
||||
`ranking` (les dix premiers coups, et jusqu'au coup joué, chacun avec
|
||||
`move`, `ply` et `equity` : comme pour `hint`, `ply` est le niveau auquel
|
||||
gnubg a réellement évalué le coup ; à la différence de `hint`, l'analyse
|
||||
réévalue au niveau demandé le meilleur coup et le coup joué quand celui-ci
|
||||
est une erreur, au-delà du seuil « douteux ») ; plus, une fois
|
||||
pour toutes, `luck` : `value` (chance du jet) et `rating`
|
||||
(`very_unlucky`, `unlucky`, `none`, `lucky`, `very_lucky`) ;
|
||||
- **videau** : `analysed` (gnubg analyse-t-il cette décision ? pas à la
|
||||
@@ -337,6 +341,10 @@ Pour chaque analyse (`engine-cli analysemove`, `analysecube`, `luck`, et
|
||||
équité que le coup joué, à la tolérance près : remarque), même meilleur coup
|
||||
sauf égalité, et `played_equity`, `best_equity`, `equity_loss` à la
|
||||
tolérance près ;
|
||||
- classement de l'analyse (`ranking`, dix coups au plus) : chaque coup de
|
||||
gnubg présent (sauf égalité avec le dixième), au même `ply` et à la même
|
||||
équité à la tolérance près ; son premier coup et le coup joué y sont
|
||||
exactement `best` et `played` ;
|
||||
- chance : `value` à la tolérance près ;
|
||||
- videau : même `analysed`, même décision et équités à la tolérance près,
|
||||
`equity_loss` à la tolérance près ;
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"ranking": [
|
||||
{
|
||||
"move": "6/off(2) 4/off 1/off",
|
||||
"ply": 0,
|
||||
"equity": 0.8888838
|
||||
}
|
||||
]
|
||||
@@ -42,6 +43,7 @@
|
||||
"ranking": [
|
||||
{
|
||||
"move": "6/off(2) 4/off 1/off",
|
||||
"ply": 0,
|
||||
"equity": 0.8888838
|
||||
}
|
||||
]
|
||||
|
||||
@@ -25,438 +25,547 @@
|
||||
"ranking": [
|
||||
{
|
||||
"move": "13/10 6/3 4/1*(2)",
|
||||
"ply": 0,
|
||||
"equity": 0.9465638
|
||||
},
|
||||
{
|
||||
"move": "8/5 6/3 4/1*(2)",
|
||||
"ply": 0,
|
||||
"equity": 0.9422797
|
||||
},
|
||||
{
|
||||
"move": "13/7 13/10 6/3",
|
||||
"ply": 0,
|
||||
"equity": 0.94128984
|
||||
},
|
||||
{
|
||||
"move": "13/7 8/5 6/3",
|
||||
"ply": 0,
|
||||
"equity": 0.9370698
|
||||
},
|
||||
{
|
||||
"move": "13/10(2) 7/4 6/3",
|
||||
"ply": 0,
|
||||
"equity": 0.93615985
|
||||
},
|
||||
{
|
||||
"move": "13/10(2) 8/5 6/3",
|
||||
"ply": 0,
|
||||
"equity": 0.9330082
|
||||
},
|
||||
{
|
||||
"move": "13/10 8/5 7/4 6/3",
|
||||
"ply": 0,
|
||||
"equity": 0.932737
|
||||
},
|
||||
{
|
||||
"move": "13/10 7/1* 6/3",
|
||||
"ply": 0,
|
||||
"equity": 0.9313148
|
||||
},
|
||||
{
|
||||
"move": "8/5 7/1* 6/3",
|
||||
"ply": 0,
|
||||
"equity": 0.93129885
|
||||
},
|
||||
{
|
||||
"move": "13/4 6/3",
|
||||
"ply": 0,
|
||||
"equity": 0.92980635
|
||||
},
|
||||
{
|
||||
"move": "13/10 8/5(2) 6/3",
|
||||
"ply": 0,
|
||||
"equity": 0.9264537
|
||||
},
|
||||
{
|
||||
"move": "13/10(3) 6/3",
|
||||
"ply": 0,
|
||||
"equity": 0.9262361
|
||||
},
|
||||
{
|
||||
"move": "13/10(2) 4/1*(2)",
|
||||
"ply": 0,
|
||||
"equity": 0.92529553
|
||||
},
|
||||
{
|
||||
"move": "8/5(2) 7/4 6/3",
|
||||
"ply": 0,
|
||||
"equity": 0.9252107
|
||||
},
|
||||
{
|
||||
"move": "7/1* 6/3 4/1",
|
||||
"ply": 0,
|
||||
"equity": 0.92395884
|
||||
},
|
||||
{
|
||||
"move": "13/10 6/3 5/2(2)",
|
||||
"ply": 0,
|
||||
"equity": 0.9204093
|
||||
},
|
||||
{
|
||||
"move": "13/7 4/1*(2)",
|
||||
"ply": 0,
|
||||
"equity": 0.9190106
|
||||
},
|
||||
{
|
||||
"move": "13/7(2)",
|
||||
"ply": 0,
|
||||
"equity": 0.9184398
|
||||
},
|
||||
{
|
||||
"move": "13/10 8/5 4/1*(2)",
|
||||
"ply": 0,
|
||||
"equity": 0.9182539
|
||||
},
|
||||
{
|
||||
"move": "7/4 6/3 5/2(2)",
|
||||
"ply": 0,
|
||||
"equity": 0.91752934
|
||||
},
|
||||
{
|
||||
"move": "13/7 13/10(2)",
|
||||
"ply": 0,
|
||||
"equity": 0.91483337
|
||||
},
|
||||
{
|
||||
"move": "13/10 8/2 6/3",
|
||||
"ply": 0,
|
||||
"equity": 0.912639
|
||||
},
|
||||
{
|
||||
"move": "13/7 13/10 8/5",
|
||||
"ply": 0,
|
||||
"equity": 0.91152656
|
||||
},
|
||||
{
|
||||
"move": "13/10(2) 7/1*",
|
||||
"ply": 0,
|
||||
"equity": 0.9115156
|
||||
},
|
||||
{
|
||||
"move": "13/1*",
|
||||
"ply": 0,
|
||||
"equity": 0.9111869
|
||||
},
|
||||
{
|
||||
"move": "13/10(2) 6/3 4/1*",
|
||||
"ply": 0,
|
||||
"equity": 0.91055506
|
||||
},
|
||||
{
|
||||
"move": "8/2 8/5 6/3",
|
||||
"ply": 0,
|
||||
"equity": 0.9091699
|
||||
},
|
||||
{
|
||||
"move": "8/2 7/4 6/3",
|
||||
"ply": 0,
|
||||
"equity": 0.90897554
|
||||
},
|
||||
{
|
||||
"move": "13/7 8/5(2)",
|
||||
"ply": 0,
|
||||
"equity": 0.90817225
|
||||
},
|
||||
{
|
||||
"move": "13/7 6/3 4/1*",
|
||||
"ply": 0,
|
||||
"equity": 0.90714073
|
||||
},
|
||||
{
|
||||
"move": "13/10(2) 8/5 7/4",
|
||||
"ply": 0,
|
||||
"equity": 0.9070756
|
||||
},
|
||||
{
|
||||
"move": "8/2(2)",
|
||||
"ply": 0,
|
||||
"equity": 0.9065211
|
||||
},
|
||||
{
|
||||
"move": "13/10 8/5 7/1*",
|
||||
"ply": 0,
|
||||
"equity": 0.90571
|
||||
},
|
||||
{
|
||||
"move": "13/10(3) 7/4",
|
||||
"ply": 0,
|
||||
"equity": 0.9040942
|
||||
},
|
||||
{
|
||||
"move": "13/10 8/5 6/3 4/1*",
|
||||
"ply": 0,
|
||||
"equity": 0.90343916
|
||||
},
|
||||
{
|
||||
"move": "8/5(2) 4/1*(2)",
|
||||
"ply": 0,
|
||||
"equity": 0.90325713
|
||||
},
|
||||
{
|
||||
"move": "5/2(2) 4/1*(2)",
|
||||
"ply": 0,
|
||||
"equity": 0.90183336
|
||||
},
|
||||
{
|
||||
"move": "13/10 7/1* 4/1",
|
||||
"ply": 0,
|
||||
"equity": 0.9013542
|
||||
},
|
||||
{
|
||||
"move": "13/4 7/4",
|
||||
"ply": 0,
|
||||
"equity": 0.9010268
|
||||
},
|
||||
{
|
||||
"move": "13/7 5/2(2)",
|
||||
"ply": 0,
|
||||
"equity": 0.90037674
|
||||
},
|
||||
{
|
||||
"move": "8/5(2) 7/1*",
|
||||
"ply": 0,
|
||||
"equity": 0.89956194
|
||||
},
|
||||
{
|
||||
"move": "13/10 8/5(2) 7/4",
|
||||
"ply": 0,
|
||||
"equity": 0.8988826
|
||||
},
|
||||
{
|
||||
"move": "8/2 6/3 5/2",
|
||||
"ply": 0,
|
||||
"equity": 0.8984738
|
||||
},
|
||||
{
|
||||
"move": "13/4 13/10",
|
||||
"ply": 0,
|
||||
"equity": 0.89749956
|
||||
},
|
||||
{
|
||||
"move": "6/3(2) 4/1*(2)",
|
||||
"ply": 0,
|
||||
"equity": 0.8951012
|
||||
},
|
||||
{
|
||||
"move": "13/10(2) 8/5(2)",
|
||||
"ply": 0,
|
||||
"equity": 0.89493525
|
||||
},
|
||||
{
|
||||
"move": "13/7 8/2",
|
||||
"ply": 0,
|
||||
"equity": 0.8946096
|
||||
},
|
||||
{
|
||||
"move": "13/7 6/3(2)",
|
||||
"ply": 0,
|
||||
"equity": 0.89403474
|
||||
},
|
||||
{
|
||||
"move": "7/1* 5/2(2)",
|
||||
"ply": 0,
|
||||
"equity": 0.8924208
|
||||
},
|
||||
{
|
||||
"move": "8/5 7/1* 4/1",
|
||||
"ply": 0,
|
||||
"equity": 0.891961
|
||||
},
|
||||
{
|
||||
"move": "13/10(2) 6/3(2)",
|
||||
"ply": 0,
|
||||
"equity": 0.89077055
|
||||
},
|
||||
{
|
||||
"move": "13/4 8/5",
|
||||
"ply": 0,
|
||||
"equity": 0.8907301
|
||||
},
|
||||
{
|
||||
"move": "8/5(2) 6/3 4/1*",
|
||||
"ply": 0,
|
||||
"equity": 0.8900244
|
||||
},
|
||||
{
|
||||
"move": "13/10(2) 5/2(2)",
|
||||
"ply": 0,
|
||||
"equity": 0.8890666
|
||||
},
|
||||
{
|
||||
"move": "7/4 6/3(3)",
|
||||
"ply": 0,
|
||||
"equity": 0.8890004
|
||||
},
|
||||
{
|
||||
"move": "13/10 7/4 5/2(2)",
|
||||
"ply": 0,
|
||||
"equity": 0.8888954
|
||||
},
|
||||
{
|
||||
"move": "8/2 8/5 7/4",
|
||||
"ply": 0,
|
||||
"equity": 0.8876569
|
||||
},
|
||||
{
|
||||
"move": "13/10(4)",
|
||||
"ply": 0,
|
||||
"equity": 0.88716996
|
||||
},
|
||||
{
|
||||
"move": "7/1* 6/3(2)",
|
||||
"ply": 0,
|
||||
"equity": 0.8856077
|
||||
},
|
||||
{
|
||||
"move": "8/2 4/1*(2)",
|
||||
"ply": 0,
|
||||
"equity": 0.8853629
|
||||
},
|
||||
{
|
||||
"move": "13/10 6/3(3)",
|
||||
"ply": 0,
|
||||
"equity": 0.8853096
|
||||
},
|
||||
{
|
||||
"move": "13/7 6/3 5/2",
|
||||
"ply": 0,
|
||||
"equity": 0.8831558
|
||||
},
|
||||
{
|
||||
"move": "13/10 8/2 7/4",
|
||||
"ply": 0,
|
||||
"equity": 0.8819176
|
||||
},
|
||||
{
|
||||
"move": "7/1* 4/1(2)",
|
||||
"ply": 0,
|
||||
"equity": 0.88113916
|
||||
},
|
||||
{
|
||||
"move": "13/10 7/4 6/3(2)",
|
||||
"ply": 0,
|
||||
"equity": 0.88064635
|
||||
},
|
||||
{
|
||||
"move": "13/10(3) 8/5",
|
||||
"ply": 0,
|
||||
"equity": 0.88049895
|
||||
},
|
||||
{
|
||||
"move": "6/3 5/2(2) 4/1*",
|
||||
"ply": 0,
|
||||
"equity": 0.8790827
|
||||
},
|
||||
{
|
||||
"move": "13/10(2) 8/2",
|
||||
"ply": 0,
|
||||
"equity": 0.8782023
|
||||
},
|
||||
{
|
||||
"move": "6/3 5/2 4/1*(2)",
|
||||
"ply": 0,
|
||||
"equity": 0.8774586
|
||||
},
|
||||
{
|
||||
"move": "8/5 6/3(3)",
|
||||
"ply": 0,
|
||||
"equity": 0.8774268
|
||||
},
|
||||
{
|
||||
"move": "8/2 7/1*",
|
||||
"ply": 0,
|
||||
"equity": 0.8772669
|
||||
},
|
||||
{
|
||||
"move": "13/7 13/10 4/1*",
|
||||
"ply": 0,
|
||||
"equity": 0.87700987
|
||||
},
|
||||
{
|
||||
"move": "13/10 8/5 6/3(2)",
|
||||
"ply": 0,
|
||||
"equity": 0.8757389
|
||||
},
|
||||
{
|
||||
"move": "13/10 8/2 8/5",
|
||||
"ply": 0,
|
||||
"equity": 0.87530077
|
||||
},
|
||||
{
|
||||
"move": "13/10(2) 6/3 5/2",
|
||||
"ply": 0,
|
||||
"equity": 0.87215745
|
||||
},
|
||||
{
|
||||
"move": "13/7 8/5 4/1*",
|
||||
"ply": 0,
|
||||
"equity": 0.87202764
|
||||
},
|
||||
{
|
||||
"move": "8/5 7/4 6/3(2)",
|
||||
"ply": 0,
|
||||
"equity": 0.86896765
|
||||
},
|
||||
{
|
||||
"move": "7/1* 6/3 5/2",
|
||||
"ply": 0,
|
||||
"equity": 0.8687051
|
||||
},
|
||||
{
|
||||
"move": "13/10 7/4 6/3 5/2",
|
||||
"ply": 0,
|
||||
"equity": 0.8679464
|
||||
},
|
||||
{
|
||||
"move": "8/2 6/3 4/1*",
|
||||
"ply": 0,
|
||||
"equity": 0.86750233
|
||||
},
|
||||
{
|
||||
"move": "13/10(2) 8/5 4/1*",
|
||||
"ply": 0,
|
||||
"equity": 0.86701244
|
||||
},
|
||||
{
|
||||
"move": "8/5(2) 6/3(2)",
|
||||
"ply": 0,
|
||||
"equity": 0.86167765
|
||||
},
|
||||
{
|
||||
"move": "13/10(3) 4/1*",
|
||||
"ply": 0,
|
||||
"equity": 0.8568015
|
||||
},
|
||||
{
|
||||
"move": "6/3(2) 5/2(2)",
|
||||
"ply": 0,
|
||||
"equity": 0.85501754
|
||||
},
|
||||
{
|
||||
"move": "8/2 7/4 5/2",
|
||||
"ply": 0,
|
||||
"equity": 0.8537997
|
||||
},
|
||||
{
|
||||
"move": "8/2 5/2(2)",
|
||||
"ply": 0,
|
||||
"equity": 0.8524791
|
||||
},
|
||||
{
|
||||
"move": "13/10 8/5(2) 4/1*",
|
||||
"ply": 0,
|
||||
"equity": 0.84969926
|
||||
},
|
||||
{
|
||||
"move": "13/10 8/2 5/2",
|
||||
"ply": 0,
|
||||
"equity": 0.8466199
|
||||
},
|
||||
{
|
||||
"move": "8/2 6/3(2)",
|
||||
"ply": 0,
|
||||
"equity": 0.80814815
|
||||
},
|
||||
{
|
||||
"move": "13/10 5/2(2) 4/1*",
|
||||
"ply": 0,
|
||||
"equity": 0.8037353
|
||||
},
|
||||
{
|
||||
"move": "13/10 5/2 4/1*(2)",
|
||||
"ply": 0,
|
||||
"equity": 0.77350885
|
||||
},
|
||||
{
|
||||
"move": "13/10 6/3(2) 4/1*",
|
||||
"ply": 0,
|
||||
"equity": 0.74067366
|
||||
},
|
||||
{
|
||||
"move": "13/7 13/10 5/2",
|
||||
"ply": 0,
|
||||
"equity": 0.72703636
|
||||
},
|
||||
{
|
||||
"move": "13/10(2) 7/4 5/2",
|
||||
"ply": 0,
|
||||
"equity": 0.7053956
|
||||
},
|
||||
{
|
||||
"move": "6/3(3) 4/1*",
|
||||
"ply": 0,
|
||||
"equity": 0.66575944
|
||||
},
|
||||
{
|
||||
"move": "13/10 7/1* 5/2",
|
||||
"ply": 0,
|
||||
"equity": 0.6462204
|
||||
},
|
||||
{
|
||||
"move": "8/5 6/3(2) 4/1*",
|
||||
"ply": 0,
|
||||
"equity": 0.6374039
|
||||
},
|
||||
{
|
||||
"move": "8/2 8/5 4/1*",
|
||||
"ply": 0,
|
||||
"equity": 0.62353325
|
||||
},
|
||||
{
|
||||
"move": "13/10 8/2 4/1*",
|
||||
"ply": 0,
|
||||
"equity": 0.61257124
|
||||
},
|
||||
{
|
||||
"move": "13/10 6/3 5/2 4/1*",
|
||||
"ply": 0,
|
||||
"equity": 0.5349428
|
||||
},
|
||||
{
|
||||
"move": "7/1* 5/2 4/1",
|
||||
"ply": 0,
|
||||
"equity": 0.45721298
|
||||
},
|
||||
{
|
||||
"move": "8/2 5/2 4/1*",
|
||||
"ply": 0,
|
||||
"equity": 0.44362232
|
||||
},
|
||||
{
|
||||
"move": "13/4 5/2",
|
||||
"ply": 0,
|
||||
"equity": 0.43828273
|
||||
},
|
||||
{
|
||||
"move": "13/10(3) 5/2",
|
||||
"ply": 0,
|
||||
"equity": 0.2798175
|
||||
},
|
||||
{
|
||||
"move": "13/10 6/3(2) 5/2",
|
||||
"ply": 0,
|
||||
"equity": 0.24207813
|
||||
},
|
||||
{
|
||||
"move": "6/3(3) 5/2",
|
||||
"ply": 0,
|
||||
"equity": 0.23385207
|
||||
},
|
||||
{
|
||||
"move": "7/4 6/3(2) 5/2",
|
||||
"ply": 0,
|
||||
"equity": 0.22096781
|
||||
},
|
||||
{
|
||||
"move": "13/7 5/2 4/1*",
|
||||
"ply": 0,
|
||||
"equity": 0.18849733
|
||||
},
|
||||
{
|
||||
"move": "13/10(2) 5/2 4/1*",
|
||||
"ply": 0,
|
||||
"equity": 0.084023334
|
||||
}
|
||||
]
|
||||
@@ -474,42 +583,52 @@
|
||||
"ranking": [
|
||||
{
|
||||
"move": "13/10 7/1* 6/3",
|
||||
"ply": 2,
|
||||
"equity": 1.0
|
||||
},
|
||||
{
|
||||
"move": "13/7 13/10 6/3",
|
||||
"ply": 2,
|
||||
"equity": 1.0
|
||||
},
|
||||
{
|
||||
"move": "13/10(2) 8/5 6/3",
|
||||
"ply": 2,
|
||||
"equity": 1.0
|
||||
},
|
||||
{
|
||||
"move": "13/7 8/5 6/3",
|
||||
"ply": 2,
|
||||
"equity": 1.0
|
||||
},
|
||||
{
|
||||
"move": "13/10(2) 7/4 6/3",
|
||||
"ply": 2,
|
||||
"equity": 1.0
|
||||
},
|
||||
{
|
||||
"move": "13/10 8/5 7/4 6/3",
|
||||
"ply": 2,
|
||||
"equity": 1.0
|
||||
},
|
||||
{
|
||||
"move": "13/10(2) 5/2 4/1*",
|
||||
"ply": 2,
|
||||
"equity": -0.022027964
|
||||
},
|
||||
{
|
||||
"move": "13/10 8/5(2) 6/3",
|
||||
"ply": 2,
|
||||
"equity": 1.0
|
||||
},
|
||||
{
|
||||
"move": "13/4 6/3",
|
||||
"ply": 2,
|
||||
"equity": 1.0
|
||||
},
|
||||
{
|
||||
"move": "13/10 6/3 4/1*(2)",
|
||||
"ply": 2,
|
||||
"equity": 0.99692726
|
||||
}
|
||||
]
|
||||
|
||||
@@ -25,42 +25,52 @@
|
||||
"ranking": [
|
||||
{
|
||||
"move": "8/5 6/2",
|
||||
"ply": 0,
|
||||
"equity": 0.7345603
|
||||
},
|
||||
{
|
||||
"move": "5/2 5/1*",
|
||||
"ply": 0,
|
||||
"equity": 0.7229391
|
||||
},
|
||||
{
|
||||
"move": "8/1*",
|
||||
"ply": 0,
|
||||
"equity": 0.71705425
|
||||
},
|
||||
{
|
||||
"move": "13/10 6/2",
|
||||
"ply": 0,
|
||||
"equity": 0.7142835
|
||||
},
|
||||
{
|
||||
"move": "13/9 8/5",
|
||||
"ply": 0,
|
||||
"equity": 0.6963019
|
||||
},
|
||||
{
|
||||
"move": "8/5 8/4",
|
||||
"ply": 0,
|
||||
"equity": 0.68610644
|
||||
},
|
||||
{
|
||||
"move": "13/10 8/4",
|
||||
"ply": 0,
|
||||
"equity": 0.6855368
|
||||
},
|
||||
{
|
||||
"move": "13/10 13/9",
|
||||
"ply": 0,
|
||||
"equity": 0.6818689
|
||||
},
|
||||
{
|
||||
"move": "13/6",
|
||||
"ply": 0,
|
||||
"equity": 0.6633271
|
||||
},
|
||||
{
|
||||
"move": "13/9 5/2",
|
||||
"ply": 0,
|
||||
"equity": 0.6538005
|
||||
}
|
||||
]
|
||||
@@ -78,42 +88,52 @@
|
||||
"ranking": [
|
||||
{
|
||||
"move": "8/5 6/2",
|
||||
"ply": 2,
|
||||
"equity": 0.7697691
|
||||
},
|
||||
{
|
||||
"move": "13/10 6/2",
|
||||
"ply": 2,
|
||||
"equity": 0.7615671
|
||||
},
|
||||
{
|
||||
"move": "13/9 8/5",
|
||||
"ply": 2,
|
||||
"equity": 0.69608325
|
||||
},
|
||||
{
|
||||
"move": "5/2 5/1*",
|
||||
"ply": 2,
|
||||
"equity": 0.6911188
|
||||
},
|
||||
{
|
||||
"move": "8/1*",
|
||||
"ply": 2,
|
||||
"equity": 0.6836242
|
||||
},
|
||||
{
|
||||
"move": "13/10 13/9",
|
||||
"ply": 2,
|
||||
"equity": 0.6807338
|
||||
},
|
||||
{
|
||||
"move": "13/10 8/4",
|
||||
"ply": 1,
|
||||
"equity": 0.6733323
|
||||
},
|
||||
{
|
||||
"move": "13/6",
|
||||
"ply": 1,
|
||||
"equity": 0.66055346
|
||||
},
|
||||
{
|
||||
"move": "8/5 8/4",
|
||||
"ply": 1,
|
||||
"equity": 0.6556259
|
||||
},
|
||||
{
|
||||
"move": "13/9 5/2",
|
||||
"ply": 1,
|
||||
"equity": 0.645951
|
||||
}
|
||||
]
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"ranking": [
|
||||
{
|
||||
"move": "bar/24",
|
||||
"ply": 0,
|
||||
"equity": -1.0
|
||||
}
|
||||
]
|
||||
@@ -42,6 +43,7 @@
|
||||
"ranking": [
|
||||
{
|
||||
"move": "bar/24",
|
||||
"ply": 0,
|
||||
"equity": -1.0
|
||||
}
|
||||
]
|
||||
|
||||
@@ -25,58 +25,72 @@
|
||||
"ranking": [
|
||||
{
|
||||
"move": "13/3",
|
||||
"ply": 0,
|
||||
"equity": 0.9328213
|
||||
},
|
||||
{
|
||||
"move": "13/9 13/7",
|
||||
"ply": 0,
|
||||
"equity": 0.9175232
|
||||
},
|
||||
{
|
||||
"move": "8/2 7/3",
|
||||
"ply": 0,
|
||||
"equity": 0.91263336
|
||||
},
|
||||
{
|
||||
"move": "13/7 8/4",
|
||||
"ply": 0,
|
||||
"equity": 0.90736973
|
||||
},
|
||||
{
|
||||
"move": "13/9 7/1*",
|
||||
"ply": 0,
|
||||
"equity": 0.9053596
|
||||
},
|
||||
{
|
||||
"move": "8/2 6/2",
|
||||
"ply": 0,
|
||||
"equity": 0.9017469
|
||||
},
|
||||
{
|
||||
"move": "8/4 7/1*",
|
||||
"ply": 0,
|
||||
"equity": 0.89993495
|
||||
},
|
||||
{
|
||||
"move": "13/7 6/2",
|
||||
"ply": 0,
|
||||
"equity": 0.8962312
|
||||
},
|
||||
{
|
||||
"move": "7/1* 5/1",
|
||||
"ply": 0,
|
||||
"equity": 0.8907822
|
||||
},
|
||||
{
|
||||
"move": "7/1* 6/2",
|
||||
"ply": 0,
|
||||
"equity": 0.87481093
|
||||
},
|
||||
{
|
||||
"move": "13/9 8/2",
|
||||
"ply": 0,
|
||||
"equity": 0.87251335
|
||||
},
|
||||
{
|
||||
"move": "8/4 8/2",
|
||||
"ply": 0,
|
||||
"equity": 0.8716274
|
||||
},
|
||||
{
|
||||
"move": "13/7 5/1*",
|
||||
"ply": 0,
|
||||
"equity": 0.87005275
|
||||
},
|
||||
{
|
||||
"move": "8/2 5/1*",
|
||||
"ply": 0,
|
||||
"equity": 0.50889754
|
||||
}
|
||||
]
|
||||
@@ -94,42 +108,52 @@
|
||||
"ranking": [
|
||||
{
|
||||
"move": "13/3",
|
||||
"ply": 2,
|
||||
"equity": 1.0
|
||||
},
|
||||
{
|
||||
"move": "13/9 13/7",
|
||||
"ply": 2,
|
||||
"equity": 0.99894416
|
||||
},
|
||||
{
|
||||
"move": "8/2 7/3",
|
||||
"ply": 2,
|
||||
"equity": 0.99641544
|
||||
},
|
||||
{
|
||||
"move": "13/7 8/4",
|
||||
"ply": 2,
|
||||
"equity": 0.98718643
|
||||
},
|
||||
{
|
||||
"move": "13/7 6/2",
|
||||
"ply": 2,
|
||||
"equity": 0.9808178
|
||||
},
|
||||
{
|
||||
"move": "13/9 7/1*",
|
||||
"ply": 2,
|
||||
"equity": 0.9452111
|
||||
},
|
||||
{
|
||||
"move": "8/2 5/1*",
|
||||
"ply": 2,
|
||||
"equity": 0.3304975
|
||||
},
|
||||
{
|
||||
"move": "8/4 7/1*",
|
||||
"ply": 1,
|
||||
"equity": 0.93114305
|
||||
},
|
||||
{
|
||||
"move": "7/1* 5/1",
|
||||
"ply": 1,
|
||||
"equity": 0.91711015
|
||||
},
|
||||
{
|
||||
"move": "8/2 6/2",
|
||||
"ply": 1,
|
||||
"equity": 0.9160911
|
||||
}
|
||||
]
|
||||
|
||||
@@ -25,42 +25,52 @@
|
||||
"ranking": [
|
||||
{
|
||||
"move": "13/5 11/3",
|
||||
"ply": 0,
|
||||
"equity": 0.16933289
|
||||
},
|
||||
{
|
||||
"move": "13/9(2) 11/3",
|
||||
"ply": 0,
|
||||
"equity": 0.16701066
|
||||
},
|
||||
{
|
||||
"move": "13/5 9/5 7/3",
|
||||
"ply": 0,
|
||||
"equity": 0.16552198
|
||||
},
|
||||
{
|
||||
"move": "13/5(2)",
|
||||
"ply": 0,
|
||||
"equity": 0.16473158
|
||||
},
|
||||
{
|
||||
"move": "13/5 13/9 11/7",
|
||||
"ply": 0,
|
||||
"equity": 0.16365689
|
||||
},
|
||||
{
|
||||
"move": "13/5 13/9 7/3",
|
||||
"ply": 0,
|
||||
"equity": 0.16245355
|
||||
},
|
||||
{
|
||||
"move": "13/5 11/7 9/5",
|
||||
"ply": 0,
|
||||
"equity": 0.16167049
|
||||
},
|
||||
{
|
||||
"move": "11/3 9/5(2)",
|
||||
"ply": 0,
|
||||
"equity": 0.16043647
|
||||
},
|
||||
{
|
||||
"move": "13/9 11/3 11/7",
|
||||
"ply": 0,
|
||||
"equity": 0.1603657
|
||||
},
|
||||
{
|
||||
"move": "13/5 11/7(2)",
|
||||
"ply": 0,
|
||||
"equity": 0.1602792
|
||||
}
|
||||
]
|
||||
@@ -78,42 +88,52 @@
|
||||
"ranking": [
|
||||
{
|
||||
"move": "13/5 11/3",
|
||||
"ply": 2,
|
||||
"equity": 0.15129912
|
||||
},
|
||||
{
|
||||
"move": "13/5 13/9 11/7",
|
||||
"ply": 2,
|
||||
"equity": 0.15059008
|
||||
},
|
||||
{
|
||||
"move": "13/9(2) 11/3",
|
||||
"ply": 2,
|
||||
"equity": 0.14950007
|
||||
},
|
||||
{
|
||||
"move": "13/5 11/7 9/5",
|
||||
"ply": 2,
|
||||
"equity": 0.14858595
|
||||
},
|
||||
{
|
||||
"move": "11/3 11/7 9/5",
|
||||
"ply": 2,
|
||||
"equity": 0.14829463
|
||||
},
|
||||
{
|
||||
"move": "13/9 11/3 11/7",
|
||||
"ply": 2,
|
||||
"equity": 0.1463402
|
||||
},
|
||||
{
|
||||
"move": "13/5 9/5 7/3",
|
||||
"ply": 1,
|
||||
"equity": 0.13938732
|
||||
},
|
||||
{
|
||||
"move": "13/5 11/7(2)",
|
||||
"ply": 1,
|
||||
"equity": 0.13921705
|
||||
},
|
||||
{
|
||||
"move": "13/9(2) 11/7(2)",
|
||||
"ply": 1,
|
||||
"equity": 0.13873637
|
||||
},
|
||||
{
|
||||
"move": "13/5 13/9 6/2",
|
||||
"ply": 1,
|
||||
"equity": 0.13852078
|
||||
}
|
||||
]
|
||||
|
||||
@@ -25,18 +25,22 @@
|
||||
"ranking": [
|
||||
{
|
||||
"move": "3/off",
|
||||
"ply": 0,
|
||||
"equity": -0.8607614
|
||||
},
|
||||
{
|
||||
"move": "2/off 1/off",
|
||||
"ply": 0,
|
||||
"equity": -0.8607614
|
||||
},
|
||||
{
|
||||
"move": "2/1 2/off",
|
||||
"ply": 0,
|
||||
"equity": -0.95561147
|
||||
},
|
||||
{
|
||||
"move": "3/1 2/1",
|
||||
"ply": 0,
|
||||
"equity": -1.0
|
||||
}
|
||||
]
|
||||
@@ -54,18 +58,22 @@
|
||||
"ranking": [
|
||||
{
|
||||
"move": "3/off",
|
||||
"ply": 2,
|
||||
"equity": -0.8607614
|
||||
},
|
||||
{
|
||||
"move": "2/off 1/off",
|
||||
"ply": 2,
|
||||
"equity": -0.8607614
|
||||
},
|
||||
{
|
||||
"move": "2/1 2/off",
|
||||
"ply": 2,
|
||||
"equity": -0.95561147
|
||||
},
|
||||
{
|
||||
"move": "3/1 2/1",
|
||||
"ply": 1,
|
||||
"equity": -1.0
|
||||
}
|
||||
]
|
||||
|
||||
@@ -25,42 +25,52 @@
|
||||
"ranking": [
|
||||
{
|
||||
"move": "18/9",
|
||||
"ply": 0,
|
||||
"equity": 0.2544027
|
||||
},
|
||||
{
|
||||
"move": "18/14 13/8",
|
||||
"ply": 0,
|
||||
"equity": 0.22496489
|
||||
},
|
||||
{
|
||||
"move": "24/20 18/13",
|
||||
"ply": 0,
|
||||
"equity": 0.20747662
|
||||
},
|
||||
{
|
||||
"move": "24/20 13/8",
|
||||
"ply": 0,
|
||||
"equity": 0.1158272
|
||||
},
|
||||
{
|
||||
"move": "13/9 13/8",
|
||||
"ply": 0,
|
||||
"equity": 0.09201278
|
||||
},
|
||||
{
|
||||
"move": "24/15",
|
||||
"ply": 0,
|
||||
"equity": 0.061017293
|
||||
},
|
||||
{
|
||||
"move": "18/13 6/2",
|
||||
"ply": 0,
|
||||
"equity": 0.03581623
|
||||
},
|
||||
{
|
||||
"move": "13/4",
|
||||
"ply": 0,
|
||||
"equity": -0.028887823
|
||||
},
|
||||
{
|
||||
"move": "13/8 6/2",
|
||||
"ply": 0,
|
||||
"equity": -0.033572298
|
||||
},
|
||||
{
|
||||
"move": "18/13 8/4",
|
||||
"ply": 0,
|
||||
"equity": -0.09662818
|
||||
}
|
||||
]
|
||||
@@ -78,42 +88,52 @@
|
||||
"ranking": [
|
||||
{
|
||||
"move": "18/9",
|
||||
"ply": 2,
|
||||
"equity": 0.27687487
|
||||
},
|
||||
{
|
||||
"move": "18/14 13/8",
|
||||
"ply": 2,
|
||||
"equity": 0.21801135
|
||||
},
|
||||
{
|
||||
"move": "24/20 18/13",
|
||||
"ply": 2,
|
||||
"equity": 0.21000355
|
||||
},
|
||||
{
|
||||
"move": "24/20 13/8",
|
||||
"ply": 2,
|
||||
"equity": 0.1144446
|
||||
},
|
||||
{
|
||||
"move": "13/9 13/8",
|
||||
"ply": 2,
|
||||
"equity": 0.09412725
|
||||
},
|
||||
{
|
||||
"move": "24/15",
|
||||
"ply": 2,
|
||||
"equity": 0.07892294
|
||||
},
|
||||
{
|
||||
"move": "18/13 6/2",
|
||||
"ply": 1,
|
||||
"equity": -0.020656409
|
||||
},
|
||||
{
|
||||
"move": "13/4",
|
||||
"ply": 0,
|
||||
"equity": -0.028887823
|
||||
},
|
||||
{
|
||||
"move": "13/8 6/2",
|
||||
"ply": 0,
|
||||
"equity": -0.033572298
|
||||
},
|
||||
{
|
||||
"move": "18/13 8/4",
|
||||
"ply": 0,
|
||||
"equity": -0.09662818
|
||||
}
|
||||
]
|
||||
|
||||
@@ -25,42 +25,52 @@
|
||||
"ranking": [
|
||||
{
|
||||
"move": "13/5",
|
||||
"ply": 0,
|
||||
"equity": 0.7553502
|
||||
},
|
||||
{
|
||||
"move": "18/13 8/5",
|
||||
"ply": 0,
|
||||
"equity": 0.5814649
|
||||
},
|
||||
{
|
||||
"move": "24/16*",
|
||||
"ply": 0,
|
||||
"equity": 0.4581785
|
||||
},
|
||||
{
|
||||
"move": "18/10",
|
||||
"ply": 0,
|
||||
"equity": 0.31719947
|
||||
},
|
||||
{
|
||||
"move": "8/5 8/3",
|
||||
"ply": 0,
|
||||
"equity": 0.31371918
|
||||
},
|
||||
{
|
||||
"move": "18/15 13/8",
|
||||
"ply": 0,
|
||||
"equity": 0.26037523
|
||||
},
|
||||
{
|
||||
"move": "24/21 18/13",
|
||||
"ply": 0,
|
||||
"equity": 0.23547156
|
||||
},
|
||||
{
|
||||
"move": "8/3 6/3",
|
||||
"ply": 0,
|
||||
"equity": 0.15432969
|
||||
},
|
||||
{
|
||||
"move": "13/10 13/8",
|
||||
"ply": 0,
|
||||
"equity": 0.13837479
|
||||
},
|
||||
{
|
||||
"move": "18/13 5/2",
|
||||
"ply": 0,
|
||||
"equity": 0.12815686
|
||||
}
|
||||
]
|
||||
@@ -78,42 +88,52 @@
|
||||
"ranking": [
|
||||
{
|
||||
"move": "13/5",
|
||||
"ply": 2,
|
||||
"equity": 0.85497326
|
||||
},
|
||||
{
|
||||
"move": "24/16*",
|
||||
"ply": 2,
|
||||
"equity": 0.46805125
|
||||
},
|
||||
{
|
||||
"move": "18/13 8/5",
|
||||
"ply": 1,
|
||||
"equity": 0.6511238
|
||||
},
|
||||
{
|
||||
"move": "18/10",
|
||||
"ply": 0,
|
||||
"equity": 0.31719947
|
||||
},
|
||||
{
|
||||
"move": "8/5 8/3",
|
||||
"ply": 0,
|
||||
"equity": 0.31371918
|
||||
},
|
||||
{
|
||||
"move": "18/15 13/8",
|
||||
"ply": 0,
|
||||
"equity": 0.26037523
|
||||
},
|
||||
{
|
||||
"move": "24/21 18/13",
|
||||
"ply": 0,
|
||||
"equity": 0.23547156
|
||||
},
|
||||
{
|
||||
"move": "8/3 6/3",
|
||||
"ply": 0,
|
||||
"equity": 0.15432969
|
||||
},
|
||||
{
|
||||
"move": "13/10 13/8",
|
||||
"ply": 0,
|
||||
"equity": 0.13837479
|
||||
},
|
||||
{
|
||||
"move": "18/13 5/2",
|
||||
"ply": 0,
|
||||
"equity": 0.12815686
|
||||
}
|
||||
]
|
||||
|
||||
@@ -25,58 +25,72 @@
|
||||
"ranking": [
|
||||
{
|
||||
"move": "24/14",
|
||||
"ply": 0,
|
||||
"equity": 0.15439394
|
||||
},
|
||||
{
|
||||
"move": "8/2 6/2",
|
||||
"ply": 0,
|
||||
"equity": 0.10821653
|
||||
},
|
||||
{
|
||||
"move": "24/18 13/9",
|
||||
"ply": 0,
|
||||
"equity": 0.10337136
|
||||
},
|
||||
{
|
||||
"move": "24/20 24/18",
|
||||
"ply": 0,
|
||||
"equity": 0.072762795
|
||||
},
|
||||
{
|
||||
"move": "13/9 13/7",
|
||||
"ply": 0,
|
||||
"equity": 0.0029048775
|
||||
},
|
||||
{
|
||||
"move": "24/20 13/7",
|
||||
"ply": 0,
|
||||
"equity": 9.1438415e-05
|
||||
},
|
||||
{
|
||||
"move": "13/3",
|
||||
"ply": 0,
|
||||
"equity": -0.012697897
|
||||
},
|
||||
{
|
||||
"move": "24/18 6/2",
|
||||
"ply": 0,
|
||||
"equity": -0.075399
|
||||
},
|
||||
{
|
||||
"move": "24/18 8/4",
|
||||
"ply": 0,
|
||||
"equity": -0.088734664
|
||||
},
|
||||
{
|
||||
"move": "24/20 8/2",
|
||||
"ply": 0,
|
||||
"equity": -0.095189385
|
||||
},
|
||||
{
|
||||
"move": "13/9 8/2",
|
||||
"ply": 0,
|
||||
"equity": -0.09528468
|
||||
},
|
||||
{
|
||||
"move": "13/7 8/4",
|
||||
"ply": 0,
|
||||
"equity": -0.12819159
|
||||
},
|
||||
{
|
||||
"move": "13/7 6/2",
|
||||
"ply": 0,
|
||||
"equity": -0.14467186
|
||||
},
|
||||
{
|
||||
"move": "8/4 8/2",
|
||||
"ply": 0,
|
||||
"equity": -0.35321844
|
||||
}
|
||||
]
|
||||
@@ -94,42 +108,52 @@
|
||||
"ranking": [
|
||||
{
|
||||
"move": "24/14",
|
||||
"ply": 2,
|
||||
"equity": 0.13111727
|
||||
},
|
||||
{
|
||||
"move": "8/2 6/2",
|
||||
"ply": 2,
|
||||
"equity": 0.11303549
|
||||
},
|
||||
{
|
||||
"move": "24/18 13/9",
|
||||
"ply": 2,
|
||||
"equity": 0.105096616
|
||||
},
|
||||
{
|
||||
"move": "24/20 24/18",
|
||||
"ply": 2,
|
||||
"equity": 0.049068402
|
||||
},
|
||||
{
|
||||
"move": "13/3",
|
||||
"ply": 2,
|
||||
"equity": -0.0040049786
|
||||
},
|
||||
{
|
||||
"move": "8/4 8/2",
|
||||
"ply": 2,
|
||||
"equity": -0.36824828
|
||||
},
|
||||
{
|
||||
"move": "13/9 13/7",
|
||||
"ply": 2,
|
||||
"equity": -0.014309813
|
||||
},
|
||||
{
|
||||
"move": "24/20 13/7",
|
||||
"ply": 1,
|
||||
"equity": -0.029367238
|
||||
},
|
||||
{
|
||||
"move": "24/18 8/4",
|
||||
"ply": 1,
|
||||
"equity": -0.081171095
|
||||
},
|
||||
{
|
||||
"move": "24/18 6/2",
|
||||
"ply": 1,
|
||||
"equity": -0.09313507
|
||||
}
|
||||
]
|
||||
|
||||
@@ -25,42 +25,52 @@
|
||||
"ranking": [
|
||||
{
|
||||
"move": "24/14",
|
||||
"ply": 0,
|
||||
"equity": 0.15439394
|
||||
},
|
||||
{
|
||||
"move": "8/2 6/2",
|
||||
"ply": 0,
|
||||
"equity": 0.10821653
|
||||
},
|
||||
{
|
||||
"move": "24/18 13/9",
|
||||
"ply": 0,
|
||||
"equity": 0.10337136
|
||||
},
|
||||
{
|
||||
"move": "24/20 24/18",
|
||||
"ply": 0,
|
||||
"equity": 0.072762795
|
||||
},
|
||||
{
|
||||
"move": "13/9 13/7",
|
||||
"ply": 0,
|
||||
"equity": 0.0029048775
|
||||
},
|
||||
{
|
||||
"move": "24/20 13/7",
|
||||
"ply": 0,
|
||||
"equity": 9.1438415e-05
|
||||
},
|
||||
{
|
||||
"move": "13/3",
|
||||
"ply": 0,
|
||||
"equity": -0.012697897
|
||||
},
|
||||
{
|
||||
"move": "24/18 6/2",
|
||||
"ply": 0,
|
||||
"equity": -0.075399
|
||||
},
|
||||
{
|
||||
"move": "24/18 8/4",
|
||||
"ply": 0,
|
||||
"equity": -0.088734664
|
||||
},
|
||||
{
|
||||
"move": "24/20 8/2",
|
||||
"ply": 0,
|
||||
"equity": -0.095189385
|
||||
}
|
||||
]
|
||||
@@ -78,42 +88,52 @@
|
||||
"ranking": [
|
||||
{
|
||||
"move": "24/14",
|
||||
"ply": 2,
|
||||
"equity": 0.13111727
|
||||
},
|
||||
{
|
||||
"move": "8/2 6/2",
|
||||
"ply": 2,
|
||||
"equity": 0.11303549
|
||||
},
|
||||
{
|
||||
"move": "24/18 13/9",
|
||||
"ply": 2,
|
||||
"equity": 0.105096616
|
||||
},
|
||||
{
|
||||
"move": "24/20 24/18",
|
||||
"ply": 2,
|
||||
"equity": 0.049068402
|
||||
},
|
||||
{
|
||||
"move": "13/3",
|
||||
"ply": 2,
|
||||
"equity": -0.0040049786
|
||||
},
|
||||
{
|
||||
"move": "13/9 13/7",
|
||||
"ply": 2,
|
||||
"equity": -0.014309813
|
||||
},
|
||||
{
|
||||
"move": "24/20 13/7",
|
||||
"ply": 1,
|
||||
"equity": -0.029367238
|
||||
},
|
||||
{
|
||||
"move": "24/18 8/4",
|
||||
"ply": 1,
|
||||
"equity": -0.081171095
|
||||
},
|
||||
{
|
||||
"move": "24/18 6/2",
|
||||
"ply": 1,
|
||||
"equity": -0.09313507
|
||||
},
|
||||
{
|
||||
"move": "24/20 8/2",
|
||||
"ply": 0,
|
||||
"equity": -0.095189385
|
||||
}
|
||||
]
|
||||
|
||||
@@ -25,42 +25,52 @@
|
||||
"ranking": [
|
||||
{
|
||||
"move": "8/5 6/5",
|
||||
"ply": 0,
|
||||
"equity": 0.21258597
|
||||
},
|
||||
{
|
||||
"move": "24/23 13/10",
|
||||
"ply": 0,
|
||||
"equity": -0.010789523
|
||||
},
|
||||
{
|
||||
"move": "24/20",
|
||||
"ply": 0,
|
||||
"equity": -0.017392516
|
||||
},
|
||||
{
|
||||
"move": "24/23 24/21",
|
||||
"ply": 0,
|
||||
"equity": -0.023609947
|
||||
},
|
||||
{
|
||||
"move": "13/9",
|
||||
"ply": 0,
|
||||
"equity": -0.027675275
|
||||
},
|
||||
{
|
||||
"move": "24/21 6/5",
|
||||
"ply": 0,
|
||||
"equity": -0.04369401
|
||||
},
|
||||
{
|
||||
"move": "13/10 6/5",
|
||||
"ply": 0,
|
||||
"equity": -0.04984435
|
||||
},
|
||||
{
|
||||
"move": "24/23 8/5",
|
||||
"ply": 0,
|
||||
"equity": -0.104100645
|
||||
},
|
||||
{
|
||||
"move": "24/23 6/3",
|
||||
"ply": 0,
|
||||
"equity": -0.10542354
|
||||
},
|
||||
{
|
||||
"move": "24/21 8/7",
|
||||
"ply": 0,
|
||||
"equity": -0.107585564
|
||||
}
|
||||
]
|
||||
@@ -78,42 +88,52 @@
|
||||
"ranking": [
|
||||
{
|
||||
"move": "8/5 6/5",
|
||||
"ply": 1,
|
||||
"equity": 0.20373425
|
||||
},
|
||||
{
|
||||
"move": "24/23 13/10",
|
||||
"ply": 1,
|
||||
"equity": -0.022293653
|
||||
},
|
||||
{
|
||||
"move": "24/20",
|
||||
"ply": 1,
|
||||
"equity": -0.031027086
|
||||
},
|
||||
{
|
||||
"move": "24/23 24/21",
|
||||
"ply": 1,
|
||||
"equity": -0.03213631
|
||||
},
|
||||
{
|
||||
"move": "13/9",
|
||||
"ply": 1,
|
||||
"equity": -0.040205758
|
||||
},
|
||||
{
|
||||
"move": "24/21 6/5",
|
||||
"ply": 0,
|
||||
"equity": -0.04369401
|
||||
},
|
||||
{
|
||||
"move": "13/10 6/5",
|
||||
"ply": 0,
|
||||
"equity": -0.04984435
|
||||
},
|
||||
{
|
||||
"move": "24/23 8/5",
|
||||
"ply": 0,
|
||||
"equity": -0.104100645
|
||||
},
|
||||
{
|
||||
"move": "24/23 6/3",
|
||||
"ply": 0,
|
||||
"equity": -0.10542354
|
||||
},
|
||||
{
|
||||
"move": "24/21 8/7",
|
||||
"ply": 0,
|
||||
"equity": -0.107585564
|
||||
}
|
||||
]
|
||||
|
||||
@@ -25,42 +25,52 @@
|
||||
"ranking": [
|
||||
{
|
||||
"move": "8/5 6/5",
|
||||
"ply": 0,
|
||||
"equity": 0.21258597
|
||||
},
|
||||
{
|
||||
"move": "24/23 13/10",
|
||||
"ply": 0,
|
||||
"equity": -0.010789523
|
||||
},
|
||||
{
|
||||
"move": "24/20",
|
||||
"ply": 0,
|
||||
"equity": -0.017392516
|
||||
},
|
||||
{
|
||||
"move": "24/23 24/21",
|
||||
"ply": 0,
|
||||
"equity": -0.023609947
|
||||
},
|
||||
{
|
||||
"move": "13/9",
|
||||
"ply": 0,
|
||||
"equity": -0.027675275
|
||||
},
|
||||
{
|
||||
"move": "24/21 6/5",
|
||||
"ply": 0,
|
||||
"equity": -0.04369401
|
||||
},
|
||||
{
|
||||
"move": "13/10 6/5",
|
||||
"ply": 0,
|
||||
"equity": -0.04984435
|
||||
},
|
||||
{
|
||||
"move": "24/23 8/5",
|
||||
"ply": 0,
|
||||
"equity": -0.104100645
|
||||
},
|
||||
{
|
||||
"move": "24/23 6/3",
|
||||
"ply": 0,
|
||||
"equity": -0.10542354
|
||||
},
|
||||
{
|
||||
"move": "24/21 8/7",
|
||||
"ply": 0,
|
||||
"equity": -0.107585564
|
||||
}
|
||||
]
|
||||
@@ -78,42 +88,52 @@
|
||||
"ranking": [
|
||||
{
|
||||
"move": "8/5 6/5",
|
||||
"ply": 2,
|
||||
"equity": 0.1998143
|
||||
},
|
||||
{
|
||||
"move": "24/23 13/10",
|
||||
"ply": 2,
|
||||
"equity": -0.0118339015
|
||||
},
|
||||
{
|
||||
"move": "24/20",
|
||||
"ply": 1,
|
||||
"equity": -0.031027086
|
||||
},
|
||||
{
|
||||
"move": "24/23 24/21",
|
||||
"ply": 1,
|
||||
"equity": -0.03213631
|
||||
},
|
||||
{
|
||||
"move": "13/9",
|
||||
"ply": 1,
|
||||
"equity": -0.040205758
|
||||
},
|
||||
{
|
||||
"move": "24/21 6/5",
|
||||
"ply": 0,
|
||||
"equity": -0.04369401
|
||||
},
|
||||
{
|
||||
"move": "13/10 6/5",
|
||||
"ply": 0,
|
||||
"equity": -0.04984435
|
||||
},
|
||||
{
|
||||
"move": "24/23 8/5",
|
||||
"ply": 0,
|
||||
"equity": -0.104100645
|
||||
},
|
||||
{
|
||||
"move": "24/23 6/3",
|
||||
"ply": 0,
|
||||
"equity": -0.10542354
|
||||
},
|
||||
{
|
||||
"move": "24/21 8/7",
|
||||
"ply": 0,
|
||||
"equity": -0.107585564
|
||||
}
|
||||
]
|
||||
|
||||
@@ -25,30 +25,37 @@
|
||||
"ranking": [
|
||||
{
|
||||
"move": "24/13",
|
||||
"ply": 0,
|
||||
"equity": 0.082985915
|
||||
},
|
||||
{
|
||||
"move": "24/18 13/8",
|
||||
"ply": 0,
|
||||
"equity": 0.03631018
|
||||
},
|
||||
{
|
||||
"move": "13/8 13/7",
|
||||
"ply": 0,
|
||||
"equity": -0.05142035
|
||||
},
|
||||
{
|
||||
"move": "13/2",
|
||||
"ply": 0,
|
||||
"equity": -0.13522762
|
||||
},
|
||||
{
|
||||
"move": "24/18 8/3",
|
||||
"ply": 0,
|
||||
"equity": -0.14635906
|
||||
},
|
||||
{
|
||||
"move": "13/7 8/3",
|
||||
"ply": 0,
|
||||
"equity": -0.17384717
|
||||
},
|
||||
{
|
||||
"move": "8/3 8/2",
|
||||
"ply": 0,
|
||||
"equity": -0.37779096
|
||||
}
|
||||
]
|
||||
@@ -66,30 +73,37 @@
|
||||
"ranking": [
|
||||
{
|
||||
"move": "24/13",
|
||||
"ply": 2,
|
||||
"equity": 0.07996891
|
||||
},
|
||||
{
|
||||
"move": "24/18 13/8",
|
||||
"ply": 2,
|
||||
"equity": 0.03882884
|
||||
},
|
||||
{
|
||||
"move": "13/8 13/7",
|
||||
"ply": 2,
|
||||
"equity": -0.061850794
|
||||
},
|
||||
{
|
||||
"move": "13/2",
|
||||
"ply": 1,
|
||||
"equity": -0.12083713
|
||||
},
|
||||
{
|
||||
"move": "24/18 8/3",
|
||||
"ply": 1,
|
||||
"equity": -0.13999933
|
||||
},
|
||||
{
|
||||
"move": "13/7 8/3",
|
||||
"ply": 0,
|
||||
"equity": -0.17384717
|
||||
},
|
||||
{
|
||||
"move": "8/3 8/2",
|
||||
"ply": 0,
|
||||
"equity": -0.37779096
|
||||
}
|
||||
]
|
||||
|
||||
@@ -25,42 +25,52 @@
|
||||
"ranking": [
|
||||
{
|
||||
"move": "24/18(2) 13/7(2)",
|
||||
"ply": 0,
|
||||
"equity": 0.539388
|
||||
},
|
||||
{
|
||||
"move": "24/18 13/7(3)",
|
||||
"ply": 0,
|
||||
"equity": 0.33099693
|
||||
},
|
||||
{
|
||||
"move": "24/18(2) 8/2(2)",
|
||||
"ply": 0,
|
||||
"equity": 0.27119082
|
||||
},
|
||||
{
|
||||
"move": "13/7(2) 8/2(2)",
|
||||
"ply": 0,
|
||||
"equity": 0.1563462
|
||||
},
|
||||
{
|
||||
"move": "24/18 13/7(2) 8/2",
|
||||
"ply": 0,
|
||||
"equity": 0.14750561
|
||||
},
|
||||
{
|
||||
"move": "13/7(4)",
|
||||
"ply": 0,
|
||||
"equity": 0.14310652
|
||||
},
|
||||
{
|
||||
"move": "24/18(2) 13/7 8/2",
|
||||
"ply": 0,
|
||||
"equity": 0.12353878
|
||||
},
|
||||
{
|
||||
"move": "13/7(3) 8/2",
|
||||
"ply": 0,
|
||||
"equity": 0.101389736
|
||||
},
|
||||
{
|
||||
"move": "24/18 13/7 8/2(2)",
|
||||
"ply": 0,
|
||||
"equity": -0.05435923
|
||||
},
|
||||
{
|
||||
"move": "24/18 8/2(3)",
|
||||
"ply": 0,
|
||||
"equity": -0.12482335
|
||||
}
|
||||
]
|
||||
@@ -78,42 +88,52 @@
|
||||
"ranking": [
|
||||
{
|
||||
"move": "24/18(2) 13/7(2)",
|
||||
"ply": 1,
|
||||
"equity": 0.5886327
|
||||
},
|
||||
{
|
||||
"move": "24/18 13/7(3)",
|
||||
"ply": 1,
|
||||
"equity": 0.33312234
|
||||
},
|
||||
{
|
||||
"move": "24/18(2) 8/2(2)",
|
||||
"ply": 0,
|
||||
"equity": 0.27119082
|
||||
},
|
||||
{
|
||||
"move": "13/7(2) 8/2(2)",
|
||||
"ply": 0,
|
||||
"equity": 0.1563462
|
||||
},
|
||||
{
|
||||
"move": "24/18 13/7(2) 8/2",
|
||||
"ply": 0,
|
||||
"equity": 0.14750561
|
||||
},
|
||||
{
|
||||
"move": "13/7(4)",
|
||||
"ply": 0,
|
||||
"equity": 0.14310652
|
||||
},
|
||||
{
|
||||
"move": "24/18(2) 13/7 8/2",
|
||||
"ply": 0,
|
||||
"equity": 0.12353878
|
||||
},
|
||||
{
|
||||
"move": "13/7(3) 8/2",
|
||||
"ply": 0,
|
||||
"equity": 0.101389736
|
||||
},
|
||||
{
|
||||
"move": "24/18 13/7 8/2(2)",
|
||||
"ply": 0,
|
||||
"equity": -0.05435923
|
||||
},
|
||||
{
|
||||
"move": "24/18 8/2(3)",
|
||||
"ply": 0,
|
||||
"equity": -0.12482335
|
||||
}
|
||||
]
|
||||
|
||||
@@ -25,118 +25,147 @@
|
||||
"ranking": [
|
||||
{
|
||||
"move": "13/9 11/6",
|
||||
"ply": 0,
|
||||
"equity": -0.22230339
|
||||
},
|
||||
{
|
||||
"move": "11/6 9/5",
|
||||
"ply": 0,
|
||||
"equity": -0.22993731
|
||||
},
|
||||
{
|
||||
"move": "13/8 9/5",
|
||||
"ply": 0,
|
||||
"equity": -0.23005769
|
||||
},
|
||||
{
|
||||
"move": "13/9 13/8",
|
||||
"ply": 0,
|
||||
"equity": -0.23073325
|
||||
},
|
||||
{
|
||||
"move": "13/8 11/7",
|
||||
"ply": 0,
|
||||
"equity": -0.23153114
|
||||
},
|
||||
{
|
||||
"move": "11/7 11/6",
|
||||
"ply": 0,
|
||||
"equity": -0.2335976
|
||||
},
|
||||
{
|
||||
"move": "13/8 7/3",
|
||||
"ply": 0,
|
||||
"equity": -0.23374501
|
||||
},
|
||||
{
|
||||
"move": "11/6 7/3",
|
||||
"ply": 0,
|
||||
"equity": -0.23394687
|
||||
},
|
||||
{
|
||||
"move": "13/4",
|
||||
"ply": 0,
|
||||
"equity": -0.23507257
|
||||
},
|
||||
{
|
||||
"move": "13/9 7/2",
|
||||
"ply": 0,
|
||||
"equity": -0.23529215
|
||||
},
|
||||
{
|
||||
"move": "7/3 7/2",
|
||||
"ply": 0,
|
||||
"equity": -0.23544219
|
||||
},
|
||||
{
|
||||
"move": "11/2",
|
||||
"ply": 0,
|
||||
"equity": -0.235758
|
||||
},
|
||||
{
|
||||
"move": "11/7 9/4",
|
||||
"ply": 0,
|
||||
"equity": -0.23589191
|
||||
},
|
||||
{
|
||||
"move": "13/8 6/2",
|
||||
"ply": 0,
|
||||
"equity": -0.24026787
|
||||
},
|
||||
{
|
||||
"move": "9/5 7/2",
|
||||
"ply": 0,
|
||||
"equity": -0.24087214
|
||||
},
|
||||
{
|
||||
"move": "13/8 5/1",
|
||||
"ply": 0,
|
||||
"equity": -0.24252185
|
||||
},
|
||||
{
|
||||
"move": "9/5 9/4",
|
||||
"ply": 0,
|
||||
"equity": -0.2440444
|
||||
},
|
||||
{
|
||||
"move": "9/4 6/2",
|
||||
"ply": 0,
|
||||
"equity": -0.24487257
|
||||
},
|
||||
{
|
||||
"move": "9/4 7/3",
|
||||
"ply": 0,
|
||||
"equity": -0.24512678
|
||||
},
|
||||
{
|
||||
"move": "11/6 5/1",
|
||||
"ply": 0,
|
||||
"equity": -0.25016445
|
||||
},
|
||||
{
|
||||
"move": "9/4 5/1",
|
||||
"ply": 0,
|
||||
"equity": -0.25572774
|
||||
},
|
||||
{
|
||||
"move": "7/2 5/1",
|
||||
"ply": 0,
|
||||
"equity": -0.258138
|
||||
},
|
||||
{
|
||||
"move": "7/3 6/1",
|
||||
"ply": 0,
|
||||
"equity": -0.25878713
|
||||
},
|
||||
{
|
||||
"move": "7/2 6/2",
|
||||
"ply": 0,
|
||||
"equity": -0.26088732
|
||||
},
|
||||
{
|
||||
"move": "13/9 6/1",
|
||||
"ply": 0,
|
||||
"equity": -0.26104733
|
||||
},
|
||||
{
|
||||
"move": "9/5 6/1",
|
||||
"ply": 0,
|
||||
"equity": -0.26680842
|
||||
},
|
||||
{
|
||||
"move": "11/7 6/1",
|
||||
"ply": 0,
|
||||
"equity": -0.2670043
|
||||
},
|
||||
{
|
||||
"move": "6/2 6/1",
|
||||
"ply": 0,
|
||||
"equity": -0.27360544
|
||||
},
|
||||
{
|
||||
"move": "6/1 5/1",
|
||||
"ply": 0,
|
||||
"equity": -0.31466243
|
||||
}
|
||||
]
|
||||
@@ -154,42 +183,52 @@
|
||||
"ranking": [
|
||||
{
|
||||
"move": "13/9 11/6",
|
||||
"ply": 2,
|
||||
"equity": -0.21922025
|
||||
},
|
||||
{
|
||||
"move": "13/9 13/8",
|
||||
"ply": 2,
|
||||
"equity": -0.2202243
|
||||
},
|
||||
{
|
||||
"move": "11/6 9/5",
|
||||
"ply": 2,
|
||||
"equity": -0.22022447
|
||||
},
|
||||
{
|
||||
"move": "13/8 9/5",
|
||||
"ply": 2,
|
||||
"equity": -0.22088149
|
||||
},
|
||||
{
|
||||
"move": "11/7 11/6",
|
||||
"ply": 2,
|
||||
"equity": -0.22277355
|
||||
},
|
||||
{
|
||||
"move": "13/4",
|
||||
"ply": 2,
|
||||
"equity": -0.2230124
|
||||
},
|
||||
{
|
||||
"move": "6/1 5/1",
|
||||
"ply": 2,
|
||||
"equity": -0.29347342
|
||||
},
|
||||
{
|
||||
"move": "11/7 9/4",
|
||||
"ply": 1,
|
||||
"equity": -0.22985312
|
||||
},
|
||||
{
|
||||
"move": "13/8 11/7",
|
||||
"ply": 1,
|
||||
"equity": -0.23169218
|
||||
},
|
||||
{
|
||||
"move": "13/9 7/2",
|
||||
"ply": 1,
|
||||
"equity": -0.23561361
|
||||
}
|
||||
]
|
||||
|
||||
@@ -210,6 +210,36 @@ def compare_move_analysis(report, cli, case):
|
||||
report.number(where, "equity_loss", expected["equity_loss"], actual["equity_loss"])
|
||||
same_label(report, where, "skill", expected["skill"], actual["skill"],
|
||||
expected["equity_loss"], SKILL_THRESHOLDS)
|
||||
compare_analysis_ranking(report, where, expected, actual)
|
||||
|
||||
|
||||
def compare_analysis_ranking(report, where, expected, actual):
|
||||
"""Classement de l'analyse : les dix premiers coups de gnubg (le moteur
|
||||
n'en rend pas plus), au même niveau et à la même équité, à la tolérance
|
||||
près ; le meilleur coup et le coup joué y ont exactement les évaluations
|
||||
de best et played."""
|
||||
engine = actual["ranking"]
|
||||
reference = expected["ranking"][:10]
|
||||
report.same(where, "taille du classement", len(reference), len(engine))
|
||||
by_move = {m["move"]: m for m in engine}
|
||||
for m in reference:
|
||||
other = by_move.get(m["move"])
|
||||
here = "%s classement %s" % (where, m["move"])
|
||||
if other is None:
|
||||
# Un coup à égalité avec le dixième peut changer de côté.
|
||||
tied = abs(m["equity"] - reference[-1]["equity"]) <= report.tolerance
|
||||
if not tied:
|
||||
report.same(here, "présent", True, False)
|
||||
continue
|
||||
report.same(here, "ply", m["ply"], other["ply"])
|
||||
report.number(here, "equity", m["equity"], other["equity"])
|
||||
if engine:
|
||||
report.same(where, "classement, meilleur coup", actual["best"], engine[0])
|
||||
for m in engine:
|
||||
if m["move"] == actual["played"]["move"]:
|
||||
report.same(where, "classement, coup joué", actual["played"], m)
|
||||
report.same(where, "classement, rang du coup joué", actual["rank"],
|
||||
engine.index(m) + 1)
|
||||
|
||||
|
||||
def compare_cube_analysis(report, cli, case):
|
||||
|
||||
@@ -221,15 +221,25 @@ def set_analysis(ply):
|
||||
|
||||
|
||||
def analyse_game():
|
||||
"""Analyse la partie en cours ; renvoie ses enregistrements et ses
|
||||
statistiques par joueur (« X », « O »). Le cache est vidé avant : les
|
||||
"""Analyse la partie en cours ; renvoie ses enregistrements, ses
|
||||
statistiques par joueur (« X », « O ») et les informations du match. Le cache est vidé avant : les
|
||||
évaluations à 2 plis faites pour choisir un coup y laissent des
|
||||
résultats à 0 pli des réseaux d'élagage, que l'analyse à 0 pli
|
||||
reprendrait (voir analyse_move)."""
|
||||
gnubg.command("clear cache")
|
||||
gnubg.command("analyse game")
|
||||
game = gnubg.match(analysis=1, verbose=1, statistics=1, boards=1)["games"][-1]
|
||||
return game["game"], game["stats"]
|
||||
match = gnubg.match(analysis=1, verbose=1, statistics=1, boards=1)
|
||||
game = match["games"][-1]
|
||||
return game["game"], game["stats"], match["match-info"]
|
||||
|
||||
|
||||
def move_plies(match_info, move):
|
||||
"""Niveau auquel gnubg a évalué un coup de l'analyse : gnubg.match ne
|
||||
donne le réglage d'un coup (« evalcontext ») que s'il diffère du réglage
|
||||
par défaut de la partie (« default-eval-context », celui du premier coup
|
||||
évalué rencontré)."""
|
||||
context = move.get("evalcontext", {})
|
||||
return context.get("plies", match_info["default-eval-context"]["plies"])
|
||||
|
||||
|
||||
def last_record(records, action):
|
||||
@@ -280,7 +290,7 @@ def analyse_move(case, ply):
|
||||
# vient de jouer est la position retournée.
|
||||
played_id = gnubg.positionid(swapped(gnubg.board()))
|
||||
set_analysis(ply)
|
||||
records, stats = analyse_game()
|
||||
records, stats, match_info = analyse_game()
|
||||
record = last_record(records, "move")
|
||||
analysis = record["analysis"]
|
||||
moves, index = analysis["moves"], analysis["imove"]
|
||||
@@ -304,7 +314,10 @@ def analyse_move(case, ply):
|
||||
# comptée en positif.
|
||||
"equity_loss": f32(-stats[record["player"]]["moves"]["error-skill"]),
|
||||
"skill": SKILLS[analysis.get("skill")],
|
||||
"ranking": [{"move": notation(m["move"]), "equity": f32(m["score"])} for m in kept],
|
||||
# Avec le niveau de chaque coup : les coups écartés par les filtres
|
||||
# gardent celui d'un niveau inférieur (voir README.md).
|
||||
"ranking": [{"move": notation(m["move"]), "ply": move_plies(match_info, m),
|
||||
"equity": f32(m["score"])} for m in kept],
|
||||
}
|
||||
luck = {"value": f32(analysis["luck-value"]), "rating": LUCKS[analysis.get("luck")]}
|
||||
return level, luck
|
||||
@@ -322,7 +335,7 @@ def analyse_cube(case, ply):
|
||||
# meilleur coup, l'analyse du videau portant sur la position avant.
|
||||
gnubg.command("set dice 2 1")
|
||||
gnubg.command("move " + best_move())
|
||||
records, stats = analyse_game()
|
||||
records, stats, _ = analyse_game()
|
||||
record = last_record(records, "move")
|
||||
analysed = "nd-cubeful-eq" in record["analysis"]
|
||||
skill = record["analysis"].get("cube-skill")
|
||||
@@ -332,7 +345,7 @@ def analyse_cube(case, ply):
|
||||
# du double.
|
||||
gnubg.command("double")
|
||||
gnubg.command("drop" if action == "pass" else "take")
|
||||
records, stats = analyse_game()
|
||||
records, stats, _ = analyse_game()
|
||||
analysed = "nd-cubeful-eq" in last_record(records, "double").get("analysis", {})
|
||||
record = last_record(records, {"double": "double", "take": "take", "pass": "drop"}[action])
|
||||
skill = record.get("analysis", {}).get("skill")
|
||||
|
||||
@@ -130,7 +130,8 @@ if .type == "move" then
|
||||
and (.played_position_id | test("^[A-Za-z0-9+/]{14}$")) and (.played_equity | nombre)
|
||||
and (.best_move | type) == "string" and (.best_equity | nombre)
|
||||
and (.equity_loss | nombre) and .equity_loss <= 0 and (.skill | skill)
|
||||
and (.ranking | length) >= ([.rank, ([.move_count, 10] | min)] | max))
|
||||
and (.ranking | length) >= ([.rank, ([.move_count, 10] | min)] | max)
|
||||
and all(.ranking[]; (.move | type) == "string" and (.ply | nombre) and (.equity | nombre)))
|
||||
then empty else "analyse de coup incomplète" end)
|
||||
elif .type == "cube" then
|
||||
(if (.plies | keys) == ["0", "2"] and (.action | IN("no_double", "double", "take", "pass"))
|
||||
|
||||
Reference in New Issue
Block a user