import React, { useState, useEffect } from 'react'; // 8방향 탐색을 위한 배열 (상하좌우 및 대각선) const DIRECTIONS = [ [-1, -1], [-1, 0], [-1, 1], [0, -1], [0, 1], [1, -1], [1, 0], [1, 1] ]; // 난이도별 설정 const DIFFICULTY_SETTINGS = { beginner: { rows: 10, cols: 10, mines: 10, label: '초급' }, intermediate: { rows: 16, cols: 16, mines: 40, label: '중급' }, advanced: { rows: 16, cols: 30, mines: 99, label: '고급' }, }; export default function App() { const [difficulty, setDifficulty] = useState('beginner'); // 기본값: 초급(10x10) const [board, setBoard] = useState([]); const [gameOver, setGameOver] = useState(false); const [gameWon, setGameWon] = useState(false); const [flagsCount, setFlagsCount] = useState(0); // 현재 난이도 설정 가져오기 const currentSetting = DIFFICULTY_SETTINGS[difficulty]; // 게임 초기화 함수 const initializeGame = () => { const { rows, cols, mines } = currentSetting; let newBoard = Array(rows).fill().map(() => Array(cols).fill().map(() => ({ isMine: false, isRevealed: false, isFlagged: false, neighborMines: 0, })) ); // 1. 지뢰 무작위 배치 let minesPlaced = 0; while (minesPlaced < mines) { const r = Math.floor(Math.random() * rows); const c = Math.floor(Math.random() * cols); if (!newBoard[r][c].isMine) { newBoard[r][c].isMine = true; minesPlaced++; } } // 2. 주변 지뢰 개수 계산 for (let r = 0; r < rows; r++) { for (let c = 0; c < cols; c++) { if (!newBoard[r][c].isMine) { let count = 0; DIRECTIONS.forEach(([dr, 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); setGameWon(false); setFlagsCount(0); }; // 난이도가 변경될 때마다 게임 보드를 새로 생성합니다. useEffect(() => { initializeGame(); }, [difficulty]); // 빈 칸 클릭 시 연쇄적으로 주변 칸 열기 (Flood Fill) const revealEmptyCells = (r, c, currentBoard) => { const { rows, cols } = currentSetting; const stack = [[r, c]]; while (stack.length > 0) { const [currR, currC] = stack.pop(); DIRECTIONS.forEach(([dr, dc]) => { const nr = currR + dr; const nc = currC + dc; if (nr >= 0 && nr < rows && nc >= 0 && nc < cols) { const cell = currentBoard[nr][nc]; if (!cell.isRevealed && !cell.isFlagged && !cell.isMine) { cell.isRevealed = true; if (cell.neighborMines === 0) { stack.push([nr, nc]); } } } }); } }; // 승리 조건 체크 const checkWinCondition = (currentBoard) => { const { rows, cols, mines } = currentSetting; let revealedCount = 0; for (let r = 0; r < rows; r++) { for (let c = 0; c < cols; c++) { if (currentBoard[r][c].isRevealed) { revealedCount++; } } } // 지뢰가 없는 모든 칸을 열었을 경우 승리 if (revealedCount === rows * cols - mines) { setGameWon(true); setGameOver(true); // 승리 시 남은 지뢰에 자동으로 깃발 꽂기 currentBoard.forEach(row => row.forEach(cell => { if (cell.isMine) cell.isFlagged = true; })); } }; // 좌클릭 (칸 열기) const handleCellClick = (r, c) => { if (gameOver || board[r][c].isRevealed || board[r][c].isFlagged) return; const newBoard = [...board.map(row => [...row])]; const cell = newBoard[r][c]; // 1. 지뢰를 클릭한 경우 if (cell.isMine) { cell.isRevealed = true; setGameOver(true); // 모든 지뢰 공개 newBoard.forEach(row => row.forEach(c => { if (c.isMine) c.isRevealed = true; })); setBoard(newBoard); return; } // 2. 일반 칸을 클릭한 경우 cell.isRevealed = true; if (cell.neighborMines === 0) { revealEmptyCells(r, c, newBoard); } setBoard(newBoard); checkWinCondition(newBoard); }; // 우클릭 (깃발 꽂기) const handleContextMenu = (e, r, c) => { e.preventDefault(); // 기본 우클릭 메뉴 방지 if (gameOver || board[r][c].isRevealed) return; const { mines } = currentSetting; const newBoard = [...board.map(row => [...row])]; const cell = newBoard[r][c]; if (!cell.isFlagged && flagsCount < mines) { cell.isFlagged = true; setFlagsCount(prev => prev + 1); } else if (cell.isFlagged) { cell.isFlagged = false; setFlagsCount(prev => prev - 1); } setBoard(newBoard); }; // 숫자에 따른 색상 지정 함수 const getNumberColor = (num) => { const colors = [ '', // 0은 표시하지 않음 'text-blue-500', 'text-green-500', 'text-red-500', 'text-purple-500', 'text-amber-500', 'text-cyan-500', 'text-black', 'text-gray-600' ]; return colors[num] || 'text-black'; }; return (
{/* 헤더 및 난이도 설정 */}

지뢰찾기

{Object.entries(DIFFICULTY_SETTINGS).map(([key, setting]) => ( ))}
{/* 게임 상태 및 지뢰 개수 (클래식 스타일 적용) */}
{String(Math.max(0, currentSetting.mines - flagsCount)).padStart(3, '0')}
{gameWon &&

🎉 승리했습니다! 🎉

} {gameOver && !gameWon &&

💥 지뢰를 밟았습니다! 💥

}
{/* 게임 보드 */}
e.preventDefault()} > {board.map((row, r) => ( row.map((cell, c) => (
handleCellClick(r, c)} onContextMenu={(e) => handleContextMenu(e, r, c)} className={` w-7 h-7 sm:w-9 sm:h-9 flex items-center justify-center text-base sm:text-lg font-bold cursor-pointer select-none ${cell.isRevealed ? cell.isMine ? 'bg-red-500 border border-red-600' : 'bg-gray-200 border border-gray-300' : 'bg-gray-300 hover:bg-gray-200 border-2 border-t-white border-l-white border-b-gray-500 border-r-gray-500 active:border-t-gray-500 active:border-l-gray-500 active:border-b-white active:border-r-white' } `} > {cell.isRevealed ? ( cell.isMine ? '💣' : cell.neighborMines > 0 ? ( {cell.neighborMines} ) : '' ) : cell.isFlagged ? '🚩' : ''}
)) ))}
{/* 조작 설명 */}

좌클릭: 칸 열기

우클릭: 깃발 꽂기 / 해제

); }