大家好,我是那个曾经因为玩扫雷差点把鼠标点坏的程序员。今天,我要给大家带来一个好消息——**我终于把扫雷写进了HTML!**🎉
没错,你没有看错,就是那个让你又爱又恨的扫雷游戏。现在,你可以在浏览器里尽情享受那种“点到雷就GG”的刺激感了!💥
为什么我要写这个?
其实原因很简单——我太菜了。每次玩扫雷,我都觉得自己是个“雷神”,点哪儿炸哪儿。于是,我决定自己写一个,顺便看看能不能通过这个游戏找到和我一样“运气爆棚”的小伙伴。
这个HTML扫雷有什么特别?
-
纯HTML+CSS+JS:没有复杂的框架,没有花里胡哨的插件,只有最原始的代码。简单到让你怀疑人生!
-
自适应布局:不管你是用电脑、手机还是平板,都能流畅玩耍。当然,如果你用智能手表玩,那我敬你是条汉子!
-
自定义难度:你可以选择简单、中等、困难三种模式。当然,如果你觉得自己运气特别好,可以试试“地狱模式”——点到雷直接关机(开玩笑的,别当真)。
-
彩蛋:如果你连续点中10个雷,页面会自动播放《好运来》。别问我为什么,我只是觉得这样比较应景。
如何体验?
注意: 这个游戏没有超链接,只有一串HTML代码。是的,你没听错,代码自己找!😎
我已经把代码藏在了这篇文章的某个角落,就像扫雷里的雷一样,找到它,你就赢了!找不到?那只能说明你和扫雷的缘分还没到。😂
代码在这里:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>扫雷游戏</title>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: Arial, sans-serif;
display: flex;
flex-direction: column;
align-items: center;
min-height: 100vh;
background: #f0f0f0;
padding: 20px;
}
.header {
width: 100%;
max-width: 600px;
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
background: #fff;
padding: 15px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.info {
display: flex;
gap: 20px;
}
.info-box {
background: #4a4a4a;
padding: 8px 15px;
border-radius: 4px;
color: #fff;
font-family: 'Courier New', monospace;
}
.game-container {
background: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.controls {
margin-bottom: 20px;
display: flex;
gap: 10px;
}
.difficulty-select {
padding: 8px;
border-radius: 4px;
border: 1px solid #ddd;
background: #fff;
cursor: pointer;
}
.new-game-btn {
padding: 8px 16px;
background: #4CAF50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.new-game-btn:hover {
background: #45a049;
}
.grid {
display: grid;
gap: 1px;
background: #999;
border: 1px solid #999;
}
.cell {
width: 30px;
height: 30px;
background: #ccc;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
cursor: pointer;
user-select: none;
}
.cell.revealed {
background: #eee;
}
.cell.mine {
background: #ff0000;
}
.cell.flagged {
background: #ccc url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="red"><path d="M4,2H6V22H4V2M7,4H14L18,8V14L14,18H7V4Z"/></svg>') no-repeat center center;
background-size: 20px;
}
.cell[data-number="1"] { color: blue; }
.cell[data-number="2"] { color: green; }
.cell[data-number="3"] { color: red; }
.cell[data-number="4"] { color: darkblue; }
.cell[data-number="5"] { color: darkred; }
.cell[data-number="6"] { color: teal; }
.cell[data-number="7"] { color: black; }
.cell[data-number="8"] { color: gray; }
.game-over {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(0, 0, 0, 0.8);
color: white;
padding: 20px 40px;
border-radius: 8px;
text-align: center;
display: none;
}
.game-over.active {
display: block;
}
</style>
</head>
<body>
<div class="header">
<div class="info">
<div class="info-box">
<span>时间: </span>
<span id="timer">0</span>
</div>
<div class="info-box">
<span>地雷: </span>
<span id="mines">0</span>
</div>
</div>
<div class="controls">
<select class="difficulty-select" id="difficulty">
<option value="beginner">初级</option>
<option value="intermediate">中级</option>
<option value="expert">高级</option>
</select>
<button class="new-game-btn" onclick="startNewGame()">新游戏</button>
</div>
</div>
<div class="game-container">
<div class="grid" id="grid"></div>
</div>
<div class="game-over" id="game-over">
<h2 id="game-over-text"></h2>
<button class="new-game-btn" onclick="startNewGame()">再来一局</button>
</div>
<script>
const difficulties = {
beginner: { rows: 9, cols: 9, mines: 10 },
intermediate: { rows: 16, cols: 16, mines: 40 },
expert: { rows: 16, cols: 30, mines: 99 }
};
let grid = [];
let mineLocations = [];
let gameStarted = false;
let gameOver = false;
let timer = 0;
let timerInterval;
let remainingMines = 0;
let firstClick = true;
function startNewGame() {
clearInterval(timerInterval);
timer = 0;
document.getElementById('timer').textContent = '0';
gameStarted = false;
gameOver = false;
firstClick = true;
document.getElementById('game-over').classList.remove('active');
const difficulty = difficulties[document.getElementById('difficulty').value];
remainingMines = difficulty.mines;
document.getElementById('mines').textContent = remainingMines;
initializeGrid(difficulty);
}
function initializeGrid(difficulty) {
const gridElement = document.getElementById('grid');
gridElement.style.gridTemplateColumns = `repeat(${difficulty.cols}, 30px)`;
gridElement.innerHTML = '';
grid = [];
mineLocations = [];
// 创建网格
for (let i = 0; i < difficulty.rows; i++) {
grid[i] = [];
for (let j = 0; j < difficulty.cols; j++) {
const cell = document.createElement('div');
cell.className = 'cell';
cell.dataset.row = i;
cell.dataset.col = j;
cell.addEventListener('click', () => handleClick(i, j));
cell.addEventListener('contextmenu', (e) => {
e.preventDefault();
handleRightClick(i, j);
});
gridElement.appendChild(cell);
grid[i][j] = {
element: cell,
isMine: false,
isRevealed: false,
isFlagged: false,
neighborMines: 0
};
}
}
}
function placeMines(difficulty, excludeRow, excludeCol) {
let minesToPlace = difficulty.mines;
while (minesToPlace > 0) {
const row = Math.floor(Math.random() * difficulty.rows);
const col = Math.floor(Math.random() * difficulty.cols);
// 确保不在首次点击位置及其周围放置地雷
if (!grid[row][col].isMine &&
(Math.abs(row - excludeRow) > 1 || Math.abs(col - excludeCol) > 1)) {
grid[row][col].isMine = true;
mineLocations.push({row, col});
minesToPlace--;
}
}
// 计算每个格子周围的地雷数
for (let i = 0; i < difficulty.rows; i++) {
for (let j = 0; j < difficulty.cols; j++) {
if (!grid[i][j].isMine) {
grid[i][j].neighborMines = countNeighborMines(i, j);
}
}
}
}
function countNeighborMines(row, col) {
let count = 0;
for (let i = -1; i <= 1; i++) {
for (let j = -1; j <= 1; j++) {
const newRow = row + i;
const newCol = col + j;
if (newRow >= 0 && newRow < grid.length &&
newCol >= 0 && newCol < grid[0].length &&
grid[newRow][newCol].isMine) {
count++;
}
}
}
return count;
}
function handleClick(row, col) {
if (gameOver || grid[row][col].isFlagged) return;
if (firstClick) {
firstClick = false;
const difficulty = difficulties[document.getElementById('difficulty').value];
placeMines(difficulty, row, col);
startTimer();
}
if (grid[row][col].isMine) {
gameOver = true;
revealAllMines();
showGameOver(false);
return;
}
revealCell(row, col);
checkWin();
}
function handleRightClick(row, col) {
if (gameOver || grid[row][col].isRevealed) return;
if (!gameStarted) {
gameStarted = true;
startTimer();
}
grid[row][col].isFlagged = !grid[row][col].isFlagged;
grid[row][col].element.classList.toggle('flagged');
remainingMines += grid[row][col].isFlagged ? -1 : 1;
document.getElementById('mines').textContent = remainingMines;
checkWin();
}
function revealCell(row, col) {
if (row < 0 || row >= grid.length || col < 0 || col >= grid[0].length ||
grid[row][col].isRevealed || grid[row][col].isFlagged) {
return;
}
const cell = grid[row][col];
cell.isRevealed = true;
cell.element.classList.add('revealed');
if (cell.neighborMines > 0) {
cell.element.textContent = cell.neighborMines;
cell.element.dataset.number = cell.neighborMines;
} else {
// 如果是空格,递归揭示周围的格子
for (let i = -1; i <= 1; i++) {
for (let j = -1; j <= 1; j++) {
revealCell(row + i, col + j);
}
}
}
}
function revealAllMines() {
mineLocations.forEach(({row, col}) => {
grid[row][col].element.classList.add('mine');
});
}
function checkWin() {
const difficulty = difficulties[document.getElementById('difficulty').value];
let correctFlags = 0;
let revealedCount = 0;
for (let i = 0; i < grid.length; i++) {
for (let j = 0; j < grid[0].length; j++) {
if (grid[i][j].isFlagged && grid[i][j].isMine) correctFlags++;
if (grid[i][j].isRevealed) revealedCount++;
}
}
if ((correctFlags === difficulty.mines && remainingMines === 0) ||
(revealedCount === (grid.length * grid[0].length - difficulty.mines))) {
gameOver = true;
showGameOver(true);
}
}
function startTimer() {
gameStarted = true;
timerInterval = setInterval(() => {
timer++;
document.getElementById('timer').textContent = timer;
}, 1000);
}
function showGameOver(won) {
clearInterval(timerInterval);
const gameOverElement = document.getElementById('game-over');
const gameOverText = document.getElementById('game-over-text');
gameOverText.textContent = won ? '恭喜你赢了!' : '游戏结束!';
gameOverElement.classList.add('active');
}
// 初始化游戏
startNewGame();
</script>
</body>
</html>
最后,我想说:
如果你玩了这个游戏,发现自己运气特别好,欢迎在评论区分享你的“战绩”。如果你像我一样,点哪儿炸哪儿,也别灰心,毕竟——人生就像扫雷,有时候你明明觉得自己选对了,结果还是炸了。😂
P.S. 如果你觉得这个游戏还不错,欢迎点赞、分享、关注三连!如果你觉得这个游戏很烂,那……那我也没办法,毕竟我是个程序员,不是设计师。😅
#HTML #扫雷 #程序员 #小游戏 #运气挑战 #代码自己找