<!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset="UTF-8"> <title>扫雷游戏</title> </head> <body> <div id="game-controls"> <select id="difficulty"> <option value="easy">简单 (9x9, 10雷)</option> <option value="medium">中等 (16x16, 40雷)</option> <option value="hard">困难 (16x30, 99雷)</option> </select> <button id="restart">重新开始</button> <button id="pause">暂停</button> <span id="timer">00:00</span> <span id="mines-left">雷数: 0</span> </div> <div id="game-board"></div> <div id="game-message"></div> </body> </html>
body { font-family: Arial, sans-serif; background: #e0e0e0; text-align: center; margin: 0; padding: 0; } #game-controls { margin: 20px auto; display: flex; justify-content: center; align-items: center; gap: 15px; } #game-board { display: inline-block; margin: 20px auto; background: #bdbdbd; border: 4px solid #888; box-shadow: 2px 2px 8px #888; } .row { display: flex; } .cell { width: 30px; height: 30px; background: #d0d0d0; border: 1px solid #888; font-size: 18px; font-weight: bold; color: #333; cursor: pointer; user-select: none; display: flex; align-items: center; justify-content: center; transition: background 0.1s; } .cell.open { background: #f0f0f0; cursor: default; } .cell.flag { background: #ffe082; color: #d84315; } .cell.mine { background: #e57373; color: #fff; } #game-message { margin: 20px auto; font-size: 24px; color: #d84315; min-height: 32px; } button, select { font-size: 16px; padding: 4px 10px; }
const DIFFICULTY = { easy: { rows: 9, cols: 9, mines: 10 }, medium: { rows: 16, cols: 16, mines: 40 }, hard: { rows: 16, cols: 30, mines: 99 } }; let board = []; let rows, cols, minesCount; let minesLeft, openedCells, totalCells; let timer = 0, timerInterval = null, paused = false, gameOver = false, firstClick = true; const boardDiv = document.getElementById('game-board'); const timerSpan = document.getElementById('timer'); const minesLeftSpan = document.getElementById('mines-left'); const messageDiv = document.getElementById('game-message'); const restartBtn = document.getElementById('restart'); const pauseBtn = document.getElementById('pause'); const difficultySel = document.getElementById('difficulty'); function initGame() { const diff = DIFFICULTY[difficultySel.value]; rows = diff.rows; cols = diff.cols; minesCount = diff.mines; minesLeft = minesCount; openedCells = 0; totalCells = rows * cols; timer = 0; firstClick = true; gameOver = false; paused = false; clearInterval(timerInterval); timerSpan.textContent = "00:00"; pauseBtn.textContent = "暂停"; messageDiv.textContent = ""; minesLeftSpan.textContent = `雷数: ${minesLeft}`; createBoard(); renderBoard(); } function createBoard() { board = []; for (let r = 0; r < rows; r++) { let row = []; for (let c = 0; c < cols; c++) { row.push({ r, c, mine: false, open: false, flag: false, count: 0 }); } board.push(row); } } function placeMines(excludeR, excludeC) { let placed = 0; while (placed < minesCount) { let r = Math.floor(Math.random() * rows); let c = Math.floor(Math.random() * cols); // 不在第一次点击的格子及其周围 if (Math.abs(r - excludeR) <= 1 && Math.abs(c - excludeC) <= 1) continue; if (!board[r][c].mine) { board[r][c].mine = true; placed++; } } // 计算周围雷数 for (let r = 0; r < rows; r++) { for (let c = 0; c < cols; c++) { board[r][c].count = countMines(r, c); } } } function countMines(r, c) { let cnt = 0; for (let dr = -1; dr <= 1; dr++) { for (let dc = -1; dc <= 1; dc++) { if (dr === 0 && dc === 0) continue; let nr = r + dr, nc = c + dc; if (nr >= 0 && nr < rows && nc >= 0 && nc < cols) { if (board[nr][nc].mine) cnt++; } } } return cnt; } function renderBoard() { boardDiv.innerHTML = ''; for (let r = 0; r < rows; r++) { let rowDiv = document.createElement('div'); rowDiv.className = 'row'; for (let c = 0; c < cols; c++) { let cell = board[r][c]; let cellDiv = document.createElement('div'); cellDiv.className = 'cell'; cellDiv.dataset.r = r; cellDiv.dataset.c = c; if (cell.open) { cellDiv.classList.add('open'); if (cell.mine) { cellDiv.classList.add('mine'); cellDiv.textContent = '💣'; } else if (cell.count > 0) { cellDiv.textContent = cell.count; cellDiv.style.color = getNumberColor(cell.count); } } else if (cell.flag) { cellDiv.classList.add('flag'); cellDiv.textContent = '🚩'; } rowDiv.appendChild(cellDiv); } boardDiv.appendChild(rowDiv); } } function getNumberColor(n) { const colors = ['#1976d2', '#388e3c', '#d32f2f', '#7b1fa2', '#fbc02d', '#0097a7', '#e65100', '#616161']; return colors[n - 1] || '#333'; } function openCell(r, c) { let cell = board[r][c]; if (cell.open || cell.flag) return; cell.open = true; openedCells++; if (cell.mine) { cell.open = true; gameOver = true; revealMines(); renderBoard(); messageDiv.textContent = "游戏失败!"; clearInterval(timerInterval); return; } if (cell.count === 0) { for (let dr = -1; dr <= 1; dr++) { for (let dc = -1; dc <= 1; dc++) { let nr = r + dr, nc = c + dc; if (nr >= 0 && nr < rows && nc >= 0 && nc < cols) { if (!board[nr][nc].open) openCell(nr, nc); } } } } if (openedCells === totalCells - minesCount) { gameOver = true; revealMines(true); renderBoard(); messageDiv.textContent = "恭喜你,胜利!"; clearInterval(timerInterval); } } function revealMines(win = false) { for (let r = 0; r < rows; r++) { for (let c = 0; c < cols; c++) { let cell = board[r][c]; if (cell.mine) { cell.open = true; } if (win && cell.mine && !cell.flag) { cell.flag = true; } } } } function toggleFlag(r, c) { let cell = board[r][c]; if (cell.open) return; cell.flag = !cell.flag; minesLeft += cell.flag ? -1 : 1; minesLeftSpan.textContent = `雷数: ${minesLeft}`; } function handleCellClick(e) { if (gameOver || paused) return; const target = e.target; if (!target.classList.contains('cell')) return; const r = +target.dataset.r, c = +target.dataset.c; if (firstClick) { placeMines(r, c); startTimer(); firstClick = false; } openCell(r, c); renderBoard(); } function handleCellRightClick(e) { e.preventDefault(); if (gameOver || paused) return; const target = e.target; if (!target.classList.contains('cell')) return; const r = +target.dataset.r, c = +target.dataset.c; toggleFlag(r, c); renderBoard(); } function startTimer() { clearInterval(timerInterval); timerInterval = setInterval(() => { if (!paused) { timer++; timerSpan.textContent = formatTime(timer); } }, 1000); } function formatTime(t) { let m = Math.floor(t / 60).toString().padStart(2, '0'); let s = (t % 60).toString().padStart(2, '0'); return `${m}:${s}`; } function pauseGame() { if (gameOver || firstClick) return; paused = !paused; pauseBtn.textContent = paused ? "继续" : "暂停"; messageDiv.textContent = paused ? "已暂停" : ""; } restartBtn.onclick = initGame; pauseBtn.onclick = pauseGame; difficultySel.onchange = initGame; boardDiv.addEventListener('click', handleCellClick); boardDiv.addEventListener('contextmenu', handleCellRightClick); initGame();