diff --git a/index.html b/index.html
index d646a5d..dfcb2f2 100644
--- a/index.html
+++ b/index.html
@@ -4,6 +4,7 @@
Chessical
+
diff --git a/scripts/board.js b/scripts/board.js
index 27f07aa..ede3354 100644
--- a/scripts/board.js
+++ b/scripts/board.js
@@ -13,6 +13,8 @@ let whiteKingsideRookMoved = false;
let whiteQueensideRookMoved = false;
let blackKingsideRookMoved = false;
let blackQueensideRookMoved = false;
+let selectedPiece = null;
+let selectedSquare = null;
function initBoard() {
const initialBoard = [
@@ -64,6 +66,8 @@ function initBoard() {
document.querySelectorAll('.piece').forEach(piece => {
piece.addEventListener('dragstart', handleDragStart);
});
+
+ initializeTouchAndClickEvents();
}
function handleDragStart(event) {
@@ -160,6 +164,7 @@ function handleDrop(event) {
}
targetSquare.appendChild(draggedPiece);
+ handlePawnPromotion(targetSquare, draggedPiece);
const currentPosition = getCurrentPosition();
@@ -174,6 +179,16 @@ function handleDrop(event) {
updateTurn();
updateEvaluationBar();
+
+ // Add checkmate detection here
+ const pieceColor = draggedPiece.getAttribute('data-piece').split('_')[0];
+ const opponentIsWhite = pieceColor === 'black';
+
+ if (isCheckmate(opponentIsWhite)) {
+ setTimeout(() => {
+ showGameEndModal(`${pieceColor.charAt(0).toUpperCase() + pieceColor.slice(1)} wins by checkmate!`);
+ }, 100); // Small delay to ensure the UI updates first
+ }
}
draggedPiece.classList.remove('dragging');
@@ -560,3 +575,168 @@ function doesMoveResolveCheck(piece, sourceIndex, targetIndex) {
// Initialize the board with pieces and listeners
initBoard();
+
+// Add this function after handleDrop
+function handlePawnPromotion(square, piece) {
+ const row = Math.floor(parseInt(square.getAttribute('data-index')) / 8);
+ const pieceColor = piece.getAttribute('data-piece').split('_')[0];
+
+ // Check if pawn reached the opposite end
+ if (piece.getAttribute('data-piece').includes('pawn') &&
+ ((pieceColor === 'white' && row === 0) || (pieceColor === 'black' && row === 7))) {
+
+ // Create and show promotion modal
+ const modal = document.createElement('div');
+ modal.className = 'promotion-modal';
+
+ // Add promotion piece options
+ const pieces = ['queen', 'rook', 'bishop', 'knight'];
+ pieces.forEach(pieceType => {
+ const option = document.createElement('img');
+ option.src = `${piecesFolder}${pieceColor}_${pieceType}.png`;
+ option.className = 'promotion-option';
+ option.addEventListener('click', () => {
+ // Update the piece
+ piece.src = option.src;
+ piece.setAttribute('data-piece', `${pieceColor}_${pieceType}`);
+ modal.remove();
+ updateBoardState();
+ updateEvaluationBar();
+ });
+ modal.appendChild(option);
+ });
+
+ document.body.appendChild(modal);
+ }
+}
+
+// Add these functions after your existing isKingInCheck function
+function isCheckmate(isWhiteKing) {
+ // First verify the king is in check
+ const kingPos = findKingPosition(isWhiteKing);
+ if (!kingPos || !isKingInCheck(kingPos.row, kingPos.col, isWhiteKing)) {
+ return false;
+ }
+
+ // Generate all possible moves
+ const allMoves = generateAllPossibleMoves(isWhiteKing);
+
+ // Try each move to see if it gets out of check
+ for (const move of allMoves) {
+ const sourceSquare = document.querySelector(`[data-index="${move.from}"]`);
+ const piece = sourceSquare.querySelector('.piece');
+ if (doesMoveResolveCheck(piece, move.from, move.to)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+function showGameEndModal(message) {
+ const modal = document.createElement('div');
+ modal.className = 'promotion-modal'; // Reuse the promotion modal styling
+
+ const messageElement = document.createElement('p');
+ messageElement.textContent = message;
+ messageElement.style.textAlign = 'center';
+ messageElement.style.marginBottom = '10px';
+
+ const newGameButton = document.createElement('button');
+ newGameButton.textContent = 'New Game';
+ newGameButton.style.padding = '5px 10px';
+ newGameButton.onclick = () => {
+ location.reload();
+ };
+
+ modal.appendChild(messageElement);
+ modal.appendChild(newGameButton);
+ document.body.appendChild(modal);
+}
+
+function initializeTouchAndClickEvents() {
+ // Add click handlers to all squares
+ document.querySelectorAll('.square').forEach(square => {
+ square.addEventListener('click', handleSquareClick);
+ });
+}
+
+function handleSquareClick(event) {
+ const square = event.currentTarget;
+ const piece = square.querySelector('.piece');
+
+ // If no piece is selected
+ if (!selectedPiece) {
+ if (piece) {
+ // Select the piece
+ selectedPiece = piece;
+ selectedSquare = square;
+ square.classList.add('selected-square');
+ }
+ return;
+ }
+
+ // If a piece is already selected
+ if (selectedPiece) {
+ const sourceIndex = parseInt(selectedSquare.getAttribute('data-index'));
+ const targetIndex = parseInt(square.getAttribute('data-index'));
+
+ // Clear previous selection
+ selectedSquare.classList.remove('selected-square');
+
+ // If clicking the same square, deselect the piece
+ if (square === selectedSquare) {
+ selectedPiece = null;
+ selectedSquare = null;
+ return;
+ }
+
+ // Try to make the move
+ if (isValidMove(selectedPiece, sourceIndex, targetIndex)) {
+ const targetPiece = square.querySelector('.piece');
+
+ // Handle capture
+ if (targetPiece) {
+ const capturedPieceColor = targetPiece.getAttribute('data-piece').split('_')[0];
+ if (capturedPieceColor === selectedPiece.getAttribute('data-piece').split('_')[0]) {
+ selectedPiece = null;
+ selectedSquare = null;
+ return;
+ }
+ targetPiece.remove();
+
+ if (capturedPieceColor === 'white') {
+ capturedWhitePieces.push(targetPiece);
+ } else {
+ capturedBlackPieces.push(targetPiece);
+ }
+
+ displayCapturedPieces();
+ }
+
+ // Move the piece
+ square.appendChild(selectedPiece);
+ handlePawnPromotion(square, selectedPiece);
+
+ const currentPosition = getCurrentPosition();
+ positionHistory.push(currentPosition);
+
+ updateTurn();
+ updateEvaluationBar();
+
+ // Check for checkmate
+ const pieceColor = selectedPiece.getAttribute('data-piece').split('_')[0];
+ const opponentIsWhite = pieceColor === 'black';
+
+ if (isCheckmate(opponentIsWhite)) {
+ setTimeout(() => {
+ showGameEndModal(`${pieceColor.charAt(0).toUpperCase() + pieceColor.slice(1)} wins by checkmate!`);
+ }, 100);
+ }
+ }
+
+ // Reset selection
+ selectedPiece = null;
+ selectedSquare = null;
+ }
+}
diff --git a/styles.css b/styles.css
index ca7a111..3ecc8cc 100644
--- a/styles.css
+++ b/styles.css
@@ -115,4 +115,92 @@ h1{
background: rgba(255, 255, 255, 0.8);
padding: 2px 4px;
border-radius: 3px;
+}
+
+.promotion-modal {
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ background: white;
+ padding: 20px;
+ border: 2px solid #333;
+ border-radius: 8px;
+ display: flex;
+ gap: 10px;
+ z-index: 1000;
+ box-shadow: 0 0 10px rgba(0,0,0,0.5);
+}
+
+.promotion-option {
+ width: 60px;
+ height: 60px;
+ cursor: pointer;
+ transition: transform 0.2s;
+}
+
+.promotion-option:hover {
+ transform: scale(1.1);
+}
+
+/* Add responsive design */
+@media (max-width: 768px) {
+ #board {
+ width: 100vw;
+ height: 100vw;
+ max-width: 640px;
+ max-height: 640px;
+ }
+
+ .square {
+ width: 12.5vw;
+ height: 12.5vw;
+ max-width: 80px;
+ max-height: 80px;
+ }
+
+ .piece {
+ width: 100%;
+ height: 100%;
+ }
+
+ #evaluation-bar {
+ right: 10px;
+ height: 300px;
+ }
+
+ .captured-pieces {
+ padding: 2px;
+ }
+
+ .captured-pieces .piece {
+ width: 30px;
+ height: 30px;
+ }
+}
+
+/* Add touch support */
+@media (hover: none) {
+ .square:hover {
+ background-color: inherit;
+ }
+
+ .square.touch-selected {
+ background-color: #c8e6c9;
+ }
+}
+
+.selected-square {
+ background-color: rgba(255, 255, 0, 0.4) !important;
+}
+
+/* Improve touch targets */
+@media (hover: none) {
+ .square:hover {
+ background-color: inherit;
+ }
+
+ .piece {
+ -webkit-tap-highlight-color: transparent;
+ }
}
\ No newline at end of file