@alepot55/chessboardjs 2.3.4 → 2.3.5
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/config/rollup.config.js +2 -1
- package/dist/chessboard.cjs.js +340 -246
- package/dist/chessboard.css +28 -6
- package/dist/chessboard.esm.js +340 -246
- package/dist/chessboard.iife.js +341 -246
- package/dist/chessboard.umd.js +340 -246
- package/package.json +6 -8
- package/src/components/Piece.js +11 -11
- package/src/components/Square.js +1 -1
- package/src/core/Chessboard.js +87 -86
- package/src/services/BoardService.js +20 -1
- package/src/services/EventService.js +131 -45
- package/src/services/MoveService.js +81 -94
- package/src/services/PieceService.js +9 -8
- package/src/styles/index.css +8 -0
- package/tests/unit/chessboard-robust.test.js +0 -44
- package/tools/build-html-examples.cjs +80 -0
- package/tools/build-html-examples.js +78 -0
- package/chessboard.bundle.js +0 -4072
package/dist/chessboard.cjs.js
CHANGED
|
@@ -1670,6 +1670,8 @@ class Piece {
|
|
|
1670
1670
|
if (callback) callback();
|
|
1671
1671
|
}
|
|
1672
1672
|
};
|
|
1673
|
+
// Se l'elemento è già stato rimosso, esci subito
|
|
1674
|
+
if (!this.element) { console.debug(`[Piece] fadeOut: ${this.id} - element is null (init)`); if (callback) callback(); return; }
|
|
1673
1675
|
fade();
|
|
1674
1676
|
}
|
|
1675
1677
|
|
|
@@ -1677,24 +1679,22 @@ class Piece {
|
|
|
1677
1679
|
if (!this.element) { console.debug(`[Piece] setDrag: ${this.id} - element is null`); return; }
|
|
1678
1680
|
this.element.ondragstart = (e) => { e.preventDefault(); };
|
|
1679
1681
|
this.element.onmousedown = f;
|
|
1682
|
+
this.element.ontouchstart = f; // Drag touch
|
|
1680
1683
|
console.debug(`[Piece] setDrag: ${this.id}`);
|
|
1681
1684
|
}
|
|
1682
1685
|
|
|
1683
1686
|
destroy() {
|
|
1687
|
+
if (!this.element) return; // Idempotente: già rimosso
|
|
1684
1688
|
console.debug(`[Piece] Destroy: ${this.id}`);
|
|
1685
1689
|
// Remove all event listeners
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
if (this.element.parentNode) {
|
|
1692
|
-
this.element.parentNode.removeChild(this.element);
|
|
1693
|
-
}
|
|
1694
|
-
|
|
1695
|
-
// Clear references
|
|
1696
|
-
this.element = null;
|
|
1690
|
+
this.element.onmousedown = null;
|
|
1691
|
+
this.element.ondragstart = null;
|
|
1692
|
+
// Remove from DOM
|
|
1693
|
+
if (this.element.parentNode) {
|
|
1694
|
+
this.element.parentNode.removeChild(this.element);
|
|
1697
1695
|
}
|
|
1696
|
+
// Clear references
|
|
1697
|
+
this.element = null;
|
|
1698
1698
|
}
|
|
1699
1699
|
|
|
1700
1700
|
translate(to, duration, transition_f, speed, callback = null) {
|
|
@@ -1819,8 +1819,8 @@ class Square {
|
|
|
1819
1819
|
// Best practice: destroy the piece object if present
|
|
1820
1820
|
if (this.piece && typeof this.piece.destroy === 'function') {
|
|
1821
1821
|
this.piece.destroy();
|
|
1822
|
-
this.piece = null;
|
|
1823
1822
|
}
|
|
1823
|
+
this.piece = null;
|
|
1824
1824
|
// Remove any orphaned img.piece elements from the DOM
|
|
1825
1825
|
const pieceElements = this.element.querySelectorAll('img.piece');
|
|
1826
1826
|
pieceElements.forEach(element => {
|
|
@@ -2314,13 +2314,32 @@ class BoardService {
|
|
|
2314
2314
|
|
|
2315
2315
|
/**
|
|
2316
2316
|
* Gets a square by its ID
|
|
2317
|
-
* @param {string} squareId - Square identifier (
|
|
2317
|
+
* @param {string} squareId - Square identifier (API pubblica)
|
|
2318
2318
|
* @returns {Square|null} The square or null if not found
|
|
2319
2319
|
*/
|
|
2320
2320
|
getSquare(squareId) {
|
|
2321
2321
|
return this.squares[squareId] || null;
|
|
2322
2322
|
}
|
|
2323
2323
|
|
|
2324
|
+
/**
|
|
2325
|
+
* Highlight a square (solo oggetto)
|
|
2326
|
+
* @param {Square} square
|
|
2327
|
+
* @param {Object} [opts]
|
|
2328
|
+
*/
|
|
2329
|
+
highlightSquare(square, opts = {}) {
|
|
2330
|
+
if (!square) throw new Error('highlightSquare richiede oggetto Square');
|
|
2331
|
+
// ... logica esistente ...
|
|
2332
|
+
}
|
|
2333
|
+
/**
|
|
2334
|
+
* Dehighlight a square (solo oggetto)
|
|
2335
|
+
* @param {Square} square
|
|
2336
|
+
* @param {Object} [opts]
|
|
2337
|
+
*/
|
|
2338
|
+
dehighlightSquare(square, opts = {}) {
|
|
2339
|
+
if (!square) throw new Error('dehighlightSquare richiede oggetto Square');
|
|
2340
|
+
// ... logica esistente ...
|
|
2341
|
+
}
|
|
2342
|
+
|
|
2324
2343
|
/**
|
|
2325
2344
|
* Gets all squares
|
|
2326
2345
|
* @returns {Object.<string, Square>} All squares indexed by ID
|
|
@@ -3160,24 +3179,77 @@ class EventService {
|
|
|
3160
3179
|
|
|
3161
3180
|
// Click handler
|
|
3162
3181
|
const handleClick = (e) => {
|
|
3182
|
+
// Ghost click prevention: ignora il click se appena gestito un touch
|
|
3183
|
+
if (square._ignoreNextClick) {
|
|
3184
|
+
square._ignoreNextClick = false;
|
|
3185
|
+
return;
|
|
3186
|
+
}
|
|
3163
3187
|
e.stopPropagation();
|
|
3164
3188
|
if (this.config.clickable && !this.isAnimating) {
|
|
3165
3189
|
onSquareClick(square);
|
|
3166
3190
|
}
|
|
3167
3191
|
};
|
|
3168
3192
|
|
|
3193
|
+
// --- Touch tap/drag separation ---
|
|
3194
|
+
let touchMoved = false;
|
|
3195
|
+
let touchStartX = 0;
|
|
3196
|
+
let touchStartY = 0;
|
|
3197
|
+
let touchTimeout = null;
|
|
3198
|
+
|
|
3199
|
+
const handleTouchStart = (e) => {
|
|
3200
|
+
if (!e.touches || e.touches.length > 1) return; // solo primo dito
|
|
3201
|
+
touchMoved = false;
|
|
3202
|
+
touchStartX = e.touches[0].clientX;
|
|
3203
|
+
touchStartY = e.touches[0].clientY;
|
|
3204
|
+
// Timeout: se il dito non si muove, dopo 150ms sarà considerato tap
|
|
3205
|
+
touchTimeout = setTimeout(() => {
|
|
3206
|
+
if (!touchMoved) {
|
|
3207
|
+
// Prevenzione ghost click: ignora il prossimo click
|
|
3208
|
+
square._ignoreNextClick = true;
|
|
3209
|
+
setTimeout(() => { square._ignoreNextClick = false; }, 400);
|
|
3210
|
+
handleClick(e); // Tap breve = selezione
|
|
3211
|
+
}
|
|
3212
|
+
}, 150);
|
|
3213
|
+
};
|
|
3214
|
+
|
|
3215
|
+
const handleTouchMove = (e) => {
|
|
3216
|
+
if (!e.touches || e.touches.length > 1) return;
|
|
3217
|
+
const dx = Math.abs(e.touches[0].clientX - touchStartX);
|
|
3218
|
+
const dy = Math.abs(e.touches[0].clientY - touchStartY);
|
|
3219
|
+
if (dx > 5 || dy > 5) {
|
|
3220
|
+
touchMoved = true;
|
|
3221
|
+
if (touchTimeout) {
|
|
3222
|
+
clearTimeout(touchTimeout);
|
|
3223
|
+
touchTimeout = null;
|
|
3224
|
+
}
|
|
3225
|
+
// Il drag vero e proprio è gestito da createDragFunction sul pezzo
|
|
3226
|
+
}
|
|
3227
|
+
};
|
|
3228
|
+
|
|
3229
|
+
const handleTouchEnd = (e) => {
|
|
3230
|
+
if (touchTimeout) {
|
|
3231
|
+
clearTimeout(touchTimeout);
|
|
3232
|
+
touchTimeout = null;
|
|
3233
|
+
}
|
|
3234
|
+
};
|
|
3235
|
+
|
|
3169
3236
|
// Add listeners
|
|
3170
3237
|
square.element.addEventListener('mouseover', throttledHover);
|
|
3171
3238
|
square.element.addEventListener('mouseout', throttledLeave);
|
|
3172
3239
|
square.element.addEventListener('click', handleClick);
|
|
3173
|
-
|
|
3240
|
+
// Touch: separa tap e drag
|
|
3241
|
+
square.element.addEventListener('touchstart', handleTouchStart);
|
|
3242
|
+
square.element.addEventListener('touchmove', handleTouchMove);
|
|
3243
|
+
square.element.addEventListener('touchend', handleTouchEnd);
|
|
3174
3244
|
|
|
3175
3245
|
// Store listeners for cleanup
|
|
3176
3246
|
listeners.push(
|
|
3177
3247
|
{ element: square.element, type: 'mouseover', handler: throttledHover },
|
|
3178
3248
|
{ element: square.element, type: 'mouseout', handler: throttledLeave },
|
|
3179
3249
|
{ element: square.element, type: 'click', handler: handleClick },
|
|
3180
|
-
{ element: square.element, type: 'touchstart', handler:
|
|
3250
|
+
{ element: square.element, type: 'touchstart', handler: handleTouchStart },
|
|
3251
|
+
{ element: square.element, type: 'touchmove', handler: handleTouchMove },
|
|
3252
|
+
{ element: square.element, type: 'touchend', handler: handleTouchEnd }
|
|
3181
3253
|
);
|
|
3182
3254
|
|
|
3183
3255
|
this.eventListeners.set(square.id, listeners);
|
|
@@ -3216,14 +3288,24 @@ class EventService {
|
|
|
3216
3288
|
}
|
|
3217
3289
|
|
|
3218
3290
|
// Track initial position for drag threshold
|
|
3291
|
+
const isTouch = event.type && event.type.startsWith('touch');
|
|
3219
3292
|
const startX = event.clientX || (event.touches && event.touches[0]?.clientX) || 0;
|
|
3220
3293
|
const startY = event.clientY || (event.touches && event.touches[0]?.clientY) || 0;
|
|
3221
3294
|
|
|
3295
|
+
// --- Touch scroll lock helper ---
|
|
3296
|
+
const addScrollLock = () => {
|
|
3297
|
+
document.body.classList.add('chessboardjs-dragging');
|
|
3298
|
+
};
|
|
3299
|
+
const removeScrollLock = () => {
|
|
3300
|
+
document.body.classList.remove('chessboardjs-dragging');
|
|
3301
|
+
};
|
|
3302
|
+
|
|
3303
|
+
// --- MOVE HANDLER (mouse + touch unified) ---
|
|
3222
3304
|
const moveAt = (event) => {
|
|
3223
3305
|
const boardElement = this.boardService.element;
|
|
3224
3306
|
const squareSize = boardElement.offsetWidth / 8;
|
|
3225
3307
|
|
|
3226
|
-
// Get mouse coordinates
|
|
3308
|
+
// Get mouse/touch coordinates
|
|
3227
3309
|
let clientX, clientY;
|
|
3228
3310
|
if (event.touches && event.touches[0]) {
|
|
3229
3311
|
clientX = event.touches[0].clientX;
|
|
@@ -3244,15 +3326,28 @@ class EventService {
|
|
|
3244
3326
|
return true;
|
|
3245
3327
|
};
|
|
3246
3328
|
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3329
|
+
// --- DRAG MOVE (mouse + touch) ---
|
|
3330
|
+
const onPointerMove = (event) => {
|
|
3331
|
+
// For touch, only handle the first finger
|
|
3332
|
+
if (event.touches && event.touches.length > 1) return;
|
|
3333
|
+
if (event.touches && !event.touches[0]) return;
|
|
3334
|
+
if (event.type && event.type.startsWith('touch')) event.preventDefault();
|
|
3335
|
+
|
|
3336
|
+
const currentX = event.clientX || (event.touches && event.touches[0]?.clientX) || 0;
|
|
3337
|
+
const currentY = event.clientY || (event.touches && event.touches[0]?.clientY) || 0;
|
|
3250
3338
|
const deltaX = Math.abs(currentX - startX);
|
|
3251
3339
|
const deltaY = Math.abs(currentY - startY);
|
|
3252
3340
|
|
|
3253
|
-
// Start dragging if mouse moved enough
|
|
3341
|
+
// Start dragging if mouse/touch moved enough
|
|
3254
3342
|
if (!isDragging && (deltaX > 3 || deltaY > 3)) {
|
|
3255
3343
|
isDragging = true;
|
|
3344
|
+
// Inizio drag: blocca update board
|
|
3345
|
+
if (this.chessboard) this.chessboard._isDragging = true;
|
|
3346
|
+
|
|
3347
|
+
// Mostra hint all'inizio del drag se attivi
|
|
3348
|
+
if (this.config.hints && typeof this.chessboard._boundOnPieceHover === 'function') {
|
|
3349
|
+
this.chessboard._boundOnPieceHover(from);
|
|
3350
|
+
}
|
|
3256
3351
|
|
|
3257
3352
|
// Set up drag state
|
|
3258
3353
|
if (!this.config.clickable) {
|
|
@@ -3265,7 +3360,6 @@ class EventService {
|
|
|
3265
3360
|
// Visual feedback
|
|
3266
3361
|
if (this.config.clickable) {
|
|
3267
3362
|
from.select();
|
|
3268
|
-
// Show hints would be handled by the main class
|
|
3269
3363
|
}
|
|
3270
3364
|
|
|
3271
3365
|
// Prepare piece for dragging
|
|
@@ -3275,6 +3369,9 @@ class EventService {
|
|
|
3275
3369
|
|
|
3276
3370
|
DragOptimizations.enableForDrag(img);
|
|
3277
3371
|
|
|
3372
|
+
// Lock scroll for touch
|
|
3373
|
+
if (isTouch) addScrollLock();
|
|
3374
|
+
|
|
3278
3375
|
// Call drag start callback
|
|
3279
3376
|
if (!onDragStart(square, piece)) {
|
|
3280
3377
|
return;
|
|
@@ -3288,8 +3385,16 @@ class EventService {
|
|
|
3288
3385
|
// Update target square
|
|
3289
3386
|
const boardElement = this.boardService.element;
|
|
3290
3387
|
const boardRect = boardElement.getBoundingClientRect();
|
|
3291
|
-
|
|
3292
|
-
|
|
3388
|
+
let clientX, clientY;
|
|
3389
|
+
if (event.touches && event.touches[0]) {
|
|
3390
|
+
clientX = event.touches[0].clientX;
|
|
3391
|
+
clientY = event.touches[0].clientY;
|
|
3392
|
+
} else {
|
|
3393
|
+
clientX = event.clientX;
|
|
3394
|
+
clientY = event.clientY;
|
|
3395
|
+
}
|
|
3396
|
+
const x = clientX - boardRect.left;
|
|
3397
|
+
const y = clientY - boardRect.top;
|
|
3293
3398
|
|
|
3294
3399
|
let newTo = null;
|
|
3295
3400
|
if (x >= 0 && x <= boardRect.width && y >= 0 && y <= boardRect.height) {
|
|
@@ -3308,18 +3413,26 @@ class EventService {
|
|
|
3308
3413
|
}
|
|
3309
3414
|
};
|
|
3310
3415
|
|
|
3311
|
-
|
|
3312
|
-
|
|
3416
|
+
// --- DRAG END (mouse + touch) ---
|
|
3417
|
+
const onPointerUp = (event) => {
|
|
3313
3418
|
previousHighlight?.dehighlight();
|
|
3314
|
-
document.removeEventListener('mousemove',
|
|
3315
|
-
window.removeEventListener('mouseup',
|
|
3419
|
+
document.removeEventListener('mousemove', onPointerMove);
|
|
3420
|
+
window.removeEventListener('mouseup', onPointerUp);
|
|
3421
|
+
document.removeEventListener('touchmove', onPointerMove);
|
|
3422
|
+
window.removeEventListener('touchend', onPointerUp);
|
|
3423
|
+
if (isTouch) removeScrollLock();
|
|
3424
|
+
// Fine drag: sblocca update board
|
|
3425
|
+
if (this.chessboard) this.chessboard._isDragging = false;
|
|
3426
|
+
|
|
3427
|
+
// Rimuovi hint alla fine del drag se attivi
|
|
3428
|
+
if (this.config.hints && typeof this.chessboard._boundOnPieceLeave === 'function') {
|
|
3429
|
+
this.chessboard._boundOnPieceLeave(from);
|
|
3430
|
+
}
|
|
3316
3431
|
|
|
3317
|
-
// If this was just a click, don't interfere
|
|
3318
3432
|
if (!isDragging) {
|
|
3319
3433
|
return;
|
|
3320
3434
|
}
|
|
3321
3435
|
|
|
3322
|
-
// Clean up drag state
|
|
3323
3436
|
img.style.zIndex = '20';
|
|
3324
3437
|
img.classList.remove('dragging');
|
|
3325
3438
|
img.style.willChange = 'auto';
|
|
@@ -3331,23 +3444,26 @@ class EventService {
|
|
|
3331
3444
|
if (isTrashDrop) {
|
|
3332
3445
|
this._handleTrashDrop(originalFrom, onRemove);
|
|
3333
3446
|
} else if (!to) {
|
|
3334
|
-
// Reset piece position instantly for snapback
|
|
3335
3447
|
img.style.position = '';
|
|
3336
3448
|
img.style.left = '';
|
|
3337
3449
|
img.style.top = '';
|
|
3338
3450
|
img.style.transform = '';
|
|
3339
|
-
|
|
3340
3451
|
this._handleSnapback(originalFrom, piece, onSnapback);
|
|
3341
3452
|
} else {
|
|
3342
|
-
// Handle drop like a click - simple and reliable
|
|
3343
3453
|
this._handleDrop(originalFrom, to, piece, onMove, onSnapback);
|
|
3344
3454
|
}
|
|
3345
3455
|
};
|
|
3346
3456
|
|
|
3347
|
-
// Attach
|
|
3348
|
-
window.addEventListener('mouseup',
|
|
3349
|
-
document.addEventListener('mousemove',
|
|
3350
|
-
img.addEventListener('mouseup',
|
|
3457
|
+
// --- Attach listeners (mouse + touch) ---
|
|
3458
|
+
window.addEventListener('mouseup', onPointerUp, { once: true });
|
|
3459
|
+
document.addEventListener('mousemove', onPointerMove);
|
|
3460
|
+
img.addEventListener('mouseup', onPointerUp, { once: true });
|
|
3461
|
+
// Touch events
|
|
3462
|
+
window.addEventListener('touchend', onPointerUp, { once: true });
|
|
3463
|
+
document.addEventListener('touchmove', onPointerMove, { passive: false });
|
|
3464
|
+
|
|
3465
|
+
// Per robustezza: se il drag parte da touch, blocca subito lo scroll
|
|
3466
|
+
if (isTouch) addScrollLock();
|
|
3351
3467
|
};
|
|
3352
3468
|
}
|
|
3353
3469
|
|
|
@@ -3570,6 +3686,7 @@ class EventService {
|
|
|
3570
3686
|
if (!from) {
|
|
3571
3687
|
if (this.moveService.canMove(square)) {
|
|
3572
3688
|
if (this.config.clickable) {
|
|
3689
|
+
this.boardService.applyToAllSquares('removeHint'); // Rimuovi hint prima di selezionare
|
|
3573
3690
|
onSelect(square);
|
|
3574
3691
|
}
|
|
3575
3692
|
this.clicked = square;
|
|
@@ -3579,9 +3696,13 @@ class EventService {
|
|
|
3579
3696
|
}
|
|
3580
3697
|
}
|
|
3581
3698
|
|
|
3582
|
-
//
|
|
3699
|
+
// --- Touch: non deselezionare su doppio tap sulla stessa casella ---
|
|
3583
3700
|
if (this.clicked === square) {
|
|
3701
|
+
if (window.event && window.event.type && window.event.type.startsWith('touch')) {
|
|
3702
|
+
return false;
|
|
3703
|
+
}
|
|
3584
3704
|
onDeselect(square);
|
|
3705
|
+
this.boardService.applyToAllSquares('removeHint');
|
|
3585
3706
|
this.clicked = null;
|
|
3586
3707
|
return false;
|
|
3587
3708
|
}
|
|
@@ -3590,68 +3711,52 @@ class EventService {
|
|
|
3590
3711
|
if (!promotion && this.moveService.requiresPromotion(new Move$1(from, square))) {
|
|
3591
3712
|
console.log('Move requires promotion:', from.id, '->', square.id);
|
|
3592
3713
|
|
|
3593
|
-
// Set up promotion UI
|
|
3594
3714
|
this.moveService.setupPromotion(
|
|
3595
3715
|
new Move$1(from, square),
|
|
3596
3716
|
this.boardService.squares,
|
|
3597
3717
|
(selectedPromotion) => {
|
|
3598
3718
|
console.log('Promotion selected:', selectedPromotion);
|
|
3599
|
-
|
|
3600
|
-
// Clear promotion UI first
|
|
3601
3719
|
this.boardService.applyToAllSquares('removePromotion');
|
|
3602
3720
|
this.boardService.applyToAllSquares('removeCover');
|
|
3603
|
-
|
|
3604
|
-
// Execute the move with promotion
|
|
3605
3721
|
const moveResult = onMove(from, square, selectedPromotion, animate);
|
|
3606
|
-
|
|
3607
3722
|
if (moveResult) {
|
|
3608
|
-
// After a successful promotion move, we need to replace the piece
|
|
3609
|
-
// after the drop animation completes
|
|
3610
3723
|
this._schedulePromotionPieceReplacement(square, selectedPromotion);
|
|
3611
|
-
|
|
3612
3724
|
onDeselect(from);
|
|
3725
|
+
this.boardService.applyToAllSquares('removeHint');
|
|
3613
3726
|
this.clicked = null;
|
|
3614
3727
|
}
|
|
3615
3728
|
},
|
|
3616
3729
|
() => {
|
|
3617
3730
|
console.log('Promotion cancelled');
|
|
3618
|
-
|
|
3619
|
-
// Clear promotion UI on cancel
|
|
3620
3731
|
this.boardService.applyToAllSquares('removePromotion');
|
|
3621
3732
|
this.boardService.applyToAllSquares('removeCover');
|
|
3622
|
-
|
|
3623
3733
|
onDeselect(from);
|
|
3734
|
+
this.boardService.applyToAllSquares('removeHint');
|
|
3624
3735
|
this.clicked = null;
|
|
3625
3736
|
}
|
|
3626
3737
|
);
|
|
3627
3738
|
return false;
|
|
3628
3739
|
}
|
|
3629
3740
|
|
|
3630
|
-
// Attempt to make move
|
|
3631
3741
|
const moveResult = onMove(from, square, promotion, animate);
|
|
3632
3742
|
|
|
3633
3743
|
if (moveResult) {
|
|
3634
|
-
// Move successful
|
|
3635
3744
|
onDeselect(from);
|
|
3745
|
+
this.boardService.applyToAllSquares('removeHint');
|
|
3636
3746
|
this.clicked = null;
|
|
3637
3747
|
return true;
|
|
3638
3748
|
} else {
|
|
3639
|
-
// Move failed - check if clicked square has a piece we can move
|
|
3640
3749
|
if (this.moveService.canMove(square)) {
|
|
3641
|
-
// Deselect the previous piece
|
|
3642
3750
|
onDeselect(from);
|
|
3643
|
-
|
|
3644
|
-
// Select the new piece if clicking is enabled
|
|
3751
|
+
this.boardService.applyToAllSquares('removeHint');
|
|
3645
3752
|
if (this.config.clickable) {
|
|
3646
3753
|
onSelect(square);
|
|
3647
3754
|
}
|
|
3648
|
-
|
|
3649
|
-
// Set the new piece as clicked
|
|
3650
3755
|
this.clicked = square;
|
|
3651
3756
|
return false;
|
|
3652
3757
|
} else {
|
|
3653
|
-
// Move failed and no valid piece to select
|
|
3654
3758
|
onDeselect(from);
|
|
3759
|
+
this.boardService.applyToAllSquares('removeHint');
|
|
3655
3760
|
this.clicked = null;
|
|
3656
3761
|
return false;
|
|
3657
3762
|
}
|
|
@@ -3915,27 +4020,27 @@ class MoveService {
|
|
|
3915
4020
|
*/
|
|
3916
4021
|
canMove(square) {
|
|
3917
4022
|
if (!square.piece) return false;
|
|
3918
|
-
|
|
4023
|
+
|
|
3919
4024
|
const { movableColors, onlyLegalMoves } = this.config;
|
|
3920
|
-
|
|
4025
|
+
|
|
3921
4026
|
if (movableColors === 'none') return false;
|
|
3922
4027
|
if (movableColors === 'w' && square.piece.color === 'b') return false;
|
|
3923
4028
|
if (movableColors === 'b' && square.piece.color === 'w') return false;
|
|
3924
|
-
|
|
4029
|
+
|
|
3925
4030
|
if (!onlyLegalMoves) return true;
|
|
3926
|
-
|
|
4031
|
+
|
|
3927
4032
|
// Check if position service and game are available
|
|
3928
4033
|
if (!this.positionService || !this.positionService.getGame()) {
|
|
3929
4034
|
return false;
|
|
3930
4035
|
}
|
|
3931
|
-
|
|
4036
|
+
|
|
3932
4037
|
const game = this.positionService.getGame();
|
|
3933
4038
|
return square.piece.color === game.turn();
|
|
3934
4039
|
}
|
|
3935
4040
|
|
|
3936
4041
|
/**
|
|
3937
4042
|
* Converts various move formats to a Move instance
|
|
3938
|
-
* @param {string|Move} move - Move in various formats
|
|
4043
|
+
* @param {string|Move|Object} move - Move in various formats
|
|
3939
4044
|
* @param {Object} squares - All board squares
|
|
3940
4045
|
* @returns {Move} Move instance
|
|
3941
4046
|
* @throws {MoveError} When move format is invalid
|
|
@@ -3944,19 +4049,22 @@ class MoveService {
|
|
|
3944
4049
|
if (move instanceof Move$1) {
|
|
3945
4050
|
return move;
|
|
3946
4051
|
}
|
|
3947
|
-
|
|
4052
|
+
if (typeof move === 'object' && move.from && move.to) {
|
|
4053
|
+
// Se sono id, converto in oggetti; se sono già oggetti, li uso direttamente
|
|
4054
|
+
const fromSquare = typeof move.from === 'string' ? squares[move.from] : move.from;
|
|
4055
|
+
const toSquare = typeof move.to === 'string' ? squares[move.to] : move.to;
|
|
4056
|
+
if (!fromSquare || !toSquare) throw new MoveError(ERROR_MESSAGES.invalid_move_format, move.from, move.to);
|
|
4057
|
+
return new Move$1(fromSquare, toSquare, move.promotion);
|
|
4058
|
+
}
|
|
3948
4059
|
if (typeof move === 'string' && move.length >= 4) {
|
|
3949
4060
|
const fromId = move.slice(0, 2);
|
|
3950
4061
|
const toId = move.slice(2, 4);
|
|
3951
4062
|
const promotion = move.slice(4, 5) || null;
|
|
3952
|
-
|
|
3953
4063
|
if (!squares[fromId] || !squares[toId]) {
|
|
3954
4064
|
throw new MoveError(ERROR_MESSAGES.invalid_move_format, fromId, toId);
|
|
3955
4065
|
}
|
|
3956
|
-
|
|
3957
4066
|
return new Move$1(squares[fromId], squares[toId], promotion);
|
|
3958
4067
|
}
|
|
3959
|
-
|
|
3960
4068
|
throw new MoveError(ERROR_MESSAGES.invalid_move_format, 'unknown', 'unknown');
|
|
3961
4069
|
}
|
|
3962
4070
|
|
|
@@ -3967,9 +4075,9 @@ class MoveService {
|
|
|
3967
4075
|
*/
|
|
3968
4076
|
isLegalMove(move) {
|
|
3969
4077
|
const legalMoves = this.getLegalMoves(move.from.id);
|
|
3970
|
-
|
|
3971
|
-
return legalMoves.some(legalMove =>
|
|
3972
|
-
legalMove.to === move.to.id &&
|
|
4078
|
+
|
|
4079
|
+
return legalMoves.some(legalMove =>
|
|
4080
|
+
legalMove.to === move.to.id &&
|
|
3973
4081
|
move.promotion === legalMove.promotion
|
|
3974
4082
|
);
|
|
3975
4083
|
}
|
|
@@ -3985,16 +4093,16 @@ class MoveService {
|
|
|
3985
4093
|
if (!this.positionService || !this.positionService.getGame()) {
|
|
3986
4094
|
return [];
|
|
3987
4095
|
}
|
|
3988
|
-
|
|
4096
|
+
|
|
3989
4097
|
const game = this.positionService.getGame();
|
|
3990
|
-
|
|
4098
|
+
|
|
3991
4099
|
if (!game) return [];
|
|
3992
|
-
|
|
4100
|
+
|
|
3993
4101
|
const options = { verbose };
|
|
3994
4102
|
if (from) {
|
|
3995
4103
|
options.square = from;
|
|
3996
4104
|
}
|
|
3997
|
-
|
|
4105
|
+
|
|
3998
4106
|
return game.moves(options);
|
|
3999
4107
|
}
|
|
4000
4108
|
|
|
@@ -4008,115 +4116,99 @@ class MoveService {
|
|
|
4008
4116
|
if (!this.positionService || !this.positionService.getGame()) {
|
|
4009
4117
|
return [];
|
|
4010
4118
|
}
|
|
4011
|
-
|
|
4119
|
+
|
|
4012
4120
|
const game = this.positionService.getGame();
|
|
4013
4121
|
if (!game) return [];
|
|
4014
|
-
|
|
4122
|
+
|
|
4015
4123
|
const cacheKey = `${square.id}-${game.fen()}`;
|
|
4016
4124
|
let moves = this._movesCache.get(cacheKey);
|
|
4017
|
-
|
|
4125
|
+
|
|
4018
4126
|
if (!moves) {
|
|
4019
4127
|
moves = game.moves({ square: square.id, verbose: true });
|
|
4020
4128
|
this._movesCache.set(cacheKey, moves);
|
|
4021
|
-
|
|
4129
|
+
|
|
4022
4130
|
// Clear cache after a short delay to prevent memory buildup
|
|
4023
4131
|
if (this._cacheTimeout) {
|
|
4024
4132
|
clearTimeout(this._cacheTimeout);
|
|
4025
4133
|
}
|
|
4026
|
-
|
|
4134
|
+
|
|
4027
4135
|
this._cacheTimeout = setTimeout(() => {
|
|
4028
4136
|
this._movesCache.clear();
|
|
4029
4137
|
}, 1000);
|
|
4030
4138
|
}
|
|
4031
|
-
|
|
4139
|
+
|
|
4032
4140
|
return moves;
|
|
4033
4141
|
}
|
|
4034
4142
|
|
|
4035
4143
|
/**
|
|
4036
4144
|
* Executes a move on the game
|
|
4037
|
-
* @param {Move} move - Move to execute
|
|
4145
|
+
* @param {Move} move - Move to execute (deve essere oggetto Move)
|
|
4038
4146
|
* @returns {Object|null} Move result from chess.js or null if invalid
|
|
4039
4147
|
*/
|
|
4040
4148
|
executeMove(move) {
|
|
4041
|
-
|
|
4149
|
+
if (!(move instanceof Move$1)) throw new Error('executeMove richiede un oggetto Move');
|
|
4042
4150
|
if (!this.positionService || !this.positionService.getGame()) {
|
|
4043
4151
|
return null;
|
|
4044
4152
|
}
|
|
4045
|
-
|
|
4046
4153
|
const game = this.positionService.getGame();
|
|
4047
4154
|
if (!game) return null;
|
|
4048
|
-
|
|
4049
4155
|
const moveOptions = {
|
|
4050
4156
|
from: move.from.id,
|
|
4051
4157
|
to: move.to.id
|
|
4052
4158
|
};
|
|
4053
|
-
|
|
4054
|
-
console.log('executeMove - move.promotion:', move.promotion);
|
|
4055
|
-
console.log('executeMove - move.hasPromotion():', move.hasPromotion());
|
|
4056
|
-
|
|
4057
4159
|
if (move.hasPromotion()) {
|
|
4058
4160
|
moveOptions.promotion = move.promotion;
|
|
4059
4161
|
}
|
|
4060
|
-
|
|
4061
|
-
console.log('executeMove - moveOptions:', moveOptions);
|
|
4062
|
-
|
|
4063
4162
|
const result = game.move(moveOptions);
|
|
4064
|
-
console.log('executeMove - result:', result);
|
|
4065
|
-
|
|
4066
|
-
// Check what's actually on the board after the move
|
|
4067
|
-
if (result) {
|
|
4068
|
-
const pieceOnDestination = game.get(move.to.id);
|
|
4069
|
-
console.log('executeMove - piece on destination after move:', pieceOnDestination);
|
|
4070
|
-
}
|
|
4071
|
-
|
|
4072
4163
|
return result;
|
|
4073
4164
|
}
|
|
4074
4165
|
|
|
4075
4166
|
/**
|
|
4076
|
-
*
|
|
4077
|
-
* @param {Move} move -
|
|
4078
|
-
* @returns {boolean}
|
|
4167
|
+
* Determina se una mossa richiede promozione
|
|
4168
|
+
* @param {Move} move - Deve essere oggetto Move
|
|
4169
|
+
* @returns {boolean}
|
|
4079
4170
|
*/
|
|
4080
4171
|
requiresPromotion(move) {
|
|
4172
|
+
if (!(move instanceof Move$1)) throw new Error('requiresPromotion richiede un oggetto Move');
|
|
4081
4173
|
console.log('Checking if move requires promotion:', move.from.id, '->', move.to.id);
|
|
4082
|
-
|
|
4174
|
+
|
|
4083
4175
|
if (!this.config.onlyLegalMoves) {
|
|
4084
4176
|
console.log('Not in legal moves mode, no promotion required');
|
|
4085
4177
|
return false;
|
|
4086
4178
|
}
|
|
4087
|
-
|
|
4179
|
+
|
|
4088
4180
|
const game = this.positionService.getGame();
|
|
4089
4181
|
if (!game) {
|
|
4090
4182
|
console.log('No game instance available');
|
|
4091
4183
|
return false;
|
|
4092
4184
|
}
|
|
4093
|
-
|
|
4185
|
+
|
|
4094
4186
|
const piece = game.get(move.from.id);
|
|
4095
4187
|
if (!piece || piece.type !== 'p') {
|
|
4096
4188
|
console.log('Not a pawn move, no promotion required');
|
|
4097
4189
|
return false;
|
|
4098
4190
|
}
|
|
4099
|
-
|
|
4191
|
+
|
|
4100
4192
|
const targetRank = move.to.row;
|
|
4101
4193
|
if (targetRank !== 1 && targetRank !== 8) {
|
|
4102
4194
|
console.log('Not reaching promotion rank, no promotion required');
|
|
4103
4195
|
return false;
|
|
4104
4196
|
}
|
|
4105
|
-
|
|
4197
|
+
|
|
4106
4198
|
console.log('Pawn reaching promotion rank, validating move...');
|
|
4107
|
-
|
|
4199
|
+
|
|
4108
4200
|
// Additional validation: check if the pawn can actually reach this square
|
|
4109
4201
|
if (!this._isPawnMoveValid(move.from, move.to, piece.color)) {
|
|
4110
4202
|
console.log('Pawn move not valid, no promotion required');
|
|
4111
4203
|
return false;
|
|
4112
4204
|
}
|
|
4113
|
-
|
|
4205
|
+
|
|
4114
4206
|
// First check if the move is legal without promotion
|
|
4115
4207
|
const simpleMoveObj = {
|
|
4116
4208
|
from: move.from.id,
|
|
4117
4209
|
to: move.to.id
|
|
4118
4210
|
};
|
|
4119
|
-
|
|
4211
|
+
|
|
4120
4212
|
try {
|
|
4121
4213
|
console.log('Testing move without promotion:', simpleMoveObj);
|
|
4122
4214
|
// Test if the move is legal without promotion first
|
|
@@ -4124,25 +4216,25 @@ class MoveService {
|
|
|
4124
4216
|
if (testMove) {
|
|
4125
4217
|
// Move was successful, but check if it was a promotion
|
|
4126
4218
|
const wasPromotion = testMove.promotion;
|
|
4127
|
-
|
|
4219
|
+
|
|
4128
4220
|
// Undo the test move
|
|
4129
4221
|
game.undo();
|
|
4130
|
-
|
|
4222
|
+
|
|
4131
4223
|
console.log('Move successful without promotion, was promotion:', wasPromotion !== undefined);
|
|
4132
|
-
|
|
4224
|
+
|
|
4133
4225
|
// If it was a promotion, return true
|
|
4134
4226
|
return wasPromotion !== undefined;
|
|
4135
4227
|
}
|
|
4136
4228
|
} catch (error) {
|
|
4137
4229
|
console.log('Move failed without promotion, trying with promotion:', error.message);
|
|
4138
|
-
|
|
4230
|
+
|
|
4139
4231
|
// If simple move fails, try with promotion
|
|
4140
4232
|
const promotionMoveObj = {
|
|
4141
4233
|
from: move.from.id,
|
|
4142
4234
|
to: move.to.id,
|
|
4143
4235
|
promotion: 'q' // test with queen
|
|
4144
4236
|
};
|
|
4145
|
-
|
|
4237
|
+
|
|
4146
4238
|
try {
|
|
4147
4239
|
console.log('Testing move with promotion:', promotionMoveObj);
|
|
4148
4240
|
const testMove = game.move(promotionMoveObj);
|
|
@@ -4158,7 +4250,7 @@ class MoveService {
|
|
|
4158
4250
|
return false;
|
|
4159
4251
|
}
|
|
4160
4252
|
}
|
|
4161
|
-
|
|
4253
|
+
|
|
4162
4254
|
console.log('Move validation complete, no promotion required');
|
|
4163
4255
|
return false;
|
|
4164
4256
|
}
|
|
@@ -4176,27 +4268,27 @@ class MoveService {
|
|
|
4176
4268
|
const toRank = to.row;
|
|
4177
4269
|
const fromFile = from.col;
|
|
4178
4270
|
const toFile = to.col;
|
|
4179
|
-
|
|
4271
|
+
|
|
4180
4272
|
console.log(`Validating pawn move: ${from.id} -> ${to.id} (${color})`);
|
|
4181
4273
|
console.log(`Ranks: ${fromRank} -> ${toRank}, Files: ${fromFile} -> ${toFile}`);
|
|
4182
|
-
|
|
4274
|
+
|
|
4183
4275
|
// Direction of pawn movement
|
|
4184
4276
|
const direction = color === 'w' ? 1 : -1;
|
|
4185
4277
|
const rankDiff = toRank - fromRank;
|
|
4186
4278
|
const fileDiff = Math.abs(toFile - fromFile);
|
|
4187
|
-
|
|
4279
|
+
|
|
4188
4280
|
// Pawn can only move forward
|
|
4189
4281
|
if (rankDiff * direction <= 0) {
|
|
4190
4282
|
console.log('Invalid: Pawn cannot move backward or stay in place');
|
|
4191
4283
|
return false;
|
|
4192
4284
|
}
|
|
4193
|
-
|
|
4285
|
+
|
|
4194
4286
|
// Pawn can only move 1 rank at a time (except for double move from starting position)
|
|
4195
4287
|
if (Math.abs(rankDiff) > 2) {
|
|
4196
4288
|
console.log('Invalid: Pawn cannot move more than 2 ranks');
|
|
4197
4289
|
return false;
|
|
4198
4290
|
}
|
|
4199
|
-
|
|
4291
|
+
|
|
4200
4292
|
// If moving 2 ranks, must be from starting position
|
|
4201
4293
|
if (Math.abs(rankDiff) === 2) {
|
|
4202
4294
|
const startingRank = color === 'w' ? 2 : 7;
|
|
@@ -4205,13 +4297,13 @@ class MoveService {
|
|
|
4205
4297
|
return false;
|
|
4206
4298
|
}
|
|
4207
4299
|
}
|
|
4208
|
-
|
|
4300
|
+
|
|
4209
4301
|
// Pawn can only move to adjacent files (diagonal capture) or same file (forward move)
|
|
4210
4302
|
if (fileDiff > 1) {
|
|
4211
4303
|
console.log('Invalid: Pawn cannot move more than 1 file');
|
|
4212
4304
|
return false;
|
|
4213
4305
|
}
|
|
4214
|
-
|
|
4306
|
+
|
|
4215
4307
|
console.log('Pawn move validation passed');
|
|
4216
4308
|
return true;
|
|
4217
4309
|
}
|
|
@@ -4226,45 +4318,45 @@ class MoveService {
|
|
|
4226
4318
|
*/
|
|
4227
4319
|
setupPromotion(move, squares, onPromotionSelect, onPromotionCancel) {
|
|
4228
4320
|
if (!this.requiresPromotion(move)) return false;
|
|
4229
|
-
|
|
4321
|
+
|
|
4230
4322
|
// Check if position service and game are available
|
|
4231
4323
|
if (!this.positionService || !this.positionService.getGame()) {
|
|
4232
4324
|
return false;
|
|
4233
4325
|
}
|
|
4234
|
-
|
|
4326
|
+
|
|
4235
4327
|
const game = this.positionService.getGame();
|
|
4236
4328
|
const piece = game.get(move.from.id);
|
|
4237
4329
|
const targetSquare = move.to;
|
|
4238
|
-
|
|
4330
|
+
|
|
4239
4331
|
// Clear any existing promotion UI
|
|
4240
4332
|
Object.values(squares).forEach(square => {
|
|
4241
4333
|
square.removePromotion();
|
|
4242
4334
|
square.removeCover();
|
|
4243
4335
|
});
|
|
4244
|
-
|
|
4336
|
+
|
|
4245
4337
|
// Always show promotion choices in a column
|
|
4246
4338
|
this._showPromotionInColumn(targetSquare, piece, squares, onPromotionSelect, onPromotionCancel);
|
|
4247
|
-
|
|
4339
|
+
|
|
4248
4340
|
return true;
|
|
4249
4341
|
}
|
|
4250
|
-
|
|
4342
|
+
|
|
4251
4343
|
/**
|
|
4252
4344
|
* Shows promotion choices in a column
|
|
4253
4345
|
* @private
|
|
4254
4346
|
*/
|
|
4255
4347
|
_showPromotionInColumn(targetSquare, piece, squares, onPromotionSelect, onPromotionCancel) {
|
|
4256
4348
|
console.log('Setting up promotion for', targetSquare.id, 'piece color:', piece.color);
|
|
4257
|
-
|
|
4349
|
+
|
|
4258
4350
|
// Set up promotion choices starting from border row
|
|
4259
4351
|
PROMOTION_PIECES.forEach((pieceType, index) => {
|
|
4260
4352
|
const choiceSquare = this._findPromotionSquare(targetSquare, index, squares);
|
|
4261
|
-
|
|
4353
|
+
|
|
4262
4354
|
if (choiceSquare) {
|
|
4263
4355
|
const pieceId = pieceType + piece.color;
|
|
4264
4356
|
const piecePath = this._getPiecePathForPromotion(pieceId);
|
|
4265
|
-
|
|
4357
|
+
|
|
4266
4358
|
console.log('Setting up promotion choice:', pieceType, 'on square:', choiceSquare.id);
|
|
4267
|
-
|
|
4359
|
+
|
|
4268
4360
|
choiceSquare.putPromotion(piecePath, () => {
|
|
4269
4361
|
console.log('Promotion choice selected:', pieceType);
|
|
4270
4362
|
onPromotionSelect(pieceType);
|
|
@@ -4273,7 +4365,7 @@ class MoveService {
|
|
|
4273
4365
|
console.log('Could not find square for promotion choice:', pieceType, 'index:', index);
|
|
4274
4366
|
}
|
|
4275
4367
|
});
|
|
4276
|
-
|
|
4368
|
+
|
|
4277
4369
|
// Set up cover squares (for cancellation)
|
|
4278
4370
|
Object.values(squares).forEach(square => {
|
|
4279
4371
|
if (!square.hasPromotion()) {
|
|
@@ -4282,7 +4374,7 @@ class MoveService {
|
|
|
4282
4374
|
});
|
|
4283
4375
|
}
|
|
4284
4376
|
});
|
|
4285
|
-
|
|
4377
|
+
|
|
4286
4378
|
return true;
|
|
4287
4379
|
}
|
|
4288
4380
|
|
|
@@ -4297,9 +4389,9 @@ class MoveService {
|
|
|
4297
4389
|
_findPromotionSquare(targetSquare, index, squares) {
|
|
4298
4390
|
const col = targetSquare.col;
|
|
4299
4391
|
const baseRow = targetSquare.row;
|
|
4300
|
-
|
|
4392
|
+
|
|
4301
4393
|
console.log('Looking for promotion square - target:', targetSquare.id, 'index:', index, 'col:', col, 'baseRow:', baseRow);
|
|
4302
|
-
|
|
4394
|
+
|
|
4303
4395
|
// Calculate row based on index and promotion direction
|
|
4304
4396
|
// Start from the border row (1 or 8) and go inward
|
|
4305
4397
|
let row;
|
|
@@ -4313,15 +4405,15 @@ class MoveService {
|
|
|
4313
4405
|
console.log('Invalid promotion row:', baseRow);
|
|
4314
4406
|
return null;
|
|
4315
4407
|
}
|
|
4316
|
-
|
|
4408
|
+
|
|
4317
4409
|
console.log('Calculated row:', row);
|
|
4318
|
-
|
|
4410
|
+
|
|
4319
4411
|
// Ensure row is within bounds
|
|
4320
4412
|
if (row < 1 || row > 8) {
|
|
4321
4413
|
console.log('Row out of bounds:', row);
|
|
4322
4414
|
return null;
|
|
4323
4415
|
}
|
|
4324
|
-
|
|
4416
|
+
|
|
4325
4417
|
// Find square by row/col
|
|
4326
4418
|
for (const square of Object.values(squares)) {
|
|
4327
4419
|
if (square.col === col && square.row === row) {
|
|
@@ -4329,7 +4421,7 @@ class MoveService {
|
|
|
4329
4421
|
return square;
|
|
4330
4422
|
}
|
|
4331
4423
|
}
|
|
4332
|
-
|
|
4424
|
+
|
|
4333
4425
|
console.log('No square found for col:', col, 'row:', row);
|
|
4334
4426
|
return null;
|
|
4335
4427
|
}
|
|
@@ -4344,11 +4436,11 @@ class MoveService {
|
|
|
4344
4436
|
// This would typically use the PieceService
|
|
4345
4437
|
// For now, we'll use a simple implementation
|
|
4346
4438
|
const { piecesPath } = this.config;
|
|
4347
|
-
|
|
4439
|
+
|
|
4348
4440
|
if (typeof piecesPath === 'string') {
|
|
4349
4441
|
return `${piecesPath}/${pieceId}.svg`;
|
|
4350
4442
|
}
|
|
4351
|
-
|
|
4443
|
+
|
|
4352
4444
|
// Fallback for other path types
|
|
4353
4445
|
return `assets/pieces/${pieceId}.svg`;
|
|
4354
4446
|
}
|
|
@@ -4362,20 +4454,20 @@ class MoveService {
|
|
|
4362
4454
|
if (typeof moveString !== 'string' || moveString.length < 4 || moveString.length > 5) {
|
|
4363
4455
|
return null;
|
|
4364
4456
|
}
|
|
4365
|
-
|
|
4457
|
+
|
|
4366
4458
|
const from = moveString.slice(0, 2);
|
|
4367
4459
|
const to = moveString.slice(2, 4);
|
|
4368
4460
|
const promotion = moveString.slice(4, 5);
|
|
4369
|
-
|
|
4461
|
+
|
|
4370
4462
|
// Basic validation
|
|
4371
4463
|
if (!/^[a-h][1-8]$/.test(from) || !/^[a-h][1-8]$/.test(to)) {
|
|
4372
4464
|
return null;
|
|
4373
4465
|
}
|
|
4374
|
-
|
|
4466
|
+
|
|
4375
4467
|
if (promotion && !['q', 'r', 'b', 'n'].includes(promotion.toLowerCase())) {
|
|
4376
4468
|
return null;
|
|
4377
4469
|
}
|
|
4378
|
-
|
|
4470
|
+
|
|
4379
4471
|
return {
|
|
4380
4472
|
from: from,
|
|
4381
4473
|
to: to,
|
|
@@ -4401,10 +4493,10 @@ class MoveService {
|
|
|
4401
4493
|
if (!this.isCastle(gameMove)) {
|
|
4402
4494
|
return null;
|
|
4403
4495
|
}
|
|
4404
|
-
|
|
4496
|
+
|
|
4405
4497
|
const isKingSide = gameMove.isKingsideCastle();
|
|
4406
4498
|
const isWhite = gameMove.color === 'w';
|
|
4407
|
-
|
|
4499
|
+
|
|
4408
4500
|
if (isKingSide) {
|
|
4409
4501
|
// King side castle
|
|
4410
4502
|
if (isWhite) {
|
|
@@ -4440,11 +4532,11 @@ class MoveService {
|
|
|
4440
4532
|
if (!this.isEnPassant(gameMove)) {
|
|
4441
4533
|
return null;
|
|
4442
4534
|
}
|
|
4443
|
-
|
|
4535
|
+
|
|
4444
4536
|
const toSquare = gameMove.to;
|
|
4445
4537
|
const rank = parseInt(toSquare[1]);
|
|
4446
4538
|
const file = toSquare[0];
|
|
4447
|
-
|
|
4539
|
+
|
|
4448
4540
|
// The captured pawn is on the same file but different rank
|
|
4449
4541
|
if (gameMove.color === 'w') {
|
|
4450
4542
|
// White captures black pawn one rank below
|
|
@@ -4552,13 +4644,14 @@ class PieceService {
|
|
|
4552
4644
|
|
|
4553
4645
|
/**
|
|
4554
4646
|
* Adds a piece to a square with optional fade-in animation
|
|
4555
|
-
* @param {Square} square - Target square
|
|
4556
|
-
* @param {Piece} piece - Piece to add
|
|
4647
|
+
* @param {Square} square - Target square (oggetto)
|
|
4648
|
+
* @param {Piece} piece - Piece to add (oggetto)
|
|
4557
4649
|
* @param {boolean} [fade=true] - Whether to fade in the piece
|
|
4558
4650
|
* @param {Function} dragFunction - Function to handle drag events
|
|
4559
4651
|
* @param {Function} [callback] - Callback when animation completes
|
|
4560
4652
|
*/
|
|
4561
4653
|
addPieceOnSquare(square, piece, fade = true, dragFunction, callback) {
|
|
4654
|
+
if (!square || !piece) throw new Error('addPieceOnSquare richiede oggetti Square e Piece');
|
|
4562
4655
|
console.debug(`[PieceService] addPieceOnSquare: ${piece.id} to ${square.id}`);
|
|
4563
4656
|
square.putPiece(piece);
|
|
4564
4657
|
|
|
@@ -4581,14 +4674,14 @@ class PieceService {
|
|
|
4581
4674
|
}
|
|
4582
4675
|
|
|
4583
4676
|
/**
|
|
4584
|
-
*
|
|
4585
|
-
* @param {Square} square -
|
|
4586
|
-
* @param {boolean} [fade=true]
|
|
4587
|
-
* @param {Function} [callback]
|
|
4588
|
-
* @returns {Piece}
|
|
4589
|
-
* @throws {PieceError} When square has no piece to remove
|
|
4677
|
+
* Rimuove un pezzo da una casella
|
|
4678
|
+
* @param {Square} square - Oggetto Square
|
|
4679
|
+
* @param {boolean} [fade=true]
|
|
4680
|
+
* @param {Function} [callback]
|
|
4681
|
+
* @returns {Piece} Il pezzo rimosso
|
|
4590
4682
|
*/
|
|
4591
4683
|
removePieceFromSquare(square, fade = true, callback) {
|
|
4684
|
+
if (!square) throw new Error('removePieceFromSquare richiede oggetto Square');
|
|
4592
4685
|
console.debug(`[PieceService] removePieceFromSquare: ${square.id}`);
|
|
4593
4686
|
square.check();
|
|
4594
4687
|
|
|
@@ -7635,68 +7728,48 @@ let Chessboard$1 = class Chessboard {
|
|
|
7635
7728
|
* @param {boolean} [isPositionLoad=false] - Whether this is a position load (affects delay)
|
|
7636
7729
|
*/
|
|
7637
7730
|
_doUpdateBoardPieces(animation = false, isPositionLoad = false) {
|
|
7731
|
+
// Blocca update se un drag è in corso
|
|
7732
|
+
if (this._isDragging) return;
|
|
7638
7733
|
// Skip update if we're in the middle of a promotion
|
|
7639
7734
|
if (this._isPromoting) {
|
|
7640
|
-
console.log('Skipping board update during promotion');
|
|
7641
7735
|
return;
|
|
7642
7736
|
}
|
|
7643
|
-
|
|
7644
|
-
// Check if services are available
|
|
7645
7737
|
if (!this.positionService || !this.positionService.getGame()) {
|
|
7646
|
-
console.log('Cannot update board pieces - position service not available');
|
|
7647
7738
|
return;
|
|
7648
7739
|
}
|
|
7649
|
-
|
|
7650
7740
|
const squares = this.boardService.getAllSquares();
|
|
7651
7741
|
const gameStateBefore = this.positionService.getGame().fen();
|
|
7652
|
-
|
|
7653
|
-
// PATCH ROBUSTA: se la board è completamente vuota, forza la rimozione di TUTTI i pezzi
|
|
7654
7742
|
if (/^8\/8\/8\/8\/8\/8\/8\/8/.test(gameStateBefore)) {
|
|
7655
|
-
console.log('Board vuota rilevata - rimozione forzata di tutti i pezzi dal DOM');
|
|
7656
|
-
|
|
7657
|
-
// 1. Rimuovi tutti gli elementi DOM dei pezzi dal contenitore della board
|
|
7658
7743
|
const boardContainer = document.getElementById(this.config.id_div);
|
|
7659
7744
|
if (boardContainer) {
|
|
7660
7745
|
const pieceElements = boardContainer.querySelectorAll('.piece');
|
|
7661
7746
|
pieceElements.forEach(element => {
|
|
7662
|
-
console.log('Rimozione forzata elemento DOM pezzo:', element);
|
|
7663
7747
|
element.remove();
|
|
7664
7748
|
});
|
|
7665
7749
|
}
|
|
7666
|
-
|
|
7667
|
-
// 2. Azzera tutti i riferimenti JS ai pezzi
|
|
7668
7750
|
Object.values(squares).forEach(sq => {
|
|
7669
7751
|
if (sq && sq.piece) {
|
|
7670
|
-
console.log('Azzero riferimento pezzo su casella:', sq.id);
|
|
7671
7752
|
sq.piece = null;
|
|
7672
7753
|
}
|
|
7673
7754
|
});
|
|
7674
|
-
|
|
7675
|
-
// 3. Forza la pulizia di eventuali selezioni/hint
|
|
7676
7755
|
this._clearVisualState();
|
|
7677
|
-
|
|
7678
|
-
// 4. Aggiungi i listener e notifica il cambio
|
|
7679
7756
|
this._addListeners();
|
|
7680
7757
|
if (this.config.onChange) this.config.onChange(gameStateBefore);
|
|
7681
|
-
|
|
7682
|
-
console.log('Rimozione forzata completata');
|
|
7683
7758
|
return;
|
|
7684
7759
|
}
|
|
7685
|
-
|
|
7686
|
-
console.log('_doUpdateBoardPieces - current FEN:', gameStateBefore);
|
|
7687
|
-
console.log('_doUpdateBoardPieces - animation:', animation, 'style:', this.config.animationStyle, 'isPositionLoad:', isPositionLoad);
|
|
7688
|
-
|
|
7689
|
-
// Determine which animation style to use
|
|
7690
7760
|
const useSimultaneous = this.config.animationStyle === 'simultaneous';
|
|
7691
|
-
console.log('_doUpdateBoardPieces - useSimultaneous:', useSimultaneous);
|
|
7692
|
-
|
|
7693
7761
|
if (useSimultaneous) {
|
|
7694
|
-
console.log('Using simultaneous animation');
|
|
7695
7762
|
this._doSimultaneousUpdate(squares, gameStateBefore, isPositionLoad);
|
|
7696
7763
|
} else {
|
|
7697
|
-
console.log('Using sequential animation');
|
|
7698
7764
|
this._doSequentialUpdate(squares, gameStateBefore, animation);
|
|
7699
7765
|
}
|
|
7766
|
+
// Pulizia finale robusta: rimuovi tutti i pezzi orfani dal DOM e dal riferimento JS
|
|
7767
|
+
Object.values(this.boardService.getAllSquares()).forEach(square => {
|
|
7768
|
+
const expectedPieceId = this.positionService.getGamePieceId(square.id);
|
|
7769
|
+
if (!expectedPieceId && typeof square.forceRemoveAllPieces === 'function') {
|
|
7770
|
+
square.forceRemoveAllPieces();
|
|
7771
|
+
}
|
|
7772
|
+
});
|
|
7700
7773
|
}
|
|
7701
7774
|
|
|
7702
7775
|
/**
|
|
@@ -7725,7 +7798,12 @@ let Chessboard$1 = class Chessboard {
|
|
|
7725
7798
|
|
|
7726
7799
|
// Se c'è un pezzo attuale ma non è quello atteso, rimuovilo
|
|
7727
7800
|
if (currentPiece && currentPieceId !== expectedPieceId) {
|
|
7728
|
-
|
|
7801
|
+
// Rimozione robusta: elimina tutti i pezzi orfani dal DOM e dal riferimento JS
|
|
7802
|
+
if (typeof square.forceRemoveAllPieces === 'function') {
|
|
7803
|
+
square.forceRemoveAllPieces();
|
|
7804
|
+
} else {
|
|
7805
|
+
this.pieceService.removePieceFromSquare(square, animation);
|
|
7806
|
+
}
|
|
7729
7807
|
}
|
|
7730
7808
|
|
|
7731
7809
|
// Se c'è un pezzo atteso ma non è quello attuale, aggiungilo
|
|
@@ -7854,7 +7932,13 @@ let Chessboard$1 = class Chessboard {
|
|
|
7854
7932
|
for (let i = 0; i < fromList.length; i++) {
|
|
7855
7933
|
if (!fromMatched[i]) {
|
|
7856
7934
|
setTimeout(() => {
|
|
7857
|
-
|
|
7935
|
+
// Rimozione robusta: elimina tutti i pezzi orfani dal DOM e dal riferimento JS
|
|
7936
|
+
if (typeof fromList[i].square.forceRemoveAllPieces === 'function') {
|
|
7937
|
+
fromList[i].square.forceRemoveAllPieces();
|
|
7938
|
+
} else {
|
|
7939
|
+
this.pieceService.removePieceFromSquare(fromList[i].square, true, onAnimationComplete);
|
|
7940
|
+
}
|
|
7941
|
+
onAnimationComplete();
|
|
7858
7942
|
}, animationIndex * animationDelay);
|
|
7859
7943
|
animationIndex++;
|
|
7860
7944
|
}
|
|
@@ -8246,6 +8330,8 @@ let Chessboard$1 = class Chessboard {
|
|
|
8246
8330
|
if (this._updateBoardPieces) {
|
|
8247
8331
|
this._updateBoardPieces(animate, true);
|
|
8248
8332
|
}
|
|
8333
|
+
// Forza la sincronizzazione dopo setPosition
|
|
8334
|
+
this._updateBoardPieces(true, false);
|
|
8249
8335
|
return true;
|
|
8250
8336
|
}
|
|
8251
8337
|
/**
|
|
@@ -8259,7 +8345,10 @@ let Chessboard$1 = class Chessboard {
|
|
|
8259
8345
|
// Use the default starting position from config or fallback
|
|
8260
8346
|
const startPosition = this.config && this.config.position ? this.config.position : 'start';
|
|
8261
8347
|
this._updateBoardPieces(animate);
|
|
8262
|
-
|
|
8348
|
+
const result = this.setPosition(startPosition, { animate });
|
|
8349
|
+
// Forza la sincronizzazione dopo reset
|
|
8350
|
+
this._updateBoardPieces(true, false);
|
|
8351
|
+
return result;
|
|
8263
8352
|
}
|
|
8264
8353
|
/**
|
|
8265
8354
|
* Clear the board
|
|
@@ -8283,6 +8372,8 @@ let Chessboard$1 = class Chessboard {
|
|
|
8283
8372
|
if (this._updateBoardPieces) {
|
|
8284
8373
|
this._updateBoardPieces(animate, true);
|
|
8285
8374
|
}
|
|
8375
|
+
// Forza la sincronizzazione dopo clear
|
|
8376
|
+
this._updateBoardPieces(true, false);
|
|
8286
8377
|
return true;
|
|
8287
8378
|
}
|
|
8288
8379
|
|
|
@@ -8297,7 +8388,8 @@ let Chessboard$1 = class Chessboard {
|
|
|
8297
8388
|
const undone = this.positionService.getGame().undo();
|
|
8298
8389
|
if (undone) {
|
|
8299
8390
|
this._undoneMoves.push(undone);
|
|
8300
|
-
|
|
8391
|
+
// Forza refresh completo di tutti i pezzi dopo undo
|
|
8392
|
+
this._updateBoardPieces(true, true);
|
|
8301
8393
|
return undone;
|
|
8302
8394
|
}
|
|
8303
8395
|
return null;
|
|
@@ -8314,7 +8406,8 @@ let Chessboard$1 = class Chessboard {
|
|
|
8314
8406
|
const moveObj = { from: move.from, to: move.to };
|
|
8315
8407
|
if (move.promotion) moveObj.promotion = move.promotion;
|
|
8316
8408
|
const result = this.positionService.getGame().move(moveObj);
|
|
8317
|
-
|
|
8409
|
+
// Forza refresh completo di tutti i pezzi dopo redo
|
|
8410
|
+
this._updateBoardPieces(true, true);
|
|
8318
8411
|
return result;
|
|
8319
8412
|
}
|
|
8320
8413
|
return false;
|
|
@@ -8333,17 +8426,19 @@ let Chessboard$1 = class Chessboard {
|
|
|
8333
8426
|
* @returns {string|null}
|
|
8334
8427
|
*/
|
|
8335
8428
|
getPiece(square) {
|
|
8336
|
-
//
|
|
8337
|
-
const
|
|
8338
|
-
if (!
|
|
8339
|
-
|
|
8429
|
+
// Sempre leggi lo stato aggiornato dal boardService
|
|
8430
|
+
const squareObj = typeof square === 'string' ? this.boardService.getSquare(square) : square;
|
|
8431
|
+
if (!squareObj || typeof squareObj !== 'object' || !('id' in squareObj)) throw new Error('[getPiece] Parametro square non valido');
|
|
8432
|
+
// Forza sync prima di leggere
|
|
8433
|
+
this._updateBoardPieces(false, false);
|
|
8434
|
+
const piece = squareObj.piece;
|
|
8340
8435
|
if (!piece) return null;
|
|
8341
8436
|
return (piece.color + piece.type).toLowerCase();
|
|
8342
8437
|
}
|
|
8343
8438
|
/**
|
|
8344
8439
|
* Put a piece on a square
|
|
8345
|
-
* @param {string} piece
|
|
8346
|
-
* @param {string} square
|
|
8440
|
+
* @param {string|Piece} piece
|
|
8441
|
+
* @param {string|Square} square
|
|
8347
8442
|
* @param {Object} [opts]
|
|
8348
8443
|
* @param {boolean} [opts.animate=true]
|
|
8349
8444
|
* @returns {boolean}
|
|
@@ -8354,7 +8449,6 @@ let Chessboard$1 = class Chessboard {
|
|
|
8354
8449
|
if (typeof piece === 'object' && piece.type && piece.color) {
|
|
8355
8450
|
pieceStr = (piece.color + piece.type).toLowerCase();
|
|
8356
8451
|
} else if (typeof piece === 'string' && piece.length === 2) {
|
|
8357
|
-
// Accetta sia 'wq' che 'qw', normalizza a 'wq'
|
|
8358
8452
|
const a = piece[0].toLowerCase();
|
|
8359
8453
|
const b = piece[1].toLowerCase();
|
|
8360
8454
|
const types = 'kqrbnp';
|
|
@@ -8367,42 +8461,36 @@ let Chessboard$1 = class Chessboard {
|
|
|
8367
8461
|
throw new Error(`[putPiece] Invalid piece: ${piece}`);
|
|
8368
8462
|
}
|
|
8369
8463
|
}
|
|
8370
|
-
const
|
|
8371
|
-
|
|
8372
|
-
if (!validSquare) throw new Error(`[putPiece] Invalid square: ${square}`);
|
|
8373
|
-
if (!validPiece) throw new Error(`[putPiece] Invalid piece: ${pieceStr}`);
|
|
8374
|
-
if (!this.positionService || !this.positionService.getGame()) {
|
|
8375
|
-
throw new Error('[putPiece] No positionService or game');
|
|
8376
|
-
}
|
|
8464
|
+
const squareObj = typeof square === 'string' ? this.boardService.getSquare(square) : square;
|
|
8465
|
+
if (!squareObj || typeof squareObj !== 'object' || !('id' in squareObj)) throw new Error('[putPiece] Parametro square non valido');
|
|
8377
8466
|
const pieceObj = this.pieceService.convertPiece(pieceStr);
|
|
8378
|
-
|
|
8379
|
-
|
|
8380
|
-
squareObj.piece = pieceObj;
|
|
8467
|
+
if (!pieceObj || typeof pieceObj !== 'object' || !('type' in pieceObj)) throw new Error('[putPiece] Parametro piece non valido');
|
|
8468
|
+
// Aggiorna solo il motore chess.js
|
|
8381
8469
|
const chessJsPiece = { type: pieceObj.type, color: pieceObj.color };
|
|
8382
8470
|
const game = this.positionService.getGame();
|
|
8383
|
-
const result = game.put(chessJsPiece,
|
|
8384
|
-
if (!result) throw new Error(`[putPiece] Game.put failed for ${pieceStr} on ${
|
|
8471
|
+
const result = game.put(chessJsPiece, squareObj.id);
|
|
8472
|
+
if (!result) throw new Error(`[putPiece] Game.put failed for ${pieceStr} on ${squareObj.id}`);
|
|
8473
|
+
// Non aggiornare direttamente square.piece!
|
|
8474
|
+
// Riallinea la board JS allo stato del motore
|
|
8385
8475
|
this._updateBoardPieces(animate);
|
|
8386
8476
|
return true;
|
|
8387
8477
|
}
|
|
8388
8478
|
/**
|
|
8389
8479
|
* Remove a piece from a square
|
|
8390
|
-
* @param {string} square
|
|
8480
|
+
* @param {string|Square} square
|
|
8391
8481
|
* @param {Object} [opts]
|
|
8392
8482
|
* @param {boolean} [opts.animate=true]
|
|
8393
8483
|
* @returns {string|null}
|
|
8394
8484
|
*/
|
|
8395
8485
|
removePiece(square, opts = {}) {
|
|
8396
8486
|
const animate = opts.animate !== undefined ? opts.animate : true;
|
|
8397
|
-
|
|
8398
|
-
|
|
8399
|
-
|
|
8400
|
-
const squareObj = this.boardService.getSquare(square);
|
|
8401
|
-
if (!squareObj) return true;
|
|
8402
|
-
if (!squareObj.piece) return true;
|
|
8403
|
-
squareObj.piece = null;
|
|
8487
|
+
const squareObj = typeof square === 'string' ? this.boardService.getSquare(square) : square;
|
|
8488
|
+
if (!squareObj || typeof squareObj !== 'object' || !('id' in squareObj)) throw new Error('[removePiece] Parametro square non valido');
|
|
8489
|
+
// Aggiorna solo il motore chess.js
|
|
8404
8490
|
const game = this.positionService.getGame();
|
|
8405
|
-
game.remove(
|
|
8491
|
+
game.remove(squareObj.id);
|
|
8492
|
+
// Non aggiornare direttamente square.piece!
|
|
8493
|
+
// Riallinea la board JS allo stato del motore
|
|
8406
8494
|
this._updateBoardPieces(animate);
|
|
8407
8495
|
return true;
|
|
8408
8496
|
}
|
|
@@ -8467,28 +8555,32 @@ let Chessboard$1 = class Chessboard {
|
|
|
8467
8555
|
// --- HIGHLIGHTING & UI ---
|
|
8468
8556
|
/**
|
|
8469
8557
|
* Highlight a square
|
|
8470
|
-
* @param {string} square
|
|
8558
|
+
* @param {string|Square} square
|
|
8471
8559
|
* @param {Object} [opts]
|
|
8472
8560
|
*/
|
|
8473
8561
|
highlight(square, opts = {}) {
|
|
8474
|
-
|
|
8562
|
+
// API: accetta id, converte subito in oggetto
|
|
8563
|
+
const squareObj = typeof square === 'string' ? this.boardService.getSquare(square) : square;
|
|
8564
|
+
if (!squareObj || typeof squareObj !== 'object' || !('id' in squareObj)) throw new Error('[highlight] Parametro square non valido');
|
|
8475
8565
|
if (this.boardService && this.boardService.highlightSquare) {
|
|
8476
|
-
this.boardService.highlightSquare(
|
|
8566
|
+
this.boardService.highlightSquare(squareObj, opts);
|
|
8477
8567
|
} else if (this.eventService && this.eventService.highlightSquare) {
|
|
8478
|
-
this.eventService.highlightSquare(
|
|
8568
|
+
this.eventService.highlightSquare(squareObj, opts);
|
|
8479
8569
|
}
|
|
8480
8570
|
}
|
|
8481
8571
|
/**
|
|
8482
8572
|
* Remove highlight from a square
|
|
8483
|
-
* @param {string} square
|
|
8573
|
+
* @param {string|Square} square
|
|
8484
8574
|
* @param {Object} [opts]
|
|
8485
8575
|
*/
|
|
8486
8576
|
dehighlight(square, opts = {}) {
|
|
8487
|
-
|
|
8577
|
+
// API: accetta id, converte subito in oggetto
|
|
8578
|
+
const squareObj = typeof square === 'string' ? this.boardService.getSquare(square) : square;
|
|
8579
|
+
if (!squareObj || typeof squareObj !== 'object' || !('id' in squareObj)) throw new Error('[dehighlight] Parametro square non valido');
|
|
8488
8580
|
if (this.boardService && this.boardService.dehighlightSquare) {
|
|
8489
|
-
this.boardService.dehighlightSquare(
|
|
8581
|
+
this.boardService.dehighlightSquare(squareObj, opts);
|
|
8490
8582
|
} else if (this.eventService && this.eventService.dehighlightSquare) {
|
|
8491
|
-
this.eventService.dehighlightSquare(
|
|
8583
|
+
this.eventService.dehighlightSquare(squareObj, opts);
|
|
8492
8584
|
}
|
|
8493
8585
|
}
|
|
8494
8586
|
|
|
@@ -8513,6 +8605,8 @@ let Chessboard$1 = class Chessboard {
|
|
|
8513
8605
|
* @returns {boolean}
|
|
8514
8606
|
*/
|
|
8515
8607
|
isGameOver() {
|
|
8608
|
+
// Forza sync prima di interrogare il motore
|
|
8609
|
+
this._updateBoardPieces(false, false);
|
|
8516
8610
|
const game = this.positionService.getGame();
|
|
8517
8611
|
if (!game) return false;
|
|
8518
8612
|
if (game.isGameOver) return game.isGameOver();
|
|
@@ -8882,7 +8976,7 @@ let Chessboard$1 = class Chessboard {
|
|
|
8882
8976
|
dehighlightSquare(square) {
|
|
8883
8977
|
return this.boardService.dehighlight(square);
|
|
8884
8978
|
}
|
|
8885
|
-
forceSync() { this._updateBoardPieces(true, true); }
|
|
8979
|
+
forceSync() { this._updateBoardPieces(true, true); this._updateBoardPieces(true, false); }
|
|
8886
8980
|
|
|
8887
8981
|
// Metodi mancanti che causano fallimenti nei test
|
|
8888
8982
|
/**
|
|
@@ -8895,37 +8989,37 @@ let Chessboard$1 = class Chessboard {
|
|
|
8895
8989
|
movePiece(move, opts = {}) {
|
|
8896
8990
|
const animate = opts.animate !== undefined ? opts.animate : true;
|
|
8897
8991
|
|
|
8898
|
-
//
|
|
8899
|
-
let
|
|
8992
|
+
// --- API: accetta id/stringhe, ma converte subito in oggetti ---
|
|
8993
|
+
let fromSquareObj, toSquareObj, promotion;
|
|
8900
8994
|
if (typeof move === 'string') {
|
|
8901
8995
|
if (move.length === 4) {
|
|
8902
|
-
|
|
8903
|
-
|
|
8996
|
+
fromSquareObj = this.boardService.getSquare(move.substring(0, 2));
|
|
8997
|
+
toSquareObj = this.boardService.getSquare(move.substring(2, 4));
|
|
8904
8998
|
} else if (move.length === 5) {
|
|
8905
|
-
|
|
8906
|
-
|
|
8999
|
+
fromSquareObj = this.boardService.getSquare(move.substring(0, 2));
|
|
9000
|
+
toSquareObj = this.boardService.getSquare(move.substring(2, 4));
|
|
8907
9001
|
promotion = move.substring(4, 5);
|
|
8908
9002
|
} else {
|
|
8909
9003
|
throw new Error(`Invalid move format: ${move}`);
|
|
8910
9004
|
}
|
|
8911
9005
|
} else if (typeof move === 'object' && move.from && move.to) {
|
|
8912
|
-
|
|
8913
|
-
|
|
9006
|
+
// Se sono id, converto in oggetti; se sono già oggetti, li uso direttamente
|
|
9007
|
+
fromSquareObj = typeof move.from === 'string' ? this.boardService.getSquare(move.from) : move.from;
|
|
9008
|
+
toSquareObj = typeof move.to === 'string' ? this.boardService.getSquare(move.to) : move.to;
|
|
8914
9009
|
promotion = move.promotion;
|
|
8915
9010
|
} else {
|
|
8916
9011
|
throw new Error(`Invalid move: ${move}`);
|
|
8917
9012
|
}
|
|
8918
9013
|
|
|
8919
|
-
// Get square objects
|
|
8920
|
-
const fromSquareObj = this.boardService.getSquare(fromSquare);
|
|
8921
|
-
const toSquareObj = this.boardService.getSquare(toSquare);
|
|
8922
|
-
|
|
8923
9014
|
if (!fromSquareObj || !toSquareObj) {
|
|
8924
|
-
throw new Error(`Invalid squares: ${
|
|
9015
|
+
throw new Error(`Invalid squares: ${move.from || move.substring(0, 2)} or ${move.to || move.substring(2, 4)}`);
|
|
8925
9016
|
}
|
|
8926
9017
|
|
|
8927
|
-
//
|
|
8928
|
-
|
|
9018
|
+
// --- Internamente: lavora solo con oggetti ---
|
|
9019
|
+
const result = this._onMove(fromSquareObj, toSquareObj, promotion, animate);
|
|
9020
|
+
// Dopo ogni mossa, forza la sincronizzazione della board
|
|
9021
|
+
this._updateBoardPieces(true, false);
|
|
9022
|
+
return result;
|
|
8929
9023
|
}
|
|
8930
9024
|
|
|
8931
9025
|
// Aliases for backward compatibility
|