Spaces:
Running
Running
| <html lang="zh-CN"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>推箱子大冒险 - 儿童益智游戏</title> | |
| <style> | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| :root { | |
| --primary-color: #6366f1; | |
| --secondary-color: #8b5cf6; | |
| --success-color: #10b981; | |
| --warning-color: #f59e0b; | |
| --danger-color: #ef4444; | |
| --dark-color: #1f2937; | |
| --light-color: #f3f4f6; | |
| --game-bg: #fef3c7; | |
| --wall-color: #92400e; | |
| --box-color: #d97706; | |
| --box-on-target: #059669; | |
| --target-color: #fbbf24; | |
| --player-color: #3b82f6; | |
| } | |
| body { | |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| min-height: 100vh; | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| padding: 20px; | |
| position: relative; | |
| overflow-x: hidden; | |
| } | |
| body::before { | |
| content: ''; | |
| position: absolute; | |
| top: -50%; | |
| left: -50%; | |
| width: 200%; | |
| height: 200%; | |
| background: repeating-linear-gradient(45deg, | |
| transparent, | |
| transparent 10px, | |
| rgba(255, 255, 255, 0.05) 10px, | |
| rgba(255, 255, 255, 0.05) 20px); | |
| animation: backgroundMove 20s linear infinite; | |
| } | |
| @keyframes backgroundMove { | |
| 0% { | |
| transform: translate(0, 0); | |
| } | |
| 100% { | |
| transform: translate(50px, 50px); | |
| } | |
| } | |
| header { | |
| background: rgba(255, 255, 255, 0.95); | |
| padding: 20px 40px; | |
| border-radius: 20px; | |
| box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2); | |
| margin-bottom: 30px; | |
| text-align: center; | |
| backdrop-filter: blur(10px); | |
| position: relative; | |
| z-index: 10; | |
| } | |
| h1 { | |
| color: var(--primary-color); | |
| font-size: 2.5rem; | |
| margin-bottom: 10px; | |
| text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1); | |
| animation: titleBounce 2s ease-in-out infinite; | |
| } | |
| @keyframes titleBounce { | |
| 0%, | |
| 100% { | |
| transform: translateY(0); | |
| } | |
| 50% { | |
| transform: translateY(-5px); | |
| } | |
| } | |
| .subtitle { | |
| color: var(--secondary-color); | |
| font-size: 1.2rem; | |
| margin-bottom: 15px; | |
| } | |
| .built-with { | |
| color: var(--dark-color); | |
| font-size: 0.9rem; | |
| opacity: 0.7; | |
| transition: opacity 0.3s; | |
| } | |
| .built-with:hover { | |
| opacity: 1; | |
| text-decoration: underline; | |
| } | |
| main { | |
| background: rgba(255, 255, 255, 0.95); | |
| padding: 30px; | |
| border-radius: 20px; | |
| box-shadow: 0 15px 40px rgba(0, 0, 0, 0.2); | |
| max-width: 800px; | |
| width: 100%; | |
| backdrop-filter: blur(10px); | |
| position: relative; | |
| z-index: 10; | |
| } | |
| .level-selector { | |
| display: none; | |
| grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); | |
| gap: 15px; | |
| margin-bottom: 30px; | |
| } | |
| .level-selector.active { | |
| display: grid; | |
| } | |
| .level-btn { | |
| background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); | |
| color: white; | |
| border: none; | |
| padding: 15px; | |
| border-radius: 15px; | |
| font-size: 1.1rem; | |
| font-weight: bold; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .level-btn::before { | |
| content: ''; | |
| position: absolute; | |
| top: 50%; | |
| left: 50%; | |
| width: 0; | |
| height: 0; | |
| background: rgba(255, 255, 255, 0.3); | |
| border-radius: 50%; | |
| transform: translate(-50%, -50%); | |
| transition: width 0.6s, height 0.6s; | |
| } | |
| .level-btn:hover::before { | |
| width: 300px; | |
| height: 300px; | |
| } | |
| .level-btn:hover { | |
| transform: translateY(-3px); | |
| box-shadow: 0 10px 20px rgba(99, 102, 241, 0.3); | |
| } | |
| .level-btn:disabled { | |
| opacity: 0.5; | |
| cursor: not-allowed; | |
| } | |
| .level-btn.completed { | |
| background: linear-gradient(135deg, var(--success-color), #059669); | |
| } | |
| .level-btn.current { | |
| animation: pulse 2s infinite; | |
| } | |
| @keyframes pulse { | |
| 0% { | |
| box-shadow: 0 0 0 0 rgba(99, 102, 241, 0.7); | |
| } | |
| 70% { | |
| box-shadow: 0 0 0 10px rgba(99, 102, 241, 0); | |
| } | |
| 100% { | |
| box-shadow: 0 0 0 0 rgba(99, 102, 241, 0); | |
| } | |
| } | |
| .game-info { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-bottom: 20px; | |
| flex-wrap: wrap; | |
| gap: 15px; | |
| } | |
| .info-item { | |
| background: linear-gradient(135deg, #f3f4f6, #e5e7eb); | |
| padding: 10px 20px; | |
| border-radius: 10px; | |
| display: flex; | |
| align-items: center; | |
| gap: 10px; | |
| box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); | |
| } | |
| .info-label { | |
| color: var(--dark-color); | |
| font-weight: bold; | |
| } | |
| .info-value { | |
| color: var(--primary-color); | |
| font-size: 1.2rem; | |
| font-weight: bold; | |
| } | |
| .game-container { | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| gap: 20px; | |
| } | |
| .game-board { | |
| display: grid; | |
| gap: 2px; | |
| padding: 20px; | |
| background: var(--game-bg); | |
| border-radius: 15px; | |
| box-shadow: inset 0 2px 10px rgba(0, 0, 0, 0.1); | |
| position: relative; | |
| } | |
| .cell { | |
| width: 50px; | |
| height: 50px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-size: 30px; | |
| transition: all 0.3s ease; | |
| position: relative; | |
| } | |
| .cell.wall { | |
| background: linear-gradient(135deg, var(--wall-color), #78350f); | |
| border-radius: 5px; | |
| box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3); | |
| } | |
| .cell.floor { | |
| background: rgba(255, 255, 255, 0.3); | |
| border-radius: 5px; | |
| } | |
| .cell.target { | |
| background: var(--target-color); | |
| border-radius: 50%; | |
| animation: targetPulse 2s infinite; | |
| } | |
| @keyframes targetPulse { | |
| 0%, | |
| 100% { | |
| transform: scale(1); | |
| opacity: 0.8; | |
| } | |
| 50% { | |
| transform: scale(1.1); | |
| opacity: 1; | |
| } | |
| } | |
| .player { | |
| animation: playerBounce 0.5s ease; | |
| } | |
| @keyframes playerBounce { | |
| 0%, | |
| 100% { | |
| transform: translateY(0); | |
| } | |
| 50% { | |
| transform: translateY(-10px); | |
| } | |
| } | |
| .box { | |
| animation: boxMove 0.3s ease; | |
| } | |
| @keyframes boxMove { | |
| 0% { | |
| transform: scale(1); | |
| } | |
| 50% { | |
| transform: scale(1.1); | |
| } | |
| 100% { | |
| transform: scale(1); | |
| } | |
| } | |
| .box-on-target { | |
| animation: successPulse 1s infinite; | |
| } | |
| @keyframes successPulse { | |
| 0%, | |
| 100% { | |
| transform: scale(1) rotate(0deg); | |
| } | |
| 50% { | |
| transform: scale(1.1) rotate(5deg); | |
| } | |
| } | |
| .controls { | |
| display: flex; | |
| gap: 10px; | |
| justify-content: center; | |
| flex-wrap: wrap; | |
| } | |
| .btn { | |
| background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); | |
| color: white; | |
| border: none; | |
| padding: 12px 24px; | |
| border-radius: 10px; | |
| font-size: 1rem; | |
| font-weight: bold; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| } | |
| .btn:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 5px 15px rgba(99, 102, 241, 0.3); | |
| } | |
| .btn:active { | |
| transform: translateY(0); | |
| } | |
| .btn-secondary { | |
| background: linear-gradient(135deg, #6b7280, #4b5563); | |
| } | |
| .btn-success { | |
| background: linear-gradient(135deg, var(--success-color), #059669); | |
| } | |
| .btn-danger { | |
| background: linear-gradient(135deg, var(--danger-color), #dc2626); | |
| } | |
| .arrow-keys { | |
| display: grid; | |
| grid-template-columns: repeat(3, 60px); | |
| grid-template-rows: repeat(3, 60px); | |
| gap: 5px; | |
| margin-top: 20px; | |
| } | |
| .arrow-key { | |
| background: linear-gradient(135deg, #e5e7eb, #d1d5db); | |
| border: 2px solid #9ca3af; | |
| border-radius: 10px; | |
| font-size: 24px; | |
| cursor: pointer; | |
| transition: all 0.2s ease; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| } | |
| .arrow-key:hover { | |
| background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); | |
| border-color: var(--primary-color); | |
| transform: scale(1.05); | |
| } | |
| .arrow-key:active { | |
| transform: scale(0.95); | |
| } | |
| .arrow-key.up { | |
| grid-column: 2; | |
| grid-row: 1; | |
| } | |
| .arrow-key.left { | |
| grid-column: 1; | |
| grid-row: 2; | |
| } | |
| .arrow-key.down { | |
| grid-column: 2; | |
| grid-row: 2; | |
| } | |
| .arrow-key.right { | |
| grid-column: 3; | |
| grid-row: 2; | |
| } | |
| .win-message { | |
| display: none; | |
| position: fixed; | |
| top: 50%; | |
| left: 50%; | |
| transform: translate(-50%, -50%); | |
| background: white; | |
| padding: 40px; | |
| border-radius: 20px; | |
| box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); | |
| text-align: center; | |
| z-index: 1000; | |
| animation: winPop 0.5s ease; | |
| } | |
| @keyframes winPop { | |
| 0% { | |
| transform: translate(-50%, -50%) scale(0); | |
| } | |
| 50% { | |
| transform: translate(-50%, -50%) scale(1.1); | |
| } | |
| 100% { | |
| transform: translate(-50%, -50%) scale(1); | |
| } | |
| } | |
| .win-message.active { | |
| display: block; | |
| } | |
| .win-title { | |
| font-size: 2rem; | |
| color: var(--success-color); | |
| margin-bottom: 20px; | |
| } | |
| .stars { | |
| font-size: 3rem; | |
| margin-bottom: 20px; | |
| animation: starSpin 1s ease; | |
| } | |
| @keyframes starSpin { | |
| 0% { | |
| transform: rotate(0deg) scale(0); | |
| } | |
| 100% { | |
| transform: rotate(360deg) scale(1); | |
| } | |
| } | |
| .overlay { | |
| display: none; | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| background: rgba(0, 0, 0, 0.5); | |
| z-index: 999; | |
| } | |
| .overlay.active { | |
| display: block; | |
| } | |
| @media (max-width: 768px) { | |
| h1 { | |
| font-size: 2rem; | |
| } | |
| .cell { | |
| width: 40px; | |
| height: 40px; | |
| font-size: 24px; | |
| } | |
| .arrow-keys { | |
| grid-template-columns: repeat(3, 50px); | |
| grid-template-rows: repeat(3, 50px); | |
| } | |
| .game-board { | |
| padding: 15px; | |
| } | |
| } | |
| @media (max-width: 480px) { | |
| .cell { | |
| width: 30px; | |
| height: 30px; | |
| font-size: 18px; | |
| } | |
| .arrow-keys { | |
| grid-template-columns: repeat(3, 40px); | |
| grid-template-rows: repeat(3, 40px); | |
| } | |
| main { | |
| padding: 20px; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <header> | |
| <h1>🎮 推箱子大冒险 📦</h1> | |
| <div class="subtitle">和小朋友一起玩益智游戏!</div> | |
| <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="built-with"> | |
| Built with anycoder | |
| </a> | |
| </header> | |
| <main> | |
| <div class="level-selector active" id="levelSelector"> | |
| <button class="level-btn" onclick="startLevel(0)">关卡 1<br>🌱 简单</button> | |
| <button class="level-btn" onclick="startLevel(1)">关卡 2<br>🌿 简单</button> | |
| <button class="level-btn" onclick="startLevel(2)">关卡 3<br>🍀 中等</button> | |
| <button class="level-btn" onclick="startLevel(3)">关卡 4<br>🌳 中等</button> | |
| <button class="level-btn" onclick="startLevel(4)">关卡 5<br>🌲 困难</button> | |
| <button class="level-btn" onclick="startLevel(5)">关卡 6<br>🎄 困难</button> | |
| </div> | |
| <div class="game-container" id="gameContainer" style="display: none;"> | |
| <div class="game-info"> | |
| <div class="info-item"> | |
| <span class="info-label">关卡:</span> | |
| <span class="info-value" id="currentLevel">1</span> | |
| </div> | |
| <div class="info-item"> | |
| <span class="info-label">步数:</span> | |
| <span class="info-value" id="moveCount">0</span> | |
| </div> | |
| <div class="info-item"> | |
| <span class="info-label">最佳:</span> | |
| <span class="info-value" id="bestScore">-</span> | |
| </div> | |
| </div> | |
| <div class="game-board" id="gameBoard"></div> | |
| <div class="controls"> | |
| <button class="btn btn-secondary" onclick="undoMove()"> | |
| ↶ 撤销 | |
| </button> | |
| <button class="btn btn-danger" onclick="resetLevel()"> | |
| 🔄 重置 | |
| </button> | |
| <button class="btn btn-success" onclick="backToMenu()"> | |
| 📋 选关 | |
| </button> | |
| </div> | |
| <div class="arrow-keys"> | |
| <button class="arrow-key up" onclick="movePlayer('up')">↑</button> | |
| <button class="arrow-key left" onclick="movePlayer('left')">←</button> | |
| <button class="arrow-key down" onclick="movePlayer('down')">↓</button> | |
| <button class="arrow-key right" onclick="movePlayer('right')">→</button> | |
| </div> | |
| </div> | |
| </main> | |
| <div class="overlay" id="overlay"></div> | |
| <div class="win-message" id="winMessage"> | |
| <div class="win-title">🎉 恭喜过关!</div> | |
| <div class="stars">⭐⭐⭐</div> | |
| <div style="margin-bottom: 20px;"> | |
| 用了 <span id="finalMoves" style="color: var(--primary-color); font-weight: bold;">0</span> 步完成! | |
| </div> | |
| <div class="controls" style="justify-content: center;"> | |
| <button class="btn btn-success" onclick="nextLevel()">下一关</button> | |
| <button class="btn btn-secondary" onclick="backToMenu()">选关菜单</button> | |
| </div> | |
| </div> | |
| <script> | |
| // 游戏数据 | |
| const levels = [ | |
| { | |
| name: "入门练习", | |
| width: 7, | |
| height: 7, | |
| map: [ | |
| "#######", | |
| "# #", | |
| "# $@$.#", | |
| "# $ #", | |
| "# . #", | |
| "# #", | |
| "#######" | |
| ] | |
| }, | |
| { | |
| name: "双箱挑战", | |
| width: 8, | |
| height: 7, | |
| map: [ | |
| "########", | |
| "# . #", | |
| "# $@$. #", | |
| "# $ #", | |
| "# . #", | |
| "# #", | |
| "########" | |
| ] | |
| }, | |
| { | |
| name: "转角遇到爱", | |
| width: 9, | |
| height: 8, | |
| map: [ | |
| "#########", | |
| "# # #", | |
| "# $ @ $ #", | |
| "# . # . #", | |
| "# # #", | |
| "# $ $ #", | |
| "# . . #", | |
| "#########" | |
| ] | |
| }, | |
| { | |
| name: "迷宫探险", | |
| width: 10, | |
| height: 8, | |
| map: [ | |
| "##########", | |
| "# # #", | |
| "# $ @ $ #", | |
| "# . # . #", | |
| "# # #", | |
| "# $ $ #", | |
| "# . . #", | |
| "##########" | |
| ] | |
| }, | |
| { | |
| name: "极限挑战", | |
| width: 11, | |
| height: 9, | |
| map: [ | |
| "###########", | |
| "# # #", | |
| "# $ @ $ #", | |
| "# . # . #", | |
| "# # #", | |
| "# $ $ $ #", | |
| "# . . . . #", | |
| "# #", | |
| "###########" | |
| ] | |
| }, | |
| { | |
| name: "终极考验", | |
| width: 12, | |
| height: 10, | |
| map: [ | |
| "############", | |
| "# # #", | |
| "# $ @ $ #", | |
| "# . # . #", | |
| "# # #", | |
| "# $ $ $ #", | |
| "# . . . . . #", | |
| "# # #", | |
| "# $ $ $ #", | |
| "############" | |
| ] | |
| } | |
| ]; | |
| let currentLevelIndex = 0; | |
| let gameState = null; | |
| let moveHistory = []; | |
| let completedLevels = JSON.parse(localStorage.getItem('completedLevels') || '[]'); | |
| let bestScores = JSON.parse(localStorage.getItem('bestScores') || '{}'); | |
| // 初始化 | |
| function init() { | |
| updateLevelButtons(); | |
| setupKeyboardControls(); | |
| } | |
| // 更新关卡按钮状态 | |
| function updateLevelButtons() { | |
| const buttons = document.querySelectorAll('.level-btn'); | |
| buttons.forEach((btn, index) => { | |
| if (completedLevels.includes(index)) { | |
| btn.classList.add('completed'); | |
| } else { | |
| btn.classList.remove('completed'); | |
| } | |
| }); | |
| } | |
| // 开始关卡 | |
| function startLevel(levelIndex) { | |
| currentLevelIndex = levelIndex; | |
| const level = levels[levelIndex]; | |
| gameState = { | |
| map: JSON.parse(JSON.stringify(level.map)), | |
| playerPos: findPlayer(level.map), | |
| boxes: findBoxes(level.map), | |
| targets: findTargets(level.map), | |
| moveCount: 0 | |
| }; | |
| moveHistory = []; | |
| document.getElementById('levelSelector').classList.remove('active'); | |
| document.getElementById('gameContainer').style.display = 'flex'; | |
| document.getElementById('currentLevel').textContent = levelIndex + 1; | |
| document.getElementById('moveCount').textContent = '0'; | |
| document.getElementById('bestScore').textContent = bestScores[levelIndex] || '-'; | |
| renderGame(); | |
| } | |
| // 渲染游戏 | |
| function renderGame() { | |
| const board = document.getElementById('gameBoard'); | |
| const level = levels[currentLevelIndex]; | |
| board.style.gridTemplateColumns = `repeat(${level.width}, 1fr)`; | |
| board.innerHTML = ''; | |
| for (let y = 0; y < level.height; y++) { | |
| for (let x = 0; x < level.width; x++) { | |
| const cell = document.createElement('div'); | |
| cell.className = 'cell'; | |
| const char = gameState.map[y][x]; | |
| const isTarget = gameState.targets.some(t => t.x === x && t.y === y); | |
| const hasBox = gameState.boxes.some(b => b.x === x && b.y === y); | |
| const isPlayer = gameState.playerPos.x === x && gameState.playerPos.y === y; | |
| if (char === '#') { | |
| cell.classList.add('wall'); | |
| cell.textContent = '🧱'; | |
| } else if (isTarget && !hasBox && !isPlayer) { | |
| cell.classList.add('target'); | |
| cell.textContent = '🎯'; | |
| } else { | |
| cell.classList.add('floor'); | |
| if (hasBox) { | |
| cell.classList.add('box'); | |
| if (isTarget) { | |
| cell.classList.add('box-on-target'); | |
| cell.textContent = '✅'; | |
| } else { | |
| cell.textContent = '📦'; | |
| } | |
| } else if (isPlayer) { | |
| cell.classList.add('player'); | |
| cell.textContent = '🤖'; | |
| } | |
| } | |
| board.appendChild(cell); | |
| } | |
| } | |
| } | |
| // 查找玩家位置 | |
| function findPlayer(map) { | |
| for (let y = 0; y < map.length; y++) { | |
| for (let x = 0; x < map[y].length; x++) { | |
| if (map[y][x] === '@') { | |
| return { x, y }; | |
| } | |
| } | |
| } | |
| return null; | |
| } | |
| // 查找箱子位置 | |
| function findBoxes(map) { | |
| const boxes = []; | |
| for (let y = 0; y < map.length; y++) { | |
| for (let x = 0; x < map[y].length; x++) { | |
| if (map[y][x] === '$') { | |
| boxes.push({ x, y }); | |
| } | |
| } | |
| } | |
| return boxes; | |
| } | |
| // 查找目标位置 | |
| function findTargets(map) { | |
| const targets = []; | |
| for (let y = 0; y < map.length; y++) { | |
| for (let x = 0; x < map[y].length; x++) { | |
| if (map[y][x] === '.') { | |
| targets.push({ x, y }); | |
| } | |
| } | |
| } | |
| return targets; | |
| } | |
| // 移动玩家 | |
| function movePlayer(direction) { | |
| if (!gameState) return; | |
| const dirs = { | |
| 'up': { x: 0, y: -1 }, | |
| 'down': { x: 0, y: 1 }, | |
| 'left': { x: -1, y: 0 }, | |
| 'right': { x: 1, y: 0 } | |
| }; | |
| const dir = dirs[direction]; | |
| const newPos = { | |
| x: gameState.playerPos.x + dir.x, | |
| y: gameState.playerPos.y + dir.y | |
| }; | |
| // 保存历史 | |
| const history = { | |
| playerPos: { ...gameState.playerPos }, | |
| boxes: gameState.boxes.map(b => ({ ...b })), | |
| moveCount: gameState.moveCount | |
| }; | |
| // 检查是否撞墙 | |
| if (gameState.map[newPos.y][newPos.x] === '#') { | |
| return; | |
| } | |
| // 检查是否有箱子 | |
| const boxIndex = gameState.boxes.findIndex(b => b.x === newPos.x && b.y === newPos.y); | |
| if (boxIndex !== -1) { | |
| const newBoxPos = { | |
| x: newPos.x + dir.x, | |
| y: newPos.y + dir.y | |
| }; | |
| // 检查箱子是否能移动 | |
| if (gameState.map[newBoxPos.y][newBoxPos.x] === '#') { | |
| return; | |
| } | |
| // 检查是否有其他箱子 | |
| if (gameState.boxes.some(b => b.x === newBoxPos.x && b.y === newBoxPos.y)) { | |
| return; | |
| } | |
| // 移动箱子 | |
| gameState.boxes[boxIndex] = newBoxPos; | |
| moveHistory.push(history); | |
| gameState.playerPos = newPos; | |
| gameState.moveCount++; | |
| } else { | |
| // 只移动玩家 | |
| moveHistory.push(history); | |
| gameState.playerPos = newPos; | |
| gameState.moveCount++; | |
| } | |
| document.getElementById('moveCount').textContent = gameState.moveCount; | |
| renderGame(); | |
| // 检查是否获胜 | |
| if (checkWin()) { | |
| setTimeout(() => showWinMessage(), 500); | |
| } | |
| } | |
| // 检查是否获胜 | |
| function checkWin() { | |
| return gameState.targets.every(target => | |
| gameState.boxes.some(box => box.x === target.x && box.y === target.y) | |
| ); | |
| } | |
| // 显示获胜消息 | |
| function showWinMessage() { | |
| if (!completedLevels.includes(currentLevelIndex)) { | |
| completedLevels.push(currentLevelIndex); | |
| localStorage.setItem('completedLevels', JSON.stringify(completedLevels)); | |
| } | |
| if (!bestScores[currentLevelIndex] || gameState.moveCount < bestScores[currentLevelIndex]) { | |
| bestScores[currentLevelIndex] = gameState.moveCount; | |
| localStorage.setItem('bestScores', JSON.stringify(bestScores)); | |
| } | |
| document.getElementById('finalMoves').textContent = gameState.moveCount; | |
| document.getElementById('overlay').classList.add('active'); | |
| document.getElementById('winMessage').classList.add('active'); | |
| } | |
| // 下一关 | |
| function nextLevel() { | |
| document.getElementById('overlay').classList.remove('active'); | |
| document.getElementById('winMessage').classList.remove('active'); | |
| if (currentLevelIndex < levels.length - 1) { | |
| startLevel(currentLevelIndex + 1); | |
| } else { | |
| backToMenu(); | |
| } | |
| } | |
| // 撤销移动 | |
| function undoMove() { | |
| if (moveHistory.length === 0) return; | |
| const history = moveHistory.pop(); | |
| gameState.playerPos = history.playerPos; | |
| gameState.boxes = history.boxes; | |
| gameState.moveCount = history.moveCount; | |
| document.getElementById('moveCount').textContent = gameState.moveCount; | |
| renderGame(); | |
| } | |
| // 重置关卡 | |
| function resetLevel() { | |
| startLevel(currentLevelIndex); | |
| } | |
| // 返回菜单 | |
| function backToMenu() { | |
| document.getElementById('overlay').classList.remove('active'); | |
| document.getElementById('winMessage').classList.remove('active'); | |
| document.getElementById('levelSelector').classList.add('active'); | |
| document.getElementById('gameContainer').style.display = 'none'; | |
| updateLevelButtons(); | |
| } | |
| // 设置键盘控制 | |
| function setupKeyboardControls() { | |
| document.addEventListener('keydown', (e) => { | |
| if (!gameState) return; | |
| const keyMap = { | |
| 'ArrowUp': 'up', | |
| 'ArrowDown': 'down', | |
| 'ArrowLeft': 'left', | |
| 'ArrowRight': 'right', | |
| 'w': 'up', | |
| 's': 'down', | |
| 'a': 'left', | |
| 'd': 'right', | |
| 'W': 'up', | |
| 'S': 'down', | |
| 'A': 'left', | |
| 'D': 'right' | |
| }; | |
| if (keyMap[e.key]) { | |
| e.preventDefault(); | |
| movePlayer(keyMap[e.key]); | |
| } | |
| if (e.key === 'z' || e.key === 'Z') { | |
| undoMove(); | |
| } | |
| if (e.key === 'r' || e.key === 'R') { | |
| resetLevel(); | |
| } | |
| }); | |
| } | |
| // 启动游戏 | |
| init(); | |
| </script> | |
| </body> | |
| </html> |