Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | 19x 8x 8x 207x 5x 5x 6x 41x 6x 6x 35x 7x 7x 7x 28x 28x 10x 10x 10x 18x | /**
* 2026 Zenith Tier: Backgammon Domain Implementation.
*/
import { tCommon as translate } from "@multi-game-engines/i18n-common";
import { Brand,
Move,
createMove,
EngineError,
EngineErrorCode,
IBaseSearchOptions,
IBaseSearchInfo,
IBaseSearchResult,
truncateLog,
createI18nKey } from "@multi-game-engines/core";
/**
* バックギャモンの盤面表現。
* 26要素の配列(0: 白バー, 1-24: ポイント, 25: 黒バー)。
*/
export type BackgammonBoard = Brand<number[], "BackgammonBoard">;
/**
* バックギャモン盤面バリデータファクトリ。
*/
export function createBackgammonBoard(board: unknown): BackgammonBoard {
if (!Array.isArray(board) || board.length !== 26) {
const i18nKey = createI18nKey("engine.errors.invalidBackgammonBoard");
throw new EngineError({
code: EngineErrorCode.VALIDATION_ERROR,
message: translate(i18nKey),
i18nKey,
});
}
if (!board.every((v) => typeof v === "number" && Number.isFinite(v))) {
const i18nKey = createI18nKey("engine.errors.invalidBackgammonBoard");
throw new EngineError({
code: EngineErrorCode.VALIDATION_ERROR,
message: translate(i18nKey),
i18nKey,
});
}
return board as BackgammonBoard;
}
/**
* バックギャモンの指し手表現(例: "24/18 18/13")。
*/
export type BackgammonMove = Move<"BackgammonMove">;
/**
* バックギャモン指し手バリデータファクトリ。
*/
export function createBackgammonMove(move: string): BackgammonMove {
if (typeof move !== "string" || move.trim().length === 0) {
const i18nKey = createI18nKey("engine.errors.invalidBackgammonMove");
throw new EngineError({
code: EngineErrorCode.VALIDATION_ERROR,
message: translate(i18nKey),
i18nKey,
});
}
// 2026 Best Practice: 制御文字(インジェクション試行)を早期に拒否
if (/[\r\n\t\f\v\0]/.test(move)) {
const i18nKey = createI18nKey("engine.errors.injectionDetected");
const i18nParams = { context: "Move", input: truncateLog(move) };
throw new EngineError({
code: EngineErrorCode.SECURITY_ERROR,
message: translate(i18nKey, i18nParams),
i18nKey,
i18nParams,
});
}
// 2026 Best Practice: バックギャモン記法のバリデーション
// bar/24, 6/off, 24/18 などをサポート。厳密なスペース分離。
const bgRegex = /^((?:bar|\d+)\/(?:off|\d+))( (?:bar|\d+)\/(?:off|\d+))*$/i;
if (!bgRegex.test(move)) {
const i18nKey = createI18nKey("engine.errors.invalidBackgammonMove");
const i18nParams = { move: truncateLog(move) };
throw new EngineError({
code: EngineErrorCode.VALIDATION_ERROR,
message: translate(i18nKey, i18nParams),
i18nKey,
i18nParams,
});
}
return createMove<"BackgammonMove">(move);
}
/**
* 探索オプション。
*/
export interface IBackgammonSearchOptions extends IBaseSearchOptions {
board: BackgammonBoard;
dice: [number, number];
cube?: number | undefined;
matchLength?: number | undefined;
[key: string]: unknown;
}
/**
* 探索状況。
*/
export interface IBackgammonSearchInfo extends IBaseSearchInfo {
equity: number;
winProbability: number;
winGammonProbability: number;
winBackgammonProbability: number;
depth?: number | undefined;
nodes?: number | undefined;
nps?: number | undefined;
hashfull?: number | undefined;
raw?: string | Record<string, unknown> | undefined;
[key: string]: unknown;
}
/**
* 探索結果。
*/
export interface IBackgammonSearchResult extends IBaseSearchResult {
bestMove: BackgammonMove | null;
equity: number;
raw?: string | Record<string, unknown> | undefined;
[key: string]: unknown;
}
|