2048数字合并小游戏

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>2048数字合并游戏</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        }
        
        body {
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            min-height: 100vh;
            background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
            color: #fff;
            padding: 20px;
        }
        
        .container {
            max-width: 500px;
            width: 100%;
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 20px;
        }
        
        header {
            text-align: center;
            margin-bottom: 10px;
        }
        
        h1 {
            font-size: 2.8rem;
            color: #FFD700;
            margin-bottom: 5px;
            text-shadow: 0 0 10px rgba(255, 215, 0, 0.3);
        }
        
        .subtitle {
            color: #a0a0c0;
            font-size: 1.1rem;
        }
        
        .game-info {
            display: flex;
            justify-content: space-between;
            width: 100%;
            max-width: 460px;
            margin-bottom: 10px;
        }
        
        .score-container, .best-container {
            background: rgba(255, 255, 255, 0.1);
            padding: 15px 20px;
            border-radius: 10px;
            text-align: center;
            min-width: 120px;
            backdrop-filter: blur(5px);
            border: 1px solid rgba(255, 255, 255, 0.1);
        }
        
        .score-title, .best-title {
            font-size: 0.9rem;
            color: #b0b0d0;
            margin-bottom: 5px;
        }
        
        .score-value, .best-value {
            font-size: 1.8rem;
            font-weight: bold;
            color: #FFD700;
        }
        
        .game-controls {
            display: flex;
            gap: 15px;
            margin-top: 10px;
        }
        
        button {
            background: linear-gradient(to right, #4a54e1, #15aabf);
            color: white;
            border: none;
            padding: 12px 25px;
            border-radius: 8px;
            font-weight: bold;
            cursor: pointer;
            transition: all 0.3s;
            font-size: 1rem;
        }
        
        button:hover {
            transform: translateY(-2px);
            box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
        }
        
        #restart-btn {
            background: linear-gradient(to right, #e15252, #ff7b7b);
        }
        
        .game-instructions {
            background: rgba(255, 255, 255, 0.08);
            padding: 15px;
            border-radius: 10px;
            width: 100%;
            max-width: 460px;
            font-size: 0.9rem;
            line-height: 1.5;
            color: #b0b0d0;
            border: 1px solid rgba(255, 255, 255, 0.05);
        }
        
        .instructions-title {
            color: #FFD700;
            margin-bottom: 8px;
            font-size: 1rem;
        }
        
        .game-board {
            background: rgba(255, 255, 255, 0.05);
            border-radius: 12px;
            padding: 15px;
            width: 100%;
            max-width: 460px;
            aspect-ratio: 1/1;
            display: grid;
            grid-template-columns: repeat(4, 1fr);
            grid-template-rows: repeat(4, 1fr);
            gap: 12px;
            border: 2px solid rgba(255, 255, 255, 0.1);
            position: relative;
            margin: 10px 0;
        }
        
        .tile {
            background: rgba(255, 255, 255, 0.1);
            border-radius: 8px;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 2.2rem;
            font-weight: bold;
            transition: all 0.2s ease;
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
        }
        
        .tile-2 { background: #2A3B8F; color: #fff; }
        .tile-4 { background: #3A4B9F; color: #fff; }
        .tile-8 { background: #4A5BAF; color: #fff; }
        .tile-16 { background: #5A6BBF; color: #fff; }
        .tile-32 { background: #6A7BCF; color: #fff; }
        .tile-64 { background: #7A8BDF; color: #fff; }
        .tile-128 { background: #8A9BEF; color: #fff; box-shadow: 0 0 15px rgba(138, 155, 239, 0.5); font-size: 1.8rem; }
        .tile-256 { background: #9AABFF; color: #fff; box-shadow: 0 0 20px rgba(154, 171, 255, 0.6); font-size: 1.8rem; }
        .tile-512 { background: #AABBFF; color: #fff; box-shadow: 0 0 25px rgba(170, 187, 255, 0.7); font-size: 1.8rem; }
        .tile-1024 { background: #BACBFF; color: #fff; box-shadow: 0 0 30px rgba(186, 203, 255, 0.8); font-size: 1.6rem; }
        .tile-2048 { background: linear-gradient(45deg, #FFD700, #FFA500); color: #333; box-shadow: 0 0 40px rgba(255, 215, 0, 0.9); font-size: 1.6rem; }
        
        .game-message {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            display: none;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            background: rgba(0, 0, 0, 0.85);
            border-radius: 12px;
            z-index: 10;
            text-align: center;
            padding: 20px;
        }
        
        .message-text {
            font-size: 2.2rem;
            font-weight: bold;
            margin-bottom: 20px;
            color: #FFD700;
        }
        
        .message-subtext {
            font-size: 1.1rem;
            margin-bottom: 25px;
            color: #ccc;
            max-width: 80%;
        }
        
        .key-hint {
            display: flex;
            gap: 10px;
            margin-top: 15px;
            color: #a0a0c0;
            font-size: 0.9rem;
        }
        
        .key {
            background: rgba(255, 255, 255, 0.1);
            padding: 5px 10px;
            border-radius: 5px;
            border: 1px solid rgba(255, 255, 255, 0.2);
        }
        
        footer {
            margin-top: 20px;
            text-align: center;
            color: #888;
            font-size: 0.9rem;
        }
        
        @media (max-width: 500px) {
            .game-board {
                max-width: 90vw;
                gap: 8px;
                padding: 10px;
            }
            
            .tile {
                font-size: 1.5rem;
            }
            
            .tile-128, .tile-256, .tile-512 {
                font-size: 1.3rem;
            }
            
            .tile-1024, .tile-2048 {
                font-size: 1.2rem;
            }
            
            h1 {
                font-size: 2.2rem;
            }
            
            .game-info {
                flex-direction: column;
                align-items: center;
                gap: 10px;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <header>
            <h1>2048</h1>
            <p class="subtitle">合并相同数字,创造2048</p>
        </header>
        
        <div class="game-info">
            <div class="score-container">
                <div class="score-title">分数</div>
                <div id="score" class="score-value">0</div>
            </div>
            
            <div class="best-container">
                <div class="best-title">最高分</div>
                <div id="best-score" class="best-value">0</div>
            </div>
        </div>
        
        <div class="game-controls">
            <button id="new-game-btn">新游戏</button>
            <button id="restart-btn">重新开始</button>
        </div>
        
        <div class="game-board" id="game-board">
            <!-- 游戏格子将通过JavaScript动态生成 -->
            <div class="game-message" id="game-message">
                <div class="message-text" id="message-text">恭喜!你赢了!</div>
                <div class="message-subtext" id="message-subtext">你成功合并出了2048方块!</div>
                <button id="continue-btn">继续游戏</button>
            </div>
        </div>
        
        <div class="game-instructions">
            <div class="instructions-title">游戏说明</div>
            <p>使用键盘方向键(↑↓←→)移动方块。当两个相同数字的方块碰撞时,它们会合并成一个数字翻倍的方块。每次移动后,会在空白格子中随机生成一个新的数字方块(24)。目标是合并出2048方块!</p>
            <div class="key-hint">
                <span>控制:</span>
                <span class="key"></span>
                <span class="key"></span>
                <span class="key"></span>
                <span class="key"></span>
            </div>
        </div>
        
        <footer>
            <p>© 2026 丙午马年 · 祝你游戏愉快!</p>
        </footer>
    </div>

    <script>
        document.addEventListener('DOMContentLoaded', function() {
            // 游戏变量
            let board = [];
            let score = 0;
            let bestScore = localStorage.getItem('bestScore2048') || 0;
            let gameOver = false;
            let gameWon = false;
            const size = 4;
            
            // DOM元素
            const gameBoard = document.getElementById('game-board');
            const scoreElement = document.getElementById('score');
            const bestScoreElement = document.getElementById('best-score');
            const newGameBtn = document.getElementById('new-game-btn');
            const restartBtn = document.getElementById('restart-btn');
            const gameMessage = document.getElementById('game-message');
            const messageText = document.getElementById('message-text');
            const messageSubtext = document.getElementById('message-subtext');
            const continueBtn = document.getElementById('continue-btn');
            
            // 初始化最高分
            bestScoreElement.textContent = bestScore;
            
            // 初始化游戏板
            function initGame() {
                board = Array(size).fill().map(() => Array(size).fill(0));
                score = 0;
                gameOver = false;
                gameWon = false;
                scoreElement.textContent = score;
                updateBoard();
                addRandomTile();
                addRandomTile();
                hideMessage();
            }
            
            // 更新游戏板显示
            function updateBoard() {
                // 清空游戏板
                gameBoard.querySelectorAll('.tile').forEach(tile => tile.remove());
                
                // 添加方块
                for (let r = 0; r < size; r++) {
                    for (let c = 0; c < size; c++) {
                        const value = board[r][c];
                        if (value !== 0) {
                            const tile = document.createElement('div');
                            tile.classList.add('tile', `tile-${value}`);
                            tile.textContent = value;
                            tile.style.gridRow = r + 1;
                            tile.style.gridColumn = c + 1;
                            gameBoard.appendChild(tile);
                        }
                    }
                }
            }
            
            // 在随机空白位置添加新方块(2或4)
            function addRandomTile() {
                const emptyCells = [];
                
                // 找到所有空白格子
                for (let r = 0; r < size; r++) {
                    for (let c = 0; c < size; c++) {
                        if (board[r][c] === 0) {
                            emptyCells.push({r, c});
                        }
                    }
                }
                
                if (emptyCells.length > 0) {
                    // 随机选择一个空白格子
                    const randomCell = emptyCells[Math.floor(Math.random() * emptyCells.length)];
                    // 90%的概率生成2,10%的概率生成4
                    const value = Math.random() < 0.9 ? 2 : 4;
                    board[randomCell.r][randomCell.c] = value;
                    
                    // 添加动画效果
                    const tile = document.createElement('div');
                    tile.classList.add('tile', `tile-${value}`);
                    tile.textContent = value;
                    tile.style.gridRow = randomCell.r + 1;
                    tile.style.gridColumn = randomCell.c + 1;
                    tile.style.transform = 'scale(0)';
                    gameBoard.appendChild(tile);
                    
                    // 触发动画
                    setTimeout(() => {
                        tile.style.transform = 'scale(1)';
                        tile.style.transition = 'transform 0.2s ease';
                    }, 10);
                }
            }
            
            // 检查游戏是否结束
            function checkGameOver() {
                // 检查是否还有空白格子
                for (let r = 0; r < size; r++) {
                    for (let c = 0; c < size; c++) {
                        if (board[r][c] === 0) {
                            return false;
                        }
                    }
                }
                
                // 检查是否还有可合并的相邻方块
                for (let r = 0; r < size; r++) {
                    for (let c = 0; c < size; c++) {
                        const current = board[r][c];
                        
                        // 检查右侧方块
                        if (c < size - 1 && board[r][c + 1] === current) {
                            return false;
                        }
                        
                        // 检查下方方块
                        if (r < size - 1 && board[r + 1][c] === current) {
                            return false;
                        }
                    }
                }
                
                return true;
            }
            
            // 检查是否获胜(达到2048)
            function checkWin() {
                for (let r = 0; r < size; r++) {
                    for (let c = 0; c < size; c++) {
                        if (board[r][c] === 2048) {
                            return true;
                        }
                    }
                }
                return false;
            }
            
            // 显示游戏消息
            function showMessage(isWin) {
                if (isWin) {
                    messageText.textContent = "恭喜!你赢了!";
                    messageSubtext.textContent = "你成功合并出了2048方块!点击继续挑战更高分数!";
                    gameWon = true;
                } else {
                    messageText.textContent = "游戏结束!";
                    messageSubtext.textContent = "没有可移动的方块了,点击按钮重新开始吧!";
                    gameOver = true;
                }
                gameMessage.style.display = 'flex';
            }
            
            // 隐藏游戏消息
            function hideMessage() {
                gameMessage.style.display = 'none';
            }
            
            // 移动方块(核心逻辑)
            function move(direction) {
                if (gameOver) return false;
                
                let moved = false;
                const oldBoard = board.map(row => [...row]);
                
                // 根据方向处理移动
                switch(direction) {
                    case 'left':
                        moved = moveLeft();
                        break;
                    case 'right':
                        moved = moveRight();
                        break;
                    case 'up':
                        moved = moveUp();
                        break;
                    case 'down':
                        moved = moveDown();
                        break;
                }
                
                // 如果移动成功,添加新方块并更新显示
                if (moved) {
                    addRandomTile();
                    updateBoard();
                    scoreElement.textContent = score;
                    
                    // 检查是否获胜
                    if (!gameWon && checkWin()) {
                        showMessage(true);
                    }
                    
                    // 检查游戏是否结束
                    if (checkGameOver()) {
                        showMessage(false);
                    }
                    
                    // 更新最高分
                    if (score > bestScore) {
                        bestScore = score;
                        bestScoreElement.textContent = bestScore;
                        localStorage.setItem('bestScore2048', bestScore);
                    }
                }
                
                return moved;
            }
            
            // 向左移动
            function moveLeft() {
                let moved = false;
                
                for (let r = 0; r < size; r++) {
                    // 过滤掉0并合并相同数字
                    const row = board[r].filter(val => val !== 0);
                    
                    // 合并相同数字
                    for (let i = 0; i < row.length - 1; i++) {
                        if (row[i] === row[i + 1]) {
                            row[i] *= 2;
                            row[i + 1] = 0;
                            score += row[i];
                            moved = true;
                        }
                    }
                    
                    // 再次过滤0
                    const mergedRow = row.filter(val => val !== 0);
                    
                    // 用合并后的行填充剩余位置为0
                    const newRow = mergedRow.concat(Array(size - mergedRow.length).fill(0));
                    
                    // 检查是否有移动
                    if (!arraysEqual(board[r], newRow)) {
                        moved = true;
                    }
                    
                    board[r] = newRow;
                }
                
                return moved;
            }
            
            // 向右移动
            function moveRight() {
                // 反转行,使用向左移动的逻辑,然后再反转回来
                for (let r = 0; r < size; r++) {
                    board[r].reverse();
                }
                
                const moved = moveLeft();
                
                for (let r = 0; r < size; r++) {
                    board[r].reverse();
                }
                
                return moved;
            }
            
            // 向上移动
            function moveUp() {
                // 转置矩阵,使用向左移动的逻辑,然后再转置回来
                transposeBoard();
                const moved = moveLeft();
                transposeBoard();
                return moved;
            }
            
            // 向下移动
            function moveDown() {
                // 转置矩阵,使用向右移动的逻辑,然后再转置回来
                transposeBoard();
                const moved = moveRight();
                transposeBoard();
                return moved;
            }
            
            // 转置矩阵(行变列,列变行)
            function transposeBoard() {
                const newBoard = Array(size).fill().map(() => Array(size).fill(0));
                
                for (let r = 0; r < size; r++) {
                    for (let c = 0; c < size; c++) {
                        newBoard[c][r] = board[r][c];
                    }
                }
                
                board = newBoard;
            }
            
            // 比较两个数组是否相等
            function arraysEqual(arr1, arr2) {
                if (arr1.length !== arr2.length) return false;
                for (let i = 0; i < arr1.length; i++) {
                    if (arr1[i] !== arr2[i]) return false;
                }
                return true;
            }
            
            // 键盘控制
            document.addEventListener('keydown', function(event) {
                if (gameOver && !gameWon) return;
                
                switch(event.key) {
                    case 'ArrowLeft':
                        event.preventDefault();
                        move('left');
                        break;
                    case 'ArrowRight':
                        event.preventDefault();
                        move('right');
                        break;
                    case 'ArrowUp':
                        event.preventDefault();
                        move('up');
                        break;
                    case 'ArrowDown':
                        event.preventDefault();
                        move('down');
                        break;
                }
            });
            
            // 触摸滑动控制(移动端)
            let touchStartX = 0;
            let touchStartY = 0;
            
            gameBoard.addEventListener('touchstart', function(event) {
                touchStartX = event.touches[0].clientX;
                touchStartY = event.touches[0].clientY;
                event.preventDefault();
            }, { passive: false });
            
            gameBoard.addEventListener('touchend', function(event) {
                if (gameOver && !gameWon) return;
                
                const touchEndX = event.changedTouches[0].clientX;
                const touchEndY = event.changedTouches[0].clientY;
                
                const diffX = touchEndX - touchStartX;
                const diffY = touchEndY - touchStartY;
                
                // 确定滑动方向(只响应明显的滑动)
                if (Math.abs(diffX) > Math.abs(diffY)) {
                    // 水平滑动
                    if (Math.abs(diffX) > 30) {
                        if (diffX > 0) {
                            move('right');
                        } else {
                            move('left');
                        }
                    }
                } else {
                    // 垂直滑动
                    if (Math.abs(diffY) > 30) {
                        if (diffY > 0) {
                            move('down');
                        } else {
                            move('up');
                        }
                    }
                }
                
                event.preventDefault();
            }, { passive: false });
            
            // 按钮事件
            newGameBtn.addEventListener('click', initGame);
            restartBtn.addEventListener('click', initGame);
            continueBtn.addEventListener('click', function() {
                hideMessage();
            });
            
            // 初始化游戏
            initGame();
            
            // 添加一些初始动画
            setTimeout(() => {
                document.querySelectorAll('.tile').forEach(tile => {
                    tile.style.transform = 'scale(1)';
                    tile.style.transition = 'transform 0.2s ease';
                });
            }, 100);
        });
    </script>
</body>
</html>
posted @ 2026-02-24 21:53  bz02_2023f2  阅读(1)  评论(0)    收藏  举报  来源