拼图游戏:Trae 轻松实现图片拼接挑战

拼图游戏,无疑是经典的益智游戏之一。玩家需要将一张完整的图片分割成多个小块,然后通过拖动这些拼图块,拼接成原本的图案。这个游戏不仅考验玩家的观察力和空间想象力,还能带来无限的乐趣。

虽然拼图游戏的玩法看似简单,但要实现一个流畅的拖动拼接、检查拼图是否完成等功能,背后需要处理不少的逻辑和细节。过去,要实现拼图游戏,通常需要编写复杂的图片切割算法、拼图块的位置调整和碰撞检测等。

不过,Trae IDE 完全解决了这些问题。通过简单的指令,Trae 能自动帮你生成拼图游戏的规则和UI设计,让你轻松实现这一经典益智游戏。接下来,我就来分享如何通过 Trae 快速生成拼图游戏,让你轻松拥有一个既好玩又具挑战性的拼图游戏。

💡 我的需求其实很简单

我的需求非常明确:制作一个拼图游戏,功能要求如下:

  • 图片分割:将一张图片分割成多个小块,打乱它们的位置。
  • 拖动拼接:玩家可以通过拖动拼图块,将它们重新拼接成完整的图案。
  • 拼图完成判断:当所有拼图块正确排列时,游戏自动判定拼图完成。
  • 流畅的UI:游戏界面简洁,操作直观,拼图块拖动和拼接流畅自然。

虽然功能简单,但涉及到图片分割、拼图块的拖动逻辑和拼接完成判断,手动实现这些功能依然需要不少时间。

✨ Trae 如何理解需求并生成代码?

我只需要在 Trae 中输入一句话:

"生成一个拼图游戏,将图片分割成多个块,玩家拖动拼接成完整的图案。"

Trae 会自动解析并生成完整的拼图游戏代码,包括:

  • 图片分割:Trae 自动将上传的图片分割成多个小块,确保每个拼图块能够准确对应到原始图案。
  • 拼图块拖动逻辑:玩家可以通过鼠标拖动拼图块到空白位置,游戏自动处理拼图块的对齐与碰撞检测。
  • 拼图完成检测:当所有拼图块正确排列时,系统会检测到拼图完成,并给出提示。
  • 简洁的UI设计:拼图游戏界面简洁、直观,玩家可以轻松操作并享受游戏的乐趣。

通过这一简单的指令,Trae 就能够为我自动生成一个完整的拼图游戏,并且提供了非常流畅的用户体验。

🧩 游戏操作直观,拼图体验流畅

Trae 生成的拼图游戏操作非常简便,玩家只需点击并拖动拼图块,便能轻松地将其拖动到正确的位置。每当玩家将拼图块拖放到正确的地方,系统会自动更新拼图块的位置,直到所有拼图块正确组合成完整的图案。

整个游戏过程流畅自然,拼图块的拖动效果也十分平滑。玩家不需要任何复杂的操作,拼图完成的提示也非常及时,给玩家带来了极好的游戏体验。

🛠 游戏拓展,功能轻松加

虽然生成的拼图游戏已经非常完备,但 Trae 还支持轻松添加更多功能:

  • 难度选择:增加不同的难度等级,例如通过增加拼图块的数量,来增加拼图的挑战性。
  • 定时器:为游戏增加一个计时器,记录玩家完成拼图所用的时间,增强游戏的竞争性。
  • 提示功能:当玩家遇到困难时,可以提供提示功能,显示拼图的正确位置。
  • 音效和动画:为拼图完成时加入音效和动画,增加游戏的互动性和趣味性。

这些功能都可以通过简单的描述,Trae 就会自动为我生成相应的代码并集成到现有的游戏中。

这就是拼图游戏开发的新体验

通过这次拼图游戏的开发,我深刻体验到了 Trae 带来的便利。以前可能需要手动编写大量的代码,涉及到图片切割、拼图块的拖动以及完成判断等,现在只需要通过简单的指令,就能轻松实现这些功能。

对于独立开发者或小团队来说,Trae 不仅节省了开发时间,还能够提供流畅且富有创意的用户体验,让开发者专注于游戏设计而非复杂的代码编写。

结语

如果你也想制作一个拼图游戏,试试 Trae IDE,输入类似的需求:

"生成一个拼图游戏,将图片分割成多个块,玩家拖动拼接成完整的图案。"

几秒钟内,Trae 就会生成完整的拼图游戏代码,带有流畅的拼图块拖动逻辑和自动拼图完成检测。你可以直接将它嵌入到你的项目中,甚至根据需求继续扩展和优化。

快来体验一下 Trae,让你的拼图游戏开发变得更加轻松、有趣!

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>
        body {
            font-family: 'Arial', sans-serif;
            background-color: #f5f5f5;
            margin: 0;
            padding: 20px;
            display: flex;
            flex-direction: column;
            align-items: center;
            min-height: 100vh;
        }
        
        h1 {
            color: #333;
            margin-bottom: 20px;
            text-align: center;
        }
        
        .game-container {
            display: flex;
            flex-direction: column;
            align-items: center;
            margin-bottom: 20px;
        }
        
        .puzzle-container {
            position: relative;
            border: 2px solid #333;
            margin-bottom: 20px;
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
            background-color: #fff;
        }
        
        .puzzle-piece {
            position: absolute;
            cursor: move;
            box-sizing: border-box;
            transition: box-shadow 0.2s;
            background-size: cover;
            border: 1px solid #fff;
        }
        
        .puzzle-piece.dragging {
            z-index: 100;
            box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
        }
        
        .puzzle-piece.correct {
            border: 1px solid #4CAF50;
        }
        
        .controls {
            display: flex;
            gap: 10px;
            margin-bottom: 20px;
            flex-wrap: wrap;
            justify-content: center;
        }
        
        button {
            padding: 10px 15px;
            background-color: #4CAF50;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 16px;
            transition: background-color 0.3s;
        }
        
        button:hover {
            background-color: #45a049;
        }
        
        button:disabled {
            background-color: #cccccc;
            cursor: not-allowed;
        }
        
        .difficulty {
            margin-bottom: 20px;
        }
        
        .difficulty select {
            padding: 8px;
            font-size: 16px;
            border-radius: 4px;
        }
        
        .image-select {
            margin-bottom: 20px;
        }
        
        .image-select select {
            padding: 8px;
            font-size: 16px;
            border-radius: 4px;
        }
        
        .timer {
            font-size: 18px;
            margin-bottom: 10px;
            color: #333;
        }
        
        .moves {
            font-size: 18px;
            margin-bottom: 20px;
            color: #333;
        }
        
        .preview {
            margin-bottom: 20px;
            text-align: center;
        }
        
        .preview img {
            max-width: 200px;
            border: 1px solid #ccc;
            border-radius: 4px;
        }
        
        .preview-toggle {
            margin-top: 10px;
        }
        
        .message {
            font-size: 20px;
            font-weight: bold;
            color: #4CAF50;
            height: 30px;
            text-align: center;
            margin-bottom: 10px;
        }
        
        .upload-container {
            margin-bottom: 20px;
            text-align: center;
        }
        
        .upload-container input {
            display: none;
        }
        
        .upload-label {
            padding: 10px 15px;
            background-color: #2196F3;
            color: white;
            border-radius: 4px;
            cursor: pointer;
            font-size: 16px;
            display: inline-block;
            transition: background-color 0.3s;
        }
        
        .upload-label:hover {
            background-color: #0b7dda;
        }
        
        @media (max-width: 600px) {
            .puzzle-container {
                max-width: 90vw;
                max-height: 90vw;
            }
            
            .preview img {
                max-width: 150px;
            }
            
            .controls {
                flex-direction: column;
            }
        }
    </style>
</head>
<body>
    <h1>拼图游戏</h1>
    
    <div class="timer">时间: <span id="minutes">00</span>:<span id="seconds">00</span></div>
    <div class="moves">移动次数: <span id="move-count">0</span></div>
    
    <div class="controls">
        <div class="difficulty">
            <select id="difficulty">
                <option value="3">简单 (3x3)</option>
                <option value="4" selected>中等 (4x4)</option>
                <option value="5">困难 (5x5)</option>
                <option value="6">专家 (6x6)</option>
            </select>
        </div>
        
        <div class="image-select">
            <select id="image-select">
                <option value="nature">自然风景</option>
                <option value="city">城市景观</option>
                <option value="animal">可爱动物</option>
                <option value="food">美食</option>
            </select>
        </div>
        
        <button id="upload-btn" class="upload-label">上传图片</button>
        <input type="file" id="image-upload" accept="image/*">
    </div>
    
    <div class="preview">
        <h3>预览图</h3>
        <img id="preview-image" src="" alt="预览图">
        <div class="preview-toggle">
            <button id="toggle-preview">隐藏预览</button>
        </div>
    </div>
    
    <div class="message" id="message"></div>
    
    <div class="game-container">
        <div class="puzzle-container" id="puzzle-container"></div>
        
        <div class="controls">
            <button id="start-btn">开始游戏</button>
            <button id="shuffle-btn">重新打乱</button>
            <button id="solve-btn">显示解答</button>
        </div>
    </div>
    
    <script>
        // 游戏状态变量
        let puzzlePieces = [];
        let puzzleSize = 4; // 默认4x4
        let pieceWidth, pieceHeight;
        let puzzleWidth = 400;
        let puzzleHeight = 400;
        let draggedPiece = null;
        let startX, startY;
        let timer = null;
        let seconds = 0;
        let minutes = 0;
        let moveCount = 0;
        let gameStarted = false;
        let currentImage = null;
        let previewVisible = true;
        
        // 预定义的图片
        const images = {
            nature: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MDAgNDAwIj48cmVjdCB3aWR0aD0iNDAwIiBoZWlnaHQ9IjQwMCIgZmlsbD0iIzg3Y2VlYiIvPjxwYXRoIGQ9Ik0wLDI1MEMyMCwyMzAgNDAsMjIwIDYwLDIzMEM4MCwyNDAgMTAwLDI1MCAxMjAsMjQwQzE0MCwyMzAgMTYwLDIyMCAxODAsMjMwQzIwMCwyNDAgMjIwLDI1MCAyNDAsMjQwQzI2MCwyMzAgMjgwLDIyMCAzMDAsMjMwQzMyMCwyNDAgMzQwLDI1MCAzNjAsMjQwQzM4MCwyMzAgNDAwLDIyMCA0MDAsMjIwVjQwMEgwWiIgZmlsbD0iIzFmNzc0MCIvPjxjaXJjbGUgY3g9IjMwMCIgY3k9IjgwIiByPSI0MCIgZmlsbD0iI2ZmZDcwMCIvPjxwYXRoIGQ9Ik0xMjAsMzAwQzEyMCwyNjAgMTYwLDI0MCAyMDAsMjQwQzI0MCwyNDAgMjgwLDI2MCAyODAsMzAwQzI4MCwzNDAgMjQwLDM2MCAyMDAsMzYwQzE2MCwzNjAgMTIwLDM0MCAxMjAsMzAwWiIgZmlsbD0iIzc3NTUzMyIvPjxwYXRoIGQ9Ik0xODAsMTgwQzE4MCwxNDAgMjIwLDEyMCAyNjAsMTIwQzMwMCwxMjAgMzQwLDE0MCAzNDAsMTgwQzM0MCwyMjAgMzAwLDI0MCAyNjAsMjQwQzIyMCwyNDAgMTgwLDIyMCAxODAsMTgwWiIgZmlsbD0iIzFmNzc0MCIvPjxwYXRoIGQ9Ik00MCwxNjBDNDAsMTIwIDgwLDEwMCAxMjAsMTAwQzE2MCwxMDAgMjAwLDEyMCAyMDAsMTYwQzIwMCwyMDAgMTYwLDIyMCAxMjAsMjIwQzgwLDIyMCA0MCwyMDAgNDAsMTYwWiIgZmlsbD0iIzFmNzc0MCIvPjwvc3ZnPg==',
            city: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MDAgNDAwIj48cmVjdCB3aWR0aD0iNDAwIiBoZWlnaHQ9IjQwMCIgZmlsbD0iIzg3Y2VlYiIvPjxyZWN0IHg9IjAiIHk9IjI1MCIgd2lkdGg9IjQwMCIgaGVpZ2h0PSIxNTAiIGZpbGw9IiM4ODg4ODgiLz48cmVjdCB4PSIxMCIgeT0iMTUwIiB3aWR0aD0iODAiIGhlaWdodD0iMjUwIiBmaWxsPSIjNTU1NTU1Ii8+PHJlY3QgeD0iMTEwIiB5PSIxMDAiIHdpZHRoPSI4MCIgaGVpZ2h0PSIzMDAiIGZpbGw9IiM3Nzc3NzciLz48cmVjdCB4PSIyMTAiIHk9IjE4MCIgd2lkdGg9IjgwIiBoZWlnaHQ9IjIyMCIgZmlsbD0iIzY2NjY2NiIvPjxyZWN0IHg9IjMxMCIgeT0iNzAiIHdpZHRoPSI4MCIgaGVpZ2h0PSIzMzAiIGZpbGw9IiM0NDQ0NDQiLz48cmVjdCB4PSIyMCIgeT0iMTcwIiB3aWR0aD0iMTUiIGhlaWdodD0iMTUiIGZpbGw9IiNmZmZmMDAiLz48cmVjdCB4PSI2MCIgeT0iMTcwIiB3aWR0aD0iMTUiIGhlaWdodD0iMTUiIGZpbGw9IiNmZmZmMDAiLz48cmVjdCB4PSIyMCIgeT0iMjEwIiB3aWR0aD0iMTUiIGhlaWdodD0iMTUiIGZpbGw9IiNmZmZmMDAiLz48cmVjdCB4PSI2MCIgeT0iMjEwIiB3aWR0aD0iMTUiIGhlaWdodD0iMTUiIGZpbGw9IiNmZmZmMDAiLz48cmVjdCB4PSIxMjAiIHk9IjEyMCIgd2lkdGg9IjE1IiBoZWlnaHQ9IjE1IiBmaWxsPSIjZmZmZjAwIi8+PHJlY3QgeD0iMTYwIiB5PSIxMjAiIHdpZHRoPSIxNSIgaGVpZ2h0PSIxNSIgZmlsbD0iI2ZmZmYwMCIvPjxyZWN0IHg9IjEyMCIgeT0iMTYwIiB3aWR0aD0iMTUiIGhlaWdodD0iMTUiIGZpbGw9IiNmZmZmMDAiLz48cmVjdCB4PSIxNjAiIHk9IjE2MCIgd2lkdGg9IjE1IiBoZWlnaHQ9IjE1IiBmaWxsPSIjZmZmZjAwIi8+PHJlY3QgeD0iMjIwIiB5PSIyMDAiIHdpZHRoPSIxNSIgaGVpZ2h0PSIxNSIgZmlsbD0iI2ZmZmYwMCIvPjxyZWN0IHg9IjI2MCIgeT0iMjAwIiB3aWR0aD0iMTUiIGhlaWdodD0iMTUiIGZpbGw9IiNmZmZmMDAiLz48cmVjdCB4PSIyMjAiIHk9IjI0MCIgd2lkdGg9IjE1IiBoZWlnaHQ9IjE1IiBmaWxsPSIjZmZmZjAwIi8+PHJlY3QgeD0iMjYwIiB5PSIyNDAiIHdpZHRoPSIxNSIgaGVpZ2h0PSIxNSIgZmlsbD0iI2ZmZmYwMCIvPjxyZWN0IHg9IjMyMCIgeT0iOTAiIHdpZHRoPSIxNSIgaGVpZ2h0PSIxNSIgZmlsbD0iI2ZmZmYwMCIvPjxyZWN0IHg9IjM2MCIgeT0iOTAiIHdpZHRoPSIxNSIgaGVpZ2h0PSIxNSIgZmlsbD0iI2ZmZmYwMCIvPjxyZWN0IHg9IjMyMCIgeT0iMTMwIiB3aWR0aD0iMTUiIGhlaWdodD0iMTUiIGZpbGw9IiNmZmZmMDAiLz48cmVjdCB4PSIzNjAiIHk9IjEzMCIgd2lkdGg9IjE1IiBoZWlnaHQ9IjE1IiBmaWxsPSIjZmZmZjAwIi8+PGNpcmNsZSBjeD0iMzAwIiBjeT0iNTAiIHI9IjMwIiBmaWxsPSIjZmZkNzAwIi8+PC9zdmc+',
            animal: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MDAgNDAwIj48cmVjdCB3aWR0aD0iNDAwIiBoZWlnaHQ9IjQwMCIgZmlsbD0iI2FhZTZmZiIvPjxyZWN0IHg9IjAiIHk9IjMwMCIgd2lkdGg9IjQwMCIgaGVpZ2h0PSIxMDAiIGZpbGw9IiM3N2RkNzciLz48Y2lyY2xlIGN4PSIyMDAiIGN5PSIyNTAiIHI9IjEwMCIgZmlsbD0iI2JiODg1NSIvPjxjaXJjbGUgY3g9IjE1MCIgY3k9IjIwMCIgcj0iMjUiIGZpbGw9IiNmZmZmZmYiLz48Y2lyY2xlIGN4PSIyNTAiIGN5PSIyMDAiIHI9IjI1IiBmaWxsPSIjZmZmZmZmIi8+PGNpcmNsZSBjeD0iMTUwIiBjeT0iMjAwIiByPSIxMCIgZmlsbD0iIzAwMDAwMCIvPjxjaXJjbGUgY3g9IjI1MCIgY3k9IjIwMCIgcj0iMTAiIGZpbGw9IiMwMDAwMDAiLz48ZWxsaXBzZSBjeD0iMjAwIiBjeT0iMjUwIiByeD0iMzAiIHJ5PSIxNSIgZmlsbD0iI2ZmYWE5OSIvPjxwYXRoIGQ9Ik0xNTAsMTIwIEMxNTAsMTAwIDEyMCw4MCAxMDAsODAgQzgwLDgwIDUwLDEwMCA1MCwxMjAgQzUwLDE0MCA4MCwxNjAgMTAwLDE2MCBDMTIwLDE2MCAxNTAsMTQwIDE1MCwxMjAiIGZpbGw9IiNiYjg4NTUiLz48cGF0aCBkPSJNMjUwLDEyMCBDMjUwLDEwMCAyODAsODAgMzAwLDgwIEMzMjAsODAgMzUwLDEwMCAzNTAsMTIwIEMzNTAsMTQwIDMyMCwxNjAgMzAwLDE2MCBDMjgwLDE2MCAyNTAsMTQwIDI1MCwxMjAiIGZpbGw9IiNiYjg4NTUiLz48L3N2Zz4=',
            food: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MDAgNDAwIj48cmVjdCB3aWR0aD0iNDAwIiBoZWlnaHQ9IjQwMCIgZmlsbD0iI2ZmZjVlNiIvPjxjaXJjbGUgY3g9IjEyMCIgY3k9IjE1MCIgcj0iODAiIGZpbGw9IiNmZjZiNmIiLz48Y2lyY2xlIGN4PSIyODAiIGN5PSIxNTAiIHI9IjgwIiBmaWxsPSIjZmZjYzY2Ii8+PGNpcmNsZSBjeD0iMTIwIiBjeT0iMjgwIiByPSI4MCIgZmlsbD0iIzc3ZGQ3NyIvPjxjaXJjbGUgY3g9IjI4MCIgY3k9IjI4MCIgcj0iODAiIGZpbGw9IiM2NmNjZmYiLz48Y2lyY2xlIGN4PSIxMjAiIGN5PSIxNTAiIHI9IjYwIiBmaWxsPSIjZmYzMzMzIi8+PGNpcmNsZSBjeD0iMjgwIiBjeT0iMTUwIiByPSI2MCIgZmlsbD0iI2ZmYmIzMyIvPjxjaXJjbGUgY3g9IjEyMCIgY3k9IjI4MCIgcj0iNjAiIGZpbGw9IiM0NGFhNDQiLz48Y2lyY2xlIGN4PSIyODAiIGN5PSIyODAiIHI9IjYwIiBmaWxsPSIjMzM5OWZmIi8+PC9zdmc+'
        };
        
        // DOM 元素
        const puzzleContainer = document.getElementById('puzzle-container');
        const difficultySelect = document.getElementById('difficulty');
        const imageSelect = document.getElementById('image-select');
        const startBtn = document.getElementById('start-btn');
        const shuffleBtn = document.getElementById('shuffle-btn');
        const solveBtn = document.getElementById('solve-btn');
        const messageEl = document.getElementById('message');
        const minutesEl = document.getElementById('minutes');
        const secondsEl = document.getElementById('seconds');
        const moveCountEl = document.getElementById('move-count');
        const previewImage = document.getElementById('preview-image');
        const togglePreviewBtn = document.getElementById('toggle-preview');
        const imageUpload = document.getElementById('image-upload');
        const uploadBtn = document.getElementById('upload-btn');
        
        // 初始化游戏
        function initGame() {
            // 设置拼图容器大小
            puzzleContainer.style.width = puzzleWidth + 'px';
            puzzleContainer.style.height = puzzleHeight + 'px';
            
            // 清空拼图容器
            puzzleContainer.innerHTML = '';
            puzzlePieces = [];
            
            // 根据难度设置拼图大小
            puzzleSize = parseInt(difficultySelect.value);
            pieceWidth = puzzleWidth / puzzleSize;
            pieceHeight = puzzleHeight / puzzleSize;
            
            // 加载选中的图片
            loadImage();
        }
        
        // 加载图片
        function loadImage() {
            const selectedImage = imageSelect.value;
            currentImage = images[selectedImage];
            previewImage.src = currentImage;
            
            // 创建拼图块
            createPuzzlePieces();
        }
        
        // 创建拼图块
        function createPuzzlePieces() {
            puzzleContainer.innerHTML = '';
            puzzlePieces = [];
            
            for (let row = 0; row < puzzleSize; row++) {
                for (let col = 0; col < puzzleSize; col++) {
                    const piece = document.createElement('div');
                    piece.className = 'puzzle-piece';
                    piece.style.width = pieceWidth + 'px';
                    piece.style.height = pieceHeight + 'px';
                    
                    // 设置拼图块的背景图片位置
                    piece.style.backgroundImage = `url(${currentImage})`;
                    piece.style.backgroundSize = `${puzzleWidth}px ${puzzleHeight}px`;
                    piece.style.backgroundPosition = `-${col * pieceWidth}px -${row * pieceHeight}px`;
                    
                    // 设置拼图块的初始位置(有序)
                    piece.style.left = (col * pieceWidth) + 'px';
                    piece.style.top = (row * pieceHeight) + 'px';
                    
                    // 存储拼图块的正确位置
                    piece.dataset.row = row;
                    piece.dataset.col = col;
                    piece.dataset.correctLeft = col * pieceWidth;
                    piece.dataset.correctTop = row * pieceHeight;
                    
                    // 添加拖拽事件
                    piece.addEventListener('mousedown', startDrag);
                    piece.addEventListener('touchstart', startDrag, { passive: false });
                    
                    puzzleContainer.appendChild(piece);
                    puzzlePieces.push(piece);
                }
            }
        }
        
        // 打乱拼图
        function shufflePuzzle() {
            if (!gameStarted) return;
            
            // 重置移动次数
            moveCount = 0;
            moveCountEl.textContent = moveCount;
            
            // 随机排列拼图块
            const positions = [];
            for (let row = 0; row < puzzleSize; row++) {
                for (let col = 0; col < puzzleSize; col++) {
                    positions.push({ left: col * pieceWidth, top: row * pieceHeight });
                }
            }
            
            // 打乱位置数组
            shuffleArray(positions);
            
            // 应用打乱后的位置
            for (let i = 0; i < puzzlePieces.length; i++) {
                const piece = puzzlePieces[i];
                const position = positions[i];
                
                piece.style.left = position.left + 'px';
                piece.style.top = position.top + 'px';
                
                // 移除正确位置标记
                piece.classList.remove('correct');
            }
            
            // 检查是否已经完成(防止随机排列恰好是正确的)
            checkCompletion();
        }
        
        // 打乱数组(Fisher-Yates 洗牌算法)
        function shuffleArray(array) {
            for (let i = array.length - 1; i > 0; i--) {
                const j = Math.floor(Math.random() * (i + 1));
                [array[i], array[j]] = [array[j], array[i]];
            }
            return array;
        }
        
        // 开始拖拽
        function startDrag(e) {
            if (!gameStarted) return;
            
            e.preventDefault();
            
            // 获取触摸或鼠标事件的坐标
            const clientX = e.clientX || e.touches[0].clientX;
            const clientY = e.clientY || e.touches[0].clientY;
            
            draggedPiece = this;
            draggedPiece.classList.add('dragging');
            
            // 计算鼠标在拼图块内的位置
            const rect = draggedPiece.getBoundingClientRect();
            startX = clientX - rect.left;
            startY = clientY - rect.top;
            
            // 添加移动和结束拖拽事件
            document.addEventListener('mousemove', dragMove);
            document.addEventListener('mouseup', dragEnd);
            document.addEventListener('touchmove', dragMove, { passive: false });
            document.addEventListener('touchend', dragEnd);
        }
        
        // 拖拽移动
        function dragMove(e) {
            if (!draggedPiece) return;
            
            e.preventDefault();
            
            // 获取触摸或鼠标事件的坐标
            const clientX = e.clientX || e.touches[0].clientX;
            const clientY = e.clientY || e.touches[0].clientY;
            
            // 计算新位置
            const puzzleRect = puzzleContainer.getBoundingClientRect();
            let left = clientX - puzzleRect.left - startX;
            let top = clientY - puzzleRect.top - startY;
            
            // 限制在拼图容器内
            left = Math.max(0, Math.min(left, puzzleWidth - pieceWidth));
            top = Math.max(0, Math.min(top, puzzleHeight - pieceHeight));
            
            // 更新位置
            draggedPiece.style.left = left + 'px';
            draggedPiece.style.top = top + 'px';
        }
        
        // 结束拖拽
        function dragEnd() {
            if (!draggedPiece) return;
            
            // 移除拖拽中的样式
            draggedPiece.classList.remove('dragging');
            
            // 吸附到网格
            snapToGrid(draggedPiece);
            
            // 增加移动次数
            moveCount++;
            moveCountEl.textContent = moveCount;
            
            // 检查是否完成拼图
            checkCompletion();
            
            // 清除拖拽状态
            draggedPiece = null;
            
            // 移除事件监听器
            document.removeEventListener('mousemove', dragMove);
            document.removeEventListener('mouseup', dragEnd);
            document.removeEventListener('touchmove', dragMove);
            document.removeEventListener('touchend', dragEnd);
        }
        
        // 吸附到网格
        function snapToGrid(piece) {
            const left = parseInt(piece.style.left);
            const top = parseInt(piece.style.top);
            
            // 计算最近的网格位置
            const col = Math.round(left / pieceWidth);
            const row = Math.round(top / pieceHeight);
            
            // 计算吸附位置
            const snapLeft = col * pieceWidth;
            const snapTop = row * pieceHeight;
            
            // 设置吸附位置
            piece.style.left = snapLeft + 'px';
            piece.style.top = snapTop + 'px';
            
            // 检查是否在正确位置
            const correctLeft = parseInt(piece.dataset.correctLeft);
            const correctTop = parseInt(piece.dataset.correctTop);
            
            if (snapLeft === correctLeft && snapTop === correctTop) {
                piece.classList.add('correct');
            } else {
                piece.classList.remove('correct');
            }
        }
        
        // 检查是否完成拼图
        function checkCompletion() {
            let completed = true;
            
            for (const piece of puzzlePieces) {
                const currentLeft = parseInt(piece.style.left);
                const currentTop = parseInt(piece.style.top);
                const correctLeft = parseInt(piece.dataset.correctLeft);
                const correctTop = parseInt(piece.dataset.correctTop);
                
                if (currentLeft !== correctLeft || currentTop !== correctTop) {
                    completed = false;
                    break;
                }
            }
            
            if (completed && gameStarted) {
                // 停止计时器
                stopTimer();
                
                // 显示完成消息
                messageEl.textContent = `恭喜!你完成了拼图!用时 ${minutes}:${seconds.toString().padStart(2, '0')},移动 ${moveCount} 次`;
                
                // 禁用重新打乱按钮
                shuffleBtn.disabled = true;
            }
        }
        
        // 显示解答
        function showSolution() {
            if (!gameStarted) return;
            
            // 将每个拼图块放回正确位置
            for (const piece of puzzlePieces) {
                const correctLeft = parseInt(piece.dataset.correctLeft);
                const correctTop = parseInt(piece.dataset.correctTop);
                
                piece.style.left = correctLeft + 'px';
                piece.style.top = correctTop + 'px';
                piece.classList.add('correct');
            }
            
            // 停止计时器
            stopTimer();
            
            // 显示消息
            messageEl.textContent = '这是拼图的解答。';
            
            // 禁用按钮
            shuffleBtn.disabled = true;
            solveBtn.disabled = true;
        }
        
        // 计时器函数
        function startTimer() {
            stopTimer();
            seconds = 0;
            minutes = 0;
            updateTimer();
            
            timer = setInterval(() => {
                seconds++;
                if (seconds === 60) {
                    seconds = 0;
                    minutes++;
                }
                updateTimer();
            }, 1000);
        }
        
        function stopTimer() {
            clearInterval(timer);
        }
        
        function updateTimer() {
            minutesEl.textContent = minutes.toString().padStart(2, '0');
            secondsEl.textContent = seconds.toString().padStart(2, '0');
        }
        
        // 开始新游戏
        function startGame() {
            // 初始化游戏
            initGame();
            
            // 开始计时
            startTimer();
            
            // 重置移动次数
            moveCount = 0;
            moveCountEl.textContent = moveCount;
            
            // 清除消息
            messageEl.textContent = '';
            
            // 启用按钮
            shuffleBtn.disabled = false;
            solveBtn.disabled = false;
            
            // 设置游戏状态
            gameStarted = true;
            
            // 打乱拼图
            shufflePuzzle();
        }
        
        // 处理图片上传
        function handleImageUpload(e) {
            const file = e.target.files[0];
            if (!file) return;
            
            // 检查文件类型
            if (!file.type.match('image.*')) {
                alert('请上传图片文件!');
                return;
            }
            
            const reader = new FileReader();
            reader.onload = function(event) {
                // 创建图像对象以获取尺寸
                const img = new Image();
                img.onload = function() {
                    // 更新当前图像
                    currentImage = event.target.result;
                    previewImage.src = currentImage;
                    
                    // 如果游戏已经开始,重新开始游戏
                    if (gameStarted) {
                        startGame();
                    } else {
                        initGame();
                    }
                };
                img.src = event.target.result;
            };
            reader.readAsDataURL(file);
        }
        
        // 切换预览图显示/隐藏
        function togglePreview() {
            previewVisible = !previewVisible;
            previewImage.style.display = previewVisible ? 'block' : 'none';
            togglePreviewBtn.textContent = previewVisible ? '隐藏预览' : '显示预览';
        }
        
        // 事件监听器
        startBtn.addEventListener('click', startGame);
        shuffleBtn.addEventListener('click', shufflePuzzle);
        solveBtn.addEventListener('click', showSolution);
        difficultySelect.addEventListener('change', initGame);
        imageSelect.addEventListener('change', loadImage);
        togglePreviewBtn.addEventListener('click', togglePreview);
        imageUpload.addEventListener('change', handleImageUpload);
        uploadBtn.addEventListener('click', () => imageUpload.click());
        
        // 窗口大小调整处理
        window.addEventListener('resize', function() {
            // 在小屏幕上调整拼图大小
            if (window.innerWidth < 600) {
                puzzleWidth = Math.min(window.innerWidth - 40, 400);
                puzzleHeight = puzzleWidth; // 保持正方形
                
                // 更新拼图容器大小
                puzzleContainer.style.width = puzzleWidth + 'px';
                puzzleContainer.style.height = puzzleHeight + 'px';
                
                // 更新拼图块大小和位置
                pieceWidth = puzzleWidth / puzzleSize;
                pieceHeight = puzzleHeight / puzzleSize;
                
                for (let i = 0; i < puzzlePieces.length; i++) {
                    const piece = puzzlePieces[i];
                    const row = parseInt(piece.dataset.row);
                    const col = parseInt(piece.dataset.col);
                    
                    // 更新拼图块大小
                    piece.style.width = pieceWidth + 'px';
                    piece.style.height = pieceHeight + 'px';
                    
                    // 更新背景大小和位置
                    piece.style.backgroundSize = `${puzzleWidth}px ${puzzleHeight}px`;
                    piece.style.backgroundPosition = `-${col * pieceWidth}px -${row * pieceHeight}px`;
                    
                    // 更新正确位置数据
                    piece.dataset.correctLeft = col * pieceWidth;
                    piece.dataset.correctTop = row * pieceHeight;
                    
                    // 如果游戏未开始,更新当前位置
                    if (!gameStarted) {
                        piece.style.left = col * pieceWidth + 'px';
                        piece.style.top = row * pieceHeight + 'px';
                    }
                }
            }
        });
        
        // 初始化游戏
        window.onload = function() {
            initGame();
            shuffleBtn.disabled = true;
            solveBtn.disabled = true;
        };
    </script>
</body>
</html>
相关推荐
草帽lufei2 小时前
用 Gemini3 Flash 做了多半天开发,我离下岗又近了一步
google·ai编程·gemini
用户4099322502123 小时前
Vue3动态样式管理:如何混合class/style绑定、穿透scoped并优化性能?
前端·ai编程·trae
和平hepingfly5 小时前
Claude code 多种模型随时切换(最简单的方法)
ai编程
CoderLiz6 小时前
我的Code的使用
ai编程
前端阿森纳6 小时前
从五个关键维度重新审视 RAG 架构设计
llm·aigc·ai编程
AskHarries7 小时前
你看到什么,决定你成为什么
ai编程
LV技术派7 小时前
适合很多公司和团队的 AI Coding 落地范式(一)
前端·aigc·ai编程
飞哥数智坊8 小时前
TRAE 内 GPT-5.2 实测:10 轮对话,生成的代码一次都没让我撤回
人工智能·gpt·trae
HashTang20 小时前
【AI 编程实战】第 4 篇:一次完美 vs 五轮对话 - UnoCSS 配置的正确姿势
前端·uni-app·ai编程
量子位1 天前
是个公司都在用AI Agent,但大家真的用明白了吗??| MEET2026圆桌论坛
aigc·ai编程