All files index.ts

100% Statements 21/21
100% Branches 14/14
100% Functions 2/2
100% Lines 21/21

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                                                                                                                    12x 4x 4x           8x 8x 1x 1x           3x                       63x 3x 3x             60x   60x 60x 2x 2x           54x 9x 9x 9x             45x    
import { tCommon as translate } from "@multi-game-engines/i18n-common";
import {
  Brand,
  EngineError,
  EngineErrorCode,
  Move,
  createMove,
  createPositionString,
  ProtocolValidator,
  IBaseSearchOptions,
  IBaseSearchInfo,
  IBaseSearchResult,
  createI18nKey,
} from "@multi-game-engines/core";
 
/** 囲碁の盤面データ */
export type GOBoard = Brand<string, "GOBoard">;
/** 囲碁の 指し手 (GTP形式: A1-Z25 (skip I), pass, resign) */
export type GOMove = Move<"GOMove">;
 
/**
 * 囲碁の探索オプション。
 */
export interface IGoSearchOptions extends IBaseSearchOptions {
  size?: number | undefined;
  komi?: number | undefined;
  /** 盤面データ (SGF等) */
  board?: string | undefined;
  /** KataGo 分析インターバル (ms) */
  kataInterval?: number | undefined;
  [key: string]: unknown;
}
 
/**
 * 囲碁の探索状況。
 */
export interface IGoSearchInfo extends IBaseSearchInfo {
  winrate?: number | undefined;
  visits?: number | undefined;
  scoreLead?: number | undefined;
  pv?: GOMove[] | undefined;
  /** ヒートマップ(各点の支配率/重要度) */
  ownerMap?: number[] | undefined;
  [key: string]: unknown;
}
 
/**
 * 囲碁の探索結果。
 */
export interface IGoSearchResult extends IBaseSearchResult {
  bestMove: GOMove | null;
  [key: string]: unknown;
}
 
/**
 * 囲碁盤面データのバリデータファクトリ。
 */
export function createGOBoard(pos: string): GOBoard {
  if (typeof pos !== "string" || pos.trim().length === 0) {
    const i18nKey = createI18nKey("engine.errors.invalidGOBoard");
    throw new EngineError({
      code: EngineErrorCode.VALIDATION_ERROR,
      message: translate(i18nKey),
      i18nKey,
    });
  }
  ProtocolValidator.assertNoInjection(pos, "GOBoard");
  if (!/^[a-zA-Z0-9.\- ]+$/.test(pos)) {
    const i18nKey = createI18nKey("engine.errors.illegalCharacters");
    throw new EngineError({
      code: EngineErrorCode.SECURITY_ERROR,
      message: translate(i18nKey),
      i18nKey,
    });
  }
  return createPositionString<"GOBoard">(pos) as GOBoard;
}
 
/**
 * Validate and normalize a Go move string into a `GOMove`.
 *
 * @param move - The input move in GTP-like format (e.g., "A1", "t19", "pass", "resign"); whitespace-only strings are rejected.
 * @returns The validated move as a `GOMove`, normalized to lowercase.
 * @throws EngineError with code `SECURITY_ERROR` if `move` contains illegal characters or injection is detected.
 * @throws EngineError with code `VALIDATION_ERROR` if `move` is not a non-empty string or does not match the allowed Go move formats.
 */
export function createGOMove(move: string): GOMove {
  if (typeof move !== "string" || move.trim().length === 0) {
    const i18nKey = createI18nKey("engine.errors.invalidGOMove");
    throw new EngineError({
      code: EngineErrorCode.VALIDATION_ERROR,
      message: translate(i18nKey, { move }),
      i18nKey,
      i18nParams: { move },
    });
  }
  ProtocolValidator.assertNoInjection(move, "GOMove");
  // 2026 Best Practice: 正規化(小文字化)をバリデータ層で実施
  const normalized = move.toLowerCase();
  if (!/^[a-z0-9]+$/.test(normalized)) {
    const i18nKey = createI18nKey("engine.errors.illegalCharacters");
    throw new EngineError({
      code: EngineErrorCode.SECURITY_ERROR,
      message: translate(i18nKey),
      i18nKey,
    });
  }
  if (!/^([a-hj-z]([1-9]|1[0-9]|2[0-5])|pass|resign)$/.test(normalized)) {
    const i18nKey = createI18nKey("engine.errors.invalidGOMove");
    const i18nParams = { move };
    throw new EngineError({
      code: EngineErrorCode.VALIDATION_ERROR,
      message: translate(i18nKey, i18nParams),
      i18nKey,
      i18nParams,
    });
  }
  return createMove<"GOMove">(normalized);
}