import React, { useState, useEffect } from 'react'; import { Bomb } from 'lucide-react'; const ChessMinesweeper = () => { const [board, setBoard] = useState([]); const [selectedPiece, setSelectedPiece] = useState(null); const [currentPlayer, setCurrentPlayer] = useState('white'); const [gameOver, setGameOver] = useState(false); const [message, setMessage] = useState(''); const [capturedPieces, setCapturedPieces] = useState({ white: [], black: [] }); const pieceSymbols = { white: { king: '♔', queen: '♕', rook: '♖', bishop: '♗', knight: '♘', pawn: '♙' }, black: { king: '♚', queen: '♛', rook: '♜', bishop: '♝', knight: '♞', pawn: '♟' } }; useEffect(() => { initBoard(); }, []); const initBoard = () => { const newBoard = Array(8).fill(null).map(() => Array(8).fill(null)); // Placer les pièces blanches (rangée 7 et 6) newBoard[7][0] = { type: 'rook', color: 'white' }; newBoard[7][1] = { type: 'knight', color: 'white' }; newBoard[7][2] = { type: 'bishop', color: 'white' }; newBoard[7][3] = { type: 'queen', color: 'white' }; newBoard[7][4] = { type: 'king', color: 'white' }; newBoard[7][5] = { type: 'bishop', color: 'white' }; newBoard[7][6] = { type: 'knight', color: 'white' }; newBoard[7][7] = { type: 'rook', color: 'white' }; for (let i = 0; i < 8; i++) { newBoard[6][i] = { type: 'pawn', color: 'white', hasMoved: false }; } // Placer les pièces noires (rangée 0 et 1) newBoard[0][0] = { type: 'rook', color: 'black' }; newBoard[0][1] = { type: 'knight', color: 'black' }; newBoard[0][2] = { type: 'bishop', color: 'black' }; newBoard[0][3] = { type: 'queen', color: 'black' }; newBoard[0][4] = { type: 'king', color: 'black' }; newBoard[0][5] = { type: 'bishop', color: 'black' }; newBoard[0][6] = { type: 'knight', color: 'black' }; newBoard[0][7] = { type: 'rook', color: 'black' }; for (let i = 0; i < 8; i++) { newBoard[1][i] = { type: 'pawn', color: 'black', hasMoved: false }; } // Générer les mines dans les rangées du milieu (2-5) const mines = new Set(); const mineCount = 12; while (mines.size < mineCount) { const row = 2 + Math.floor(Math.random() * 4); const col = Math.floor(Math.random() * 8); mines.add(`${row},${col}`); } // Ajouter les mines et calculer les nombres adjacents for (let row = 2; row <= 5; row++) { for (let col = 0; col < 8; col++) { const key = `${row},${col}`; if (mines.has(key)) { newBoard[row][col] = { type: 'mine', revealed: false }; } else { const adjacentMines = countAdjacentMines(row, col, mines); newBoard[row][col] = { type: 'empty', revealed: false, count: adjacentMines }; } } } // Ajouter les indices de mines pour les rangées 1 et 6 (où sont les pions) for (let col = 0; col < 8; col++) { // Rangée 1 (pions noirs) const count1 = countAdjacentMines(1, col, mines); if (count1 > 0) { newBoard[1][col] = { ...newBoard[1][col], mineCount: count1 }; } // Rangée 6 (pions blancs) const count6 = countAdjacentMines(6, col, mines); if (count6 > 0) { newBoard[6][col] = { ...newBoard[6][col], mineCount: count6 }; } } setBoard(newBoard); setCurrentPlayer('white'); setGameOver(false); setMessage(''); setCapturedPieces({ white: [], black: [] }); }; const countAdjacentMines = (row, col, mines) => { let count = 0; for (let dr = -1; dr <= 1; dr++) { for (let dc = -1; dc <= 1; dc++) { if (dr === 0 && dc === 0) continue; const nr = row + dr; const nc = col + dc; // Vérifier toutes les cases adjacentes, pas seulement la zone démineur if (nr >= 0 && nr < 8 && nc >= 0 && nc < 8) { if (mines.has(`${nr},${nc}`)) count++; } } } return count; }; const handleSquareClick = (row, col) => { if (gameOver) return; const cell = board[row][col]; const actualPiece = cell?.piece || cell; // Si on clique sur une pièce if (actualPiece && actualPiece.color && ['king', 'queen', 'rook', 'bishop', 'knight', 'pawn'].includes(actualPiece.type)) { if (actualPiece.color === currentPlayer) { setSelectedPiece({ row, col }); return; } } // Si on a une pièce sélectionnée, essayer de la déplacer if (selectedPiece) { movePiece(selectedPiece.row, selectedPiece.col, row, col); } }; const movePiece = (fromRow, fromCol, toRow, toCol) => { const fromCell = board[fromRow][fromCol]; const piece = fromCell?.piece || fromCell; const toCell = board[toRow][toCol]; const targetPiece = toCell?.piece || toCell; if (!piece || !['king', 'queen', 'rook', 'bishop', 'knight', 'pawn'].includes(piece.type)) { setSelectedPiece(null); return; } // Vérifier que le mouvement est valide if (!isValidMove(fromRow, fromCol, toRow, toCol, piece)) { setSelectedPiece(null); return; } const newBoard = board.map(row => [...row]); let newMessage = ''; let pieceCaptured = false; // Si on capture une pièce ennemie if (targetPiece && targetPiece.color && targetPiece.color !== piece.color) { const newCaptured = { ...capturedPieces }; newCaptured[targetPiece.color].push(targetPiece.type); setCapturedPieces(newCaptured); pieceCaptured = true; newMessage = `${piece.type} ${piece.color} capture ${targetPiece.type} ${targetPiece.color}!`; } // Si on se déplace sur une case du démineur const isMinesweeperZone = toRow >= 2 && toRow <= 5; if (isMinesweeperZone && toCell && (toCell.type === 'mine' || toCell.type === 'empty')) { // Si c'est une mine (révélée ou non) if (toCell.type === 'mine') { // BOOM! La pièce est éliminée newBoard[fromRow][fromCol] = fromCell.revealed ? { ...fromCell, piece: null } : null; newBoard[toRow][toCol] = { type: 'mine', revealed: true }; newMessage = `💥 BOOM! ${piece.type} ${piece.color} a explosé sur une mine!`; const newCaptured = { ...capturedPieces }; newCaptured[piece.color].push(piece.type); setCapturedPieces(newCaptured); if (piece.type === 'king') { setGameOver(true); newMessage += ` ${piece.color === 'white' ? 'Noirs' : 'Blancs'} gagnent!`; } } else if (!toCell.revealed) { // Case sûre non révélée, révéler le nombre const updatedPiece = piece.type === 'pawn' ? { ...piece, hasMoved: true } : piece; newBoard[toRow][toCol] = { ...toCell, revealed: true, piece: updatedPiece }; newBoard[fromRow][fromCol] = fromCell.revealed ? { ...fromCell, piece: null } : null; if (!pieceCaptured) { newMessage = `Case révélée: ${toCell.count} mine(s) adjacente(s)`; } } else { // Case sûre déjà révélée const updatedPiece = piece.type === 'pawn' ? { ...piece, hasMoved: true } : piece; newBoard[toRow][toCol] = { ...toCell, piece: updatedPiece }; newBoard[fromRow][fromCol] = fromCell.revealed ? { ...fromCell, piece: null } : null; } } else if (toCell && toCell.revealed && toCell.type === 'empty') { // Se déplacer sur une case déjà révélée const updatedPiece = piece.type === 'pawn' ? { ...piece, hasMoved: true } : piece; newBoard[toRow][toCol] = { ...toCell, piece: updatedPiece }; newBoard[fromRow][fromCol] = fromCell.revealed ? { ...fromCell, piece: null } : null; } else { // Se déplacer sur une case normale (hors zone démineur ou pièce ennemie) const updatedPiece = piece.type === 'pawn' ? { ...piece, hasMoved: true } : piece; newBoard[toRow][toCol] = updatedPiece; newBoard[fromRow][fromCol] = fromCell.revealed ? { ...fromCell, piece: null } : null; } setBoard(newBoard); setSelectedPiece(null); setMessage(newMessage); if (!gameOver) { setCurrentPlayer(currentPlayer === 'white' ? 'black' : 'white'); } }; const isValidMove = (fromRow, fromCol, toRow, toCol, piece) => { if (fromRow === toRow && fromCol === toCol) return false; const toCell = board[toRow][toCol]; const targetPiece = toCell?.piece || toCell; // Ne peut pas capturer sa propre pièce if (targetPiece && targetPiece.color === piece.color) return false; const rowDiff = toRow - fromRow; const colDiff = toCol - fromCol; switch (piece.type) { case 'pawn': return isValidPawnMove(fromRow, fromCol, toRow, toCol, piece, targetPiece); case 'rook': return isValidRookMove(fromRow, fromCol, toRow, toCol); case 'knight': return isValidKnightMove(rowDiff, colDiff); case 'bishop': return isValidBishopMove(fromRow, fromCol, toRow, toCol); case 'queen': return isValidQueenMove(fromRow, fromCol, toRow, toCol); case 'king': return isValidKingMove(rowDiff, colDiff); default: return false; } }; const isValidPawnMove = (fromRow, fromCol, toRow, toCol, piece, targetPiece) => { const direction = piece.color === 'white' ? -1 : 1; const rowDiff = toRow - fromRow; const colDiff = Math.abs(toCol - fromCol); // Capture en diagonale if (rowDiff === direction && colDiff === 1) { return targetPiece && targetPiece.color && targetPiece.color !== piece.color; } // Avancer d'une case if (rowDiff === direction && colDiff === 0) { return !targetPiece || !targetPiece.color; } // Premier mouvement: avancer de 2 cases if (!piece.hasMoved && colDiff === 0 && rowDiff === direction * 2) { const middleRow = fromRow + direction; const middleCell = board[middleRow][fromCol]; const middlePiece = middleCell?.piece || middleCell; return (!targetPiece || !targetPiece.color) && (!middlePiece || !middlePiece.color); } return false; }; const isValidRookMove = (fromRow, fromCol, toRow, toCol) => { if (fromRow !== toRow && fromCol !== toCol) return false; return isPathClear(fromRow, fromCol, toRow, toCol); }; const isValidKnightMove = (rowDiff, colDiff) => { return (Math.abs(rowDiff) === 2 && Math.abs(colDiff) === 1) || (Math.abs(rowDiff) === 1 && Math.abs(colDiff) === 2); }; const isValidBishopMove = (fromRow, fromCol, toRow, toCol) => { if (Math.abs(toRow - fromRow) !== Math.abs(toCol - fromCol)) return false; return isPathClear(fromRow, fromCol, toRow, toCol); }; const isValidQueenMove = (fromRow, fromCol, toRow, toCol) => { return isValidRookMove(fromRow, fromCol, toRow, toCol) || isValidBishopMove(fromRow, fromCol, toRow, toCol); }; const isValidKingMove = (rowDiff, colDiff) => { return Math.abs(rowDiff) <= 1 && Math.abs(colDiff) <= 1; }; const isPathClear = (fromRow, fromCol, toRow, toCol) => { const rowStep = toRow > fromRow ? 1 : toRow < fromRow ? -1 : 0; const colStep = toCol > fromCol ? 1 : toCol < fromCol ? -1 : 0; let currentRow = fromRow + rowStep; let currentCol = fromCol + colStep; while (currentRow !== toRow || currentCol !== toCol) { const cell = board[currentRow][currentCol]; const piece = cell?.piece || cell; if (piece && piece.color) return false; currentRow += rowStep; currentCol += colStep; } return true; }; const renderSquare = (row, col) => { const cell = board[row][col]; const piece = cell?.piece || cell; const isSelected = selectedPiece && selectedPiece.row === row && selectedPiece.col === col; const isDark = (row + col) % 2 === 1; let bgColor = isDark ? 'bg-amber-700' : 'bg-amber-100'; if (isSelected) bgColor = 'bg-blue-400'; const isMinesweeperZone = row >= 2 && row <= 5; if (isMinesweeperZone && cell && !cell.revealed) { bgColor = isDark ? 'bg-gray-600' : 'bg-gray-400'; } else if (isMinesweeperZone && cell && cell.revealed) { // Cases révélées en gris clair comme dans le vrai démineur bgColor = 'bg-gray-300'; if (isSelected) bgColor = 'bg-blue-400'; } return (
♟️ Règles complètes des échecs appliquées
💣 Attention: Les 4 rangées du milieu cachent des mines!
🎯 Capturez le roi ennemi ou faites-le exploser pour gagner