import { useState, useEffect } from 'react'; import { Flag, RotateCcw, Trophy } from 'lucide-react'; interface Cell { isMine: boolean; isRevealed: boolean; isFlagged: boolean; neighborMines: number; } export function Minesweeper() { const [board, setBoard] = useState([]); const [gameOver, setGameOver] = useState(false); const [won, setWon] = useState(false); const [mineCount, setMineCount] = useState(10); const [flagCount, setFlagCount] = useState(0); const [timer, setTimer] = useState(0); const [isRunning, setIsRunning] = useState(false); const ROWS = 9; const COLS = 9; const MINES = 10; useEffect(() => { initGame(); }, []); useEffect(() => { let interval: NodeJS.Timeout; if (isRunning && !gameOver && !won) { interval = setInterval(() => setTimer(t => t + 1), 1000); } return () => clearInterval(interval); }, [isRunning, gameOver, won]); const initGame = () => { const newBoard: Cell[][] = Array(ROWS).fill(null).map(() => Array(COLS).fill(null).map(() => ({ isMine: false, isRevealed: false, isFlagged: false, neighborMines: 0, })) ); // Place mines let minesPlaced = 0; while (minesPlaced < MINES) { const row = Math.floor(Math.random() * ROWS); const col = Math.floor(Math.random() * COLS); if (!newBoard[row][col].isMine) { newBoard[row][col].isMine = true; minesPlaced++; } } // Calculate neighbor mines for (let r = 0; r < ROWS; r++) { for (let c = 0; c < COLS; c++) { if (!newBoard[r][c].isMine) { let count = 0; for (let dr = -1; dr <= 1; dr++) { for (let dc = -1; dc <= 1; dc++) { const nr = r + dr; const nc = c + dc; if (nr >= 0 && nr < ROWS && nc >= 0 && nc < COLS && newBoard[nr][nc].isMine) { count++; } } } newBoard[r][c].neighborMines = count; } } } setBoard(newBoard); setGameOver(false); setWon(false); setFlagCount(0); setMineCount(MINES); setTimer(0); setIsRunning(false); }; const revealCell = (row: number, col: number) => { if (gameOver || won || board[row][col].isRevealed || board[row][col].isFlagged) return; if (!isRunning) setIsRunning(true); const newBoard = [...board.map(r => [...r])]; if (newBoard[row][col].isMine) { // Game over for (let r = 0; r < ROWS; r++) { for (let c = 0; c < COLS; c++) { if (newBoard[r][c].isMine) newBoard[r][c].isRevealed = true; } } setBoard(newBoard); setGameOver(true); setIsRunning(false); return; } const reveal = (r: number, c: number) => { if (r < 0 || r >= ROWS || c < 0 || c >= COLS) return; if (newBoard[r][c].isRevealed || newBoard[r][c].isFlagged) return; newBoard[r][c].isRevealed = true; if (newBoard[r][c].neighborMines === 0 && !newBoard[r][c].isMine) { for (let dr = -1; dr <= 1; dr++) { for (let dc = -1; dc <= 1; dc++) { reveal(r + dr, c + dc); } } } }; reveal(row, col); setBoard(newBoard); // Check win let revealedCount = 0; for (let r = 0; r < ROWS; r++) { for (let c = 0; c < COLS; c++) { if (newBoard[r][c].isRevealed) revealedCount++; } } if (revealedCount === ROWS * COLS - MINES) { setWon(true); setIsRunning(false); } }; const toggleFlag = (e: React.MouseEvent, row: number, col: number) => { e.preventDefault(); if (gameOver || won || board[row][col].isRevealed) return; if (!isRunning) setIsRunning(true); const newBoard = [...board.map(r => [...r])]; newBoard[row][col].isFlagged = !newBoard[row][col].isFlagged; setBoard(newBoard); setFlagCount(prev => newBoard[row][col].isFlagged ? prev + 1 : prev - 1); }; const getCellColor = (cell: Cell) => { if (!cell.isRevealed) return 'bg-slate-700 hover:bg-slate-600'; if (cell.isMine) return 'bg-red-600'; if (cell.neighborMines === 0) return 'bg-slate-800'; return 'bg-slate-900'; }; const getNumberColor = (num: number) => { const colors = ['', 'text-blue-400', 'text-green-400', 'text-red-400', 'text-purple-400', 'text-yellow-400', 'text-pink-400', 'text-cyan-400', 'text-white']; return colors[num] || 'text-white'; }; return (
{mineCount - flagCount}
{String(timer).padStart(3, '0')}
{won && (
You Won! Time: {timer}s
)} {gameOver && (
Game Over! Try Again
)}
{board.map((row, r) => row.map((cell, c) => ( )) )}
Left click to reveal • Right click to flag
); }