| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>ChessMaster Pro</title> |
| <script src="https://cdn.tailwindcss.com"></script> |
| <script src="https://unpkg.com/feather-icons"></script> |
| <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script> |
| <style> |
| .chess-square { |
| position: relative; |
| width: 100%; |
| padding-bottom: 100%; |
| } |
| .chess-piece { |
| position: absolute; |
| width: 80%; |
| height: 80%; |
| top: 10%; |
| left: 10%; |
| background-size: contain; |
| background-repeat: no-repeat; |
| background-position: center; |
| cursor: pointer; |
| transition: transform 0.2s ease; |
| z-index: 10; |
| } |
| .chess-piece.dragging { |
| z-index: 20; |
| transform: scale(1.1); |
| } |
| .legal-move { |
| position: absolute; |
| width: 30%; |
| height: 30%; |
| border-radius: 50%; |
| background-color: rgba(0, 255, 0, 0.3); |
| top: 35%; |
| left: 35%; |
| z-index: 5; |
| } |
| .selected { |
| background-color: rgba(255, 215, 0, 0.3) !important; |
| } |
| .check { |
| background-color: rgba(255, 0, 0, 0.3) !important; |
| } |
| .captured-piece { |
| width: 30px; |
| height: 30px; |
| background-size: contain; |
| background-repeat: no-repeat; |
| background-position: center; |
| margin: 2px; |
| } |
| @media (max-width: 768px) { |
| .captured-piece { |
| width: 20px; |
| height: 20px; |
| } |
| } |
| .promotion-modal { |
| display: none; |
| position: fixed; |
| top: 0; |
| left: 0; |
| width: 100%; |
| height: 100%; |
| background-color: rgba(0, 0, 0, 0.7); |
| z-index: 100; |
| justify-content: center; |
| align-items: center; |
| } |
| .promotion-options { |
| display: flex; |
| background-color: #2d3748; |
| padding: 20px; |
| border-radius: 10px; |
| } |
| .promotion-piece { |
| width: 60px; |
| height: 60px; |
| margin: 10px; |
| background-size: contain; |
| background-repeat: no-repeat; |
| background-position: center; |
| cursor: pointer; |
| transition: transform 0.2s; |
| } |
| .promotion-piece:hover { |
| transform: scale(1.1); |
| } |
| </style> |
| </head> |
| <body class="bg-gray-900 text-white min-h-screen flex flex-col"> |
| <header class="bg-indigo-900 py-4 px-6 shadow-lg"> |
| <div class="container mx-auto flex justify-between items-center"> |
| <h1 class="text-2xl md:text-3xl font-bold flex items-center"> |
| <i data-feather="shield" class="mr-2"></i> ChessMaster Pro |
| </h1> |
| <div class="flex space-x-4"> |
| <button id="resetBtn" class="bg-red-600 hover:bg-red-700 px-4 py-2 rounded-lg flex items-center"> |
| <i data-feather="refresh-cw" class="mr-2"></i> Reset |
| </button> |
| <button id="undoBtn" class="bg-blue-600 hover:bg-blue-700 px-4 py-2 rounded-lg flex items-center"> |
| <i data-feather="corner-up-left" class="mr-2"></i> Undo |
| </button> |
| </div> |
| </div> |
| </header> |
|
|
| <main class="flex-grow container mx-auto px-4 py-8 flex flex-col md:flex-row items-center md:items-start justify-center gap-8"> |
| |
| <div class="w-full md:w-1/4 bg-gray-800 p-6 rounded-lg shadow-lg"> |
| <div class="mb-6"> |
| <h2 class="text-xl font-semibold mb-2">Game Status</h2> |
| <div id="gameStatus" class="bg-gray-700 p-3 rounded">White's turn</div> |
| </div> |
| |
| <div class="mb-6"> |
| <h2 class="text-xl font-semibold mb-2">Captured Pieces</h2> |
| <div class="bg-gray-700 p-3 rounded"> |
| <h3 class="font-medium mb-1">White captured:</h3> |
| <div id="whiteCaptured" class="flex flex-wrap"></div> |
| |
| <h3 class="font-medium mt-3 mb-1">Black captured:</h3> |
| <div id="blackCaptured" class="flex flex-wrap"></div> |
| </div> |
| </div> |
| |
| <div> |
| <h2 class="text-xl font-semibold mb-2">Move History</h2> |
| <div id="moveHistory" class="bg-gray-700 p-3 rounded max-h-40 overflow-y-auto"> |
| |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="w-full md:w-2/4 max-w-lg mx-auto"> |
| <div class="bg-gray-800 p-4 rounded-lg shadow-lg"> |
| <div id="chessBoard" class="grid grid-cols-8 aspect-square"> |
| |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="w-full md:w-1/4 bg-gray-800 p-6 rounded-lg shadow-lg"> |
| <div class="mb-6"> |
| <h2 class="text-xl font-semibold mb-2">Current Player</h2> |
| <div id="currentPlayer" class="bg-gray-700 p-3 rounded flex items-center"> |
| <div class="w-6 h-6 bg-white rounded-full mr-2"></div> White |
| </div> |
| </div> |
| |
| <div class="mb-6"> |
| <h2 class="text-xl font-semibold mb-2">Game Controls</h2> |
| <div class="space-y-2"> |
| <button id="flipBoardBtn" class="w-full bg-purple-600 hover:bg-purple-700 py-2 rounded flex items-center justify-center"> |
| <i data-feather="rotate-cw" class="mr-2"></i> Flip Board |
| </button> |
| <button id="hintBtn" class="w-full bg-green-600 hover:bg-green-700 py-2 rounded flex items-center justify-center"> |
| <i data-feather="help-circle" class="mr-2"></i> Show Hints |
| </button> |
| </div> |
| </div> |
| |
| <div> |
| <h2 class="text-xl font-semibold mb-2">Game Time</h2> |
| <div class="bg-gray-700 p-3 rounded"> |
| <div class="flex justify-between"> |
| <span>White:</span> |
| <span id="whiteTime">10:00</span> |
| </div> |
| <div class="flex justify-between mt-2"> |
| <span>Black:</span> |
| <span id="blackTime">10:00</span> |
| </div> |
| </div> |
| </div> |
| </div> |
| </main> |
|
|
| |
| <div id="promotionModal" class="promotion-modal"> |
| <div class="promotion-options"> |
| <div class="promotion-piece" data-piece="q"></div> |
| <div class="promotion-piece" data-piece="r"></div> |
| <div class="promotion-piece" data-piece="b"></div> |
| <div class="promotion-piece" data-piece="n"></div> |
| </div> |
| </div> |
|
|
| <footer class="bg-indigo-900 py-4 text-center"> |
| <p>ChessMaster Pro ♟️ - Play like a grandmaster!</p> |
| </footer> |
|
|
| <script> |
| document.addEventListener('DOMContentLoaded', function() { |
| feather.replace(); |
| |
| |
| const chessBoard = document.getElementById('chessBoard'); |
| const gameStatus = document.getElementById('gameStatus'); |
| const currentPlayer = document.getElementById('currentPlayer'); |
| const whiteCaptured = document.getElementById('whiteCaptured'); |
| const blackCaptured = document.getElementById('blackCaptured'); |
| const moveHistory = document.getElementById('moveHistory'); |
| const resetBtn = document.getElementById('resetBtn'); |
| const undoBtn = document.getElementById('undoBtn'); |
| const flipBoardBtn = document.getElementById('flipBoardBtn'); |
| const hintBtn = document.getElementById('hintBtn'); |
| const promotionModal = document.getElementById('promotionModal'); |
| |
| let board = []; |
| let selectedPiece = null; |
| let legalMoves = []; |
| let isWhiteTurn = true; |
| let boardFlipped = false; |
| let moveHistoryList = []; |
| let capturedPieces = { white: [], black: [] }; |
| |
| |
| function initializeBoard() { |
| board = [ |
| ['br', 'bn', 'bb', 'bq', 'bk', 'bb', 'bn', 'br'], |
| ['bp', 'bp', 'bp', 'bp', 'bp', 'bp', 'bp', 'bp'], |
| ['', '', '', '', '', '', '', ''], |
| ['', '', '', '', '', '', '', ''], |
| ['', '', '', '', '', '', '', ''], |
| ['', '', '', '', '', '', '', ''], |
| ['wp', 'wp', 'wp', 'wp', 'wp', 'wp', 'wp', 'wp'], |
| ['wr', 'wn', 'wb', 'wq', 'wk', 'wb', 'wn', 'wr'] |
| ]; |
| |
| renderBoard(); |
| updateGameStatus(); |
| updateCapturedPieces(); |
| moveHistory.innerHTML = ''; |
| moveHistoryList = []; |
| isWhiteTurn = true; |
| updateCurrentPlayerDisplay(); |
| } |
| |
| function getPieceImageUrl(piece) { |
| const color = piece[0] === 'w' ? 'white' : 'black'; |
| const pieceType = piece[1]; |
| const pieceSymbols = { |
| 'p': '♟', 'r': '♜', 'n': '♞', 'b': '♝', 'q': '♛', 'k': '♚' |
| }; |
| |
| |
| const canvas = document.createElement('canvas'); |
| canvas.width = 200; |
| canvas.height = 200; |
| const ctx = canvas.getContext('2d'); |
| |
| |
| ctx.fillStyle = 'transparent'; |
| ctx.fillRect(0, 0, 200, 200); |
| |
| |
| ctx.font = '120px Arial, sans-serif'; |
| ctx.fillStyle = color === 'white' ? '#ffffff' : '#000000'; |
| ctx.textAlign = 'center'; |
| ctx.textBaseline = 'middle'; |
| ctx.fillText(pieceSymbols[pieceType], 100, 100); |
| |
| return canvas.toDataURL(); |
| } |
| |
| |
| function renderBoard() { |
| chessBoard.innerHTML = ''; |
| |
| for (let row = 0; row < 8; row++) { |
| for (let col = 0; col < 8; col++) { |
| const displayRow = boardFlipped ? 7 - row : row; |
| const displayCol = boardFlipped ? 7 - col : col; |
| |
| const square = document.createElement('div'); |
| square.className = `chess-square ${(displayRow + displayCol) % 2 === 0 ? 'bg-amber-100' : 'bg-amber-800'}`; |
| square.dataset.row = row; |
| square.dataset.col = col; |
| |
| const piece = board[row][col]; |
| if (piece) { |
| const pieceElement = document.createElement('div'); |
| pieceElement.className = `chess-piece ${piece[0] === 'w' ? 'white-piece' : 'black-piece'}`; |
| pieceElement.style.backgroundImage = `url('${getPieceImageUrl(piece)})`; |
| pieceElement.dataset.piece = piece; |
| square.appendChild(pieceElement); |
| } |
| |
| square.addEventListener('click', handleSquareClick); |
| chessBoard.appendChild(square); |
| } |
| } |
| } |
| |
| |
| function handleSquareClick(event) { |
| const square = event.target.closest('.chess-square'); |
| if (!square) return; |
| |
| const row = parseInt(square.dataset.row); |
| const col = parseInt(square.dataset.col); |
| const piece = board[row][col]; |
| |
| |
| if (selectedPiece) { |
| |
| const isLegalMove = legalMoves.some(move => |
| move.toRow === row && move.toCol === col |
| ); |
| |
| if (isLegalMove) { |
| movePiece(selectedPiece.row, selectedPiece.col, row, col); |
| clearSelection(); |
| } else if (piece && piece[0] === (isWhiteTurn ? 'w' : 'b')) { |
| |
| selectPiece(row, col); |
| } else { |
| |
| clearSelection(); |
| } |
| } else if (piece && piece[0] === (isWhiteTurn ? 'w' : 'b')) { |
| |
| selectPiece(row, col); |
| } |
| } |
| |
| |
| function selectPiece(row, col) { |
| clearSelection(); |
| |
| selectedPiece = { row, col, piece: board[row][col] }; |
| highlightSquare(row, col, true); |
| |
| |
| legalMoves = calculateLegalMoves(row, col); |
| highlightLegalMoves(legalMoves); |
| } |
| |
| |
| function clearSelection() { |
| if (selectedPiece) { |
| highlightSquare(selectedPiece.row, selectedPiece.col, false); |
| } |
| |
| selectedPiece = null; |
| legalMoves = []; |
| |
| |
| document.querySelectorAll('.legal-move').forEach(el => el.remove()); |
| } |
| |
| |
| function highlightSquare(row, col, isSelected) { |
| const displayRow = boardFlipped ? 7 - row : row; |
| const displayCol = boardFlipped ? 7 - col : col; |
| const index = displayRow * 8 + displayCol; |
| const square = chessBoard.children[index]; |
| |
| if (isSelected) { |
| square.classList.add('selected'); |
| } else { |
| square.classList.remove('selected'); |
| } |
| } |
| |
| |
| function highlightLegalMoves(moves) { |
| moves.forEach(move => { |
| const displayRow = boardFlipped ? 7 - move.toRow : move.toRow; |
| const displayCol = boardFlipped ? 7 - move.toCol : move.toCol; |
| const index = displayRow * 8 + displayCol; |
| const square = chessBoard.children[index]; |
| |
| const indicator = document.createElement('div'); |
| indicator.className = 'legal-move'; |
| square.appendChild(indicator); |
| }); |
| } |
| |
| |
| function calculateLegalMoves(row, col) { |
| const piece = board[row][col]; |
| const moves = []; |
| |
| |
| if (piece[1] === 'p') { |
| const direction = piece[0] === 'w' ? -1 : 1; |
| |
| |
| if (isInBounds(row + direction, col) && !board[row + direction][col]) { |
| moves.push({ toRow: row + direction, toCol: col }); |
| |
| |
| if ((piece[0] === 'w' && row === 6) || (piece[0] === 'b' && row === 1)) { |
| if (!board[row + 2 * direction][col]) { |
| moves.push({ toRow: row + 2 * direction, toCol: col }); |
| } |
| } |
| } |
| |
| |
| for (let offset of [-1, 1]) { |
| if (isInBounds(row + direction, col + offset)) { |
| const target = board[row + direction][col + offset]; |
| if (target && target[0] !== piece[0]) { |
| moves.push({ toRow: row + direction, toCol: col + offset }); |
| } |
| } |
| } |
| } |
| |
| |
| |
| return moves; |
| } |
| |
| |
| function isInBounds(row, col) { |
| return row >= 0 && row < 8 && col >= 0 && col < 8; |
| } |
| |
| |
| function movePiece(fromRow, fromCol, toRow, toCol) { |
| const piece = board[fromRow][fromCol]; |
| const targetPiece = board[toRow][toCol]; |
| |
| |
| if (targetPiece) { |
| capturedPieces[piece[0] === 'w' ? 'black' : 'white'].push(targetPiece); |
| updateCapturedPieces(); |
| } |
| |
| |
| if (piece[1] === 'p' && (toRow === 0 || toRow === 7)) { |
| showPromotionModal(fromRow, fromCol, toRow, toCol); |
| return; |
| } |
| |
| |
| board[toRow][toCol] = piece; |
| board[fromRow][fromCol] = ''; |
| |
| |
| const moveNotation = getMoveNotation(fromRow, fromCol, toRow, toCol, targetPiece); |
| moveHistoryList.push(moveNotation); |
| updateMoveHistory(); |
| |
| |
| isWhiteTurn = !isWhiteTurn; |
| updateCurrentPlayerDisplay(); |
| |
| renderBoard(); |
| updateGameStatus(); |
| } |
| |
| |
| function getMoveNotation(fromRow, fromCol, toRow, toCol, capturedPiece) { |
| const piece = board[fromRow][fromCol]; |
| const pieceSymbol = piece[1] === 'p' ? '' : piece[1].toUpperCase(); |
| const captureSymbol = capturedPiece ? 'x' : ''; |
| const file = String.fromCharCode(97 + fromCol); |
| const rank = 8 - fromRow; |
| const toFile = String.fromCharCode(97 + toCol); |
| const toRank = 8 - toRow; |
| |
| return `${isWhiteTurn ? 'White' : 'Black'}: ${pieceSymbol}${file}${rank}${captureSymbol}${toFile}${toRank}`; |
| } |
| |
| |
| function updateMoveHistory() { |
| moveHistory.innerHTML = ''; |
| moveHistoryList.forEach((move, index) => { |
| const moveElement = document.createElement('div'); |
| moveElement.textContent = `${index + 1}. ${move}`; |
| moveHistory.appendChild(moveElement); |
| }); |
| |
| |
| moveHistory.scrollTop = moveHistory.scrollHeight; |
| } |
| |
| |
| function updateCapturedPieces() { |
| whiteCaptured.innerHTML = ''; |
| blackCaptured.innerHTML = ''; |
| |
| capturedPieces.white.forEach(piece => { |
| const pieceElement = document.createElement('div'); |
| pieceElement.className = 'captured-piece'; |
| pieceElement.style.backgroundImage = `url('${getPieceImageUrl('w' + piece[1])})`; |
| whiteCaptured.appendChild(pieceElement); |
| }); |
| |
| capturedPieces.black.forEach(piece => { |
| const pieceElement = document.createElement('div'); |
| pieceElement.className = 'captured-piece'; |
| pieceElement.style.backgroundImage = `url('${getPieceImageUrl('b' + piece[1])})`; |
| blackCaptured.appendChild(pieceElement); |
| }); |
| } |
| |
| |
| function updateGameStatus() { |
| |
| gameStatus.textContent = isWhiteTurn ? "White's turn" : "Black's turn"; |
| gameStatus.className = 'bg-gray-700 p-3 rounded'; |
| } |
| |
| |
| function updateCurrentPlayerDisplay() { |
| currentPlayer.innerHTML = isWhiteTurn ? |
| '<div class="w-6 h-6 bg-white rounded-full mr-2"></div> White' : |
| '<div class="w-6 h-6 bg-black border border-white rounded-full mr-2"></div> Black'; |
| } |
| |
| |
| function showPromotionModal(fromRow, fromCol, toRow, toCol) { |
| promotionModal.style.display = 'flex'; |
| |
| |
| const color = board[fromRow][fromCol][0]; |
| const options = document.querySelectorAll('.promotion-piece'); |
| |
| options.forEach(option => { |
| const pieceType = option.dataset.piece; |
| option.style.backgroundImage = `url('${getPieceImageUrl(color + pieceType)})`; |
| option.onclick = function() { |
| |
| board[toRow][toCol] = color + pieceType; |
| board[fromRow][fromCol] = ''; |
| |
| |
| const moveNotation = getMoveNotation(fromRow, fromCol, toRow, toCol, null) + '=' + pieceType.toUpperCase(); |
| moveHistoryList.push(moveNotation); |
| updateMoveHistory(); |
| |
| |
| isWhiteTurn = !isWhiteTurn; |
| updateCurrentPlayerDisplay(); |
| |
| renderBoard(); |
| updateGameStatus(); |
| |
| |
| promotionModal.style.display = 'none'; |
| }; |
| }); |
| } |
| |
| |
| resetBtn.addEventListener('click', initializeBoard); |
| |
| undoBtn.addEventListener('click', function() { |
| if (moveHistoryList.length > 0) { |
| |
| alert("Undo functionality would be implemented here!"); |
| } |
| }); |
| |
| flipBoardBtn.addEventListener('click', function() { |
| boardFlipped = !boardFlipped; |
| renderBoard(); |
| }); |
| |
| hintBtn.addEventListener('click', function() { |
| if (selectedPiece) { |
| |
| return; |
| } |
| |
| |
| for (let row = 0; row < 8; row++) { |
| for (let col = 0; col < 8; col++) { |
| const piece = board[row][col]; |
| if (piece && piece[0] === (isWhiteTurn ? 'w' : 'b')) { |
| const moves = calculateLegalMoves(row, col); |
| if (moves.length > 0) { |
| selectPiece(row, col); |
| return; |
| } |
| } |
| } |
| } |
| }); |
| |
| |
| initializeBoard(); |
| }); |
| </script> |
| </body> |
| </html> |
|
|