《扫雷:从入门到入土,我写了个HTML版,快来挑战你的运气!》

124 阅读5分钟

大家好,我是那个曾经因为玩扫雷差点把鼠标点坏的程序员。今天,我要给大家带来一个好消息——**我终于把扫雷写进了HTML!**🎉

没错,你没有看错,就是那个让你又爱又恨的扫雷游戏。现在,你可以在浏览器里尽情享受那种“点到雷就GG”的刺激感了!💥

为什么我要写这个?

其实原因很简单——我太菜了。每次玩扫雷,我都觉得自己是个“雷神”,点哪儿炸哪儿。于是,我决定自己写一个,顺便看看能不能通过这个游戏找到和我一样“运气爆棚”的小伙伴。

这个HTML扫雷有什么特别?

  1. 纯HTML+CSS+JS:没有复杂的框架,没有花里胡哨的插件,只有最原始的代码。简单到让你怀疑人生!

  2. 自适应布局:不管你是用电脑、手机还是平板,都能流畅玩耍。当然,如果你用智能手表玩,那我敬你是条汉子!

  3. 自定义难度:你可以选择简单、中等、困难三种模式。当然,如果你觉得自己运气特别好,可以试试“地狱模式”——点到雷直接关机(开玩笑的,别当真)。

  4. 彩蛋:如果你连续点中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 #扫雷 #程序员 #小游戏 #运气挑战 #代码自己找