@connectorvol/chessops 1.0.0 → 2.0.1
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/{esm/attacks.js → attacks.js} +2 -3
- package/dist/{esm/board.js → board.js} +25 -9
- package/dist/{esm/chess.js → chess.js} +24 -18
- package/dist/{esm/compat.js → compat.js} +1 -2
- package/dist/{esm/debug.js → debug.js} +0 -1
- package/dist/{esm/fen.js → fen.js} +1 -2
- package/dist/{types/index.d.ts → index.d.ts} +6 -3
- package/dist/{esm/index.js → index.js} +1 -2
- package/dist/{esm/pgn.js → pgn.js} +54 -34
- package/dist/pgnOps.svelte.d.ts +31 -0
- package/dist/pgnOps.svelte.js +122 -0
- package/dist/{esm/san.js → san.js} +2 -5
- package/dist/{esm/setup.js → setup.js} +29 -26
- package/dist/{esm/squareSet.js → squareSet.js} +17 -16
- package/dist/{esm/transform.js → transform.js} +10 -14
- package/dist/{esm/types.js → types.js} +0 -1
- package/dist/{esm/util.js → util.js} +0 -1
- package/dist/{types/variant.d.ts → variant.d.ts} +2 -1
- package/dist/{esm/variant.js → variant.js} +21 -26
- package/package.json +31 -59
- package/dist/cjs/attacks.js +0 -152
- package/dist/cjs/attacks.js.map +0 -1
- package/dist/cjs/board.js +0 -143
- package/dist/cjs/board.js.map +0 -1
- package/dist/cjs/chess.js +0 -638
- package/dist/cjs/chess.js.map +0 -1
- package/dist/cjs/compat.js +0 -89
- package/dist/cjs/compat.js.map +0 -1
- package/dist/cjs/debug.js +0 -103
- package/dist/cjs/debug.js.map +0 -1
- package/dist/cjs/fen.js +0 -325
- package/dist/cjs/fen.js.map +0 -1
- package/dist/cjs/index.js +0 -94
- package/dist/cjs/index.js.map +0 -1
- package/dist/cjs/pgn.js +0 -796
- package/dist/cjs/pgn.js.map +0 -1
- package/dist/cjs/san.js +0 -174
- package/dist/cjs/san.js.map +0 -1
- package/dist/cjs/setup.js +0 -167
- package/dist/cjs/setup.js.map +0 -1
- package/dist/cjs/squareSet.js +0 -206
- package/dist/cjs/squareSet.js.map +0 -1
- package/dist/cjs/transform.js +0 -57
- package/dist/cjs/transform.js.map +0 -1
- package/dist/cjs/types.js +0 -24
- package/dist/cjs/types.js.map +0 -1
- package/dist/cjs/util.js +0 -104
- package/dist/cjs/util.js.map +0 -1
- package/dist/cjs/variant.js +0 -833
- package/dist/cjs/variant.js.map +0 -1
- package/dist/esm/attacks.js.map +0 -1
- package/dist/esm/board.js.map +0 -1
- package/dist/esm/chess.js.map +0 -1
- package/dist/esm/compat.js.map +0 -1
- package/dist/esm/debug.js.map +0 -1
- package/dist/esm/fen.js.map +0 -1
- package/dist/esm/index.js.map +0 -1
- package/dist/esm/pgn.js.map +0 -1
- package/dist/esm/san.js.map +0 -1
- package/dist/esm/setup.js.map +0 -1
- package/dist/esm/squareSet.js.map +0 -1
- package/dist/esm/transform.js.map +0 -1
- package/dist/esm/types.js.map +0 -1
- package/dist/esm/util.js.map +0 -1
- package/dist/esm/variant.js.map +0 -1
- package/src/attacks.ts +0 -160
- package/src/board.ts +0 -168
- package/src/chess.ts +0 -687
- package/src/compat.ts +0 -120
- package/src/debug.ts +0 -100
- package/src/fen.ts +0 -328
- package/src/index.ts +0 -85
- package/src/pgn.ts +0 -876
- package/src/san.ts +0 -190
- package/src/setup.ts +0 -203
- package/src/squareSet.ts +0 -243
- package/src/transform.ts +0 -49
- package/src/types.ts +0 -93
- package/src/util.ts +0 -116
- package/src/variant.ts +0 -939
- /package/dist/{types/attacks.d.ts → attacks.d.ts} +0 -0
- /package/dist/{types/board.d.ts → board.d.ts} +0 -0
- /package/dist/{types/chess.d.ts → chess.d.ts} +0 -0
- /package/dist/{types/compat.d.ts → compat.d.ts} +0 -0
- /package/dist/{types/debug.d.ts → debug.d.ts} +0 -0
- /package/dist/{types/fen.d.ts → fen.d.ts} +0 -0
- /package/dist/{types/pgn.d.ts → pgn.d.ts} +0 -0
- /package/dist/{types/san.d.ts → san.d.ts} +0 -0
- /package/dist/{types/setup.d.ts → setup.d.ts} +0 -0
- /package/dist/{types/squareSet.d.ts → squareSet.d.ts} +0 -0
- /package/dist/{types/transform.d.ts → transform.d.ts} +0 -0
- /package/dist/{types/types.d.ts → types.d.ts} +0 -0
- /package/dist/{types/util.d.ts → util.d.ts} +0 -0
|
@@ -51,12 +51,12 @@ export const pawnAttacks = (color, square) => PAWN_ATTACKS[color][square];
|
|
|
51
51
|
const FILE_RANGE = tabulate((sq) => SquareSet.fromFile(squareFile(sq)).without(sq));
|
|
52
52
|
const RANK_RANGE = tabulate((sq) => SquareSet.fromRank(squareRank(sq)).without(sq));
|
|
53
53
|
const DIAG_RANGE = tabulate((sq) => {
|
|
54
|
-
const diag = new SquareSet(
|
|
54
|
+
const diag = new SquareSet(0x0804_0201, 0x8040_2010);
|
|
55
55
|
const shift = 8 * (squareRank(sq) - squareFile(sq));
|
|
56
56
|
return (shift >= 0 ? diag.shl64(shift) : diag.shr64(-shift)).without(sq);
|
|
57
57
|
});
|
|
58
58
|
const ANTI_DIAG_RANGE = tabulate((sq) => {
|
|
59
|
-
const diag = new SquareSet(
|
|
59
|
+
const diag = new SquareSet(0x1020_4080, 0x0102_0408);
|
|
60
60
|
const shift = 8 * (squareRank(sq) + squareFile(sq) - 7);
|
|
61
61
|
return (shift >= 0 ? diag.shl64(shift) : diag.shr64(-shift)).without(sq);
|
|
62
62
|
});
|
|
@@ -137,4 +137,3 @@ export const ray = (a, b) => {
|
|
|
137
137
|
export const between = (a, b) => ray(a, b)
|
|
138
138
|
.intersect(SquareSet.full().shl64(a).xor(SquareSet.full().shl64(b)))
|
|
139
139
|
.withoutFirst();
|
|
140
|
-
//# sourceMappingURL=attacks.js.map
|
|
@@ -9,6 +9,23 @@ import { COLORS, ROLES } from "./types.js";
|
|
|
9
9
|
* care to keep them consistent.
|
|
10
10
|
*/
|
|
11
11
|
export class Board {
|
|
12
|
+
/**
|
|
13
|
+
* All occupied squares.
|
|
14
|
+
*/
|
|
15
|
+
occupied;
|
|
16
|
+
/**
|
|
17
|
+
* All squares occupied by pieces known to be promoted. This information is
|
|
18
|
+
* relevant in chess variants like Crazyhouse.
|
|
19
|
+
*/
|
|
20
|
+
promoted;
|
|
21
|
+
white;
|
|
22
|
+
black;
|
|
23
|
+
pawn;
|
|
24
|
+
knight;
|
|
25
|
+
bishop;
|
|
26
|
+
rook;
|
|
27
|
+
queen;
|
|
28
|
+
king;
|
|
12
29
|
constructor() { }
|
|
13
30
|
static default() {
|
|
14
31
|
const board = new Board();
|
|
@@ -19,16 +36,16 @@ export class Board {
|
|
|
19
36
|
* Resets all pieces to the default starting position for standard chess.
|
|
20
37
|
*/
|
|
21
38
|
reset() {
|
|
22
|
-
this.occupied = new SquareSet(0xffff,
|
|
39
|
+
this.occupied = new SquareSet(0xffff, 0xffff_0000);
|
|
23
40
|
this.promoted = SquareSet.empty();
|
|
24
41
|
this.white = new SquareSet(0xffff, 0);
|
|
25
|
-
this.black = new SquareSet(0,
|
|
26
|
-
this.pawn = new SquareSet(0xff00,
|
|
27
|
-
this.knight = new SquareSet(0x42,
|
|
28
|
-
this.bishop = new SquareSet(0x24,
|
|
29
|
-
this.rook = new SquareSet(0x81,
|
|
30
|
-
this.queen = new SquareSet(0x8,
|
|
31
|
-
this.king = new SquareSet(0x10,
|
|
42
|
+
this.black = new SquareSet(0, 0xffff_0000);
|
|
43
|
+
this.pawn = new SquareSet(0xff00, 0x00ff_0000);
|
|
44
|
+
this.knight = new SquareSet(0x42, 0x4200_0000);
|
|
45
|
+
this.bishop = new SquareSet(0x24, 0x2400_0000);
|
|
46
|
+
this.rook = new SquareSet(0x81, 0x8100_0000);
|
|
47
|
+
this.queen = new SquareSet(0x8, 0x0800_0000);
|
|
48
|
+
this.king = new SquareSet(0x10, 0x1000_0000);
|
|
32
49
|
}
|
|
33
50
|
static empty() {
|
|
34
51
|
const board = new Board();
|
|
@@ -135,4 +152,3 @@ export class Board {
|
|
|
135
152
|
export const boardEquals = (left, right) => left.white.equals(right.white) &&
|
|
136
153
|
left.promoted.equals(right.promoted) &&
|
|
137
154
|
ROLES.every((role) => left[role].equals(right[role]));
|
|
138
|
-
//# sourceMappingURL=board.js.map
|
|
@@ -21,6 +21,9 @@ const attacksTo = (square, attacker, board, occupied) => board[attacker].interse
|
|
|
21
21
|
.union(kingAttacks(square).intersect(board.king))
|
|
22
22
|
.union(pawnAttacks(opposite(attacker), square).intersect(board.pawn)));
|
|
23
23
|
export class Castles {
|
|
24
|
+
castlingRights;
|
|
25
|
+
rook;
|
|
26
|
+
path;
|
|
24
27
|
constructor() { }
|
|
25
28
|
static default() {
|
|
26
29
|
const castles = new Castles();
|
|
@@ -108,6 +111,15 @@ export class Castles {
|
|
|
108
111
|
}
|
|
109
112
|
}
|
|
110
113
|
export class Position {
|
|
114
|
+
rules;
|
|
115
|
+
board;
|
|
116
|
+
pockets;
|
|
117
|
+
turn;
|
|
118
|
+
castles;
|
|
119
|
+
epSquare;
|
|
120
|
+
remainingChecks;
|
|
121
|
+
halfmoves;
|
|
122
|
+
fullmoves;
|
|
111
123
|
constructor(rules) {
|
|
112
124
|
this.rules = rules;
|
|
113
125
|
}
|
|
@@ -185,14 +197,13 @@ export class Position {
|
|
|
185
197
|
};
|
|
186
198
|
}
|
|
187
199
|
clone() {
|
|
188
|
-
var _a, _b;
|
|
189
200
|
const pos = new this.constructor();
|
|
190
201
|
pos.board = this.board.clone();
|
|
191
|
-
pos.pockets =
|
|
202
|
+
pos.pockets = this.pockets?.clone();
|
|
192
203
|
pos.turn = this.turn;
|
|
193
204
|
pos.castles = this.castles.clone();
|
|
194
205
|
pos.epSquare = this.epSquare;
|
|
195
|
-
pos.remainingChecks =
|
|
206
|
+
pos.remainingChecks = this.remainingChecks?.clone();
|
|
196
207
|
pos.halfmoves = this.halfmoves;
|
|
197
208
|
pos.fullmoves = this.fullmoves;
|
|
198
209
|
return pos;
|
|
@@ -297,14 +308,13 @@ export class Position {
|
|
|
297
308
|
}
|
|
298
309
|
// The following should be identical in all subclasses
|
|
299
310
|
toSetup() {
|
|
300
|
-
var _a, _b;
|
|
301
311
|
return {
|
|
302
312
|
board: this.board.clone(),
|
|
303
|
-
pockets:
|
|
313
|
+
pockets: this.pockets?.clone(),
|
|
304
314
|
turn: this.turn,
|
|
305
315
|
castlingRights: this.castles.castlingRights,
|
|
306
316
|
epSquare: legalEpSquare(this),
|
|
307
|
-
remainingChecks:
|
|
317
|
+
remainingChecks: this.remainingChecks?.clone(),
|
|
308
318
|
halfmoves: Math.min(this.halfmoves, 150),
|
|
309
319
|
fullmoves: Math.min(Math.max(this.fullmoves, 1), 9999),
|
|
310
320
|
};
|
|
@@ -551,17 +561,14 @@ export const pseudoDests = (pos, square, ctx) => {
|
|
|
551
561
|
else
|
|
552
562
|
return pseudo;
|
|
553
563
|
};
|
|
554
|
-
export const equalsIgnoreMoves = (left, right) =>
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
((right.remainingChecks && ((_b = left.remainingChecks) === null || _b === void 0 ? void 0 : _b.equals(right.remainingChecks))) ||
|
|
563
|
-
(!left.remainingChecks && !right.remainingChecks));
|
|
564
|
-
};
|
|
564
|
+
export const equalsIgnoreMoves = (left, right) => left.rules === right.rules &&
|
|
565
|
+
boardEquals(left.board, right.board) &&
|
|
566
|
+
((right.pockets && left.pockets?.equals(right.pockets)) || (!left.pockets && !right.pockets)) &&
|
|
567
|
+
left.turn === right.turn &&
|
|
568
|
+
left.castles.castlingRights.equals(right.castles.castlingRights) &&
|
|
569
|
+
legalEpSquare(left) === legalEpSquare(right) &&
|
|
570
|
+
((right.remainingChecks && left.remainingChecks?.equals(right.remainingChecks)) ||
|
|
571
|
+
(!left.remainingChecks && !right.remainingChecks));
|
|
565
572
|
export const castlingSide = (pos, move) => {
|
|
566
573
|
if (isDrop(move))
|
|
567
574
|
return;
|
|
@@ -621,4 +628,3 @@ export const isImpossibleCheck = (pos) => {
|
|
|
621
628
|
checkers.intersect(pos.board.steppers()).moreThanOne());
|
|
622
629
|
}
|
|
623
630
|
};
|
|
624
|
-
//# sourceMappingURL=chess.js.map
|
|
@@ -22,7 +22,7 @@ export const chessgroundDests = (pos, opts) => {
|
|
|
22
22
|
for (const [from, squares] of pos.allDests(ctx)) {
|
|
23
23
|
if (squares.nonEmpty()) {
|
|
24
24
|
const d = Array.from(squares, makeSquare);
|
|
25
|
-
if (!
|
|
25
|
+
if (!opts?.chess960 && from === ctx.king && squareFile(from) === 4) {
|
|
26
26
|
// Chessground needs both types of castling dests and filters based on
|
|
27
27
|
// a rookCastles setting.
|
|
28
28
|
if (squares.has(0))
|
|
@@ -78,4 +78,3 @@ export const lichessVariant = (rules) => {
|
|
|
78
78
|
return rules;
|
|
79
79
|
}
|
|
80
80
|
};
|
|
81
|
-
//# sourceMappingURL=compat.js.map
|
|
@@ -298,11 +298,10 @@ export const makeFen = (setup, opts) => [
|
|
|
298
298
|
makeCastlingFen(setup.board, setup.castlingRights),
|
|
299
299
|
defined(setup.epSquare) ? makeSquare(setup.epSquare) : "-",
|
|
300
300
|
...(setup.remainingChecks ? [makeRemainingChecks(setup.remainingChecks)] : []),
|
|
301
|
-
...(
|
|
301
|
+
...(opts?.epd
|
|
302
302
|
? []
|
|
303
303
|
: [
|
|
304
304
|
Math.max(0, Math.min(setup.halfmoves, 9999)),
|
|
305
305
|
Math.max(1, Math.min(setup.fullmoves, 9999)),
|
|
306
306
|
]),
|
|
307
307
|
].join(" ");
|
|
308
|
-
//# sourceMappingURL=fen.js.map
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
export { ByCastlingSide, ByColor, ByRole, BySquare,
|
|
1
|
+
export type { ByCastlingSide, ByColor, ByRole, BySquare, CastlingSide, Color, DropMove, FileName, Move, NormalMove, Outcome, Piece, RankName, Role, RoleChar, Rules, Square, SquareName, } from "./types.js";
|
|
2
|
+
export { CASTLING_SIDES, COLORS, FILE_NAMES, isDrop, isNormal, RANK_NAMES, ROLE_CHARS, ROLES, RULES, } from "./types.js";
|
|
2
3
|
export { charToRole, defined, kingCastlesTo, makeSquare, makeUci, opposite, parseSquare, parseUci, roleToChar, squareFile, squareRank, } from "./util.js";
|
|
3
4
|
export { SquareSet } from "./squareSet.js";
|
|
4
5
|
export { attacks, between, bishopAttacks, kingAttacks, knightAttacks, pawnAttacks, queenAttacks, ray, rookAttacks, } from "./attacks.js";
|
|
5
6
|
export { Board, boardEquals } from "./board.js";
|
|
6
|
-
export {
|
|
7
|
-
export {
|
|
7
|
+
export type { Setup } from "./setup.js";
|
|
8
|
+
export { defaultSetup, emptySetup, Material, MaterialSide, RemainingChecks, setupClone, setupEquals, } from "./setup.js";
|
|
9
|
+
export type { Context } from "./chess.js";
|
|
10
|
+
export { Castles, Chess, IllegalSetup, Position, PositionError } from "./chess.js";
|
|
8
11
|
export * as compat from "./compat.js";
|
|
9
12
|
export * as debug from "./debug.js";
|
|
10
13
|
export * as fen from "./fen.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { CASTLING_SIDES, COLORS, FILE_NAMES, isDrop, isNormal, RANK_NAMES, ROLE_CHARS, ROLES, } from "./types.js";
|
|
1
|
+
export { CASTLING_SIDES, COLORS, FILE_NAMES, isDrop, isNormal, RANK_NAMES, ROLE_CHARS, ROLES, RULES, } from "./types.js";
|
|
2
2
|
export { charToRole, defined, kingCastlesTo, makeSquare, makeUci, opposite, parseSquare, parseUci, roleToChar, squareFile, squareRank, } from "./util.js";
|
|
3
3
|
export { SquareSet } from "./squareSet.js";
|
|
4
4
|
export { attacks, between, bishopAttacks, kingAttacks, knightAttacks, pawnAttacks, queenAttacks, ray, rookAttacks, } from "./attacks.js";
|
|
@@ -12,4 +12,3 @@ export * as san from "./san.js";
|
|
|
12
12
|
export * as transform from "./transform.js";
|
|
13
13
|
export * as variant from "./variant.js";
|
|
14
14
|
export * as pgn from "./pgn.js";
|
|
15
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -110,9 +110,7 @@ export const defaultGame = (initHeaders = defaultHeaders) => ({
|
|
|
110
110
|
moves: new Node(),
|
|
111
111
|
});
|
|
112
112
|
export class Node {
|
|
113
|
-
|
|
114
|
-
this.children = [];
|
|
115
|
-
}
|
|
113
|
+
children = [];
|
|
116
114
|
*mainlineNodes() {
|
|
117
115
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
118
116
|
let node = this;
|
|
@@ -135,6 +133,7 @@ export class Node {
|
|
|
135
133
|
}
|
|
136
134
|
}
|
|
137
135
|
export class ChildNode extends Node {
|
|
136
|
+
data;
|
|
138
137
|
constructor(data) {
|
|
139
138
|
super();
|
|
140
139
|
this.data = data;
|
|
@@ -150,6 +149,7 @@ export const extend = (node, data) => {
|
|
|
150
149
|
return node;
|
|
151
150
|
};
|
|
152
151
|
export class Box {
|
|
152
|
+
value;
|
|
153
153
|
constructor(value) {
|
|
154
154
|
this.value = value;
|
|
155
155
|
}
|
|
@@ -219,6 +219,12 @@ export const parseOutcome = (s) => {
|
|
|
219
219
|
};
|
|
220
220
|
const escapeHeader = (value) => value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
221
221
|
const safeComment = (comment) => comment.replace(/\}/g, "");
|
|
222
|
+
var MakePgnState;
|
|
223
|
+
(function (MakePgnState) {
|
|
224
|
+
MakePgnState[MakePgnState["Pre"] = 0] = "Pre";
|
|
225
|
+
MakePgnState[MakePgnState["Sidelines"] = 1] = "Sidelines";
|
|
226
|
+
MakePgnState[MakePgnState["End"] = 2] = "End";
|
|
227
|
+
})(MakePgnState || (MakePgnState = {}));
|
|
222
228
|
export const makePgn = (game) => {
|
|
223
229
|
const builder = [], tokens = [];
|
|
224
230
|
if (game.headers.size) {
|
|
@@ -238,7 +244,7 @@ export const makePgn = (game) => {
|
|
|
238
244
|
const firstVariation = variations.next();
|
|
239
245
|
if (!firstVariation.done) {
|
|
240
246
|
stack.push({
|
|
241
|
-
state:
|
|
247
|
+
state: MakePgnState.Pre,
|
|
242
248
|
ply: initialPly,
|
|
243
249
|
node: firstVariation.value,
|
|
244
250
|
sidelines: variations,
|
|
@@ -255,7 +261,7 @@ export const makePgn = (game) => {
|
|
|
255
261
|
forceMoveNumber = true;
|
|
256
262
|
}
|
|
257
263
|
switch (frame.state) {
|
|
258
|
-
case
|
|
264
|
+
case MakePgnState.Pre:
|
|
259
265
|
for (const comment of frame.node.data.startingComments || []) {
|
|
260
266
|
tokens.push("{", safeComment(comment), "}");
|
|
261
267
|
forceMoveNumber = true;
|
|
@@ -272,15 +278,15 @@ export const makePgn = (game) => {
|
|
|
272
278
|
for (const comment of frame.node.data.comments || []) {
|
|
273
279
|
tokens.push("{", safeComment(comment), "}");
|
|
274
280
|
}
|
|
275
|
-
frame.state =
|
|
276
|
-
case
|
|
281
|
+
frame.state = MakePgnState.Sidelines; // fall through
|
|
282
|
+
case MakePgnState.Sidelines: {
|
|
277
283
|
const child = frame.sidelines.next();
|
|
278
284
|
if (child.done) {
|
|
279
285
|
const variations = frame.node.children[Symbol.iterator]();
|
|
280
286
|
const firstVariation = variations.next();
|
|
281
287
|
if (!firstVariation.done) {
|
|
282
288
|
stack.push({
|
|
283
|
-
state:
|
|
289
|
+
state: MakePgnState.Pre,
|
|
284
290
|
ply: frame.ply + 1,
|
|
285
291
|
node: firstVariation.value,
|
|
286
292
|
sidelines: variations,
|
|
@@ -288,13 +294,13 @@ export const makePgn = (game) => {
|
|
|
288
294
|
inVariation: false,
|
|
289
295
|
});
|
|
290
296
|
}
|
|
291
|
-
frame.state =
|
|
297
|
+
frame.state = MakePgnState.End;
|
|
292
298
|
}
|
|
293
299
|
else {
|
|
294
300
|
tokens.push("(");
|
|
295
301
|
forceMoveNumber = true;
|
|
296
302
|
stack.push({
|
|
297
|
-
state:
|
|
303
|
+
state: MakePgnState.Pre,
|
|
298
304
|
ply: frame.ply,
|
|
299
305
|
node: child.value,
|
|
300
306
|
sidelines: [][Symbol.iterator](),
|
|
@@ -305,7 +311,7 @@ export const makePgn = (game) => {
|
|
|
305
311
|
}
|
|
306
312
|
break;
|
|
307
313
|
}
|
|
308
|
-
case
|
|
314
|
+
case MakePgnState.End:
|
|
309
315
|
stack.pop();
|
|
310
316
|
}
|
|
311
317
|
}
|
|
@@ -326,21 +332,38 @@ export const emptyHeaders = () => new Map();
|
|
|
326
332
|
const BOM = "\ufeff";
|
|
327
333
|
const isWhitespace = (line) => /^\s*$/.test(line);
|
|
328
334
|
const isCommentLine = (line) => line.startsWith("%");
|
|
335
|
+
var ParserState;
|
|
336
|
+
(function (ParserState) {
|
|
337
|
+
ParserState[ParserState["Bom"] = 0] = "Bom";
|
|
338
|
+
ParserState[ParserState["Pre"] = 1] = "Pre";
|
|
339
|
+
ParserState[ParserState["Headers"] = 2] = "Headers";
|
|
340
|
+
ParserState[ParserState["Moves"] = 3] = "Moves";
|
|
341
|
+
ParserState[ParserState["Comment"] = 4] = "Comment";
|
|
342
|
+
})(ParserState || (ParserState = {}));
|
|
329
343
|
export class PgnError extends Error {
|
|
330
344
|
}
|
|
331
345
|
export class PgnParser {
|
|
332
|
-
|
|
346
|
+
emitGame;
|
|
347
|
+
initHeaders;
|
|
348
|
+
maxBudget;
|
|
349
|
+
lineBuf = [];
|
|
350
|
+
budget;
|
|
351
|
+
found;
|
|
352
|
+
state;
|
|
353
|
+
game;
|
|
354
|
+
stack;
|
|
355
|
+
commentBuf;
|
|
356
|
+
constructor(emitGame, initHeaders = defaultHeaders, maxBudget = 1_000_000) {
|
|
333
357
|
this.emitGame = emitGame;
|
|
334
358
|
this.initHeaders = initHeaders;
|
|
335
359
|
this.maxBudget = maxBudget;
|
|
336
|
-
this.lineBuf = [];
|
|
337
360
|
this.resetGame();
|
|
338
|
-
this.state =
|
|
361
|
+
this.state = ParserState.Bom;
|
|
339
362
|
}
|
|
340
363
|
resetGame() {
|
|
341
364
|
this.budget = this.maxBudget;
|
|
342
365
|
this.found = false;
|
|
343
|
-
this.state =
|
|
366
|
+
this.state = ParserState.Pre;
|
|
344
367
|
this.game = defaultGame(this.initHeaders);
|
|
345
368
|
this.stack = [{ parent: this.game.moves, root: true }];
|
|
346
369
|
this.commentBuf = [];
|
|
@@ -368,7 +391,7 @@ export class PgnParser {
|
|
|
368
391
|
}
|
|
369
392
|
this.consumeBudget(data.length - idx);
|
|
370
393
|
this.lineBuf.push(data.slice(idx));
|
|
371
|
-
if (!
|
|
394
|
+
if (!options?.stream) {
|
|
372
395
|
this.handleLine();
|
|
373
396
|
this.emit(undefined);
|
|
374
397
|
}
|
|
@@ -383,16 +406,16 @@ export class PgnParser {
|
|
|
383
406
|
this.lineBuf = [];
|
|
384
407
|
continuedLine: for (;;) {
|
|
385
408
|
switch (this.state) {
|
|
386
|
-
case
|
|
409
|
+
case ParserState.Bom:
|
|
387
410
|
if (line.startsWith(BOM))
|
|
388
411
|
line = line.slice(BOM.length);
|
|
389
|
-
this.state =
|
|
390
|
-
case
|
|
412
|
+
this.state = ParserState.Pre; // fall through
|
|
413
|
+
case ParserState.Pre:
|
|
391
414
|
if (isWhitespace(line) || isCommentLine(line))
|
|
392
415
|
return;
|
|
393
416
|
this.found = true;
|
|
394
|
-
this.state =
|
|
395
|
-
case
|
|
417
|
+
this.state = ParserState.Headers; // fall through
|
|
418
|
+
case ParserState.Headers: {
|
|
396
419
|
if (isCommentLine(line))
|
|
397
420
|
return;
|
|
398
421
|
let moreHeaders = true;
|
|
@@ -408,9 +431,9 @@ export class PgnParser {
|
|
|
408
431
|
}
|
|
409
432
|
if (isWhitespace(line))
|
|
410
433
|
return;
|
|
411
|
-
this.state =
|
|
434
|
+
this.state = ParserState.Moves; // fall through
|
|
412
435
|
}
|
|
413
|
-
case
|
|
436
|
+
case ParserState.Moves: {
|
|
414
437
|
if (freshLine) {
|
|
415
438
|
if (isCommentLine(line))
|
|
416
439
|
return;
|
|
@@ -463,7 +486,7 @@ export class PgnParser {
|
|
|
463
486
|
const openIndex = tokenRegex.lastIndex;
|
|
464
487
|
const beginIndex = line[openIndex] === " " ? openIndex + 1 : openIndex;
|
|
465
488
|
line = line.slice(beginIndex);
|
|
466
|
-
this.state =
|
|
489
|
+
this.state = ParserState.Comment;
|
|
467
490
|
continue continuedLine;
|
|
468
491
|
}
|
|
469
492
|
else {
|
|
@@ -486,7 +509,7 @@ export class PgnParser {
|
|
|
486
509
|
}
|
|
487
510
|
return;
|
|
488
511
|
}
|
|
489
|
-
case
|
|
512
|
+
case ParserState.Comment: {
|
|
490
513
|
const closeIndex = line.indexOf("}");
|
|
491
514
|
if (closeIndex === -1) {
|
|
492
515
|
this.commentBuf.push(line);
|
|
@@ -497,7 +520,7 @@ export class PgnParser {
|
|
|
497
520
|
this.commentBuf.push(line.slice(0, endIndex));
|
|
498
521
|
this.handleComment();
|
|
499
522
|
line = line.slice(closeIndex);
|
|
500
|
-
this.state =
|
|
523
|
+
this.state = ParserState.Moves;
|
|
501
524
|
freshLine = false;
|
|
502
525
|
}
|
|
503
526
|
}
|
|
@@ -508,35 +531,33 @@ export class PgnParser {
|
|
|
508
531
|
this.game.headers.set(name, name === "Result" ? makeOutcome(parseOutcome(value)) : value);
|
|
509
532
|
}
|
|
510
533
|
handleNag(nag) {
|
|
511
|
-
var _a;
|
|
512
534
|
this.consumeBudget(50);
|
|
513
535
|
const frame = this.stack[this.stack.length - 1];
|
|
514
536
|
if (frame.node) {
|
|
515
|
-
|
|
537
|
+
frame.node.data.nags ||= [];
|
|
516
538
|
frame.node.data.nags.push(nag);
|
|
517
539
|
}
|
|
518
540
|
}
|
|
519
541
|
handleComment() {
|
|
520
|
-
var _a, _b;
|
|
521
542
|
this.consumeBudget(100);
|
|
522
543
|
const frame = this.stack[this.stack.length - 1];
|
|
523
544
|
const comment = this.commentBuf.join("\n");
|
|
524
545
|
this.commentBuf = [];
|
|
525
546
|
if (frame.node) {
|
|
526
|
-
|
|
547
|
+
frame.node.data.comments ||= [];
|
|
527
548
|
frame.node.data.comments.push(comment);
|
|
528
549
|
}
|
|
529
550
|
else if (frame.root) {
|
|
530
|
-
|
|
551
|
+
this.game.comments ||= [];
|
|
531
552
|
this.game.comments.push(comment);
|
|
532
553
|
}
|
|
533
554
|
else {
|
|
534
|
-
frame.startingComments
|
|
555
|
+
frame.startingComments ||= [];
|
|
535
556
|
frame.startingComments.push(comment);
|
|
536
557
|
}
|
|
537
558
|
}
|
|
538
559
|
emit(err) {
|
|
539
|
-
if (this.state ===
|
|
560
|
+
if (this.state === ParserState.Comment)
|
|
540
561
|
this.handleComment();
|
|
541
562
|
if (err)
|
|
542
563
|
return this.emitGame(this.game, err);
|
|
@@ -766,4 +787,3 @@ export const parseComment = (comment) => {
|
|
|
766
787
|
evaluation,
|
|
767
788
|
};
|
|
768
789
|
};
|
|
769
|
-
//# sourceMappingURL=pgn.js.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Color, Square, PieceType, Move } from "@connectorvol/shared";
|
|
2
|
+
import type { Rules } from "./types.js";
|
|
3
|
+
import { type Position } from "./variant.js";
|
|
4
|
+
/** Представляет обёртку над позицией chessops с реактивным состоянием, SAN и удобными операциями для UI. */
|
|
5
|
+
export declare class PgnOps {
|
|
6
|
+
pos: Position;
|
|
7
|
+
rule: Rules;
|
|
8
|
+
constructor(fen: string, rule: Rules);
|
|
9
|
+
turn(): Color;
|
|
10
|
+
getAllPossibleMoves(): string[];
|
|
11
|
+
moves(square: Square): ("a1" | "a2" | "a3" | "a4" | "a5" | "a6" | "a7" | "a8" | "b1" | "b2" | "b3" | "b4" | "b5" | "b6" | "b7" | "b8" | "c1" | "c2" | "c3" | "c4" | "c5" | "c6" | "c7" | "c8" | "d1" | "d2" | "d3" | "d4" | "d5" | "d6" | "d7" | "d8" | "e1" | "e2" | "e3" | "e4" | "e5" | "e6" | "e7" | "e8" | "f1" | "f2" | "f3" | "f4" | "f5" | "f6" | "f7" | "f8" | "g1" | "g2" | "g3" | "g4" | "g5" | "g6" | "g7" | "g8" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "h7" | "h8")[];
|
|
12
|
+
/** Представляет получение SAN-нотации для хода без его выполнения. */
|
|
13
|
+
getSanForMove(move: Pick<Move, "from" | "to" | "promotion">): string;
|
|
14
|
+
makeMove(move: Pick<Move, "from" | "to" | "promotion" | "flags">): {
|
|
15
|
+
ply: number;
|
|
16
|
+
fullMoves: number;
|
|
17
|
+
san: string;
|
|
18
|
+
move: {
|
|
19
|
+
from: string;
|
|
20
|
+
to: string;
|
|
21
|
+
promotion: PieceType | undefined;
|
|
22
|
+
flags: string;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
makeSanMove(move: string): Move;
|
|
26
|
+
fen(): string;
|
|
27
|
+
/** Представляет установку FEN; при передаче rule обновляет и запомненный вариант. */
|
|
28
|
+
setFen(fen: string, rule?: Rules): void;
|
|
29
|
+
/** Представляет сброс позиции на стартовую для текущего варианта. */
|
|
30
|
+
reset(): void;
|
|
31
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { calculatePly } from "@connectorvol/shared";
|
|
2
|
+
import { chessgroundDests } from "./compat.js";
|
|
3
|
+
import { INITIAL_FEN, makeFen, parseFen } from "./fen.js";
|
|
4
|
+
import { makeSan, parseSan } from "./san.js";
|
|
5
|
+
import { makeSquare, parseSquare } from "./util.js";
|
|
6
|
+
import { Antichess, Atomic, castlingSide, Chess, Crazyhouse, Horde, KingOfTheHill, RacingKings, ThreeCheck, } from "./variant.js";
|
|
7
|
+
/** Представляет обёртку над позицией chessops с реактивным состоянием, SAN и удобными операциями для UI. */
|
|
8
|
+
export class PgnOps {
|
|
9
|
+
pos;
|
|
10
|
+
rule;
|
|
11
|
+
constructor(fen, rule) {
|
|
12
|
+
this.rule = $state(rule);
|
|
13
|
+
this.pos = $derived(setPosition(fen, rule));
|
|
14
|
+
}
|
|
15
|
+
turn() {
|
|
16
|
+
return this.pos.turn === "white" ? "w" : "b";
|
|
17
|
+
}
|
|
18
|
+
getAllPossibleMoves() {
|
|
19
|
+
const dests = chessgroundDests(this.pos);
|
|
20
|
+
const result = [];
|
|
21
|
+
for (const [from, tos] of dests) {
|
|
22
|
+
for (const to of tos) {
|
|
23
|
+
result.push(makeSan(this.pos, { from: parseSquare(from), to: parseSquare(to) }));
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
moves(square) {
|
|
29
|
+
const dests = chessgroundDests(this.pos).get(square) ?? [];
|
|
30
|
+
return dests;
|
|
31
|
+
}
|
|
32
|
+
/** Представляет получение SAN-нотации для хода без его выполнения. */
|
|
33
|
+
getSanForMove(move) {
|
|
34
|
+
const mov = {
|
|
35
|
+
from: parseSquare(move.from) ?? 0,
|
|
36
|
+
to: parseSquare(move.to) ?? 0,
|
|
37
|
+
promotion: move.promotion ? promotionMap[move.promotion] : undefined,
|
|
38
|
+
};
|
|
39
|
+
return makeSan(this.pos, mov);
|
|
40
|
+
}
|
|
41
|
+
makeMove(move) {
|
|
42
|
+
const mov = {
|
|
43
|
+
from: parseSquare(move.from) ?? 0,
|
|
44
|
+
to: parseSquare(move.to) ?? 0,
|
|
45
|
+
promotion: move.promotion ? promotionMap[move.promotion] : undefined,
|
|
46
|
+
};
|
|
47
|
+
let flags = move.flags ?? "";
|
|
48
|
+
const castling = castlingSide(this.pos, mov);
|
|
49
|
+
if (castling) {
|
|
50
|
+
flags = castling === "a" ? "q" : "k";
|
|
51
|
+
}
|
|
52
|
+
const san = makeSan(this.pos, mov);
|
|
53
|
+
this.pos.play(mov);
|
|
54
|
+
const fen = makeFen(this.pos.toSetup());
|
|
55
|
+
const { halfMoves, fullMoves } = calculatePly(fen);
|
|
56
|
+
const moveRecord = {
|
|
57
|
+
from: move.from,
|
|
58
|
+
to: move.to,
|
|
59
|
+
promotion: move.promotion,
|
|
60
|
+
flags,
|
|
61
|
+
};
|
|
62
|
+
return { ply: halfMoves, fullMoves, san, move: moveRecord };
|
|
63
|
+
}
|
|
64
|
+
makeSanMove(move) {
|
|
65
|
+
const mov = parseSan(this.pos, move);
|
|
66
|
+
if (mov) {
|
|
67
|
+
this.pos.play(mov);
|
|
68
|
+
if ("from" in mov) {
|
|
69
|
+
return {
|
|
70
|
+
from: makeSquare(mov.from),
|
|
71
|
+
to: makeSquare(mov.to),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
throw new Error("Invalid move");
|
|
76
|
+
}
|
|
77
|
+
fen() {
|
|
78
|
+
return makeFen(this.pos.toSetup());
|
|
79
|
+
}
|
|
80
|
+
/** Представляет установку FEN; при передаче rule обновляет и запомненный вариант. */
|
|
81
|
+
setFen(fen, rule) {
|
|
82
|
+
if (rule !== undefined) {
|
|
83
|
+
this.rule = rule;
|
|
84
|
+
}
|
|
85
|
+
this.pos = setPosition(fen, this.rule);
|
|
86
|
+
}
|
|
87
|
+
/** Представляет сброс позиции на стартовую для текущего варианта. */
|
|
88
|
+
reset() {
|
|
89
|
+
this.pos = setPosition(INITIAL_FEN, this.rule);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function setPosition(fen, rule) {
|
|
93
|
+
const setup = parseFen(fen).unwrap();
|
|
94
|
+
switch (rule) {
|
|
95
|
+
case "kingofthehill":
|
|
96
|
+
return KingOfTheHill.fromSetup(setup).unwrap();
|
|
97
|
+
case "chess":
|
|
98
|
+
return Chess.fromSetup(setup).unwrap();
|
|
99
|
+
case "antichess":
|
|
100
|
+
return Antichess.fromSetup(setup).unwrap();
|
|
101
|
+
case "3check":
|
|
102
|
+
return ThreeCheck.fromSetup(setup).unwrap();
|
|
103
|
+
case "atomic":
|
|
104
|
+
return Atomic.fromSetup(setup).unwrap();
|
|
105
|
+
case "horde":
|
|
106
|
+
return Horde.fromSetup(setup).unwrap();
|
|
107
|
+
case "racingkings":
|
|
108
|
+
return RacingKings.fromSetup(setup).unwrap();
|
|
109
|
+
case "crazyhouse":
|
|
110
|
+
return Crazyhouse.fromSetup(setup).unwrap();
|
|
111
|
+
default:
|
|
112
|
+
return Chess.fromSetup(setup).unwrap();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
const promotionMap = {
|
|
116
|
+
q: "queen",
|
|
117
|
+
r: "rook",
|
|
118
|
+
b: "bishop",
|
|
119
|
+
n: "knight",
|
|
120
|
+
p: "pawn",
|
|
121
|
+
k: "king",
|
|
122
|
+
};
|