| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Chess Multiplayer</title> |
| <script src="https://cdn.tailwindcss.com"></script> |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
| <style> |
| .chess-board { |
| width: min(90vw, 90vh); |
| height: min(90vw, 90vh); |
| display: grid; |
| grid-template-columns: repeat(8, 1fr); |
| grid-template-rows: repeat(8, 1fr); |
| box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2); |
| border-radius: 8px; |
| overflow: hidden; |
| } |
| |
| .square { |
| position: relative; |
| display: flex; |
| justify-content: center; |
| align-items: center; |
| cursor: pointer; |
| transition: all 0.2s ease; |
| } |
| |
| .square:hover { |
| filter: brightness(1.1); |
| } |
| |
| .light { |
| background-color: #f0d9b5; |
| } |
| |
| .dark { |
| background-color: #b58863; |
| } |
| |
| .highlight { |
| box-shadow: inset 0 0 20px 5px rgba(255, 255, 0, 0.5); |
| } |
| |
| .possible-move { |
| position: absolute; |
| width: 30%; |
| height: 30%; |
| background-color: rgba(0, 0, 0, 0.3); |
| border-radius: 50%; |
| } |
| |
| .capture-move { |
| position: absolute; |
| width: 80%; |
| height: 80%; |
| border: 5px solid rgba(0, 0, 0, 0.3); |
| border-radius: 50%; |
| box-sizing: border-box; |
| } |
| |
| .piece { |
| width: 80%; |
| height: 80%; |
| background-size: contain; |
| background-repeat: no-repeat; |
| background-position: center; |
| z-index: 10; |
| transition: all 0.2s ease; |
| } |
| |
| .piece.dragging { |
| transform: scale(1.2); |
| z-index: 20; |
| } |
| |
| .player-info { |
| transition: all 0.3s ease; |
| } |
| |
| .player-info.active { |
| transform: scale(1.05); |
| box-shadow: 0 0 15px rgba(255, 215, 0, 0.5); |
| } |
| |
| @keyframes pulse { |
| 0% { transform: scale(1); } |
| 50% { transform: scale(1.05); } |
| 100% { transform: scale(1); } |
| } |
| |
| .waiting-animation { |
| animation: pulse 1.5s infinite; |
| } |
| </style> |
| </head> |
| <body class="bg-gray-100 min-h-screen flex flex-col items-center justify-center p-4"> |
| <div class="w-full max-w-6xl"> |
| <header class="text-center mb-6"> |
| <h1 class="text-4xl font-bold text-gray-800 mb-2">Chess Multiplayer</h1> |
| <p class="text-gray-600">Play chess in real-time with friends</p> |
| </header> |
| |
| <div class="flex flex-col lg:flex-row gap-6 items-center lg:items-start justify-center"> |
| |
| <div id="player1" class="player-info bg-white rounded-lg shadow-md p-4 w-full lg:w-64 order-1 lg:order-1"> |
| <div class="flex items-center mb-3"> |
| <div class="w-10 h-10 rounded-full bg-blue-500 flex items-center justify-center text-white font-bold mr-3">P1</div> |
| <div> |
| <h3 class="font-semibold text-gray-800">Player 1</h3> |
| <p class="text-sm text-gray-500">White Pieces</p> |
| </div> |
| </div> |
| <div class="flex justify-between items-center"> |
| <span class="text-gray-700">Rating: 1200</span> |
| <div class="relative"> |
| <div class="w-3 h-3 rounded-full bg-green-500 animate-pulse"></div> |
| <span class="text-xs text-gray-500 ml-1">Online</span> |
| </div> |
| </div> |
| <div class="mt-3 pt-3 border-t border-gray-200"> |
| <div class="flex justify-between"> |
| <span class="text-gray-600">Time:</span> |
| <span class="font-mono text-gray-800">15:00</span> |
| </div> |
| </div> |
| </div> |
| |
| |
| <div class="order-3 lg:order-2"> |
| <div class="chess-board" id="board"></div> |
| |
| <div class="mt-4 flex justify-center gap-4"> |
| <button id="newGameBtn" class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg transition"> |
| <i class="fas fa-plus mr-2"></i>New Game |
| </button> |
| <button id="offerDrawBtn" class="bg-yellow-500 hover:bg-yellow-600 text-white px-4 py-2 rounded-lg transition"> |
| <i class="fas fa-handshake mr-2"></i>Offer Draw |
| </button> |
| <button id="resignBtn" class="bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-lg transition"> |
| <i class="fas fa-flag mr-2"></i>Resign |
| </button> |
| </div> |
| </div> |
| |
| |
| <div id="player2" class="player-info bg-white rounded-lg shadow-md p-4 w-full lg:w-64 order-2 lg:order-3"> |
| <div class="flex items-center mb-3"> |
| <div class="w-10 h-10 rounded-full bg-red-500 flex items-center justify-center text-white font-bold mr-3">P2</div> |
| <div> |
| <h3 class="font-semibold text-gray-800">Player 2</h3> |
| <p class="text-sm text-gray-500">Black Pieces</p> |
| </div> |
| </div> |
| <div class="flex justify-between items-center"> |
| <span class="text-gray-700">Rating: 1250</span> |
| <div class="relative"> |
| <div class="w-3 h-3 rounded-full bg-green-500 animate-pulse"></div> |
| <span class="text-xs text-gray-500 ml-1">Online</span> |
| </div> |
| </div> |
| <div class="mt-3 pt-3 border-t border-gray-200"> |
| <div class="flex justify-between"> |
| <span class="text-gray-600">Time:</span> |
| <span class="font-mono text-gray-800">15:00</span> |
| </div> |
| </div> |
| </div> |
| </div> |
| |
| |
| <div class="mt-8 bg-white rounded-lg shadow-md p-4"> |
| <h3 class="font-semibold text-lg text-gray-800 mb-3">Game Controls</h3> |
| <div class="flex flex-wrap gap-3"> |
| <button class="bg-gray-200 hover:bg-gray-300 text-gray-800 px-3 py-1 rounded-lg transition flex items-center"> |
| <i class="fas fa-undo-alt mr-2"></i> Undo |
| </button> |
| <button class="bg-gray-200 hover:bg-gray-300 text-gray-800 px-3 py-1 rounded-lg transition flex items-center"> |
| <i class="fas fa-redo-alt mr-2"></i> Redo |
| </button> |
| <button class="bg-gray-200 hover:bg-gray-300 text-gray-800 px-3 py-1 rounded-lg transition flex items-center"> |
| <i class="fas fa-volume-up mr-2"></i> Sound |
| </button> |
| <button class="bg-gray-200 hover:bg-gray-300 text-gray-800 px-3 py-1 rounded-lg transition flex items-center"> |
| <i class="fas fa-cog mr-2"></i> Settings |
| </button> |
| </div> |
| </div> |
| |
| |
| <div class="mt-6 bg-white rounded-lg shadow-md p-4"> |
| <h3 class="font-semibold text-lg text-gray-800 mb-3">Game Chat</h3> |
| <div class="h-40 overflow-y-auto mb-3 border border-gray-200 rounded-lg p-2" id="chatMessages"> |
| <div class="text-sm text-gray-500 italic">No messages yet. Start chatting!</div> |
| </div> |
| <div class="flex gap-2"> |
| <input type="text" placeholder="Type your message..." class="flex-1 border border-gray-300 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"> |
| <button class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg transition"> |
| <i class="fas fa-paper-plane"></i> |
| </button> |
| </div> |
| </div> |
| </div> |
| |
| |
| <div id="gameOverModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden z-50"> |
| <div class="bg-white rounded-lg p-6 max-w-md w-full mx-4"> |
| <h2 class="text-2xl font-bold text-center mb-4">Game Over</h2> |
| <p id="gameResult" class="text-center text-lg mb-6">White wins by checkmate!</p> |
| <div class="flex justify-center gap-4"> |
| <button id="rematchBtn" class="bg-green-600 hover:bg-green-700 text-white px-4 py-2 rounded-lg transition"> |
| <i class="fas fa-redo mr-2"></i> Rematch |
| </button> |
| <button id="closeModalBtn" class="bg-gray-600 hover:bg-gray-700 text-white px-4 py-2 rounded-lg transition"> |
| Close |
| </button> |
| </div> |
| </div> |
| </div> |
| |
| <script> |
| document.addEventListener('DOMContentLoaded', function() { |
| |
| const board = document.getElementById('board'); |
| const squares = []; |
| let selectedSquare = null; |
| let currentPlayer = 'white'; |
| let gameActive = true; |
| |
| |
| for (let i = 0; i < 8; i++) { |
| for (let j = 0; j < 8; j++) { |
| const square = document.createElement('div'); |
| const isLight = (i + j) % 2 === 0; |
| square.className = `square ${isLight ? 'light' : 'dark'}`; |
| square.dataset.row = i; |
| square.dataset.col = j; |
| |
| |
| if (i === 0 || i === 7) { |
| const coord = document.createElement('div'); |
| coord.className = `absolute ${i === 0 ? 'top-1 left-1' : 'bottom-1 right-1'} text-xs font-mono ${isLight ? 'text-gray-800' : 'text-gray-100'}`; |
| coord.textContent = String.fromCharCode(97 + j); |
| square.appendChild(coord); |
| } |
| |
| |
| if (j === 0 || j === 7) { |
| const coord = document.createElement('div'); |
| coord.className = `absolute ${j === 0 ? 'top-1 left-1' : 'bottom-1 right-1'} text-xs font-mono ${isLight ? 'text-gray-800' : 'text-gray-100'}`; |
| coord.textContent = 8 - i; |
| square.appendChild(coord); |
| } |
| |
| square.addEventListener('click', () => handleSquareClick(square)); |
| |
| |
| square.addEventListener('dragstart', handleDragStart); |
| square.addEventListener('dragover', handleDragOver); |
| square.addEventListener('drop', handleDrop); |
| square.addEventListener('dragend', handleDragEnd); |
| |
| board.appendChild(square); |
| squares.push(square); |
| } |
| } |
| |
| |
| setupInitialPieces(); |
| |
| |
| updateActivePlayer(); |
| |
| |
| document.getElementById('closeModalBtn').addEventListener('click', () => { |
| document.getElementById('gameOverModal').classList.add('hidden'); |
| }); |
| |
| document.getElementById('rematchBtn').addEventListener('click', () => { |
| document.getElementById('gameOverModal').classList.add('hidden'); |
| resetGame(); |
| }); |
| |
| document.getElementById('newGameBtn').addEventListener('click', resetGame); |
| |
| |
| |
| |
| |
| |
| function setupInitialPieces() { |
| |
| const createPiece = (type, color) => { |
| const piece = document.createElement('div'); |
| piece.className = `piece ${color}`; |
| piece.draggable = true; |
| piece.dataset.type = type; |
| piece.dataset.color = color; |
| |
| |
| const symbols = { |
| king: '♔', |
| queen: '♕', |
| rook: '♖', |
| bishop: '♗', |
| knight: '♘', |
| pawn: '♙' |
| }; |
| |
| piece.textContent = color === 'white' ? symbols[type] : symbols[type].toLowerCase(); |
| piece.style.fontSize = '2.5rem'; |
| piece.style.lineHeight = '1'; |
| piece.style.textShadow = '1px 1px 2px rgba(0,0,0,0.3)'; |
| piece.style.color = color === 'white' ? '#fff' : '#000'; |
| |
| return piece; |
| }; |
| |
| |
| for (let i = 0; i < 8; i++) { |
| squares[8 + i].appendChild(createPiece('pawn', 'white')); |
| squares[48 + i].appendChild(createPiece('pawn', 'black')); |
| } |
| |
| |
| const backRow = ['rook', 'knight', 'bishop', 'queen', 'king', 'bishop', 'knight', 'rook']; |
| for (let i = 0; i < 8; i++) { |
| squares[i].appendChild(createPiece(backRow[i], 'white')); |
| squares[56 + i].appendChild(createPiece(backRow[i], 'black')); |
| } |
| } |
| |
| function handleSquareClick(square) { |
| if (!gameActive) return; |
| |
| const piece = square.firstChild; |
| |
| |
| if (!selectedSquare && piece && piece.dataset.color === currentPlayer) { |
| selectedSquare = square; |
| square.classList.add('highlight'); |
| showPossibleMoves(square); |
| return; |
| } |
| |
| |
| if (selectedSquare) { |
| |
| if (square === selectedSquare) { |
| clearSelection(); |
| return; |
| } |
| |
| |
| if (piece && piece.dataset.color === currentPlayer) { |
| clearSelection(); |
| selectedSquare = square; |
| square.classList.add('highlight'); |
| showPossibleMoves(square); |
| return; |
| } |
| |
| |
| const fromRow = parseInt(selectedSquare.dataset.row); |
| const fromCol = parseInt(selectedSquare.dataset.col); |
| const toRow = parseInt(square.dataset.row); |
| const toCol = parseInt(square.dataset.col); |
| |
| if (isValidMove(fromRow, fromCol, toRow, toCol, selectedSquare.firstChild)) { |
| |
| const pieceToMove = selectedSquare.firstChild; |
| square.innerHTML = ''; |
| square.appendChild(pieceToMove); |
| |
| |
| currentPlayer = currentPlayer === 'white' ? 'black' : 'white'; |
| updateActivePlayer(); |
| } |
| |
| clearSelection(); |
| } |
| } |
| |
| function showPossibleMoves(square) { |
| const piece = square.firstChild; |
| if (!piece) return; |
| |
| const row = parseInt(square.dataset.row); |
| const col = parseInt(square.dataset.col); |
| const type = piece.dataset.type; |
| const color = piece.dataset.color; |
| |
| |
| if (type === 'pawn') { |
| const direction = color === 'white' ? 1 : -1; |
| |
| |
| const forwardRow = row + direction; |
| if (forwardRow >= 0 && forwardRow < 8) { |
| const forwardSquare = squares[forwardRow * 8 + col]; |
| if (!forwardSquare.firstChild) { |
| addPossibleMoveIndicator(forwardSquare); |
| } |
| |
| |
| if ((color === 'white' && row === 1) || (color === 'black' && row === 6)) { |
| const doubleRow = row + 2 * direction; |
| const doubleSquare = squares[doubleRow * 8 + col]; |
| if (!doubleSquare.firstChild && !forwardSquare.firstChild) { |
| addPossibleMoveIndicator(doubleSquare); |
| } |
| } |
| } |
| |
| |
| const captureCols = [col - 1, col + 1]; |
| for (const capCol of captureCols) { |
| if (capCol >= 0 && capCol < 8) { |
| const capRow = row + direction; |
| if (capRow >= 0 && capRow < 8) { |
| const capSquare = squares[capRow * 8 + capCol]; |
| if (capSquare.firstChild && capSquare.firstChild.dataset.color !== color) { |
| addCaptureMoveIndicator(capSquare); |
| } |
| } |
| } |
| } |
| } else if (type === 'knight') { |
| const moves = [ |
| [row + 2, col + 1], [row + 2, col - 1], |
| [row - 2, col + 1], [row - 2, col - 1], |
| [row + 1, col + 2], [row + 1, col - 2], |
| [row - 1, col + 2], [row - 1, col - 2] |
| ]; |
| |
| for (const [r, c] of moves) { |
| if (r >= 0 && r < 8 && c >= 0 && c < 8) { |
| const moveSquare = squares[r * 8 + c]; |
| if (!moveSquare.firstChild || moveSquare.firstChild.dataset.color !== color) { |
| if (moveSquare.firstChild) { |
| addCaptureMoveIndicator(moveSquare); |
| } else { |
| addPossibleMoveIndicator(moveSquare); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| function addPossibleMoveIndicator(square) { |
| const indicator = document.createElement('div'); |
| indicator.className = 'possible-move'; |
| square.appendChild(indicator); |
| } |
| |
| function addCaptureMoveIndicator(square) { |
| const indicator = document.createElement('div'); |
| indicator.className = 'capture-move'; |
| square.appendChild(indicator); |
| } |
| |
| function clearSelection() { |
| if (selectedSquare) { |
| selectedSquare.classList.remove('highlight'); |
| |
| |
| document.querySelectorAll('.possible-move, .capture-move').forEach(ind => ind.remove()); |
| |
| selectedSquare = null; |
| } |
| } |
| |
| function isValidMove(fromRow, fromCol, toRow, toCol, piece) { |
| |
| const type = piece.dataset.type; |
| const color = piece.dataset.color; |
| |
| if (type === 'pawn') { |
| const direction = color === 'white' ? 1 : -1; |
| |
| |
| if (fromCol === toCol) { |
| |
| if (toRow === fromRow + direction) { |
| return !squares[toRow * 8 + toCol].firstChild; |
| } |
| |
| |
| if ((color === 'white' && fromRow === 1 && toRow === 3) || |
| (color === 'black' && fromRow === 6 && toRow === 4)) { |
| return !squares[toRow * 8 + toCol].firstChild && |
| !squares[(fromRow + direction) * 8 + toCol].firstChild; |
| } |
| } |
| |
| |
| if (Math.abs(toCol - fromCol) === 1 && toRow === fromRow + direction) { |
| const targetSquare = squares[toRow * 8 + toCol]; |
| return targetSquare.firstChild && targetSquare.firstChild.dataset.color !== color; |
| } |
| |
| return false; |
| } else if (type === 'knight') { |
| const rowDiff = Math.abs(toRow - fromRow); |
| const colDiff = Math.abs(toCol - fromCol); |
| return (rowDiff === 2 && colDiff === 1) || (rowDiff === 1 && colDiff === 2); |
| } |
| |
| |
| return true; |
| } |
| |
| function updateActivePlayer() { |
| const player1 = document.getElementById('player1'); |
| const player2 = document.getElementById('player2'); |
| |
| if (currentPlayer === 'white') { |
| player1.classList.add('active'); |
| player2.classList.remove('active'); |
| } else { |
| player1.classList.remove('active'); |
| player2.classList.add('active'); |
| } |
| } |
| |
| function showGameOver(message) { |
| gameActive = false; |
| document.getElementById('gameResult').textContent = message; |
| document.getElementById('gameOverModal').classList.remove('hidden'); |
| } |
| |
| function resetGame() { |
| |
| squares.forEach(square => { |
| square.innerHTML = ''; |
| const isLight = (parseInt(square.dataset.row) + parseInt(square.dataset.col)) % 2 === 0; |
| square.className = `square ${isLight ? 'light' : 'dark'}`; |
| |
| |
| const i = parseInt(square.dataset.row); |
| const j = parseInt(square.dataset.col); |
| |
| if (i === 0 || i === 7) { |
| const coord = document.createElement('div'); |
| coord.className = `absolute ${i === 0 ? 'top-1 left-1' : 'bottom-1 right-1'} text-xs font-mono ${isLight ? 'text-gray-800' : 'text-gray-100'}`; |
| coord.textContent = String.fromCharCode(97 + j); |
| square.appendChild(coord); |
| } |
| |
| if (j === 0 || j === 7) { |
| const coord = document.createElement('div'); |
| coord.className = `absolute ${j === 0 ? 'top-1 left-1' : 'bottom-1 right-1'} text-xs font-mono ${isLight ? 'text-gray-800' : 'text-gray-100'}`; |
| coord.textContent = 8 - i; |
| square.appendChild(coord); |
| } |
| }); |
| |
| |
| currentPlayer = 'white'; |
| gameActive = true; |
| selectedSquare = null; |
| |
| |
| setupInitialPieces(); |
| updateActivePlayer(); |
| } |
| |
| |
| function handleDragStart(e) { |
| if (!gameActive) { |
| e.preventDefault(); |
| return; |
| } |
| |
| const piece = e.target; |
| if (piece.classList.contains('piece') && piece.dataset.color === currentPlayer) { |
| e.dataTransfer.setData('text/plain', piece.dataset.type); |
| piece.classList.add('dragging'); |
| |
| |
| const dragImage = document.createElement('div'); |
| dragImage.style.opacity = '0'; |
| document.body.appendChild(dragImage); |
| e.dataTransfer.setDragImage(dragImage, 0, 0); |
| setTimeout(() => document.body.removeChild(dragImage), 0); |
| } else { |
| e.preventDefault(); |
| } |
| } |
| |
| function handleDragOver(e) { |
| e.preventDefault(); |
| } |
| |
| function handleDrop(e) { |
| e.preventDefault(); |
| if (!gameActive) return; |
| |
| const fromSquare = document.querySelector('.piece.dragging').parentElement; |
| const toSquare = e.currentTarget; |
| |
| if (fromSquare === toSquare) { |
| document.querySelector('.piece.dragging').classList.remove('dragging'); |
| return; |
| } |
| |
| const piece = fromSquare.firstChild; |
| const fromRow = parseInt(fromSquare.dataset.row); |
| const fromCol = parseInt(fromSquare.dataset.col); |
| const toRow = parseInt(toSquare.dataset.row); |
| const toCol = parseInt(toSquare.dataset.col); |
| |
| if (isValidMove(fromRow, fromCol, toRow, toCol, piece)) { |
| |
| toSquare.innerHTML = ''; |
| toSquare.appendChild(piece); |
| |
| |
| currentPlayer = currentPlayer === 'white' ? 'black' : 'white'; |
| updateActivePlayer(); |
| } |
| |
| piece.classList.remove('dragging'); |
| } |
| |
| function handleDragEnd(e) { |
| document.querySelectorAll('.piece.dragging').forEach(p => p.classList.remove('dragging')); |
| } |
| }); |
| </script> |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Naikjit/chees" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
| </html> |