147 lines
4.1 KiB
JavaScript
147 lines
4.1 KiB
JavaScript
import { Chessground } from "chessground";
|
|
import { Chess } from "chess.js";
|
|
|
|
export const ChessBoard = {
|
|
mounted() {
|
|
this.chess = new Chess();
|
|
|
|
this.ground = Chessground(this.el, {
|
|
fen: this.chess.fen(),
|
|
movable: {
|
|
color: "white",
|
|
free: false,
|
|
dests: this.getValidDests(),
|
|
},
|
|
events: {
|
|
move: (orig, dest, _capturedPiece) => {
|
|
this.handleMove(orig, dest);
|
|
},
|
|
},
|
|
});
|
|
|
|
this.handleEvent("set_fen", ({ fen }) => {
|
|
try {
|
|
this.chess.load(fen);
|
|
|
|
const currentTurnColor = this.chess.turn() === "w" ? "white" : "black";
|
|
|
|
this.ground.set({
|
|
fen: fen,
|
|
turnColor: currentTurnColor,
|
|
movable: {
|
|
color: currentTurnColor,
|
|
dests: this.getValidDests(),
|
|
},
|
|
});
|
|
} catch (error) {
|
|
console.error("Invalid FEN string submitted:", error);
|
|
alert("Invalid FEN configuration!");
|
|
}
|
|
});
|
|
},
|
|
|
|
getValidDests() {
|
|
const dests = new Map();
|
|
|
|
this.chess.moves({ verbose: true }).forEach((m) => {
|
|
if (!dests.has(m.from)) dests.set(m.from, []);
|
|
dests.get(m.from).push(m.to);
|
|
});
|
|
|
|
return dests;
|
|
},
|
|
|
|
// handleMove(orig, dest) {
|
|
// const { role, color } = this.ground.state.pieces.get(dest);
|
|
// const destRank = dest.split("").pop()
|
|
|
|
// const isPromotion = this.isPromotion(role, destRank)
|
|
|
|
// if (isPromotion) {
|
|
// this.showPromotionDialog()
|
|
// this.executeMove(orig, dest, "q");
|
|
// return
|
|
// }
|
|
|
|
// this.executeMove(orig, dest, "q");
|
|
// },
|
|
|
|
handleMove(orig, dest) {
|
|
const piece = this.ground.state.pieces.get(dest);
|
|
const destRank = dest[1];
|
|
if (piece?.role === 'pawn' && (destRank === '1' || destRank === '8')) {
|
|
this.showPromotionDialog(orig, dest);
|
|
return; // don't execute yet — wait for user choice
|
|
}
|
|
this.executeMove(orig, dest);
|
|
},
|
|
|
|
isPromotion(piece, rank) {
|
|
return piece === "pawn" && (rank === "1" || rank === "8");
|
|
},
|
|
|
|
showPromotionDialog(orig, dest) {
|
|
console.log("trigger promotion dialog");
|
|
this.pendingPromotion = { orig, dest };
|
|
const color = this.chess.turn() === 'w' ? 'white' : 'black';
|
|
const overlay = document.createElement('div');
|
|
overlay.className = 'promotion-overlay';
|
|
const picker = document.createElement('div');
|
|
picker.className = 'promotion-picker';
|
|
const pieces = [
|
|
{ type: 'queen', symbol: { white: '♕', black: '♛' } },
|
|
{ type: 'rook', symbol: { white: '♖', black: '♜' } },
|
|
{ type: 'bishop', symbol: { white: '♗', black: '♝' } },
|
|
{ type: 'knight', symbol: { white: '♘', black: '♞' } },
|
|
];
|
|
// chess.js promotion codes
|
|
const code = { queen: 'q', rook: 'r', bishop: 'b', knight: 'n' };
|
|
pieces.forEach(({ type, symbol }) => {
|
|
const btn = document.createElement('button');
|
|
btn.className = 'promotion-piece';
|
|
btn.textContent = symbol[color];
|
|
btn.addEventListener('click', () => this.completePromotion(code[type]));
|
|
picker.appendChild(btn);
|
|
});
|
|
overlay.appendChild(picker);
|
|
const boardContainer = this.el.closest('.shadow-inner');
|
|
boardContainer.style.position = 'relative';
|
|
boardContainer.appendChild(overlay);
|
|
},
|
|
|
|
completePromotion(pieceType) {
|
|
const { orig, dest } = this.pendingPromotion;
|
|
this.pendingPromotion = null;
|
|
const picker = this.el.closest('.shadow-inner')
|
|
?.querySelector('.promotion-overlay');
|
|
if (picker) picker.remove();
|
|
this.executeMove(orig, dest, pieceType);
|
|
},
|
|
|
|
executeMove(orig, dest, promotion) {
|
|
const move = this.chess.move({ from: orig, to: dest, promotion });
|
|
|
|
if (move) {
|
|
this.pushEvent("move_played", {
|
|
from: orig,
|
|
to: dest,
|
|
fen: this.chess.fen(),
|
|
san: move.san,
|
|
});
|
|
|
|
const nextTurnColor = this.chess.turn() === "w" ? "white" : "black";
|
|
|
|
this.ground.set({
|
|
turnColor: nextTurnColor,
|
|
fen: this.chess.fen(),
|
|
movable: {
|
|
color: nextTurnColor,
|
|
dests: this.getValidDests(),
|
|
},
|
|
});
|
|
} else {
|
|
this.ground.set({ fen: this.chess.fen() });
|
|
}
|
|
},
|
|
};
|