首页 前端知识 js jquery实现扫雷小游戏

js jquery实现扫雷小游戏

2025-03-05 17:03:07 前端知识 前端哥 877 529 我要收藏

javascript,jquery实现的扫雷小游戏,可以下载到本地直接双击index.html即可运行

游戏展示

开始

在这里插入图片描述

失败

在这里插入图片描述

代码展示

在这里插入图片描述

html文件

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>扫雷游戏</title>
    <link rel="stylesheet" href="style.css">
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>扫雷游戏</h1>
            <div class="controls">
                <button id="newGame">新游戏</button>
                <span id="mineCount">剩余地雷: 10</span>
                <span id="timer">时间: 0</span>
            </div>
            <div class="instructions">
                <h3>游戏说明:</h3>
                <p>1. 左键点击格子揭开方块</p>
                <p>2. 右键点击标记地雷位置(最多标记10个)</p>
                <p>3. 找出所有非地雷位置即可获胜</p>
                <p>4. 点击地雷则游戏结束</p>
            </div>
        </div>
        <div id="gameBoard"></div>
    </div>
    <div id="gameOverModal" class="modal">
        <div class="modal-content">
            <div class="modal-header">
                <img src="data:image/svg+xml;base64,PHN2ZyB0PSIxNzA5MTk4NTI3NDU3IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjI3ODEiIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCI+PHBhdGggZD0iTTUxMiA2NEMyNjQuNiA2NCA2NCAyNjQuNiA2NCA1MTJzMjAwLjYgNDQ4IDQ0OCA0NDggNDQ4LTIwMC42IDQ0OC00NDhTNzU5LjQgNjQgNTEyIDY0eiBtMCA4MjBjLTIwNS40IDAtMzcyLTE2Ni42LTM3Mi0zNzJzMTY2LjYtMzcyIDM3Mi0zNzIgMzcyIDE2Ni42IDM3MiAzNzJ6IiBmaWxsPSIjZTc0YzNjIiBwLWlkPSIyNzgyIj48L3BhdGg+PHBhdGggZD0iTTUxMiAxNDBjLTIwNS40IDAtMzcyIDE2Ni42LTM3MiAzNzJzMTY2LjYgMzcyIDM3MiAzNzIgMzcyLTE2Ni42IDM3Mi0zNzItMTY2LjYtMzcyLTM3Mi0zNzJ6TTQ2NCA2ODhjLTQuNCAwLTguOC0yLTExLjYtNS45TDM0OCA1NjQuOGMtNS00LjEtNS45LTExLjQtMi4xLTE2LjQgMy44LTUgMTAuOS02IDE1LjktMi4ybDk2LjEgNzguNSAyMDIuMy0yNzEuOGM0LjEtNS41IDExLjktNi42IDE3LjQtMi41IDUuNSA0LjEgNi42IDExLjkgMi41IDE3LjRMNDc3LjggNjgxLjRjLTIuNiAzLjUtNi43IDUuNi0xMS4xIDYuMi0wLjkgMC4zLTEuOCAwLjQtMi43IDAuNHoiIGZpbGw9IiNlNzRjM2MiIHAtaWQ9IjI3ODMiPjwvcGF0aD48L3N2Zz4=" class="modal-icon" />
                <h2 id="modalTitle">游戏结束</h2>
            </div>
            <div class="modal-body">
                <p id="modalMessage">很遗憾,你踩到了地雷!</p>
                <div class="game-stats">
                    <div class="stat-item">
                        <span class="stat-label">用时</span>
                        <span id="finalTime" class="stat-value">0秒</span>
                    </div>
                    <div class="stat-item">
                        <span class="stat-label">已排除</span>
                        <span id="clearedCells" class="stat-value">0格</span>
                    </div>
                </div>
            </div>
            <div class="modal-footer">
                <button id="newGameBtn" class="btn primary">重新开始</button>
                <button id="continueBtn" class="btn secondary">继续查看</button>
            </div>
        </div>
    </div>
    <script src="game.js"></script>
</body>
</html> 

css文件

body {
    background: #f0f2f5;
    font-family: 'Microsoft YaHei', sans-serif;
}

.container {
    max-width: 600px;
    margin: 20px auto;
    padding: 20px;
    background: white;
    border-radius: 10px;
    box-shadow: 0 0 20px rgba(0,0,0,0.1);
}

.header {
    text-align: center;
    margin-bottom: 20px;
}

.header h1 {
    color: #2c3e50;
    margin-bottom: 20px;
}

.controls {
    margin: 20px 0;
    display: flex;
    justify-content: center;
    gap: 20px;
}

#newGame {
    padding: 8px 20px;
    background: #3498db;
    color: white;
    border: none;
    border-radius: 5px;
    cursor: pointer;
    transition: background 0.3s;
}

#newGame:hover {
    background: #2980b9;
}

#mineCount, #timer {
    background: linear-gradient(145deg, #2c3e50, #34495e);
    color: white;
    padding: 8px 15px;
    border-radius: 5px;
    min-width: 120px;
    display: inline-block;
    font-weight: bold;
    box-shadow: 0 2px 5px rgba(0,0,0,0.1);
    transition: all 0.3s ease;
}

#gameBoard {
    display: grid;
    grid-template-columns: repeat(9, 40px);
    gap: 2px;
    background: #bdc3c7;
    border: 2px solid #95a5a6;
    border-radius: 5px;
    width: fit-content;
    margin: 0 auto;
    padding: 2px;
}

.cell {
    width: 40px;
    height: 40px;
    background: #ecf0f1;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: default;
    font-weight: bold;
    border-radius: 3px;
    transition: all 0.2s;
    font-size: 18px;
    user-select: none;
}

.cell.unrevealed {
    background: linear-gradient(145deg, #e6e6e6, #ffffff);
    cursor: pointer;
    box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}

.cell.unrevealed:hover {
    background: #d5d8dc;
    transform: scale(0.95);
}

.cell.revealed {
    background: #ecf0f1;
    box-shadow: inset 0 1px 3px rgba(0,0,0,0.1);
}

.cell.mine {
    background: #e74c3c;
    animation: explosion 0.5s ease-out;
}

.cell.flagged {
    background: #f1c40f;
}

.instructions {
    text-align: left;
    margin: 20px 0;
    padding: 15px;
    background: #f8f9fa;
    border-radius: 8px;
    border-left: 4px solid #3498db;
}

.instructions h3 {
    color: #2c3e50;
    margin-top: 0;
}

.instructions p {
    color: #34495e;
    margin: 8px 0;
}

/* 数字颜色 */
.cell[data-number="1"] { color: #3498db; }
.cell[data-number="2"] { color: #27ae60; }
.cell[data-number="3"] { color: #e74c3c; }
.cell[data-number="4"] { color: #8e44ad; }
.cell[data-number="5"] { color: #c0392b; }
.cell[data-number="6"] { color: #16a085; }
.cell[data-number="7"] { color: #2c3e50; }
.cell[data-number="8"] { color: #7f8c8d; }

/* 模态框样式 */
.modal {
    display: none;
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.5);
    z-index: 1000;
}

.modal-content {
    position: relative;
    background-color: #fff;
    margin: 15% auto;
    padding: 20px;
    width: 90%;
    max-width: 400px;
    border-radius: 12px;
    box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
}

.modal-header {
    text-align: center;
    margin-bottom: 20px;
}

.modal-icon {
    width: 48px;
    height: 48px;
    margin-bottom: 10px;
}

.modal-header h2 {
    margin: 0;
    color: #2c3e50;
    font-size: 24px;
}

.modal-body {
    text-align: center;
    margin-bottom: 20px;
}

.modal-body p {
    color: #34495e;
    font-size: 16px;
    margin-bottom: 20px;
}

.game-stats {
    display: flex;
    justify-content: center;
    gap: 30px;
    margin: 20px 0;
}

.stat-item {
    display: flex;
    flex-direction: column;
    align-items: center;
}

.stat-label {
    color: #7f8c8d;
    font-size: 14px;
    margin-bottom: 5px;
}

.stat-value {
    color: #2c3e50;
    font-size: 20px;
    font-weight: bold;
}

.modal-footer {
    display: flex;
    justify-content: center;
    gap: 15px;
    margin-top: 20px;
}

.btn {
    padding: 10px 20px;
    border: none;
    border-radius: 6px;
    font-size: 16px;
    cursor: pointer;
    transition: all 0.3s;
    margin: 0 10px;
}

.btn.primary {
    background-color: #3498db;
    color: white;
}

.btn.primary:hover {
    background-color: #2980b9;
    transform: translateY(-2px);
}

.btn.secondary {
    background-color: #ecf0f1;
    color: #34495e;
}

.btn.secondary:hover {
    background-color: #bdc3c7;
    transform: translateY(-2px);
}

/* 动画 */
@keyframes explosion {
    0% { transform: scale(1); opacity: 1; }
    20% { transform: scale(1.2); background: #e74c3c; }
    40% { transform: scale(0.9); background: #c0392b; }
    60% { transform: scale(1.1); background: #e74c3c; }
    80% { transform: scale(0.95); background: #c0392b; }
    100% { transform: scale(1); background: #e74c3c; }
}

@keyframes fadeIn {
    from { opacity: 0; }
    to { opacity: 1; }
}

@keyframes slideIn {
    from { transform: translateY(-20px); opacity: 0; }
    to { transform: translateY(0); opacity: 1; }
}

/* 提示消息样式 */
.tip-message {
    position: fixed;
    top: 20px;
    left: 50%;
    transform: translateX(-50%);
    background-color: rgba(44, 62, 80, 0.95);
    color: white;
    padding: 12px 24px;
    border-radius: 25px;
    font-size: 14px;
    z-index: 1000;
    opacity: 0;
    transition: opacity 0.3s ease;
    box-shadow: 0 3px 10px rgba(0,0,0,0.2);
}

.tip-message.animate {
    opacity: 1;
    animation: tipBounce 0.5s ease-out;
}

.tip-message.fade-out {
    opacity: 0;
}

@keyframes tipBounce {
    0% { transform: translate(-50%, -20px); }
    50% { transform: translate(-50%, 5px); }
    100% { transform: translate(-50%, 0); }
}

/* 胜利样式 */
.modal-content.victory {
    background: linear-gradient(135deg, #43cea2 0%, #185a9d 100%);
}

.modal-content.victory .modal-header h2,
.modal-content.victory .modal-body p,
.modal-content.victory .stat-label,
.modal-content.victory .stat-value {
    color: white;
} 

js文件

$(document).ready(function() {
    const BOARD_SIZE = 9;
    const MINE_COUNT = 10;
    let board = [];
    let revealed = [];
    let flagged = [];
    let gameOver = false;
    let timer = 0;
    let timerInterval;
    let firstClick = true;

    function bindEvents() {
        $('#gameBoard').off('click contextmenu');

        $('#gameBoard').on('click', '.cell', function(e) {
            if(gameOver) return;
            
            const x = parseInt($(this).attr('data-x'));
            const y = parseInt($(this).attr('data-y'));
            
            console.log(`Clicking cell at (${x}, ${y})`);
            
            if(isNaN(x) || isNaN(y)) {
                console.log('Invalid coordinates');
                return;
            }

            if(firstClick) {
                ensureSafeFirstClick(x, y);
                firstClick = false;
                startTimer();
            }

            revealCell(x, y);
        });

        $('#gameBoard').on('contextmenu', '.cell', function(e) {
            e.preventDefault();
            if(gameOver) return;
            
            const x = parseInt($(this).attr('data-x'));
            const y = parseInt($(this).attr('data-y'));
            
            console.log(`Right clicking cell at (${x}, ${y})`);
            
            if(isNaN(x) || isNaN(y)) {
                console.log('Invalid coordinates');
                return;
            }

            toggleFlag(x, y);
        });
    }

    function ensureSafeFirstClick(clickX, clickY) {
        if(board[clickX][clickY] === -1) {
            let found = false;
            for(let i = 0; i < BOARD_SIZE && !found; i++) {
                for(let j = 0; j < BOARD_SIZE && !found; j++) {
                    if(board[i][j] !== -1) {
                        board[clickX][clickY] = 0;
                        board[i][j] = -1;
                        found = true;
                    }
                }
            }
            recalculateNumbers();
        }
    }

    function recalculateNumbers() {
        for(let i = 0; i < BOARD_SIZE; i++) {
            for(let j = 0; j < BOARD_SIZE; j++) {
                if(board[i][j] !== -1) {
                    board[i][j] = countAdjacentMines(i, j);
                }
            }
        }
    }

    function revealCell(x, y) {
        console.log(`Attempting to reveal cell at (${x}, ${y})`);
        
        if(x < 0 || x >= BOARD_SIZE || y < 0 || y >= BOARD_SIZE || 
           gameOver || revealed[x][y] || flagged[x][y]) {
            console.log('Invalid reveal attempt');
            return;
        }

        revealed[x][y] = true;
        console.log(`Cell value at (${x}, ${y}): ${board[x][y]}`);

        if(board[x][y] === -1) {
            gameOver = true;
            createExplosionEffect(x, y);
            setTimeout(() => {
                revealAllMines();
                showGameOverModal(false);
            }, 1000);
            clearInterval(timerInterval);
            return;
        }

        if(board[x][y] === 0) {
            for(let i = -1; i <= 1; i++) {
                for(let j = -1; j <= 1; j++) {
                    const newX = x + i;
                    const newY = y + j;
                    if(newX >= 0 && newX < BOARD_SIZE && 
                       newY >= 0 && newY < BOARD_SIZE && 
                       !revealed[newX][newY]) {
                        revealCell(newX, newY);
                    }
                }
            }
        }

        renderBoard();
        checkWin();
    }

    function initGame() {
        board = [];
        revealed = [];
        flagged = [];
        gameOver = false;
        timer = 0;
        firstClick = true;
        clearInterval(timerInterval);
        
        $('#timer').text('时间: 0');
        $('#mineCount').text(`剩余地雷: ${MINE_COUNT}`);

        for(let i = 0; i < BOARD_SIZE; i++) {
            board[i] = new Array(BOARD_SIZE).fill(0);
            revealed[i] = new Array(BOARD_SIZE).fill(false);
            flagged[i] = new Array(BOARD_SIZE).fill(false);
        }

        let minesPlaced = 0;
        while(minesPlaced < MINE_COUNT) {
            const x = Math.floor(Math.random() * BOARD_SIZE);
            const y = Math.floor(Math.random() * BOARD_SIZE);
            if(board[x][y] !== -1) {
                board[x][y] = -1;
                minesPlaced++;
            }
        }

        recalculateNumbers();
        renderBoard();
        bindEvents();
    }

    function countAdjacentMines(x, y) {
        let count = 0;
        for(let i = -1; i <= 1; i++) {
            for(let j = -1; j <= 1; j++) {
                const newX = x + i;
                const newY = y + j;
                if(newX >= 0 && newX < BOARD_SIZE && 
                   newY >= 0 && newY < BOARD_SIZE && 
                   board[newX][newY] === -1) {
                    count++;
                }
            }
        }
        return count;
    }

    function renderBoard() {
        $('#gameBoard').empty();
        for(let i = 0; i < BOARD_SIZE; i++) {
            for(let j = 0; j < BOARD_SIZE; j++) {
                const cell = $('<div>')
                    .addClass('cell')
                    .attr('data-x', i)
                    .attr('data-y', j);

                if(revealed[i][j]) {
                    cell.addClass('revealed');
                    if(board[i][j] === -1) {
                        cell.addClass('mine').text('💣');
                    } else if(board[i][j] > 0) {
                        cell.text(board[i][j])
                            .attr('data-number', board[i][j]);
                    }
                } else if(flagged[i][j]) {
                    cell.addClass('flagged').text('🚩');
                }

                $('#gameBoard').append(cell);
            }
        }
    }

    function toggleFlag(x, y) {
        if(gameOver || revealed[x][y]) return;
        
        const currentFlagged = flagged.flat().filter(f => f).length;
        
        if(!flagged[x][y] && currentFlagged >= MINE_COUNT) {
            showTipMessage('最多只能标记10个地雷!');
            return;
        }
        
        flagged[x][y] = !flagged[x][y];
        
        const remainingMines = MINE_COUNT - flagged.flat().filter(f => f).length;
        $('#mineCount').text(`剩余地雷: ${remainingMines}`);
        
        renderBoard();
    }

    function revealAllMines() {
        for(let i = 0; i < BOARD_SIZE; i++) {
            for(let j = 0; j < BOARD_SIZE; j++) {
                if(board[i][j] === -1) {
                    revealed[i][j] = true;
                }
            }
        }
        renderBoard();
    }

    function checkWin() {
        let win = true;
        for(let i = 0; i < BOARD_SIZE; i++) {
            for(let j = 0; j < BOARD_SIZE; j++) {
                if(board[i][j] !== -1 && !revealed[i][j]) {
                    win = false;
                    break;
                }
            }
        }
        if(win) {
            gameOver = true;
            clearInterval(timerInterval);
            showGameOverModal(true);
        }
    }

    function startTimer() {
        timer = 0;
        timerInterval = setInterval(() => {
            timer++;
            $('#timer').text(`时间: ${timer}`);
        }, 1000);
    }

    function createExplosionEffect(x, y) {
        const cell = $(`.cell[data-x="${x}"][data-y="${y}"]`);
        if (!cell.length) return;

        cell.addClass('mine');

        const cellRect = cell[0].getBoundingClientRect();
        const centerX = cellRect.left + cellRect.width / 2;
        const centerY = cellRect.top + cellRect.height / 2;

        for (let i = 0; i < 20; i++) {
            createExplosionParticle(centerX, centerY);
        }

        $('#gameBoard').css('animation', 'shake 0.5s ease-out');
    }

    function createExplosionParticle(centerX, centerY) {
        const particle = $('<div>').addClass('explosion-particle');
        const angle = Math.random() * Math.PI * 2;
        const velocity = 100 + Math.random() * 50;
        const tx = Math.cos(angle) * velocity;
        const ty = Math.sin(angle) * velocity;

        particle.css({
            left: centerX + 'px',
            top: centerY + 'px',
            '--tx': tx + 'px',
            '--ty': ty + 'px'
        });

        $('body').append(particle);
        setTimeout(() => particle.remove(), 800);
    }

    function showGameOverModal(isVictory) {
        const modal = $('#gameOverModal');
        const modalContent = modal.find('.modal-content');
        const modalTitle = $('#modalTitle');
        const modalMessage = $('#modalMessage');
        const finalTime = $('#finalTime');
        const clearedCells = $('#clearedCells');
        
        if (isVictory) {
            modalContent.addClass('victory');
            modalTitle.text('恭喜胜利!');
            modalMessage.text('太棒了!你成功找出了所有地雷!');
        } else {
            modalContent.removeClass('victory');
            modalTitle.text('游戏结束');
            modalMessage.text('很遗憾,你踩到了地雷!');
        }
        
        finalTime.text(timer + '秒');
        const revealedCount = revealed.flat().filter(r => r).length;
        clearedCells.text(revealedCount + '格');
        
        modal.fadeIn(300);

        bindModalEvents();
    }

    function showTipMessage(message) {
        $('.tip-message').remove();
        
        const tip = $('<div>')
            .addClass('tip-message')
            .text(message)
            .appendTo('body');
        
        tip.addClass('animate');
        
        setTimeout(() => {
            tip.removeClass('animate').addClass('fade-out');
            setTimeout(() => tip.remove(), 300);
        }, 3000);
    }

    function bindModalEvents() {
        $('#newGameBtn').off('click').on('click', function() {
            console.log('New game button clicked');
            $('#gameOverModal').fadeOut(300, function() {
                initGame();
            });
        });

        $('#continueBtn').off('click').on('click', function() {
            console.log('Continue button clicked');
            $('#gameOverModal').fadeOut(300);
        });

        $('#newGame').off('click').on('click', function() {
            console.log('Header new game button clicked');
            $('#gameOverModal').fadeOut(300);
            initGame();
        });
    }

    initGame();
    bindModalEvents();

    function printBoardState() {
        console.log('Current board state:');
        for(let i = 0; i < BOARD_SIZE; i++) {
            let row = '';
            for(let j = 0; j < BOARD_SIZE; j++) {
                if(revealed[i][j]) {
                    row += board[i][j] === -1 ? ' * ' : ` ${board[i][j]} `;
                } else if(flagged[i][j]) {
                    row += ' F ';
                } else {
                    row += ' - ';
                }
            }
            console.log(row);
        }
    }
}); 

总结

下载本地直接双击index.html文件运行即可体验游戏效果。

转载请注明出处或者链接地址:https://www.qianduange.cn//article/22722.html
标签
评论
发布的文章

python连接neo4j的方式汇总

2025-03-05 18:03:12

五子棋对弈

2025-03-05 18:03:12

奖学金(acwing)c

2025-03-05 18:03:11

大家推荐的文章
会员中心 联系我 留言建议 回顶部
复制成功!