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 | 3x 3x 3x 1x 2x 2x 1x 4x 4x 15x 14x 5x 5x 5x 21x 21x 21x 5x 5x 1x 1x 1x 1x 5x 5x 5x 5x 5x 4x 6x 6x 1x 6x 4x 4x 21x 11x 10x 1x 1x 9x | import { IProtocolParser, ScoreNormalizer, PositionId, MiddlewareCommand, ProtocolValidator } from "@multi-game-engines/core";
import { IJanggiSearchOptions,
IJanggiSearchInfo,
IJanggiSearchResult,
createJanggiMove, } from "@multi-game-engines/domain-janggi";
export class JanggiParser implements IProtocolParser<
IJanggiSearchOptions,
IJanggiSearchInfo,
IJanggiSearchResult
> {
createSearchCommand(options: IJanggiSearchOptions): MiddlewareCommand {
ProtocolValidator.assertNoInjection(options, "JanggiSearchOptions", true);
const commands: string[] = [];
if (options.position) {
commands.push(`position ${options.position}`);
}
commands.push("go");
return commands;
}
createStopCommand(): MiddlewareCommand {
return "stop";
}
createOptionCommand(name: string, value: unknown): MiddlewareCommand {
ProtocolValidator.assertNoInjection({ name, value }, "JanggiOption", true);
return `setoption name ${name} value ${value}`;
}
parseInfo(
data: string | Record<string, unknown>,
positionId?: PositionId,
): IJanggiSearchInfo | null {
if (typeof data !== "string") return null;
if (!data.startsWith("info ")) return null;
const info: IJanggiSearchInfo = { raw: data, positionId };
const parts = data.split(/\s+/);
for (let i = 0; i < parts.length; i++) {
const token = parts[i];
Iif (!token) continue;
switch (token) {
case "depth":
info.depth = parseInt(parts[++i] || "0", 10);
break;
case "nodes":
info.nodes = parseInt(parts[++i] || "0", 10);
break;
case "nps":
info.nps = parseInt(parts[++i] || "0", 10);
break;
case "score": {
const type = parts[++i];
const val = parseInt(parts[++i] || "0", 10);
Eif (type === "cp" || type === "mate") {
info.score = {
unit: type,
[type]: val,
normalized: ScoreNormalizer.normalize(val, type, "janggi"),
};
}
break;
}
case "pv": {
const pvMoves = parts.slice(i + 1).map((m) => {
try {
return createJanggiMove(m);
} catch {
return null;
}
});
info.pv = pvMoves.filter((m) => m !== null);
i = parts.length; // PV is always last
break;
}
}
}
return info;
}
parseResult(
data: string | Record<string, unknown>,
): IJanggiSearchResult | null {
if (typeof data !== "string") return null;
if (data.startsWith("bestmove ")) {
const parts = data.split(/\s+/);
return {
bestMove: parts[1] ? createJanggiMove(parts[1]) : null,
raw: data,
};
}
return null;
}
}
|