@alepot55/chessboardjs 2.2.1 → 2.2.2
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/.eslintrc.json +227 -0
- package/README.md +125 -401
- package/assets/themes/alepot/theme.json +42 -0
- package/assets/themes/default/theme.json +42 -0
- package/chessboard.bundle.js +708 -58
- package/config/jest.config.js +15 -0
- package/config/rollup.config.js +36 -0
- package/dist/chessboard.cjs.js +10690 -0
- package/dist/chessboard.css +228 -0
- package/dist/chessboard.esm.js +10621 -0
- package/dist/chessboard.iife.js +10696 -0
- package/dist/chessboard.umd.js +10696 -0
- package/jest.config.js +2 -7
- package/package.json +18 -3
- package/rollup.config.js +2 -11
- package/{chessboard.move.js → src/components/Move.js} +3 -3
- package/src/components/Piece.js +288 -0
- package/{chessboard.square.js → src/components/Square.js} +60 -7
- package/src/constants/index.js +15 -0
- package/src/constants/positions.js +62 -0
- package/src/core/Chessboard.js +1939 -0
- package/src/core/ChessboardConfig.js +458 -0
- package/src/core/ChessboardFactory.js +385 -0
- package/src/core/index.js +141 -0
- package/src/errors/ChessboardError.js +133 -0
- package/src/errors/index.js +15 -0
- package/src/errors/messages.js +189 -0
- package/src/index.js +103 -0
- package/src/services/AnimationService.js +180 -0
- package/src/services/BoardService.js +156 -0
- package/src/services/CoordinateService.js +355 -0
- package/src/services/EventService.js +955 -0
- package/src/services/MoveService.js +629 -0
- package/src/services/PieceService.js +312 -0
- package/src/services/PositionService.js +237 -0
- package/src/services/ValidationService.js +673 -0
- package/src/services/index.js +14 -0
- package/src/styles/animations.css +46 -0
- package/{chessboard.css → src/styles/board.css} +8 -4
- package/src/styles/index.css +4 -0
- package/src/styles/pieces.css +70 -0
- package/src/utils/animations.js +37 -0
- package/{chess.js → src/utils/chess.js} +16 -16
- package/src/utils/coordinates.js +62 -0
- package/src/utils/cross-browser.js +150 -0
- package/src/utils/logger.js +422 -0
- package/src/utils/performance.js +311 -0
- package/src/utils/validation.js +458 -0
- package/tests/unit/chessboard-config-animations.test.js +106 -0
- package/tests/unit/chessboard-robust.test.js +163 -0
- package/tests/unit/chessboard.test.js +183 -0
- package/chessboard.config.js +0 -147
- package/chessboard.js +0 -979
- package/chessboard.piece.js +0 -115
- package/test/chessboard.test.js +0 -128
- /package/{alepot_theme → assets/themes/alepot}/bb.svg +0 -0
- /package/{alepot_theme → assets/themes/alepot}/bw.svg +0 -0
- /package/{alepot_theme → assets/themes/alepot}/kb.svg +0 -0
- /package/{alepot_theme → assets/themes/alepot}/kw.svg +0 -0
- /package/{alepot_theme → assets/themes/alepot}/nb.svg +0 -0
- /package/{alepot_theme → assets/themes/alepot}/nw.svg +0 -0
- /package/{alepot_theme → assets/themes/alepot}/pb.svg +0 -0
- /package/{alepot_theme → assets/themes/alepot}/pw.svg +0 -0
- /package/{alepot_theme → assets/themes/alepot}/qb.svg +0 -0
- /package/{alepot_theme → assets/themes/alepot}/qw.svg +0 -0
- /package/{alepot_theme → assets/themes/alepot}/rb.svg +0 -0
- /package/{alepot_theme → assets/themes/alepot}/rw.svg +0 -0
- /package/{default_pieces → assets/themes/default}/bb.svg +0 -0
- /package/{default_pieces → assets/themes/default}/bw.svg +0 -0
- /package/{default_pieces → assets/themes/default}/kb.svg +0 -0
- /package/{default_pieces → assets/themes/default}/kw.svg +0 -0
- /package/{default_pieces → assets/themes/default}/nb.svg +0 -0
- /package/{default_pieces → assets/themes/default}/nw.svg +0 -0
- /package/{default_pieces → assets/themes/default}/pb.svg +0 -0
- /package/{default_pieces → assets/themes/default}/pw.svg +0 -0
- /package/{default_pieces → assets/themes/default}/qb.svg +0 -0
- /package/{default_pieces → assets/themes/default}/qw.svg +0 -0
- /package/{default_pieces → assets/themes/default}/rb.svg +0 -0
- /package/{default_pieces → assets/themes/default}/rw.svg +0 -0
- /package/{.babelrc → config/.babelrc} +0 -0
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Service for managing chess pieces and their operations
|
|
3
|
+
* @module services/PieceService
|
|
4
|
+
* @since 2.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import Piece from '../components/Piece.js';
|
|
8
|
+
import { PieceError, ValidationError } from '../errors/ChessboardError.js';
|
|
9
|
+
import { ERROR_MESSAGES } from '../errors/messages.js';
|
|
10
|
+
import { PIECE_TYPES, PIECE_COLORS } from '../constants/positions.js';
|
|
11
|
+
import { DragOptimizations } from '../utils/cross-browser.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Service responsible for piece management and operations
|
|
15
|
+
* @class
|
|
16
|
+
*/
|
|
17
|
+
export class PieceService {
|
|
18
|
+
/**
|
|
19
|
+
* Creates a new PieceService instance
|
|
20
|
+
* @param {ChessboardConfig} config - Board configuration
|
|
21
|
+
*/
|
|
22
|
+
constructor(config) {
|
|
23
|
+
this.config = config;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Gets the path to a piece asset
|
|
28
|
+
* @param {string} piece - Piece identifier (e.g., 'wK', 'bP')
|
|
29
|
+
* @returns {string} Path to piece asset
|
|
30
|
+
* @throws {ValidationError} When piecesPath configuration is invalid
|
|
31
|
+
*/
|
|
32
|
+
getPiecePath(piece) {
|
|
33
|
+
const { piecesPath } = this.config;
|
|
34
|
+
|
|
35
|
+
if (typeof piecesPath === 'string') {
|
|
36
|
+
return `${piecesPath}/${piece}.svg`;
|
|
37
|
+
} else if (typeof piecesPath === 'object' && piecesPath !== null) {
|
|
38
|
+
return piecesPath[piece];
|
|
39
|
+
} else if (typeof piecesPath === 'function') {
|
|
40
|
+
return piecesPath(piece);
|
|
41
|
+
} else {
|
|
42
|
+
throw new ValidationError(ERROR_MESSAGES.invalid_piecesPath, 'piecesPath', piecesPath);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Converts various piece formats to a Piece instance
|
|
48
|
+
* @param {string|Piece} piece - Piece in various formats
|
|
49
|
+
* @returns {Piece} Piece instance
|
|
50
|
+
* @throws {PieceError} When piece format is invalid
|
|
51
|
+
*/
|
|
52
|
+
convertPiece(piece) {
|
|
53
|
+
if (piece instanceof Piece) {
|
|
54
|
+
return piece;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (typeof piece === 'string' && piece.length === 2) {
|
|
58
|
+
const [first, second] = piece.split('');
|
|
59
|
+
let type, color;
|
|
60
|
+
|
|
61
|
+
// Check format: [type][color] (e.g., 'pw')
|
|
62
|
+
if (PIECE_TYPES.includes(first.toLowerCase()) && PIECE_COLORS.includes(second)) {
|
|
63
|
+
type = first.toLowerCase();
|
|
64
|
+
color = second;
|
|
65
|
+
}
|
|
66
|
+
// Check format: [color][type] (e.g., 'wP')
|
|
67
|
+
else if (PIECE_COLORS.includes(first) && PIECE_TYPES.includes(second.toLowerCase())) {
|
|
68
|
+
color = first;
|
|
69
|
+
type = second.toLowerCase();
|
|
70
|
+
} else {
|
|
71
|
+
throw new PieceError(ERROR_MESSAGES.invalid_piece + piece, piece);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const piecePath = this.getPiecePath(type + color);
|
|
75
|
+
return new Piece(color, type, piecePath);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
throw new PieceError(ERROR_MESSAGES.invalid_piece + piece, piece);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Adds a piece to a square with optional fade-in animation
|
|
83
|
+
* @param {Square} square - Target square
|
|
84
|
+
* @param {Piece} piece - Piece to add
|
|
85
|
+
* @param {boolean} [fade=true] - Whether to fade in the piece
|
|
86
|
+
* @param {Function} dragFunction - Function to handle drag events
|
|
87
|
+
* @param {Function} [callback] - Callback when animation completes
|
|
88
|
+
*/
|
|
89
|
+
addPieceOnSquare(square, piece, fade = true, dragFunction, callback) {
|
|
90
|
+
console.debug(`[PieceService] addPieceOnSquare: ${piece.id} to ${square.id}`);
|
|
91
|
+
square.putPiece(piece);
|
|
92
|
+
|
|
93
|
+
if (dragFunction) {
|
|
94
|
+
piece.setDrag(dragFunction(square, piece));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (fade && this.config.fadeTime > 0) {
|
|
98
|
+
piece.fadeIn(
|
|
99
|
+
this.config.fadeTime,
|
|
100
|
+
this.config.fadeAnimation,
|
|
101
|
+
this._getTransitionTimingFunction(),
|
|
102
|
+
callback
|
|
103
|
+
);
|
|
104
|
+
} else {
|
|
105
|
+
if (callback) callback();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
piece.visible();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Removes a piece from a square with optional fade-out animation
|
|
113
|
+
* @param {Square} square - Source square
|
|
114
|
+
* @param {boolean} [fade=true] - Whether to fade out the piece
|
|
115
|
+
* @param {Function} [callback] - Callback when animation completes
|
|
116
|
+
* @returns {Piece} The removed piece
|
|
117
|
+
* @throws {PieceError} When square has no piece to remove
|
|
118
|
+
*/
|
|
119
|
+
removePieceFromSquare(square, fade = true, callback) {
|
|
120
|
+
console.debug(`[PieceService] removePieceFromSquare: ${square.id}`);
|
|
121
|
+
square.check();
|
|
122
|
+
|
|
123
|
+
const piece = square.piece;
|
|
124
|
+
if (!piece) {
|
|
125
|
+
if (callback) callback();
|
|
126
|
+
throw new PieceError(ERROR_MESSAGES.square_no_piece, null, square.getId());
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (fade && this.config.fadeTime > 0) {
|
|
130
|
+
piece.fadeOut(
|
|
131
|
+
this.config.fadeTime,
|
|
132
|
+
this.config.fadeAnimation,
|
|
133
|
+
this._getTransitionTimingFunction(),
|
|
134
|
+
callback
|
|
135
|
+
);
|
|
136
|
+
} else {
|
|
137
|
+
if (callback) callback();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
square.removePiece();
|
|
141
|
+
return piece;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Moves a piece to a new position with animation
|
|
146
|
+
* @param {Piece} piece - Piece to move
|
|
147
|
+
* @param {Square} targetSquare - Target square
|
|
148
|
+
* @param {number} duration - Animation duration
|
|
149
|
+
* @param {Function} [callback] - Callback function when animation completes
|
|
150
|
+
*/
|
|
151
|
+
movePiece(piece, targetSquare, duration, callback) {
|
|
152
|
+
console.debug(`[PieceService] movePiece: ${piece.id} to ${targetSquare.id}`);
|
|
153
|
+
if (!piece) {
|
|
154
|
+
console.warn('PieceService.movePiece: piece is null, skipping animation');
|
|
155
|
+
if (callback) callback();
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
piece.translate(
|
|
160
|
+
targetSquare,
|
|
161
|
+
duration,
|
|
162
|
+
this._getTransitionTimingFunction(),
|
|
163
|
+
this.config.moveAnimation,
|
|
164
|
+
callback
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Handles piece translation with optional capture
|
|
170
|
+
* @param {Move} move - Move object containing from/to squares and piece
|
|
171
|
+
* @param {boolean} removeTarget - Whether to remove piece from target square
|
|
172
|
+
* @param {boolean} animate - Whether to animate the move
|
|
173
|
+
* @param {Function} [dragFunction] - Function to create drag handlers
|
|
174
|
+
* @param {Function} [callback] - Callback function when complete
|
|
175
|
+
*/
|
|
176
|
+
translatePiece(move, removeTarget, animate, dragFunction = null, callback = null) {
|
|
177
|
+
console.debug(`[PieceService] translatePiece: ${move.piece.id} from ${move.from.id} to ${move.to.id}`);
|
|
178
|
+
if (!move.piece) {
|
|
179
|
+
console.warn('PieceService.translatePiece: move.piece is null, skipping translation');
|
|
180
|
+
if (callback) callback();
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (removeTarget) {
|
|
185
|
+
// Deselect the captured piece before removing it
|
|
186
|
+
move.to.deselect();
|
|
187
|
+
this.removePieceFromSquare(move.to, false);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const changeSquareCallback = () => {
|
|
191
|
+
// Check if piece still exists and is on the source square
|
|
192
|
+
if (move.from.piece === move.piece) {
|
|
193
|
+
move.from.removePiece(true); // Preserve the piece when moving
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Only put piece if destination square doesn't already have it
|
|
197
|
+
if (move.to.piece !== move.piece) {
|
|
198
|
+
move.to.putPiece(move.piece);
|
|
199
|
+
|
|
200
|
+
// Re-attach drag handler if provided
|
|
201
|
+
if (dragFunction && this.config.draggable && move.piece.element) {
|
|
202
|
+
move.piece.setDrag(dragFunction(move.to, move.piece));
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (callback) callback();
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
// Check if piece is currently being dragged
|
|
210
|
+
const isDragging = move.piece.element.classList.contains('dragging');
|
|
211
|
+
|
|
212
|
+
if (isDragging) {
|
|
213
|
+
// If piece is being dragged, don't animate - just move it immediately
|
|
214
|
+
// The piece is already visually in the correct position from the drag
|
|
215
|
+
changeSquareCallback();
|
|
216
|
+
} else {
|
|
217
|
+
// Normal animation
|
|
218
|
+
const duration = animate ? this.config.moveTime : 0;
|
|
219
|
+
this.movePiece(move.piece, move.to, duration, changeSquareCallback);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Snaps a piece back to its original position
|
|
225
|
+
* @param {Square} square - Square containing the piece
|
|
226
|
+
* @param {boolean} [animate=true] - Whether to animate the snapback
|
|
227
|
+
*/
|
|
228
|
+
snapbackPiece(square, animate = true) {
|
|
229
|
+
if (!square || !square.piece) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
const piece = square.piece;
|
|
233
|
+
const duration = animate ? this.config.snapbackTime : 0;
|
|
234
|
+
console.debug(`[PieceService] snapbackPiece: ${piece.id} on ${square.id}`);
|
|
235
|
+
piece.translate(
|
|
236
|
+
square,
|
|
237
|
+
duration,
|
|
238
|
+
this._getTransitionTimingFunction(),
|
|
239
|
+
this.config.snapbackAnimation
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Centers a piece in its square with animation (after successful drop)
|
|
245
|
+
* @param {Square} square - Square containing the piece to center
|
|
246
|
+
* @param {boolean} animate - Whether to animate the centering
|
|
247
|
+
*/
|
|
248
|
+
centerPiece(square, animate = true) {
|
|
249
|
+
if (!square || !square.piece) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
const piece = square.piece;
|
|
253
|
+
const duration = animate ? this.config.dropCenterTime : 0;
|
|
254
|
+
console.debug(`[PieceService] centerPiece: ${piece.id} on ${square.id}`);
|
|
255
|
+
piece.translate(
|
|
256
|
+
square,
|
|
257
|
+
duration,
|
|
258
|
+
this._getTransitionTimingFunction(),
|
|
259
|
+
this.config.dropCenterAnimation,
|
|
260
|
+
() => {
|
|
261
|
+
// After animation, reset all drag-related styles
|
|
262
|
+
if (!piece.element) return;
|
|
263
|
+
piece.element.style.position = '';
|
|
264
|
+
piece.element.style.left = '';
|
|
265
|
+
piece.element.style.top = '';
|
|
266
|
+
piece.element.style.transform = '';
|
|
267
|
+
piece.element.style.zIndex = '';
|
|
268
|
+
// Remove inline dimensions set during drag
|
|
269
|
+
piece.element.style.width = '';
|
|
270
|
+
piece.element.style.height = '';
|
|
271
|
+
// Remove dragging class
|
|
272
|
+
piece.element.classList.remove('dragging');
|
|
273
|
+
|
|
274
|
+
// Clean up drag optimizations that might interfere with click
|
|
275
|
+
DragOptimizations.cleanupAfterDrag(piece.element);
|
|
276
|
+
}
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Gets the transition timing function for animations
|
|
282
|
+
* @private
|
|
283
|
+
* @returns {Function} Timing function
|
|
284
|
+
*/
|
|
285
|
+
_getTransitionTimingFunction() {
|
|
286
|
+
return (elapsed, duration, type = 'ease') => {
|
|
287
|
+
const x = elapsed / duration;
|
|
288
|
+
|
|
289
|
+
switch (type) {
|
|
290
|
+
case 'linear':
|
|
291
|
+
return x;
|
|
292
|
+
case 'ease':
|
|
293
|
+
return (x ** 2) * (3 - 2 * x);
|
|
294
|
+
case 'ease-in':
|
|
295
|
+
return x ** 2;
|
|
296
|
+
case 'ease-out':
|
|
297
|
+
return -1 * (x - 1) ** 2 + 1;
|
|
298
|
+
case 'ease-in-out':
|
|
299
|
+
return (x < 0.5) ? 2 * x ** 2 : 4 * x - 2 * x ** 2 - 1;
|
|
300
|
+
default:
|
|
301
|
+
return x;
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Cleans up resources
|
|
308
|
+
*/
|
|
309
|
+
destroy() {
|
|
310
|
+
// Cleanup any cached pieces or references
|
|
311
|
+
}
|
|
312
|
+
}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Service for managing chess positions and FEN conversion
|
|
3
|
+
* @module services/PositionService
|
|
4
|
+
* @since 2.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { Chess, validateFen } from '../utils/chess.js';
|
|
8
|
+
import { STANDARD_POSITIONS, DEFAULT_STARTING_POSITION, BOARD_LETTERS } from '../constants/positions.js';
|
|
9
|
+
import { ValidationError } from '../errors/ChessboardError.js';
|
|
10
|
+
import { ERROR_MESSAGES } from '../errors/messages.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Service responsible for position management and FEN operations
|
|
14
|
+
* @class
|
|
15
|
+
*/
|
|
16
|
+
export class PositionService {
|
|
17
|
+
/**
|
|
18
|
+
* Creates a new PositionService instance
|
|
19
|
+
* @param {ChessboardConfig} config - Board configuration
|
|
20
|
+
*/
|
|
21
|
+
constructor(config) {
|
|
22
|
+
this.config = config;
|
|
23
|
+
this.game = null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Converts various position formats to FEN string
|
|
28
|
+
* @param {string|Object} position - Position in various formats
|
|
29
|
+
* @returns {string} FEN string representation
|
|
30
|
+
* @throws {ValidationError} When position format is invalid
|
|
31
|
+
*/
|
|
32
|
+
convertFen(position) {
|
|
33
|
+
if (typeof position === 'string') {
|
|
34
|
+
return this._convertStringPosition(position);
|
|
35
|
+
} else if (typeof position === 'object' && position !== null) {
|
|
36
|
+
return this._convertObjectPosition(position);
|
|
37
|
+
} else {
|
|
38
|
+
throw new ValidationError(ERROR_MESSAGES.invalid_position + position, 'position', position);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Converts string position to FEN
|
|
44
|
+
* @private
|
|
45
|
+
* @param {string} position - String position
|
|
46
|
+
* @returns {string} FEN string
|
|
47
|
+
*/
|
|
48
|
+
_convertStringPosition(position) {
|
|
49
|
+
if (position === 'start') {
|
|
50
|
+
return DEFAULT_STARTING_POSITION;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (this.validateFen(position)) {
|
|
54
|
+
return position;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (STANDARD_POSITIONS[position]) {
|
|
58
|
+
return STANDARD_POSITIONS[position];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
throw new ValidationError(ERROR_MESSAGES.invalid_position + position, 'position', position);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Converts object position to FEN
|
|
66
|
+
* @private
|
|
67
|
+
* @param {Object} position - Object with square->piece mapping
|
|
68
|
+
* @returns {string} FEN string
|
|
69
|
+
*/
|
|
70
|
+
_convertObjectPosition(position) {
|
|
71
|
+
const parts = [];
|
|
72
|
+
|
|
73
|
+
for (let row = 0; row < 8; row++) {
|
|
74
|
+
const rowParts = [];
|
|
75
|
+
let empty = 0;
|
|
76
|
+
|
|
77
|
+
for (let col = 0; col < 8; col++) {
|
|
78
|
+
const square = this._getSquareID(row, col);
|
|
79
|
+
const piece = position[square];
|
|
80
|
+
|
|
81
|
+
if (piece) {
|
|
82
|
+
if (empty > 0) {
|
|
83
|
+
rowParts.push(empty);
|
|
84
|
+
empty = 0;
|
|
85
|
+
}
|
|
86
|
+
// Convert piece notation: white pieces become uppercase, black remain lowercase
|
|
87
|
+
const fenPiece = piece[1] === 'w' ? piece[0].toUpperCase() : piece[0].toLowerCase();
|
|
88
|
+
rowParts.push(fenPiece);
|
|
89
|
+
} else {
|
|
90
|
+
empty++;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (empty > 0) {
|
|
95
|
+
rowParts.push(empty);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
parts.push(rowParts.join(''));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return parts.join('/') + ' w KQkq - 0 1';
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Sets up the game with the given position
|
|
106
|
+
* @param {string|Object} position - Position to set
|
|
107
|
+
* @param {Object} [options] - Additional options for game setup
|
|
108
|
+
*/
|
|
109
|
+
setGame(position, options = {}) {
|
|
110
|
+
const fen = this.convertFen(position);
|
|
111
|
+
|
|
112
|
+
if (this.game) {
|
|
113
|
+
this.game.load(fen, options);
|
|
114
|
+
} else {
|
|
115
|
+
this.game = new Chess(fen);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Gets the current game instance
|
|
121
|
+
* @returns {Chess} Current chess.js game instance
|
|
122
|
+
*/
|
|
123
|
+
getGame() {
|
|
124
|
+
return this.game;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Validates a FEN string
|
|
129
|
+
* @param {string} fen - FEN string to validate
|
|
130
|
+
* @returns {boolean} True if valid, false otherwise
|
|
131
|
+
*/
|
|
132
|
+
validateFen(fen) {
|
|
133
|
+
return validateFen(fen);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Gets piece information for a specific square
|
|
138
|
+
* @param {string} squareId - Square identifier
|
|
139
|
+
* @returns {string|null} Piece ID or null if no piece
|
|
140
|
+
*/
|
|
141
|
+
getGamePieceId(squareId) {
|
|
142
|
+
if (!this.game) return null;
|
|
143
|
+
const piece = this.game.get(squareId);
|
|
144
|
+
return piece ? piece.color + piece.type : null;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Checks if a specific piece is on a specific square
|
|
149
|
+
* @param {string} piece - Piece ID to check
|
|
150
|
+
* @param {string} square - Square to check
|
|
151
|
+
* @returns {boolean} True if piece is on square
|
|
152
|
+
*/
|
|
153
|
+
isPiece(piece, square) {
|
|
154
|
+
return this.getGamePieceId(square) === piece;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Converts board coordinates to square ID
|
|
159
|
+
* @private
|
|
160
|
+
* @param {number} row - Row index (0-7)
|
|
161
|
+
* @param {number} col - Column index (0-7)
|
|
162
|
+
* @returns {string} Square ID (e.g., 'e4')
|
|
163
|
+
*/
|
|
164
|
+
_getSquareID(row, col) {
|
|
165
|
+
row = parseInt(row);
|
|
166
|
+
col = parseInt(col);
|
|
167
|
+
|
|
168
|
+
if (this.config.orientation === 'w') {
|
|
169
|
+
row = 8 - row;
|
|
170
|
+
col = col + 1;
|
|
171
|
+
} else {
|
|
172
|
+
row = row + 1;
|
|
173
|
+
col = 8 - col;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const letter = BOARD_LETTERS[col - 1];
|
|
177
|
+
return letter + row;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Changes the turn in a FEN string
|
|
182
|
+
* @param {string} fen - Original FEN string
|
|
183
|
+
* @param {string} color - New turn color ('w' or 'b')
|
|
184
|
+
* @returns {string} Modified FEN string
|
|
185
|
+
*/
|
|
186
|
+
changeFenTurn(fen, color) {
|
|
187
|
+
const parts = fen.split(' ');
|
|
188
|
+
parts[1] = color;
|
|
189
|
+
return parts.join(' ');
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Gets the current position as an object
|
|
194
|
+
* @returns {Object} Position object with piece placements
|
|
195
|
+
*/
|
|
196
|
+
getPosition() {
|
|
197
|
+
const position = {};
|
|
198
|
+
const game = this.getGame();
|
|
199
|
+
|
|
200
|
+
// Convert chess.js board to position object
|
|
201
|
+
const squares = ['a1', 'b1', 'c1', 'd1', 'e1', 'f1', 'g1', 'h1',
|
|
202
|
+
'a2', 'b2', 'c2', 'd2', 'e2', 'f2', 'g2', 'h2',
|
|
203
|
+
'a3', 'b3', 'c3', 'd3', 'e3', 'f3', 'g3', 'h3',
|
|
204
|
+
'a4', 'b4', 'c4', 'd4', 'e4', 'f4', 'g4', 'h4',
|
|
205
|
+
'a5', 'b5', 'c5', 'd5', 'e5', 'f5', 'g5', 'h5',
|
|
206
|
+
'a6', 'b6', 'c6', 'd6', 'e6', 'f6', 'g6', 'h6',
|
|
207
|
+
'a7', 'b7', 'c7', 'd7', 'e7', 'f7', 'g7', 'h7',
|
|
208
|
+
'a8', 'b8', 'c8', 'd8', 'e8', 'f8', 'g8', 'h8'];
|
|
209
|
+
|
|
210
|
+
for (const square of squares) {
|
|
211
|
+
const piece = game.get(square);
|
|
212
|
+
if (piece) {
|
|
213
|
+
position[square] = piece.type + piece.color;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return position;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Toggles the turn in a FEN string
|
|
222
|
+
* @param {string} fen - Original FEN string
|
|
223
|
+
* @returns {string} Modified FEN string
|
|
224
|
+
*/
|
|
225
|
+
changeFenColor(fen) {
|
|
226
|
+
const parts = fen.split(' ');
|
|
227
|
+
parts[1] = parts[1] === 'w' ? 'b' : 'w';
|
|
228
|
+
return parts.join(' ');
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Cleans up resources
|
|
233
|
+
*/
|
|
234
|
+
destroy() {
|
|
235
|
+
this.game = null;
|
|
236
|
+
}
|
|
237
|
+
}
|