pgn-manager 2.0.0 → 2.2.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/dist/index.d.ts +2 -0
- package/dist/index.js +93 -44
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -12,6 +12,8 @@ declare class PGNManager {
|
|
|
12
12
|
private sortedMoves;
|
|
13
13
|
/** Map of moves to their FEN position strings */
|
|
14
14
|
private moveFen;
|
|
15
|
+
/** Map of FEN position string to their move object */
|
|
16
|
+
private fenMove;
|
|
15
17
|
/** Map of moves to their parent variations (or null for mainline) */
|
|
16
18
|
private moveParent;
|
|
17
19
|
/** Map of variations to their parent moves */
|
package/dist/index.js
CHANGED
|
@@ -16,6 +16,8 @@ class PGNManager {
|
|
|
16
16
|
sortedMoves;
|
|
17
17
|
/** Map of moves to their FEN position strings */
|
|
18
18
|
moveFen;
|
|
19
|
+
/** Map of FEN position string to their move object */
|
|
20
|
+
fenMove = new Map();
|
|
19
21
|
/** Map of moves to their parent variations (or null for mainline) */
|
|
20
22
|
moveParent;
|
|
21
23
|
/** Map of variations to their parent moves */
|
|
@@ -33,6 +35,7 @@ class PGNManager {
|
|
|
33
35
|
this.moveParent = new Map();
|
|
34
36
|
this.ravParent = new Map();
|
|
35
37
|
this.moveFen = new Map();
|
|
38
|
+
this.fenMove = new Map();
|
|
36
39
|
this.moveColor = new Map();
|
|
37
40
|
this.dfOnGame(this.game);
|
|
38
41
|
}
|
|
@@ -42,7 +45,8 @@ class PGNManager {
|
|
|
42
45
|
*/
|
|
43
46
|
dfOnGame = (game) => {
|
|
44
47
|
this.sortedMoves = [];
|
|
45
|
-
var chessGame = new Chess(
|
|
48
|
+
var chessGame = new Chess(game.headers?.find((h) => h.name.toUpperCase() === "FEN")?.value ||
|
|
49
|
+
exports.FEN_START_POSITION);
|
|
46
50
|
for (let move of game.moves) {
|
|
47
51
|
this.dfsOnGame(move, game, chessGame);
|
|
48
52
|
}
|
|
@@ -70,6 +74,7 @@ class PGNManager {
|
|
|
70
74
|
!chessGame.move(move.move, { sloppy: true }))
|
|
71
75
|
console.log("Invalid move: " + move.move);
|
|
72
76
|
this.moveFen.set(move, chessGame.fen());
|
|
77
|
+
this.fenMove.set(chessGame.fen(), move);
|
|
73
78
|
this.moveColor.set(move, chessGame.turn() === "w" ? "b" : "w");
|
|
74
79
|
};
|
|
75
80
|
/**
|
|
@@ -267,56 +272,99 @@ class PGNManager {
|
|
|
267
272
|
* @throws Error if the move parameter is invalid
|
|
268
273
|
*/
|
|
269
274
|
pushMove = (moveId, newMove, result = "*") => {
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
+
let chess;
|
|
276
|
+
let parentRav;
|
|
277
|
+
let current = null;
|
|
278
|
+
// 1) Initialize Chess and parent variation
|
|
279
|
+
if (moveId === 0) {
|
|
280
|
+
parentRav = this.parsedPGN;
|
|
281
|
+
const fenHdr = this.headers.find((h) => h.name.toLowerCase() === "fen");
|
|
282
|
+
const startFen = fenHdr ? fenHdr.value : exports.FEN_START_POSITION;
|
|
283
|
+
chess = new Chess(startFen);
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
286
|
+
current = this.getMove(moveId);
|
|
287
|
+
if (!current)
|
|
288
|
+
throw new Error("Invalid moveId while pushing a new move!");
|
|
289
|
+
parentRav = this.moveParent.get(current) || this.parsedPGN;
|
|
290
|
+
chess = new Chess(this.getMoveFen(current));
|
|
275
291
|
}
|
|
276
|
-
//
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
if (
|
|
280
|
-
|
|
292
|
+
// 2) Play the SAN move (strict → sloppy)
|
|
293
|
+
const played = chess.move(newMove, { sloppy: false }) ||
|
|
294
|
+
chess.move(newMove, { sloppy: true });
|
|
295
|
+
if (!played)
|
|
296
|
+
throw new Error("Invalid move");
|
|
297
|
+
const san = chess.history().slice(-1)[0];
|
|
298
|
+
const nextToMove = chess.turn(); // 'w' or 'b'
|
|
299
|
+
// If the FEN already exists, we are trying to add a move that is already played
|
|
300
|
+
if (this.fenMove.has(chess.fen())) {
|
|
301
|
+
return this.fenMove.get(chess.fen());
|
|
281
302
|
}
|
|
282
|
-
//
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
303
|
+
// 3) Build move object with provisional move_number
|
|
304
|
+
const provisionalNumber = (() => {
|
|
305
|
+
if (!current) {
|
|
306
|
+
return 1; // brand‐new mainline
|
|
307
|
+
}
|
|
308
|
+
const currNum = current.move_number || this.previousMove(current)?.move_number;
|
|
309
|
+
const currColor = this.getMoveColor(current);
|
|
310
|
+
return currColor === "w" ? currNum : currNum + 1;
|
|
311
|
+
})();
|
|
312
|
+
const moveObj = {
|
|
313
|
+
move: san,
|
|
314
|
+
ravs: undefined,
|
|
315
|
+
move_number: provisionalNumber,
|
|
316
|
+
comments: [],
|
|
317
|
+
};
|
|
318
|
+
// 4) Insert into structure & flag first‐of‐variation
|
|
319
|
+
let isFirstOfVariation = false;
|
|
320
|
+
if (!current) {
|
|
321
|
+
// brand-new mainline => variation of first move if exists
|
|
322
|
+
const first = parentRav.moves[0];
|
|
323
|
+
if (first) {
|
|
324
|
+
isFirstOfVariation = true;
|
|
325
|
+
const newRav = { moves: [moveObj], result };
|
|
326
|
+
first.ravs = first.ravs || [];
|
|
327
|
+
first.ravs.push(newRav);
|
|
328
|
+
this.moveParent.set(moveObj, newRav);
|
|
329
|
+
this.ravParent.set(newRav, first);
|
|
330
|
+
}
|
|
331
|
+
else {
|
|
332
|
+
parentRav.moves.push(moveObj);
|
|
333
|
+
this.moveParent.set(moveObj, parentRav);
|
|
334
|
+
}
|
|
301
335
|
}
|
|
302
336
|
else {
|
|
303
|
-
move
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
337
|
+
// existing‐move branch
|
|
338
|
+
const lastInRav = parentRav.moves[parentRav.moves.length - 1];
|
|
339
|
+
const isContinuation = lastInRav === current;
|
|
340
|
+
if (!isContinuation) {
|
|
341
|
+
// new variation off the next move
|
|
342
|
+
isFirstOfVariation = true;
|
|
343
|
+
const anchor = this.nextMove(current) || current;
|
|
344
|
+
anchor.ravs = anchor.ravs || [];
|
|
345
|
+
const newRav = { moves: [moveObj], result };
|
|
346
|
+
anchor.ravs.push(newRav);
|
|
347
|
+
this.moveParent.set(moveObj, newRav);
|
|
348
|
+
this.ravParent.set(newRav, anchor);
|
|
349
|
+
}
|
|
350
|
+
else {
|
|
351
|
+
// continuation
|
|
352
|
+
parentRav.moves.push(moveObj);
|
|
353
|
+
this.moveParent.set(moveObj, parentRav);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
// 5) Conditionally remove move_number
|
|
357
|
+
// Keep it only if first‐of‐variation OR new move is White to play
|
|
358
|
+
if (!(isFirstOfVariation || nextToMove === "b")) {
|
|
359
|
+
delete moveObj.move_number;
|
|
316
360
|
}
|
|
361
|
+
// 6) Final bookkeeping
|
|
362
|
+
this.moveFen.set(moveObj, chess.fen());
|
|
363
|
+
this.fenMove.set(chess.fen(), moveObj);
|
|
364
|
+
this.moveColor.set(moveObj, nextToMove === "w" ? "b" : "w");
|
|
317
365
|
this.rawPGN = (0, utils_1.regeneratePGN)(this.game, this.moveColor);
|
|
318
366
|
this.dfOnGame(this.game);
|
|
319
|
-
return
|
|
367
|
+
return moveObj;
|
|
320
368
|
};
|
|
321
369
|
/**
|
|
322
370
|
* Delete a move and all subsequent moves in its variation from the game
|
|
@@ -348,6 +396,7 @@ class PGNManager {
|
|
|
348
396
|
// clean up all maps at once
|
|
349
397
|
movsToClean.forEach((move) => {
|
|
350
398
|
this.moveParent.delete(move);
|
|
399
|
+
this.fenMove.delete(this.getMoveFen(move));
|
|
351
400
|
this.moveFen.delete(move);
|
|
352
401
|
this.moveColor.delete(move);
|
|
353
402
|
});
|