HTML扫雷

代码解析

<!DOCTYPE html>
<html lang="zh">
  • 声明文档类型为 HTML5,并设置页面语言为中文
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>扫雷游戏</title>
  • <meta charset="UTF-8">: 设置字符编码为 UTF-8,确保页面正确显示各种语言字符
  • <meta name="viewport" content="width=device-width, initial-scale=1.0">: 控制页面的视口宽度,确保在移动设备上呈现良好
  • <title>扫雷游戏</title>: 设置浏览器标签上的页面标题为“扫雷游戏”
        <style>
            body {
                background-color: #222;
                color: #fff;
                margin: 0;
                font-family: Arial, sans-serif;
                display: flex;
                justify-content: center;
                align-items: center;
                min-height: 100vh; /* 确保页面高度 */
                overflow: auto; /* 允许滚动 */
            }
    
  • background-color: #222;: 设置页面背景色为深灰色
  • color: #fff;: 设置文本颜色为白色
  • margin: 0;: 移除页面默认的外边距
  • font-family: Arial, sans-serif;: 设置字体为 Arial,后备字体为 sans-serif
  • display: flex;: 使用 Flexbox 布局模型
  • justify-content: center;: 水平居中内容
  • align-items: center;: 垂直居中内容
  • min-height: 100vh;: 确保页面至少占满视口高度
  • overflow: auto;: 允许页面内容溢出时出现滚动条
            #minesweeper {
                display: flex;
                flex-direction: column;
                align-items: center;
                padding: 20px;
                overflow: auto; /* 确保内容溢出时可以滚动 */
                margin: 20px 0; /* 添加间距以允许滚动 */
            }
    
  • display: flex;: 使用 Flexbox 布局模型
  • flex-direction: column;: 垂直排列子元素
  • align-items: center;: 水平居中子元素
  • padding: 20px;: 添加内边距
  • overflow: auto;: 内容溢出时出现滚动条
  • margin: 20px 0;: 添加上下外边距,允许滚动
            #header {
                text-align: center;
                margin-bottom: 10px;
            }
    

  • text-align: center;: 水平居中对齐文本
  • margin-bottom: 10px;: 设置底部外边距为 10 像素
            #difficulty {
                display: flex;
                justify-content: center;
                margin-bottom: 10px;
            }
    
  • display: flex;: 使用 Flexbox 布局模型
  • justify-content: center;: 水平居中对齐子元素
  • margin-bottom: 10px;: 设置底部外边距为 10 像素
            .button {
                background-color: #556;
                border: 1px solid #444;
                padding: 10px;
                margin: 0 5px;
                cursor: pointer;
            }
            .button:hover {
                background-color: #667;
            }
    

  • .button: 设置按钮样式,背景色为 #556,边框为 1 像素实线,内边距 10 像素,左右外边距 5 像素,光标为手形
  • .button:hover: 鼠标悬停时,背景色变为 #667
            #status {
                display: flex;
                justify-content: space-between;
                width: 200px;
                margin-bottom: 10px;
            }
    
  • display: flex;: 使用 Flexbox 布局模型
  • justify-content: space-between;: 在子元素之间分配空间
  • width: 200px;: 设置宽度为 200 像素
  • margin-bottom: 10px;: 设置底部外边距为 10 像素
            #board {
                display: grid;
                gap: 1px; /* 设置格子之间的较小间隙 */
                background-color: #222; /* 间隙的背景颜色 */
                padding: 1px; /* 确保最外层边框一致 */
                box-sizing: border-box; /* 将填充和边框包含在元素的总宽度和高度内 */
            }
    
  • display: grid;: 使用网格布局模型
  • gap: 1px;: 设置网格间隙为 1 像素
  • background-color: #222;: 设置背景色为深灰色
  • padding: 1px;: 添加内边距 1 像素
  • box-sizing: border-box;: 包含填充和边框在元素的总宽度和高度内
            .cell {
                width: 20px;
                height: 20px;
                background-color: #334;
                display: flex;
                justify-content: center;
                align-items: center;
                cursor: pointer;
                box-sizing: border-box; /* 将填充和边框包含在元素的总宽度和高度内 */
            }
            .cell.open {
                background-color: #ccc;
            }
            .cell.flag {
                background-color: #add8e6; /* 标记单元格的浅蓝色 */
            }
            .cell.mine {
                background-color: #f00;
                color: #fff; /* 白色星号 */
            }
            .face-button {
                background-color: transparent;
                border: none;
                font-size: 24px;
                cursor: pointer;
            }
        </style>
    </head>
    <body>
        <div id="minesweeper">
            <div id="header">
                <h1>扫雷</h1>
            </div>
            <div id="difficulty">
                <div class="button" data-difficulty="easy">简单</div>
                <div class="button" data-difficulty="medium">中等</div>
                <div class="button" data-difficulty="hard">困难</div>
            </div>
            <div id="status">
                <div id="time">000</div>
                <button id="face" class="face-button">😊</button>
                <div id="mines-count">010</div>
            </div>
            <div id="board">
                <!-- 单元格将由JavaScript添加 -->
            </div>
        </div>
    
  • .cell: 单元格的基本样式,包括宽高、背景色、居中对齐和手形光标
  • .cell.open: 单元格被打开时的背景色
  • .cell.flag: 单元格被标记为旗帜时的背景色
  • .cell.mine: 单元格为地雷时的背景色和文本颜色
  • .face-button: 面板按钮的样式,包括透明背景、无边框、字体大小和手形光标
        <script>
            const board = document.getElementById('board');
            const timeDisplay = document.getElementById('time');
            const minesCountDisplay = document.getElementById('mines-count');
            const faceDisplay = document.getElementById('face');
            let timer;
            let time = 0;
            let minesCount = 0;
            let gameOver = false;
            let gameStarted = false; // 添加标志以检查游戏是否已开始
            let currentDifficulty = 'easy'; // 默认难度
            let rows, cols, mines;
            let cells, minePositions;
    
  • 获取 HTML 元素的引用,如 boardtimeDisplayminesCountDisplayfaceDisplay
  • 定义变量来管理游戏状态,如计时器、时间、地雷计数、游戏结束标志、游戏开始标志、当前难度、行数、列数、地雷数、单元格和地雷位置
            function createBoard(rows, cols, mines) {
                board.innerHTML = '';
                board.style.gridTemplateColumns = `repeat(${cols}, 20px)`;
                board.style.gridTemplateRows = `repeat(${rows}, 20px)`;
                minesCount = mines;
                gameOver = false;
                gameStarted = false; // 重置游戏开始标志
                minesCountDisplay.textContent = mines.toString().padStart(3, '0');
                cells = [];
                minePositions = new Set();
    
  • 清空棋盘内容
  • 设置棋盘的网格布局
  • 初始化地雷数、游戏结束标志、游戏开始标志
  • 更新地雷计数显示
  • 初始化单元格和地雷位置
                while (minePositions.size < mines) {
                    minePositions.add(Math.floor(Math.random() * rows * cols));
                }
    
  • 随机生成地雷的位置,确保地雷数目正确

                for (let i = 0; i < rows * cols; i++) {
                    const cell = document.createElement('div');
                    cell.classList.add('cell');
                    board.appendChild(cell);
                    cells.push(cell);
    
  • 创建单元格并添加到棋盘中,同时将单元格添加到 cells 数组

                    cell.addEventListener('contextmenu', (e) => {
                        e.preventDefault();
                        if (!cell.classList.contains('open') && !gameOver) {
                            cell.classList.toggle('flag');
                            minesCount += cell.classList.contains('flag') ? -1 : 1;
                            minesCountDisplay.textContent = minesCount.toString().padStart(3, '0');
                        }
                    });
    
  • 右键单击单元格时,切换标记状态,并更新地雷计数

                    cell.addEventListener('click', () => {
                        if (!cell.classList.contains('open') && !cell.classList.contains('flag') && !gameOver) {
                            if (!gameStarted) {
                                startTimer();
                                gameStarted = true;
                            }
                            openCell(i);
                        }
                    });
                }
            }
    
  • 左键单击单元格时,开始计时并打开单元格

            function openCell(index) {
                const cell = cells[index];
                if (minePositions.has(index)) {
                    cell.classList.add('mine');
                    cell.textContent = '*';
                    gameOver = true;
                    faceDisplay.textContent = '😭';
                    clearInterval(timer);
                    showAllMines();
                } else {
                    cell.classList.add('open');
                    const minesAround = countMinesAround(index);
                    if (minesAround > 0) {
                        cell.textContent = minesAround;
                    } else {
                        openSurroundingCells(index);
                    }
                }
            }
    
  • 打开单元格。如果单元格是地雷,显示所有地雷并结束游戏;如果不是地雷,则显示周围地雷数目或打开周围单元格

            function showAllMines() {
                minePositions.forEach(index => {
                    const mineCell = cells[index];
                    if (!mineCell.classList.contains('mine')) {
                        mineCell.classList.add('mine');
                        mineCell.textContent = '*';
                    }
                });
            }
    
  • 显示所有地雷位置

            function countMinesAround(index) {
                const row = Math.floor(index / cols);
                const col = index % cols;
                let count = 0;
                for (let i = -1; i <= 1; i++) {
                    for (let j = -1; j <= 1; j++) {
                        if (i === 0 && j === 0) continue;
                        const newRow = row + i;
                        const newCol = col + j;
                        if (newRow >= 0 && newRow < rows && newCol >= 0 && newCol < cols) {
                            const newIndex = newRow * cols + newCol;
                            if (minePositions.has(newIndex)) {
                                count++;
                            }
                        }
                    }
                }
                return count;
            }
    
  • 计算给定单元格周围的地雷数目

            function openSurroundingCells(index) {
                const row = Math.floor(index / cols);
                const col = index % cols;
                for (let i = -1; i <= 1; i++) {
                    for (let j = -1; j <= 1; j++) {
                        if (i === 0 && j === 0) continue;
                        const newRow = row + i;
                        const newCol = col + j;
                        if (newRow >= 0 && newRow < rows && newCol >= 0 && newCol < cols) {
                            const newIndex = newRow * cols + newCol;
                            const newCell = cells[newIndex];
                            if (!newCell.classList.contains('open') && !newCell.classList.contains('flag')) {
                                openCell(newIndex);
                            }
                        }
                    }
                }
            }
    
  • 打开指定单元格周围的所有单元格

            function startTimer() {
                timer = setInterval(() => {
                    time++;
                    timeDisplay.textContent = time.toString().padStart(3, '0');
                }, 1000);
            }
    
  • 启动计时器,每秒更新一次时间显示

            function startGame(difficulty) {
                clearInterval(timer);
                time = 0;
                timeDisplay.textContent = '000';
                faceDisplay.textContent = '😊';
                currentDifficulty = difficulty;
    
                switch (difficulty) {
                    case 'easy':
                        rows = cols = 8;
                        mines = 10;
                        break;
                    case 'medium':
                        rows = cols = 16;
                        mines = 40;
                        break;
                    case 'hard':
                        rows = cols = 24;
                        mines = 99;
                        break;
                }
                createBoard(rows, cols, mines);
            }
    
  • 启动游戏,根据难度设置行数、列数和地雷数,清空计时器并重置时间和面板按钮

            document.querySelectorAll('.button').forEach(button => {
                button.addEventListener('click', () => {
                    const difficulty = button.dataset.difficulty;
                    startGame(difficulty);
                });
            });
    
            faceDisplay.addEventListener('click', () => {
                startGame(currentDifficulty);
            });
    
            // 使用默认难度开始游戏
            startGame(currentDifficulty);
        </script>
    </body>
    </html>
    
  • 为每个难度按钮添加点击事件处理程序,启动对应难度的游戏
  • 为面板按钮添加点击事件处理程序,重启游戏
  • 使用默认难度启动游戏

全部代码

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>扫雷游戏</title>
    <style>
        body {
            background-color: #222;
            color: #fff;
            margin: 0;
            font-family: Arial, sans-serif;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh; /* 确保页面高度 */
            overflow: auto; /* 允许滚动 */
        }
        #minesweeper {
            display: flex;
            flex-direction: column;
            align-items: center;
            padding: 20px;
            overflow: auto; /* 确保内容溢出时可以滚动 */
            margin: 20px 0; /* 添加间距以允许滚动 */
        }
        #header {
            text-align: center;
            margin-bottom: 10px;
        }
        #difficulty {
            display: flex;
            justify-content: center;
            margin-bottom: 10px;
        }
        .button {
            background-color: #556;
            border: 1px solid #444;
            padding: 10px;
            margin: 0 5px;
            cursor: pointer;
        }
        .button:hover {
            background-color: #667;
        }
        #status {
            display: flex;
            justify-content: space-between;
            width: 200px;
            margin-bottom: 10px;
        }
        #board {
            display: grid;
            gap: 1px; /* 设置格子之间的较小间隙 */
            background-color: #222; /* 间隙的背景颜色 */
            padding: 1px; /* 确保最外层边框一致 */
            box-sizing: border-box; /* 将填充和边框包含在元素的总宽度和高度内 */
        }
        .cell {
            width: 20px;
            height: 20px;
            background-color: #334;
            display: flex;
            justify-content: center;
            align-items: center;
            cursor: pointer;
            box-sizing: border-box; /* 将填充和边框包含在元素的总宽度和高度内 */
        }
        .cell.open {
            background-color: #ccc;
        }
        .cell.flag {
            background-color: #add8e6; /* 标记单元格的浅蓝色 */
        }
        .cell.mine {
            background-color: #f00;
            color: #fff; /* 白色星号 */
        }
        .face-button {
            background-color: transparent;
            border: none;
            font-size: 24px;
            cursor: pointer;
        }
    </style>
</head>
<body>
    <div id="minesweeper">
        <div id="header">
            <h1>扫雷</h1>
        </div>
        <div id="difficulty">
            <div class="button" data-difficulty="easy">简单</div>
            <div class="button" data-difficulty="medium">中等</div>
            <div class="button" data-difficulty="hard">困难</div>
        </div>
        <div id="status">
            <div id="time">000</div>
            <button id="face" class="face-button">😊</button>
            <div id="mines-count">010</div>
        </div>
        <div id="board">
            <!-- 单元格将由JavaScript添加 -->
        </div>
    </div>

    <script>
        const board = document.getElementById('board');
        const timeDisplay = document.getElementById('time');
        const minesCountDisplay = document.getElementById('mines-count');
        const faceDisplay = document.getElementById('face');
        let timer;
        let time = 0;
        let minesCount = 0;
        let gameOver = false;
        let gameStarted = false; // 添加标志以检查游戏是否已开始
        let currentDifficulty = 'easy'; // 默认难度
        let rows, cols, mines;
        let cells, minePositions;

        function createBoard(rows, cols, mines) {
            board.innerHTML = '';
            board.style.gridTemplateColumns = `repeat(${cols}, 20px)`;
            board.style.gridTemplateRows = `repeat(${rows}, 20px)`;
            minesCount = mines;
            gameOver = false;
            gameStarted = false; // 重置游戏开始标志
            minesCountDisplay.textContent = mines.toString().padStart(3, '0');
            cells = [];
            minePositions = new Set();

            while (minePositions.size < mines) {
                minePositions.add(Math.floor(Math.random() * rows * cols));
            }

            for (let i = 0; i < rows * cols; i++) {
                const cell = document.createElement('div');
                cell.classList.add('cell');
                board.appendChild(cell);
                cells.push(cell);

                cell.addEventListener('contextmenu', (e) => {
                    e.preventDefault();
                    if (!cell.classList.contains('open') && !gameOver) {
                        cell.classList.toggle('flag');
                        minesCount += cell.classList.contains('flag') ? -1 : 1;
                        minesCountDisplay.textContent = minesCount.toString().padStart(3, '0');
                    }
                });

                cell.addEventListener('click', () => {
                    if (!cell.classList.contains('open') && !cell.classList.contains('flag') && !gameOver) {
                        if (!gameStarted) {
                            startTimer();
                            gameStarted = true;
                        }
                        openCell(i);
                    }
                });
            }
        }

        function openCell(index) {
            const cell = cells[index];
            if (minePositions.has(index)) {
                cell.classList.add('mine');
                cell.textContent = '*';
                gameOver = true;
                faceDisplay.textContent = '😭';
                clearInterval(timer);
                showAllMines();
            } else {
                cell.classList.add('open');
                const minesAround = countMinesAround(index);
                if (minesAround > 0) {
                    cell.textContent = minesAround;
                } else {
                    openSurroundingCells(index);
                }
            }
        }

        function showAllMines() {
            minePositions.forEach(index => {
                const mineCell = cells[index];
                if (!mineCell.classList.contains('mine')) {
                    mineCell.classList.add('mine');
                    mineCell.textContent = '*';
                }
            });
        }

        function countMinesAround(index) {
            const row = Math.floor(index / cols);
            const col = index % cols;
            let count = 0;
            for (let i = -1; i <= 1; i++) {
                for (let j = -1; j <= 1; j++) {
                    if (i === 0 && j === 0) continue;
                    const newRow = row + i;
                    const newCol = col + j;
                    if (newRow >= 0 && newRow < rows && newCol >= 0 && newCol < cols) {
                        const newIndex = newRow * cols + newCol;
                        if (minePositions.has(newIndex)) {
                            count++;
                        }
                    }
                }
            }
            return count;
        }

        function openSurroundingCells(index) {
            const row = Math.floor(index / cols);
            const col = index % cols;
            for (let i = -1; i <= 1; i++) {
                for (let j = -1; j <= 1; j++) {
                    if (i === 0 && j === 0) continue;
                    const newRow = row + i;
                    const newCol = col + j;
                    if (newRow >= 0 && newRow < rows && newCol >= 0 && newCol < cols) {
                        const newIndex = newRow * cols + newCol;
                        const newCell = cells[newIndex];
                        if (!newCell.classList.contains('open') && !newCell.classList.contains('flag')) {
                            openCell(newIndex);
                        }
                    }
                }
            }
        }

        function startTimer() {
            timer = setInterval(() => {
                time++;
                timeDisplay.textContent = time.toString().padStart(3, '0');
            }, 1000);
        }

        function startGame(difficulty) {
            clearInterval(timer);
            time = 0;
            timeDisplay.textContent = '000';
            faceDisplay.textContent = '😊';
            currentDifficulty = difficulty;

            switch (difficulty) {
                case 'easy':
                    rows = cols = 8;
                    mines = 10;
                    break;
                case 'medium':
                    rows = cols = 16;
                    mines = 40;
                    break;
                case 'hard':
                    rows = cols = 24;
                    mines = 99;
                    break;
            }
            createBoard(rows, cols, mines);
        }

        document.querySelectorAll('.button').forEach(button => {
            button.addEventListener('click', () => {
                const difficulty = button.dataset.difficulty;
                startGame(difficulty);
            });
        });

        faceDisplay.addEventListener('click', () => {
            startGame(currentDifficulty);
        });

        // 使用默认难度开始游戏
        startGame(currentDifficulty);
    </script>
</body>
</html>

  • 9
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值