打开/关闭菜单
933
1525
112
1.4万
植物大战僵尸杂交版Wiki
打开/关闭外观设置菜单
打开/关闭个人菜单
未登录
未登录用户的IP地址会在进行任意编辑后公开展示。

微件:推箱子:修订间差异

来自植物大战僵尸杂交版Wiki
创建页面,内容为“<includeonly> <div class="tuixiangzi-widget"> <!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"> <title>推箱子 📦</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { display: flex; justify-content: center; align-items: center; min-height: 100vh;…”
 
无编辑摘要
 
第105行: 第105行:
         const H = ROWS * CELL;
         const H = ROWS * CELL;


         // 边缘边界(箱子不能出现在这个范围内)
         // 边界限制:箱子不能出现在边缘
         const EDGE_MARGIN = 2; // 距离边界至少2格
         const EDGE_MARGIN = 2;


         // 游戏状态
         // 游戏状态
第118行: 第118行:
         let animationId = null;
         let animationId = null;


         // ---- 检查位置是否在内部区域(不在边缘) ----
         // ---- 检查位置是否在内部区域 ----
         function isInsideArea(x, y) {
         function isInsideArea(x, y) {
             return x >= EDGE_MARGIN && x < COLS - EDGE_MARGIN &&
             return x >= EDGE_MARGIN && x < COLS - EDGE_MARGIN &&
第124行: 第124行:
         }
         }


         // ---- 检查一个位置是否可通行 ----
         // ---- 检查位置是否可通行 ----
         function isWalkable(x, y) {
         function isWalkable(x, y) {
             if (x < 0 || x >= COLS || y < 0 || y >= ROWS) return false;
             if (x < 0 || x >= COLS || y < 0 || y >= ROWS) return false;
第131行: 第131行:
         }
         }


         // ---- 检查箱子是否在死角 ----
         // ---- 检查位置是否可站立(用于箱子摆放) ----
         function isBoxInDeadEnd(bx, by) {
        function isStandable(x, y, boxList, targetList) {
            if (!isWalkable(x, y)) return false;
            if (!isInsideArea(x, y)) return false;
            if (boxList.some(b => b.x === x && b.y === y)) return false;
            if (targetList.some(t => t.x === x && t.y === y)) return false;
            if (x === 1 && y === 1) return false;
            return true;
        }
 
        // ---- 深度检测:箱子是否在死角(全面版) ----
         function isBoxInDeadEnd(bx, by, boxList, targetList) {
            // 如果箱子已经在目标上,不算死角
            if (targetList.some(t => t.x === bx && t.y === by)) return false;
 
             const dirs = [[0, -1], [0, 1], [-1, 0], [1, 0]];
             const dirs = [[0, -1], [0, 1], [-1, 0], [1, 0]];
             let blockedDirections = 0;
             let movableDirs = [];
 
            for (const [dx, dy] of dirs) {
                const nx = bx + dx;
                const ny = by + dy;
                // 检查推动方向是否可通行
                if (!isWalkable(nx, ny)) continue;
                // 检查推动后箱子的新位置是否可通行
                const nx2 = bx + dx * 2;
                const ny2 = by + dy * 2;
                if (!isWalkable(nx2, ny2)) continue;
                if (boxList.some(b => b.x === nx2 && b.y === ny2)) continue;
                // 这个方向可以推动
                movableDirs.push([dx, dy]);
            }
 
            // 如果没有任何方向可以推动,箱子在死角
            if (movableDirs.length === 0) return true;
 
            // 如果只有一个方向可以推动,检查这个方向是否通向死路
            if (movableDirs.length === 1) {
                const [dx, dy] = movableDirs[0];
                // 检查推动后,箱子是否会进入新的死角
                const newX = bx + dx;
                const newY = by + dy;
                // 模拟推动后的状态
                const tempBoxes = boxList.map(b => ({ ...b }));
                const idx = tempBoxes.findIndex(b => b.x === bx && b.y === by);
                if (idx !== -1) {
                    tempBoxes[idx].x = newX;
                    tempBoxes[idx].y = newY;
                }
                // 检查推动后箱子是否在死角(递归检测,避免无限循环)
                if (isBoxInDeadEnd(newX, newY, tempBoxes, targetList)) {
                    return true;
                }
                // 检查推动后玩家能否到达箱子后面(推动位置)
                // 玩家需要站在 (bx - dx, by - dy) 位置来推箱子
                const playerPos = { x: bx - dx, y: by - dy };
                if (!isWalkable(playerPos.x, playerPos.y)) return true;
                // 检查玩家是否能到达这个位置(简化检查:位置必须可通行且不被箱子占据)
                if (tempBoxes.some(b => b.x === playerPos.x && b.y === playerPos.y)) return true;
            }
 
            // 检查箱子是否在角落且两个方向都是墙
            let wallCount = 0;
            for (const [dx, dy] of dirs) {
                const nx = bx + dx;
                const ny = by + dy;
                if (!isWalkable(nx, ny)) wallCount++;
            }
            if (wallCount >= 3) return true;


            // 特殊检查:箱子在两个墙之间(水平或垂直夹住)
            // 水平方向被夹住
            const leftBlocked = !isWalkable(bx - 1, by);
            const rightBlocked = !isWalkable(bx + 1, by);
            if (leftBlocked && rightBlocked) return true;
            // 垂直方向被夹住
            const upBlocked = !isWalkable(bx, by - 1);
            const downBlocked = !isWalkable(bx, by + 1);
            if (upBlocked && downBlocked) return true;
            // 检查箱子是否在"凹"形死角中
            // 如果三个方向被挡住,且第四个方向的延伸也是死路
            const openDirs = [];
             for (const [dx, dy] of dirs) {
             for (const [dx, dy] of dirs) {
                 const nx = bx + dx;
                 const nx = bx + dx;
                 const ny = by + dy;
                 const ny = by + dy;
                 if (!isWalkable(nx, ny)) {
                 if (isWalkable(nx, ny) && !boxList.some(b => b.x === nx && b.y === ny)) {
                     blockedDirections++;
                     openDirs.push([dx, dy]);
                 } else {
                 }
                    const nx2 = bx + dx * 2;
            }
                    const ny2 = by + dy * 2;
            if (openDirs.length === 1) {
                     if (!isWalkable(nx2, ny2) || boxes.some(b => b.x === nx2 && b.y === ny2)) {
                const [dx, dy] = openDirs[0];
                        blockedDirections++;
                // 检查这个方向的尽头是否也是死路
                let steps = 0;
                let cx = bx + dx;
                let cy = by + dy;
                while (steps < 5) {
                    steps++;
                    // 检查这个位置是否可通行
                     if (!isWalkable(cx, cy)) break;
                    // 检查周围的墙数
                    let aroundWall = 0;
                    for (const [ddx, ddy] of dirs) {
                        const nx2 = cx + ddx;
                        const ny2 = cy + ddy;
                        if (!isWalkable(nx2, ny2)) aroundWall++;
                     }
                     }
                    if (aroundWall >= 3) return true;
                    // 继续向前
                    cx += dx;
                    cy += dy;
                 }
                 }
             }
             }
             return blockedDirections >= 3;
 
             return false;
         }
         }


         // ---- 检查箱子是否可到达目标 ----
         // ---- 检查箱子是否可到达目标(BFS模拟) ----
         function canBoxReachTarget(boxesList, targetsList) {
         function canBoxReachTarget(boxList, targetList) {
             for (const box of boxesList) {
             for (const box of boxList) {
                 if (targetsList.some(t => t.x === box.x && t.y === box.y)) continue;
                 if (targetList.some(t => t.x === box.x && t.y === box.y)) continue;
                 if (isBoxInDeadEnd(box.x, box.y)) return false;
                // 使用更严格的死角检测
 
                 if (isBoxInDeadEnd(box.x, box.y, boxList, targetList)) {
                 let hasReachableTarget = false;
                    return false;
                 for (const target of targetsList) {
                }
                // 检查是否有目标在合理范围内
                 let hasTarget = false;
                 for (const target of targetList) {
                     const dist = Math.abs(box.x - target.x) + Math.abs(box.y - target.y);
                     const dist = Math.abs(box.x - target.x) + Math.abs(box.y - target.y);
                     if (dist < 6) {
                     if (dist < 8) {
                         hasReachableTarget = true;
                         hasTarget = true;
                         break;
                         break;
                     }
                     }
                 }
                 }
                 if (!hasReachableTarget) return false;
                 if (!hasTarget) return false;
             }
             }
             return true;
             return true;
        }
        // ---- 检查目标位置是否合理 ----
        function isTargetValid(tx, ty, targetList) {
            if (!isInsideArea(tx, ty)) return false;
            if (!isWalkable(tx, ty)) return false;
            if (targetList.some(t => t.x === tx && t.y === ty)) return false;
            // 目标不能在角落(至少有两个相邻的可通行方向)
            let walkableNeighbors = 0;
            const dirs = [[0, 1], [0, -1], [1, 0], [-1, 0]];
            for (const [dx, dy] of dirs) {
                if (isWalkable(tx + dx, ty + dy)) walkableNeighbors++;
            }
            return walkableNeighbors >= 2;
         }
         }


第176行: 第288行:
             let valid = false;
             let valid = false;


             while (!valid && attempts < 100) {
             while (!valid && attempts < 200) {
                 attempts++;
                 attempts++;
                 walls = [];
                 walls = [];
第192行: 第304行:
                 }
                 }


                 // 生成内部障碍物(只在内部区域)
                 // 生成内部障碍物(内部区域)
                 const numWalls = 3 + (lvl % 4);
                 const numWalls = 3 + (lvl % 4);
                 for (let i = 0; i < numWalls; i++) {
                 for (let i = 0; i < numWalls; i++) {
第210行: 第322行:
                 }
                 }


                 // 生成目标和箱子(都在内部区域)
                 // 生成目标(内部区域,且不在死角)
                 const numBoxes = Math.min(3 + Math.floor(lvl / 2), 4);
                 const numBoxes = Math.min(3 + Math.floor(lvl / 2), 4);
                // 先生成目标位置
                 for (let i = 0; i < numBoxes; i++) {
                 for (let i = 0; i < numBoxes; i++) {
                     let tx, ty;
                     let tx, ty;
第221行: 第331行:
                         ty = EDGE_MARGIN + Math.floor(Math.random() * (ROWS - EDGE_MARGIN * 2));
                         ty = EDGE_MARGIN + Math.floor(Math.random() * (ROWS - EDGE_MARGIN * 2));
                         attempts2++;
                         attempts2++;
                     } while (attempts2 < 80 && (
                     } while (attempts2 < 100 && !isTargetValid(tx, ty, targets));
                        (tx === 1 && ty === 1) ||
                     if (attempts2 < 100) {
                        walls.some(w => w.x === tx && w.y === ty) ||
                        targets.some(t => t.x === tx && t.y === ty) ||
                        !isInsideArea(tx, ty) ||
                        (isWalkable(tx - 1, ty) + isWalkable(tx + 1, ty) +
                        isWalkable(tx, ty - 1) + isWalkable(tx, ty + 1) < 2)
                    ));
                     if (attempts2 < 80) {
                         targets.push({ x: tx, y: ty });
                         targets.push({ x: tx, y: ty });
                     }
                     }
第236行: 第339行:
                 if (targets.length < numBoxes) continue;
                 if (targets.length < numBoxes) continue;


                 // 生成箱子(在目标周围)
                 // 生成箱子(在目标周围,且不在死角)
                 for (let i = 0; i < targets.length; i++) {
                 for (let i = 0; i < targets.length; i++) {
                     let bx, by;
                     let bx, by;
第249行: 第352行:
                     }
                     }


                    // 先尝试近距离
                     for (const [dx, dy] of dirs) {
                     for (const [dx, dy] of dirs) {
                         const nx = target.x + dx;
                         const nx = target.x + dx;
                         const ny = target.y + dy;
                         const ny = target.y + dy;
                         if (isInsideArea(nx, ny) &&
                         if (isStandable(nx, ny, boxes, targets)) {
                             isWalkable(nx, ny) &&
                             // 检查这个位置是否是死角
                             !boxes.some(b => b.x === nx && b.y === ny) &&
                             const testBoxes = boxes.map(b => ({ ...b }));
                             !targets.some(t => t.x === nx && t.y === ny) &&
                             testBoxes.push({ x: nx, y: ny });
                             !(nx === 1 && ny === 1) &&
                             if (!isBoxInDeadEnd(nx, ny, testBoxes, targets)) {
                            !isBoxInDeadEnd(nx, ny)) {
                                bx = nx;
                            bx = nx;
                                by = ny;
                            by = ny;
                                found = true;
                            found = true;
                                break;
                            break;
                            }
                         }
                         }
                     }
                     }
第267行: 第371行:
                     // 如果没找到,扩大搜索范围
                     // 如果没找到,扩大搜索范围
                     if (!found) {
                     if (!found) {
                         for (let dx = -3; dx <= 3; dx++) {
                        const searchRadius = 4;
                             for (let dy = -3; dy <= 3; dy++) {
                         for (let dx = -searchRadius; dx <= searchRadius; dx++) {
                             for (let dy = -searchRadius; dy <= searchRadius; dy++) {
                                 if (dx === 0 && dy === 0) continue;
                                 if (dx === 0 && dy === 0) continue;
                                 const nx = target.x + dx;
                                 const nx = target.x + dx;
                                 const ny = target.y + dy;
                                 const ny = target.y + dy;
                                 if (isInsideArea(nx, ny) &&
                                 if (isStandable(nx, ny, boxes, targets)) {
                                    isWalkable(nx, ny) &&
                                     const testBoxes = boxes.map(b => ({ ...b }));
                                     !boxes.some(b => b.x === nx && b.y === ny) &&
                                     testBoxes.push({ x: nx, y: ny });
                                     !targets.some(t => t.x === nx && t.y === ny) &&
                                     if (!isBoxInDeadEnd(nx, ny, testBoxes, targets)) {
                                     !(nx === 1 && ny === 1) &&
                                        bx = nx;
                                    !isBoxInDeadEnd(nx, ny)) {
                                        by = ny;
                                    bx = nx;
                                        found = true;
                                    by = ny;
                                        break;
                                    found = true;
                                    }
                                    break;
                                 }
                                 }
                             }
                             }
第295行: 第399行:
                 if (boxes.length < targets.length) continue;
                 if (boxes.length < targets.length) continue;


                 // 检查所有箱子是否在内部区域
                 // 最终验证:所有箱子都不在死角
                 let allInside = true;
                 let allValid = true;
                 for (const box of boxes) {
                 for (const box of boxes) {
                     if (!isInsideArea(box.x, box.y)) {
                     if (!isInsideArea(box.x, box.y)) {
                         allInside = false;
                         allValid = false;
                         break;
                         break;
                     }
                     }
                }
                     if (isBoxInDeadEnd(box.x, box.y, boxes, targets)) {
                if (!allInside) continue;
                         allValid = false;
 
                // 检查箱子是否在死角
                let hasDeadEnd = false;
                for (const box of boxes) {
                     if (isBoxInDeadEnd(box.x, box.y)) {
                         hasDeadEnd = true;
                         break;
                         break;
                     }
                     }
                 }
                 }
                 if (hasDeadEnd) continue;
                 if (!allValid) continue;


                 // 检查箱子是否可到达目标
                 // 验证箱子可到达目标
                 if (!canBoxReachTarget(boxes, targets)) continue;
                 if (!canBoxReachTarget(boxes, targets)) continue;


第334行: 第432行:
                     walls.push({ x: COLS - 1, y: i });
                     walls.push({ x: COLS - 1, y: i });
                 }
                 }
                 // 添加一些内部墙
                 // 预设内部墙
                 const presetWalls = [[3, 2], [6, 2], [2, 5], [7, 5], [4, 7]];
                 const presetWalls = [[3, 2], [6, 2], [2, 5], [7, 5]];
                 for (const [wx, wy] of presetWalls) {
                 for (const [wx, wy] of presetWalls) {
                     if (isInsideArea(wx, wy)) {
                     if (isInsideArea(wx, wy)) {
第341行: 第439行:
                     }
                     }
                 }
                 }
                 // 预设箱子和目标(都在内部区域)
                 // 预设箱子和目标(都在内部区域,且不在死角)
                 const presetBoxes = [[3, 3], [5, 4], [4, 6]];
                 const presetBoxes = [[3, 3], [5, 4], [4, 6]];
                 const presetTargets = [[6, 3], [3, 5], [7, 6]];
                 const presetTargets = [[6, 3], [3, 5], [7, 6]];
第438行: 第536行:
             ctx.clearRect(0, 0, W, H);
             ctx.clearRect(0, 0, W, H);


            // 绘制网格
             for (let r = 0; r < ROWS; r++) {
             for (let r = 0; r < ROWS; r++) {
                 for (let c = 0; c < COLS; c++) {
                 for (let c = 0; c < COLS; c++) {
第446行: 第543行:
             }
             }


            // 绘制墙壁
             for (const wall of walls) {
             for (const wall of walls) {
                 ctx.fillStyle = '#2d2d4a';
                 ctx.fillStyle = '#2d2d4a';
第455行: 第551行:
             ctx.shadowBlur = 0;
             ctx.shadowBlur = 0;


            // 绘制目标
             for (const target of targets) {
             for (const target of targets) {
                 ctx.fillStyle = 'rgba(251, 191, 36, 0.15)';
                 ctx.fillStyle = 'rgba(251, 191, 36, 0.15)';
第466行: 第561行:
             }
             }


            // 绘制箱子
             for (const box of boxes) {
             for (const box of boxes) {
                 const onTarget = targets.some(t => t.x === box.x && t.y === box.y);
                 const onTarget = targets.some(t => t.x === box.x && t.y === box.y);
第481行: 第575行:
             }
             }


            // 绘制玩家
             ctx.fillStyle = '#60a5fa';
             ctx.fillStyle = '#60a5fa';
             ctx.shadowColor = '#60a5fa';
             ctx.shadowColor = '#60a5fa';
第504行: 第597行:
             ctx.fill();
             ctx.fill();


            // 胜利信息
             if (gameOver) {
             if (gameOver) {
                 ctx.fillStyle = 'rgba(0,0,0,0.4)';
                 ctx.fillStyle = 'rgba(0,0,0,0.4)';
第518行: 第610行:
             }
             }


            // 显示剩余箱子
             ctx.fillStyle = 'rgba(255,255,255,0.08)';
             ctx.fillStyle = 'rgba(255,255,255,0.08)';
             ctx.font = '12px Arial';
             ctx.font = '12px Arial';

2026年7月19日 (日) 05:30的最新版本