All files index.ts

100% Statements 21/21
100% Branches 16/16
100% Functions 3/3
100% Lines 20/20

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;
}