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