llm-chess-mcp 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -3
- package/dist/chess-copy.js +235 -15
- package/dist/domain.d.ts +14 -4
- package/dist/domain.js +2 -0
- package/dist/engines/stockfish-info.js +8 -2
- package/dist/engines/stockfish.d.ts +3 -0
- package/dist/engines/stockfish.js +451 -77
- package/dist/explorer-retry.d.ts +2 -2
- package/dist/explorer-retry.js +15 -6
- package/dist/explorer-transport.d.ts +2 -0
- package/dist/explorer-transport.js +21 -2
- package/dist/explorer.js +29 -8
- package/dist/games.d.ts +2 -0
- package/dist/games.js +24 -6
- package/dist/http.js +6 -1
- package/dist/pgn.js +1 -0
- package/dist/server.js +25 -11
- package/dist/services.js +37 -5
- package/dist/string-length.d.ts +1 -0
- package/dist/string-length.js +6 -0
- package/dist/tool-fields.d.ts +3 -0
- package/dist/tool-fields.js +15 -0
- package/dist/tool-inputs.js +11 -10
- package/dist/tool-result.js +0 -2
- package/dist/tool-schemas.d.ts +2 -8
- package/dist/tool-schemas.js +97 -21
- package/dist/tools/analysis.js +53 -6
- package/dist/tools/candidates.js +45 -21
- package/dist/tools/explorer.js +15 -2
- package/dist/tools/move-boundary.d.ts +8 -0
- package/dist/tools/move-boundary.js +13 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -207,9 +207,10 @@ human-readable summary and must not be parsed as data.
|
|
|
207
207
|
`best / excellent / good / inaccuracy / mistake / blunder`.
|
|
208
208
|
- `maia3Prob` is a **human-likelihood**, not move quality. A high-probability move
|
|
209
209
|
can still be objectively bad.
|
|
210
|
-
-
|
|
211
|
-
|
|
212
|
-
|
|
210
|
+
- Successful analysis continuations return corresponding `pv` and `pvSan`
|
|
211
|
+
arrays of equal length in UCI and SAN. An invalid engine continuation is
|
|
212
|
+
rejected at the internal tool boundary instead of returning a truncated
|
|
213
|
+
`pvSan`.
|
|
213
214
|
|
|
214
215
|
## Candidate structure
|
|
215
216
|
|
package/dist/chess-copy.js
CHANGED
|
@@ -10,25 +10,41 @@ const CHESS_STATE_KEYS = Reflect.ownKeys(new Chess());
|
|
|
10
10
|
function squareColor(square) {
|
|
11
11
|
return ((square.charCodeAt(0) - 97 + Number(square[1])) % 2);
|
|
12
12
|
}
|
|
13
|
-
function minimumPawnCaptures(chess, color) {
|
|
14
|
-
const
|
|
13
|
+
function minimumPawnCaptures(chess, color, promotedPieces, promotedBishops) {
|
|
14
|
+
const requirements = chess
|
|
15
15
|
.findPiece({ type: "p", color })
|
|
16
16
|
.map((square) => ({
|
|
17
|
+
kind: "pawn",
|
|
17
18
|
advances: color === "w" ? Number(square[1]) - 2 : 7 - Number(square[1]),
|
|
18
19
|
file: square.charCodeAt(0) - 97,
|
|
19
|
-
}))
|
|
20
|
-
|
|
20
|
+
}));
|
|
21
|
+
for (const bishopColor of [0, 1]) {
|
|
22
|
+
for (let count = 0; count < promotedBishops[bishopColor]; count += 1) {
|
|
23
|
+
requirements.push({ kind: "bishop", color: bishopColor });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
for (let count = 0; count < promotedPieces; count += 1) {
|
|
27
|
+
requirements.push({ kind: "promotion" });
|
|
28
|
+
}
|
|
21
29
|
let costs = new Map([[0, 0]]);
|
|
22
|
-
for (const
|
|
30
|
+
for (const requirement of requirements) {
|
|
23
31
|
const next = new Map();
|
|
24
32
|
for (const [mask, cost] of costs) {
|
|
25
33
|
for (let original = 0; original < 8; original += 1) {
|
|
26
34
|
const bit = 1 << original;
|
|
27
35
|
if (mask & bit)
|
|
28
36
|
continue;
|
|
29
|
-
|
|
30
|
-
if (
|
|
31
|
-
|
|
37
|
+
let captures = 0;
|
|
38
|
+
if (requirement.kind === "pawn") {
|
|
39
|
+
captures = Math.abs(original - requirement.file);
|
|
40
|
+
if (captures > requirement.advances)
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
else if (requirement.kind === "bishop") {
|
|
44
|
+
const promotionRank = color === "w" ? 8 : 1;
|
|
45
|
+
const promotionColor = ((original + promotionRank) % 2);
|
|
46
|
+
captures = promotionColor === requirement.color ? 0 : 1;
|
|
47
|
+
}
|
|
32
48
|
const nextMask = mask | bit;
|
|
33
49
|
next.set(nextMask, Math.min(next.get(nextMask) ?? Infinity, cost + captures));
|
|
34
50
|
}
|
|
@@ -66,6 +82,69 @@ function clonedChess(chess) {
|
|
|
66
82
|
function exactFen(chess) {
|
|
67
83
|
return chess.fen({ forceEnpassantSquare: true });
|
|
68
84
|
}
|
|
85
|
+
const FILES = "abcdefgh";
|
|
86
|
+
function squareAt(file, rank) {
|
|
87
|
+
return `${FILES[file]}${rank}`;
|
|
88
|
+
}
|
|
89
|
+
function squareCoordinates(square) {
|
|
90
|
+
return [square.charCodeAt(0) - 97, Number(square[1])];
|
|
91
|
+
}
|
|
92
|
+
function squaresBetween(from, to) {
|
|
93
|
+
const [fromFile, fromRank] = squareCoordinates(from);
|
|
94
|
+
const [toFile, toRank] = squareCoordinates(to);
|
|
95
|
+
const fileDistance = toFile - fromFile;
|
|
96
|
+
const rankDistance = toRank - fromRank;
|
|
97
|
+
if (fileDistance !== 0 &&
|
|
98
|
+
rankDistance !== 0 &&
|
|
99
|
+
Math.abs(fileDistance) !== Math.abs(rankDistance)) {
|
|
100
|
+
return [];
|
|
101
|
+
}
|
|
102
|
+
const fileStep = Math.sign(fileDistance);
|
|
103
|
+
const rankStep = Math.sign(rankDistance);
|
|
104
|
+
const squares = [];
|
|
105
|
+
let file = fromFile + fileStep;
|
|
106
|
+
let rank = fromRank + rankStep;
|
|
107
|
+
while (file !== toFile || rank !== toRank) {
|
|
108
|
+
squares.push(squareAt(file, rank));
|
|
109
|
+
file += fileStep;
|
|
110
|
+
rank += rankStep;
|
|
111
|
+
}
|
|
112
|
+
return squares;
|
|
113
|
+
}
|
|
114
|
+
function priorFullmove(fields, previous) {
|
|
115
|
+
const fullmove = fields[5] ?? "";
|
|
116
|
+
if (!isSafeDecimal(fullmove, 1))
|
|
117
|
+
return null;
|
|
118
|
+
const value = Number(fullmove) - (previous === "b" ? 1 : 0);
|
|
119
|
+
return value >= 1 ? value : null;
|
|
120
|
+
}
|
|
121
|
+
function priorChess(setup, previous, castling, enPassant, halfmove, fullmove) {
|
|
122
|
+
try {
|
|
123
|
+
return new Chess([
|
|
124
|
+
setup.fen().split(" ")[0],
|
|
125
|
+
previous,
|
|
126
|
+
castling,
|
|
127
|
+
enPassant,
|
|
128
|
+
String(halfmove),
|
|
129
|
+
String(fullmove),
|
|
130
|
+
].join(" "));
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function reachesPosition(prior, move, currentFen) {
|
|
137
|
+
if (!prior)
|
|
138
|
+
return false;
|
|
139
|
+
try {
|
|
140
|
+
assertLegalPositionInternal(prior, false);
|
|
141
|
+
prior.move(move);
|
|
142
|
+
return exactFen(prior) === currentFen;
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
69
148
|
function hasPiece(chess, square, type, color) {
|
|
70
149
|
const piece = chess.get(square);
|
|
71
150
|
return piece?.type === type && piece.color === color;
|
|
@@ -106,6 +185,35 @@ function assertEnPassantPosition(chess) {
|
|
|
106
185
|
(turn === "w" && fields[5] === "1")) {
|
|
107
186
|
throw new ChessError("INVALID_FEN", "FEN en passant target does not match a double pawn move");
|
|
108
187
|
}
|
|
188
|
+
const fullmove = fields[5] ?? "";
|
|
189
|
+
if (!isSafeDecimal(fullmove, 1)) {
|
|
190
|
+
throw new ChessError("INVALID_FEN", "FEN fullmove number must be a positive safe decimal integer");
|
|
191
|
+
}
|
|
192
|
+
const setup = new Chess(fields.join(" "));
|
|
193
|
+
setup.remove(pawnSquare);
|
|
194
|
+
setup.put({ type: "p", color: pawnColor }, originSquare);
|
|
195
|
+
const priorFullmove = turn === "w" ? Number(fullmove) - 1 : Number(fullmove);
|
|
196
|
+
const priorFen = [
|
|
197
|
+
setup.fen().split(" ")[0],
|
|
198
|
+
pawnColor,
|
|
199
|
+
fields[2],
|
|
200
|
+
"-",
|
|
201
|
+
"0",
|
|
202
|
+
String(priorFullmove),
|
|
203
|
+
].join(" ");
|
|
204
|
+
const prior = new Chess(priorFen);
|
|
205
|
+
assertLegalPosition(prior);
|
|
206
|
+
try {
|
|
207
|
+
prior.move({ from: originSquare, to: pawnSquare });
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
throw new ChessError("INVALID_FEN", "FEN en passant target does not follow a legal double pawn move");
|
|
211
|
+
}
|
|
212
|
+
const transitioned = exactFen(prior).split(" ");
|
|
213
|
+
transitioned[3] = target;
|
|
214
|
+
if (transitioned.join(" ") !== fields.join(" ")) {
|
|
215
|
+
throw new ChessError("INVALID_FEN", "FEN en passant target does not match the previous position");
|
|
216
|
+
}
|
|
109
217
|
}
|
|
110
218
|
function moveDescriptor(move) {
|
|
111
219
|
const base = { from: move.from, to: move.to };
|
|
@@ -116,6 +224,111 @@ function isSafeDecimal(value, minimum) {
|
|
|
116
224
|
Number.isSafeInteger(Number(value)) &&
|
|
117
225
|
Number(value) >= minimum);
|
|
118
226
|
}
|
|
227
|
+
const CAPTURED_PIECES = ["p", "n", "b", "r", "q"];
|
|
228
|
+
function ordinaryDoubleCheckPredecessor(chess, king, checkers) {
|
|
229
|
+
const currentFen = exactFen(chess);
|
|
230
|
+
const fields = currentFen.split(" ");
|
|
231
|
+
const previous = chess.turn() === "w" ? "b" : "w";
|
|
232
|
+
const active = chess.turn();
|
|
233
|
+
const fullmove = priorFullmove(fields, previous);
|
|
234
|
+
const currentHalfmove = Number(fields[4]);
|
|
235
|
+
if (fullmove === null || !Number.isSafeInteger(currentHalfmove))
|
|
236
|
+
return false;
|
|
237
|
+
for (let movedIndex = 0; movedIndex < 2; movedIndex += 1) {
|
|
238
|
+
const to = checkers[movedIndex];
|
|
239
|
+
const other = checkers[1 - movedIndex];
|
|
240
|
+
const moved = chess.get(to);
|
|
241
|
+
const otherType = chess.get(other)?.type;
|
|
242
|
+
if (!moved ||
|
|
243
|
+
moved.color !== previous ||
|
|
244
|
+
(otherType !== "b" && otherType !== "r" && otherType !== "q")) {
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
for (const from of squaresBetween(other, king)) {
|
|
248
|
+
if (chess.get(from) !== undefined)
|
|
249
|
+
continue;
|
|
250
|
+
for (const captured of [undefined, ...CAPTURED_PIECES]) {
|
|
251
|
+
if (captured === "p" &&
|
|
252
|
+
(to[1] === "1" || to[1] === "8")) {
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
const halfmove = moved.type === "p" || captured ? 0 : currentHalfmove - 1;
|
|
256
|
+
if (halfmove < 0 || (moved.type === "p" || captured) && currentHalfmove !== 0) {
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
const setup = new Chess(currentFen);
|
|
260
|
+
setup.remove(to);
|
|
261
|
+
setup.put(moved, from);
|
|
262
|
+
if (captured)
|
|
263
|
+
setup.put({ type: captured, color: active }, to);
|
|
264
|
+
const prior = priorChess(setup, previous, fields[2], "-", halfmove, fullmove);
|
|
265
|
+
if (reachesPosition(prior, { from, to }, currentFen))
|
|
266
|
+
return true;
|
|
267
|
+
const promotionRank = previous === "w" ? "8" : "1";
|
|
268
|
+
const pawnRank = previous === "w" ? "7" : "2";
|
|
269
|
+
if (moved.type !== "p" &&
|
|
270
|
+
moved.type !== "k" &&
|
|
271
|
+
to[1] === promotionRank &&
|
|
272
|
+
from[1] === pawnRank) {
|
|
273
|
+
const promotedSetup = new Chess(currentFen);
|
|
274
|
+
promotedSetup.remove(to);
|
|
275
|
+
promotedSetup.put({ type: "p", color: previous }, from);
|
|
276
|
+
if (captured) {
|
|
277
|
+
promotedSetup.put({ type: captured, color: active }, to);
|
|
278
|
+
}
|
|
279
|
+
const promotionPrior = priorChess(promotedSetup, previous, fields[2], "-", 0, fullmove);
|
|
280
|
+
if (currentHalfmove === 0 &&
|
|
281
|
+
reachesPosition(promotionPrior, { from, to, promotion: moved.type }, currentFen)) {
|
|
282
|
+
return true;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return false;
|
|
289
|
+
}
|
|
290
|
+
function enPassantDoubleCheckPredecessor(chess) {
|
|
291
|
+
const currentFen = exactFen(chess);
|
|
292
|
+
const fields = currentFen.split(" ");
|
|
293
|
+
if (fields[3] !== "-" || fields[4] !== "0")
|
|
294
|
+
return false;
|
|
295
|
+
const previous = chess.turn() === "w" ? "b" : "w";
|
|
296
|
+
const active = chess.turn();
|
|
297
|
+
const fullmove = priorFullmove(fields, previous);
|
|
298
|
+
if (fullmove === null)
|
|
299
|
+
return false;
|
|
300
|
+
const destinationRank = previous === "w" ? 6 : 3;
|
|
301
|
+
const originRank = previous === "w" ? 5 : 4;
|
|
302
|
+
for (const to of chess.findPiece({ type: "p", color: previous })) {
|
|
303
|
+
if (Number(to[1]) !== destinationRank)
|
|
304
|
+
continue;
|
|
305
|
+
const [toFile] = squareCoordinates(to);
|
|
306
|
+
const capturedSquare = squareAt(toFile, originRank);
|
|
307
|
+
if (chess.get(capturedSquare) !== undefined)
|
|
308
|
+
continue;
|
|
309
|
+
for (const originFile of [toFile - 1, toFile + 1]) {
|
|
310
|
+
if (originFile < 0 || originFile > 7)
|
|
311
|
+
continue;
|
|
312
|
+
const from = squareAt(originFile, originRank);
|
|
313
|
+
if (chess.get(from) !== undefined)
|
|
314
|
+
continue;
|
|
315
|
+
const setup = new Chess(currentFen);
|
|
316
|
+
setup.remove(to);
|
|
317
|
+
setup.put({ type: "p", color: previous }, from);
|
|
318
|
+
setup.put({ type: "p", color: active }, capturedSquare);
|
|
319
|
+
const prior = priorChess(setup, previous, fields[2], to, 0, fullmove);
|
|
320
|
+
if (reachesPosition(prior, { from, to }, currentFen))
|
|
321
|
+
return true;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return false;
|
|
325
|
+
}
|
|
326
|
+
function hasDoubleCheckPredecessor(chess, king, checkers) {
|
|
327
|
+
if (exactFen(chess).split(" ")[3] !== "-")
|
|
328
|
+
return true;
|
|
329
|
+
return (ordinaryDoubleCheckPredecessor(chess, king, checkers) ||
|
|
330
|
+
enPassantDoubleCheckPredecessor(chess));
|
|
331
|
+
}
|
|
119
332
|
export function assertSafeFenCounters(fen) {
|
|
120
333
|
const fields = fen.split(/\s+/);
|
|
121
334
|
if (fields.length >= 5 && !isSafeDecimal(fields[4] ?? "", 0)) {
|
|
@@ -126,6 +339,9 @@ export function assertSafeFenCounters(fen) {
|
|
|
126
339
|
}
|
|
127
340
|
}
|
|
128
341
|
export function assertLegalPosition(chess) {
|
|
342
|
+
assertLegalPositionInternal(chess, true);
|
|
343
|
+
}
|
|
344
|
+
function assertLegalPositionInternal(chess, validateDoubleCheck) {
|
|
129
345
|
for (const color of ["w", "b"]) {
|
|
130
346
|
if (chess.findPiece({ type: "k", color }).length !== 1) {
|
|
131
347
|
throw new ChessError("INVALID_FEN", "FEN must contain exactly one king per side");
|
|
@@ -140,19 +356,18 @@ export function assertLegalPosition(chess) {
|
|
|
140
356
|
const promotedPieces = Object.entries(ORIGINAL_PIECES).reduce((total, [type, original]) => total +
|
|
141
357
|
Math.max(0, chess.findPiece({ type: type, color }).length -
|
|
142
358
|
original), 0);
|
|
143
|
-
const promotedBishops = [0, 1].
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
const promoted = promotedPieces + promotedBishops;
|
|
359
|
+
const promotedBishops = [0, 1].map((squareColorValue) => Math.max(0, chess
|
|
360
|
+
.findPiece({ type: "b", color })
|
|
361
|
+
.filter((square) => squareColor(square) === squareColorValue)
|
|
362
|
+
.length - 1));
|
|
363
|
+
const promoted = promotedPieces + promotedBishops[0] + promotedBishops[1];
|
|
149
364
|
if (promoted > 8 - pawns.length) {
|
|
150
365
|
throw new ChessError("INVALID_FEN", "FEN contains more promoted material than missing pawns allow");
|
|
151
366
|
}
|
|
152
367
|
assertCastlingPosition(chess, color);
|
|
153
368
|
const opponent = color === "w" ? "b" : "w";
|
|
154
369
|
const missingOpponentMaterial = 15 - nonKingMaterial(chess, opponent);
|
|
155
|
-
if (minimumPawnCaptures(chess, color) > missingOpponentMaterial) {
|
|
370
|
+
if (minimumPawnCaptures(chess, color, promotedPieces, promotedBishops) > missingOpponentMaterial) {
|
|
156
371
|
throw new ChessError("INVALID_FEN", "FEN pawn files require more captures than opposing material allows");
|
|
157
372
|
}
|
|
158
373
|
}
|
|
@@ -173,6 +388,11 @@ export function assertLegalPosition(chess) {
|
|
|
173
388
|
if (checkers.length > 2 || leapers.length > 1) {
|
|
174
389
|
throw new ChessError("INVALID_FEN", "FEN contains an impossible check topology");
|
|
175
390
|
}
|
|
391
|
+
if (validateDoubleCheck &&
|
|
392
|
+
checkers.length === 2 &&
|
|
393
|
+
!hasDoubleCheckPredecessor(chess, king, checkers)) {
|
|
394
|
+
throw new ChessError("INVALID_FEN", "FEN double check has no legal previous move");
|
|
395
|
+
}
|
|
176
396
|
}
|
|
177
397
|
}
|
|
178
398
|
function expectedInitialFen(headers) {
|
package/dist/domain.d.ts
CHANGED
|
@@ -13,6 +13,8 @@ export declare const MOVE_EVALUATION_RESULTS: readonly ["ongoing", "checkmate",
|
|
|
13
13
|
export type MoveEvaluationResult = (typeof MOVE_EVALUATION_RESULTS)[number];
|
|
14
14
|
export declare const ANALYSIS_LEVELS: readonly ["fast", "normal", "deep"];
|
|
15
15
|
export type AnalysisLevel = (typeof ANALYSIS_LEVELS)[number];
|
|
16
|
+
export declare const MAX_HUMAN_MOVES = 20;
|
|
17
|
+
export declare const HUMAN_PROBABILITY_TOLERANCE = 0.00001;
|
|
16
18
|
export declare const MOVE_CLASSIFICATIONS: readonly ["best", "excellent", "good", "inaccuracy", "mistake", "blunder"];
|
|
17
19
|
export type MoveClassification = (typeof MOVE_CLASSIFICATIONS)[number];
|
|
18
20
|
export declare const INTENTS: readonly ["best", "strong", "natural", "balanced", "ease_off", "give_chance"];
|
|
@@ -49,13 +51,21 @@ export interface ChessState {
|
|
|
49
51
|
};
|
|
50
52
|
}
|
|
51
53
|
export type Wdl = [number, number, number];
|
|
52
|
-
|
|
54
|
+
type SfScore = {
|
|
55
|
+
scoreCp: number;
|
|
56
|
+
scoreMate: null;
|
|
57
|
+
} | {
|
|
58
|
+
scoreCp: null;
|
|
59
|
+
scoreMate: number;
|
|
60
|
+
} | {
|
|
61
|
+
scoreCp: null;
|
|
62
|
+
scoreMate: null;
|
|
63
|
+
};
|
|
64
|
+
export type SfLine = {
|
|
53
65
|
multipv: number;
|
|
54
|
-
scoreCp: number | null;
|
|
55
|
-
scoreMate: number | null;
|
|
56
66
|
wdl: Wdl | null;
|
|
57
67
|
pv: string[];
|
|
58
|
-
}
|
|
68
|
+
} & SfScore;
|
|
59
69
|
export interface Maia3Move {
|
|
60
70
|
uci: string;
|
|
61
71
|
san: string;
|
package/dist/domain.js
CHANGED
|
@@ -25,6 +25,8 @@ export const MOVE_EVALUATION_RESULTS = [
|
|
|
25
25
|
...DRAW_RESULTS,
|
|
26
26
|
];
|
|
27
27
|
export const ANALYSIS_LEVELS = ["fast", "normal", "deep"];
|
|
28
|
+
export const MAX_HUMAN_MOVES = 20;
|
|
29
|
+
export const HUMAN_PROBABILITY_TOLERANCE = 1e-5;
|
|
28
30
|
export const MOVE_CLASSIFICATIONS = [
|
|
29
31
|
"best",
|
|
30
32
|
"excellent",
|
|
@@ -58,10 +58,16 @@ export function parseAnalysisInfo(line) {
|
|
|
58
58
|
};
|
|
59
59
|
}
|
|
60
60
|
export function mergeAnalysisInfo(previous, info) {
|
|
61
|
+
const scoreCp = info.score ? info.score.cp : (previous?.scoreCp ?? null);
|
|
62
|
+
const scoreMate = info.score ? info.score.mate : (previous?.scoreMate ?? null);
|
|
63
|
+
const scoreFields = scoreCp !== null
|
|
64
|
+
? { scoreCp, scoreMate: null }
|
|
65
|
+
: scoreMate !== null
|
|
66
|
+
? { scoreCp: null, scoreMate }
|
|
67
|
+
: { scoreCp: null, scoreMate: null };
|
|
61
68
|
return {
|
|
62
69
|
multipv: info.multipv,
|
|
63
|
-
|
|
64
|
-
scoreMate: info.score ? info.score.mate : (previous?.scoreMate ?? null),
|
|
70
|
+
...scoreFields,
|
|
65
71
|
wdl: info.wdl ?? previous?.wdl ?? null,
|
|
66
72
|
pv: info.pv ?? previous?.pv ?? [],
|
|
67
73
|
};
|
|
@@ -33,12 +33,15 @@ export declare class Stockfish {
|
|
|
33
33
|
private teardownPending;
|
|
34
34
|
private readonly drainWaiters;
|
|
35
35
|
private readonly terminations;
|
|
36
|
+
private readonly processListenerCleanups;
|
|
36
37
|
private readonly initEngine;
|
|
37
38
|
private readonly configuredFlavor;
|
|
38
39
|
private readonly maxQueue;
|
|
39
40
|
private readonly timeouts;
|
|
40
41
|
constructor(options?: StockfishOptions);
|
|
41
42
|
private disposeInitEngine;
|
|
43
|
+
private claimProcessListenerCleanups;
|
|
44
|
+
private moveProcessListenerCleanups;
|
|
42
45
|
private adoptInitEngine;
|
|
43
46
|
private completeInit;
|
|
44
47
|
private init;
|