pgn-manager 1.1.0 → 2.0.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 CHANGED
@@ -65,7 +65,6 @@ const headers = manager.headers;
65
65
  - `getLastMove()`: Get the last move of the game
66
66
  - `getMoveFen(moveOrMoveId: Move | number)`: Get FEN position after move
67
67
  - `getParentRav(moveOrMoveId: Move | number)`: Get parent variation for move
68
- - `getChessJSInstance(moveOrMoveId: Move | number)`: Get or create a ChessJS instance for the position after the specified move
69
68
  - `getMoveColor(moveOrMoveId: Move | number)`: Gets the color of the player who made the move ("w" for white or "b" for black)
70
69
 
71
70
  ## Examples 🎯
package/dist/index.d.ts CHANGED
@@ -1,4 +1,6 @@
1
- import { ParsedPGN, Move, Rav, Header } from "pgn-parser";
1
+ import { ShortMove } from "chess.js";
2
+ import * as pgnParser from "pgn-parser";
3
+ import type { ParsedPGN, Move, Rav, Header, Result } from "pgn-parser";
2
4
  export declare const FEN_START_POSITION = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
3
5
  export declare const FEN_EMPTY_POSITION = "8/8/8/8/8/8/8/8";
4
6
  declare class PGNManager {
@@ -53,7 +55,7 @@ declare class PGNManager {
53
55
  * @param moveNumber - The 1-based index of the move
54
56
  * @returns The move object at the specified position
55
57
  */
56
- getMove: (moveNumber: number) => Move;
58
+ getMove: (moveNumber: number) => pgnParser.Move;
57
59
  /**
58
60
  * Gets the number of a move in the sequence
59
61
  * @param move - The move object
@@ -85,13 +87,13 @@ declare class PGNManager {
85
87
  * @returns The first move
86
88
  * @throws Error if there are no moves in the game
87
89
  */
88
- getFirstMove: () => Move;
90
+ getFirstMove: () => pgnParser.Move;
89
91
  /**
90
92
  * Gets the last move in the game
91
93
  * @returns The last move
92
94
  * @throws Error if there are no moves in the game
93
95
  */
94
- getLastMove: () => any;
96
+ getLastMove: () => pgnParser.Move;
95
97
  /**
96
98
  * Gets the FEN string for a specific move
97
99
  * @param moveOrId - The move object or move number
@@ -113,5 +115,20 @@ declare class PGNManager {
113
115
  * @throws Error if the move parameter is invalid
114
116
  */
115
117
  getMoveColor: (moveOrId: Move | number) => "w" | "b";
118
+ /***
119
+ * Pushes a new move into the game
120
+ * @param moveId - The ID of the move to push
121
+ * @param newMove - The move object to add
122
+ * @param result - The result of the game after this move (default is "*")
123
+ * @returns The newly created move object
124
+ * @throws Error if the move parameter is invalid
125
+ */
126
+ pushMove: (moveId: number, newMove: ShortMove, result?: Result) => Move;
127
+ /**
128
+ * Delete a move and all subsequent moves in its variation from the game
129
+ * @param moveId - The ID of the move to delete from
130
+ * @throws Error if the move parameter is invalid
131
+ */
132
+ deleteMove: (moveId: number) => void;
116
133
  }
117
134
  export default PGNManager;
package/dist/index.js CHANGED
@@ -3,7 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.FEN_EMPTY_POSITION = exports.FEN_START_POSITION = void 0;
4
4
  const ChessJS = require("chess.js");
5
5
  const Chess = typeof ChessJS === "function" ? ChessJS : ChessJS.Chess;
6
- const pgn_parser_1 = require("pgn-parser");
6
+ const pgnParser = require("pgn-parser");
7
+ const utils_1 = require("./utils");
7
8
  exports.FEN_START_POSITION = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
8
9
  exports.FEN_EMPTY_POSITION = "8/8/8/8/8/8/8/8";
9
10
  class PGNManager {
@@ -27,7 +28,7 @@ class PGNManager {
27
28
  */
28
29
  constructor(pgn) {
29
30
  this.rawPGN = pgn;
30
- this.game = pgn_parser_1.default.parse(pgn + " *")[0];
31
+ this.game = pgnParser.parse(pgn + " *")[0];
31
32
  this.sortedMoves = [];
32
33
  this.moveParent = new Map();
33
34
  this.ravParent = new Map();
@@ -224,7 +225,7 @@ class PGNManager {
224
225
  */
225
226
  getMoveFen = (moveOrId) => {
226
227
  const move = typeof moveOrId === "number" ? this.getMove(moveOrId) : moveOrId;
227
- if (!move) {
228
+ if (!move || !this.moveFen.has(move)) {
228
229
  throw Error("Invalid 'move' parameter while getting fen");
229
230
  }
230
231
  let moveFen = this.moveFen.get(move);
@@ -238,7 +239,7 @@ class PGNManager {
238
239
  */
239
240
  getParentRav = (moveOrId) => {
240
241
  const move = typeof moveOrId === "number" ? this.getMove(moveOrId) : moveOrId;
241
- if (!move) {
242
+ if (!move || !this.moveParent.has(move)) {
242
243
  throw Error("Invalid 'move' parameter while getting parent rav");
243
244
  }
244
245
  let parentRav = this.moveParent.get(move);
@@ -252,10 +253,110 @@ class PGNManager {
252
253
  */
253
254
  getMoveColor = (moveOrId) => {
254
255
  const move = typeof moveOrId === "number" ? this.getMove(moveOrId) : moveOrId;
255
- if (!move) {
256
+ if (!move || !this.moveColor.has(move)) {
256
257
  throw Error("Invalid 'move' parameter while getting move color");
257
258
  }
258
259
  return this.moveColor.get(move);
259
260
  };
261
+ /***
262
+ * Pushes a new move into the game
263
+ * @param moveId - The ID of the move to push
264
+ * @param newMove - The move object to add
265
+ * @param result - The result of the game after this move (default is "*")
266
+ * @returns The newly created move object
267
+ * @throws Error if the move parameter is invalid
268
+ */
269
+ pushMove = (moveId, newMove, result = "*") => {
270
+ const currentMove = this.getMove(moveId);
271
+ // check if it's a valid move
272
+ const chess = new Chess(this.getMoveFen(currentMove));
273
+ if (!chess.move(newMove)) {
274
+ throw Error("Invalid move");
275
+ }
276
+ // check if this is a new variation
277
+ let isNewVariation = false;
278
+ const parentRav = this.getParentRav(currentMove);
279
+ if (parentRav.moves[parentRav.moves.length - 1] != currentMove) {
280
+ isNewVariation = true;
281
+ }
282
+ // add the move to the game
283
+ let move = undefined;
284
+ if (isNewVariation) {
285
+ move = {
286
+ move: chess.history().slice(-1)[0],
287
+ ravs: undefined,
288
+ move_number: currentMove.move_number,
289
+ comments: [],
290
+ };
291
+ const newVar = {
292
+ moves: [move],
293
+ result,
294
+ };
295
+ currentMove.ravs.push(newVar);
296
+ // update class variables
297
+ this.moveParent.set(move, newVar);
298
+ this.ravParent.set(newVar, currentMove);
299
+ this.moveFen.set(move, chess.fen());
300
+ this.moveColor.set(move, this.getMoveColor(currentMove));
301
+ }
302
+ else {
303
+ move = {
304
+ move: chess.history().slice(-1)[0],
305
+ ravs: undefined,
306
+ move_number: this.getMoveColor(currentMove) === "w"
307
+ ? undefined
308
+ : currentMove.move_number + 1,
309
+ comments: [],
310
+ };
311
+ parentRav.moves.push(move);
312
+ // update class variables
313
+ this.moveParent.set(move, parentRav);
314
+ this.moveFen.set(move, chess.fen());
315
+ this.moveColor.set(move, this.getMoveColor(currentMove) === "w" ? "b" : "w");
316
+ }
317
+ this.rawPGN = (0, utils_1.regeneratePGN)(this.game, this.moveColor);
318
+ this.dfOnGame(this.game);
319
+ return move;
320
+ };
321
+ /**
322
+ * Delete a move and all subsequent moves in its variation from the game
323
+ * @param moveId - The ID of the move to delete from
324
+ * @throws Error if the move parameter is invalid
325
+ */
326
+ deleteMove = (moveId) => {
327
+ const move = this.getMove(moveId);
328
+ if (!move) {
329
+ throw Error("Invalid move");
330
+ }
331
+ // delete the move and subsequent moves from the game
332
+ const parentRav = this.getParentRav(move);
333
+ const index = parentRav.moves.indexOf(move);
334
+ const deletedMoves = parentRav.moves.splice(index);
335
+ // collect all moves and variations that need cleanup
336
+ const movsToClean = [];
337
+ const ravsToClean = [];
338
+ for (const deletedMove of deletedMoves) {
339
+ movsToClean.push(deletedMove);
340
+ if (deletedMove.ravs) {
341
+ for (const rav of deletedMove.ravs) {
342
+ ravsToClean.push(rav);
343
+ }
344
+ }
345
+ }
346
+ // Sort moves by their moveId in decreasing order
347
+ movsToClean.sort((a, b) => this.getMoveNumber(b) - this.getMoveNumber(a));
348
+ // clean up all maps at once
349
+ movsToClean.forEach((move) => {
350
+ this.moveParent.delete(move);
351
+ this.moveFen.delete(move);
352
+ this.moveColor.delete(move);
353
+ });
354
+ ravsToClean.forEach((rav) => {
355
+ this.ravParent.delete(rav);
356
+ });
357
+ // update the PGN and rebuild internal state
358
+ this.rawPGN = (0, utils_1.regeneratePGN)(this.game, this.moveColor);
359
+ this.dfOnGame(this.game);
360
+ };
260
361
  }
261
362
  exports.default = PGNManager;
package/package.json CHANGED
@@ -1,11 +1,13 @@
1
1
  {
2
2
  "name": "pgn-manager",
3
- "version": "1.1.0",
3
+ "version": "2.0.0",
4
4
  "description": "Libraray built on top of chess.js and pgn-parser to load and process PGN files in typescript.",
5
5
  "main": "dist/index",
6
6
  "typings": "dist/index",
7
7
  "scripts": {
8
- "test": "echo \"Error: no test specified\" && exit 1",
8
+ "test": "jest",
9
+ "test:watch": "jest --watch",
10
+ "test:coverage": "jest --coverage",
9
11
  "prepublishOnly": "npm run compile",
10
12
  "compile": "npm run clean && tsc -p .",
11
13
  "watch": "tsc -w -p .",
@@ -32,7 +34,11 @@
32
34
  },
33
35
  "devDependencies": {
34
36
  "@types/chess.js": "^0.11.2",
35
- "typescript": "4.5.2"
37
+ "@types/jest": "^30.0.0",
38
+ "@types/pgn-parser": "^2.1.0",
39
+ "jest": "^30.0.4",
40
+ "ts-jest": "^29.4.0",
41
+ "typescript": "^5.8.3"
36
42
  },
37
43
  "files": [
38
44
  "dist/index.js",