当前位置: 首页 > news >正文

11.8 脚本网页 推箱子

 <!DOCTYPE html>

<html lang="zh-CN">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">

    <title>智能推箱子 - 手机版</title>

    <style>

        /* 1. 基础样式重置 */

        * {

            margin: 0;

            padding: 0;

            box-sizing: border-box;

            -webkit-tap-highlight-color: transparent;

        }

 

        /* 2. 页面布局样式 - 竖屏适配 */

        body {

            font-family: 'Arial', sans-serif;

            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);

            min-height: 100vh;

            display: flex;

            flex-direction: column;

            align-items: center;

            padding: 0;

            overflow: hidden;

            position: relative;

        }

 

        /* 3. 游戏容器 - 收腰设计 */

        .game-container {

            width: 100%;

            max-width: 400px;

            height: 100vh;

            display: flex;

            flex-direction: column;

            position: relative;

        }

 

        /* 4. 顶部信息栏 */

        .game-header {

            background: rgba(255, 255, 255, 0.95);

            padding: 15px 20px;

            display: flex;

            justify-content: space-between;

            align-items: center;

            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);

            position: relative;

            z-index: 10;

        }

 

        .game-title {

            font-size: 20px;

            color: #764ba2;

            font-weight: bold;

        }

 

       /* 修改得分记录位置 - 往左移动 */

.info-board {

    display: flex;

    gap: 15px;

    font-size: 14px;

    font-weight: bold;

    color: #333;

    margin-right: 100px; /* 添加右边距,给重新开始按钮留出空间 */

}

 

 

 

        .info-item {

            display: flex;

            flex-direction: column;

            align-items: center;

        }

 

        .info-label {

            font-size: 10px;

            color: #666;

            margin-bottom: 2px;

        }

 

        .info-value {

            font-size: 16px;

            color: #764ba2;

        }

 

        /* 5. 重新开始按钮 - 右上角 */

        .restart-btn {

            position: absolute;

            right: 20px;

            top: 50%;

            transform: translateY(-50%);

            padding: 8px 15px;

            font-size: 12px;

            font-weight: bold;

            color: white;

            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);

            border: none;

            border-radius: 8px;

            cursor: pointer;

            transition: all 0.3s ease;

            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);

        }

 

        .restart-btn:hover {

            transform: translateY(-50%) scale(1.05);

        }

 

        .restart-btn:active {

            transform: translateY(-50%) scale(0.95);

        }

 

        /* 6. 游戏主体区域 - 收腰设计 */

        .game-main {

            flex: 1;

            display: flex;

            flex-direction: column;

            align-items: center;

            justify-content: center;

            padding: 20px 10px;

            position: relative;

        }

 

        /* 7. 游戏画布 - 中间收腰 */

        #gameCanvas {

            border: 3px solid #333;

            border-radius: 15px;

            background: #2c3e50;

            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);

            max-width: 90%;

            max-height: 60vh;

            display: block;

        }

 

/* 修改方向键位置 - 往右移动 */

.controls-container {

    position: fixed;

    bottom: 20px;

    left: 40px; /* 从20px改为40px,往右移动20px */

    z-index: 100;

}

 

 

        .controls {

            display: grid;

            grid-template-columns: repeat(3, 50px);

            gap: 5px;

        }

 

        .control-btn {

            width: 50px;

            height: 50px;

            font-size: 20px;

            font-weight: bold;

            color: white;

            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);

            border: none;

            border-radius: 10px;

            cursor: pointer;

            transition: all 0.3s ease;

            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);

            display: flex;

            align-items: center;

            justify-content: center;

            user-select: none;

            -webkit-user-select: none;

        }

 

        .control-btn:hover {

            transform: translateY(-2px);

            box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);

        }

 

        .control-btn:active {

            transform: translateY(0);

        }

 

        .control-btn.up {

            grid-column: 2;

        }

 

        .control-btn.down {

            grid-column: 2;

        }

 

        .control-btn.empty {

            visibility: hidden;

        }

 

        /* 9. 功能按钮区域 */

        .action-buttons {

            position: fixed;

            bottom: 20px;

            right: 20px;

            display: flex;

            flex-direction: column;

            gap: 10px;

            z-index: 100;

        }

 

        .action-btn {

            padding: 10px 15px;

            font-size: 12px;

            font-weight: bold;

            color: white;

            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);

            border: none;

            border-radius: 8px;

            cursor: pointer;

            transition: all 0.3s ease;

            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);

            min-width: 80px;

        }

 

        .action-btn:hover {

            transform: translateY(-2px);

            box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);

        }

 

        .action-btn:active {

            transform: translateY(0);

        }

 

        /* 10. 图例说明 */

        .legend {

            position: fixed;

            top: 80px;

            left: 10px;

            display: flex;

            flex-direction: column;

            gap: 8px;

            z-index: 10;

        }

 

        .legend-item {

            display: flex;

            align-items: center;

            gap: 5px;

            font-size: 12px;

            color: white;

            background: rgba(0, 0, 0, 0.3);

            padding: 5px 10px;

            border-radius: 5px;

        }

 

        .legend-color {

            width: 16px;

            height: 16px;

            border-radius: 3px;

            border: 1px solid #fff;

        }

 

        /* 11. 关卡完成提示 */

        .level-complete {

            position: fixed;

            top: 50%;

            left: 50%;

            transform: translate(-50%, -50%);

            background: white;

            padding: 30px;

            border-radius: 20px;

            text-align: center;

            box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);

            display: none;

            z-index: 1000;

            animation: bounceIn 0.5s ease;

            max-width: 90vw;

        }

 

        @keyframes bounceIn {

            0% {

                transform: translate(-50%, -50%) scale(0);

                opacity: 0;

            }

            50% {

                transform: translate(-50%, -50%) scale(1.1);

            }

            100% {

                transform: translate(-50%, -50%) scale(1);

                opacity: 1;

            }

        }

 

        .level-complete-title {

            font-size: 24px;

            color: #764ba2;

            margin-bottom: 15px;

        }

 

        .level-complete-info {

            font-size: 16px;

            color: #333;

            margin-bottom: 20px;

        }

 

        .next-btn {

            padding: 10px 25px;

            font-size: 14px;

            font-weight: bold;

            color: white;

            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);

            border: none;

            border-radius: 10px;

            cursor: pointer;

            transition: all 0.3s ease;

        }

 

        .next-btn:hover {

            transform: scale(1.05);

        }

 

        /* 12. 响应式设计 */

        @media (max-width: 480px) {

            .game-header {

                padding: 10px 15px;

            }

            

            .game-title {

                font-size: 18px;

            }

            

            .info-board {

                gap: 10px;

            }

            

            .controls {

                grid-template-columns: repeat(3, 45px);

                gap: 3px;

            }

            

            .control-btn {

                width: 45px;

                height: 45px;

                font-size: 18px;

            }

            

            .legend {

                top: 70px;

            }

        }

    </style>

</head>

<body>

    <div class="game-container">

        <!-- 游戏头部信息 -->

        <div class="game-header">

            <h1 class="game-title">智能推箱子</h1>

            <div class="info-board">

                <div class="info-item">

                    <span class="info-label">关卡</span>

                    <span class="info-value" id="level">1</span>

                </div>

                <div class="info-item">

                    <span class="info-label">步数</span>

                    <span class="info-value" id="moves">0</span>

                </div>

                <div class="info-item">

                    <span class="info-label">最少步数</span>

                    <span class="info-value" id="minMoves">-</span>

                </div>

            </div>

            <button class="restart-btn" id="restartBtn">重新开始</button>

        </div>

 

        <!-- 游戏主体区域 -->

        <div class="game-main">

            <canvas id="gameCanvas"></canvas>

        </div>

 

        <!-- 图例说明 -->

        <div class="legend">

            <div class="legend-item">

                <div class="legend-color" style="background: #3498db;"></div>

                <span>玩家</span>

            </div>

            <div class="legend-item">

                <div class="legend-color" style="background: #e67e22;"></div>

                <span>箱子</span>

            </div>

            <div class="legend-item">

                <div class="legend-color" style="background: #27ae60;"></div>

                <span>目标点</span>

            </div>

            <div class="legend-item">

                <div class="legend-color" style="background: #c0392b;"></div>

                <span>已完成</span>

            </div>

        </div>

 

        <!-- 控制按钮 - 左下角 -->

        <div class="controls-container">

            <div class="controls">

                <div class="control-btn empty"></div>

                <button class="control-btn up" id="upBtn">↑</button>

                <div class="control-btn empty"></div>

                <button class="control-btn" id="leftBtn">←</button>

                <button class="control-btn down" id="downBtn">↓</button>

                <button class="control-btn" id="rightBtn">→</button>

            </div>

        </div>

 

        <!-- 功能按钮 - 右下角 -->

        <div class="action-buttons">

            <button class="action-btn" id="undoBtn">撤销</button>

            <button class="action-btn" id="newLevelBtn">新关卡</button>

        </div>

 

        <!-- 关卡完成提示 -->

        <div class="level-complete" id="levelComplete">

            <div class="level-complete-title">关卡完成!</div>

            <div class="level-complete-info">

                <p>用了<span id="completeMoves">0</span> 步</p>

                <p>最少步数: <span id="completeMinMoves">0</span></p>

            </div>

            <button class="next-btn" id="nextLevelBtn">下一关</button>

        </div>

    </div>

 

    <script>

        // 一、游戏配置变量

        const CONFIG = {

            // 1. 画布配置

            canvas: {

                maxWidth: 350,

                maxHeight: 400,

                cellSize: 35

            },

            // 2. 关卡生成配置

            level: {

                minSize: 8,

                maxSize: 10,

                minBoxes: 3,

                maxBoxes: 5,

                maxRetries: 100

            },

            // 3. 颜色配置

            colors: {

                floor: '#34495e',

                wall: '#2c3e50',

                player: '#3498db',

                box: '#e67e22',

                target: '#27ae60',

                boxOnTarget: '#c0392b',

                playerOnTarget: '#2980b9'

            },

            // 4. 游戏元素

            elements: {

                floor: 0,

                wall: 1,

                player: 2,

                box: 3,

                target: 4,

                boxOnTarget: 5,

                playerOnTarget: 6

            }

        };

 

        // 二、游戏状态管理

        class SokobanGame {

            // 1. 初始化游戏

            constructor() {

                this.canvas = document.getElementById('gameCanvas');

                this.ctx = this.canvas.getContext('2d');

                

                // 游戏状态

                this.level = 1;

                this.moves = 0;

                this.minMoves = 0;

                this.gameBoard = [];

                this.playerPos = { x: 0, y: 0 };

                this.history = [];

                this.isComplete = false;

                this.totalBoxes = 0;

                

                // 绑定事件

                this.bindEvents();

                

                // 生成第一关

                this.generateValidLevel();

            }

 

            // 2. 生成有效关卡

            generateValidLevel() {

                let attempts = 0;

                while (attempts < CONFIG.level.maxRetries) {

                    if (this.generateLevel()) {

                        if (this.isSolvable()) {

                            this.minMoves = this.calculateMinMoves();

                            this.moves = 0;

                            this.history = [];

                            this.isComplete = false;

                            this.updateUI();

                            this.draw();

                            return;

                        }

                    }

                    attempts++;

                }

                // 如果生成失败,使用预设关卡

                this.usePresetLevel();

            }

 

            // 3. 生成关卡 - 四周开放

            generateLevel() {

                // 1. 随机生成关卡大小

                const size = Math.floor(Math.random() * (CONFIG.level.maxSize - CONFIG.level.minSize + 1)) + CONFIG.level.minSize;

                this.gridSize = size;

                

                // 2. 计算合适的单元格大小

                const maxCellSize = Math.floor(Math.min(CONFIG.canvas.maxWidth, CONFIG.canvas.maxHeight) / size);

                this.cellSize = Math.min(maxCellSize, CONFIG.canvas.cellSize);

                

                // 3. 设置画布实际尺寸

                this.canvas.width = size * this.cellSize;

                this.canvas.height = size * this.cellSize;

                

                // 4. 初始化空地图 - 四周不生成墙壁

                this.gameBoard = Array(size).fill(null).map(() => Array(size).fill(CONFIG.elements.floor));

                

                // 5. 随机生成箱子和目标点数量

                const boxCount = Math.floor(Math.random() * (CONFIG.level.maxBoxes - CONFIG.level.minBoxes + 1)) + CONFIG.level.minBoxes;

                const targetCount = boxCount + 1; // 目标点比箱子多一个

                const boxes = [];

                const targets = [];

                

                // 6. 收集内部可用位置(排除四周)

                const availablePositions = [];

                for (let y = 2; y < size - 2; y++) {

                    for (let x = 2; x < size - 2; x++) {

                        availablePositions.push({ x, y });

                    }

                }

                

                // 7. 随机选择位置

                this.shuffle(availablePositions);

                

                // 8. 放置目标点

                for (let i = 0; i < targetCount && i < availablePositions.length; i++) {

                    const targetPos = availablePositions[i];

                    targets.push(targetPos);

                    this.gameBoard[targetPos.y][targetPos.x] = CONFIG.elements.target;

                }

                

                // 9. 放置箱子(不在目标点上)

                let boxIndex = 0;

                for (let i = targetCount; i < targetCount + boxCount && i < availablePositions.length; i++) {

                    const boxPos = availablePositions[i];

                    // 确保箱子不在目标点上

                    if (this.gameBoard[boxPos.y][boxPos.x] === CONFIG.elements.floor) {

                        boxes.push(boxPos);

                        this.gameBoard[boxPos.y][boxPos.x] = CONFIG.elements.box;

                        boxIndex++;

                    }

                }

                

                // 10. 放置玩家

                for (let i = targetCount + boxCount; i < availablePositions.length; i++) {

                    const playerPos = availablePositions[i];

                    if (this.gameBoard[playerPos.y][playerPos.x] === CONFIG.elements.floor) {

                        this.playerPos = { x: playerPos.x, y: playerPos.y };

                        this.gameBoard[playerPos.y][playerPos.x] = CONFIG.elements.player;

                        break;

                    }

                }

                

                // 11. 生成少量内部墙壁

                this.generateInternalWalls(boxes, targets);

                

                // 12. 记录总箱子数

                this.totalBoxes = boxes.length;

                

                return boxes.length > 0 && targets.length > 0;

            }

 

            // 4. 生成内部墙壁

            generateInternalWalls(boxes, targets) {

                const wallCount = Math.floor(this.gridSize * 0.1); // 墙壁数量约为地图大小的10%

                let placedWalls = 0;

                

                for (let i = 0; i < wallCount * 3 && placedWalls < wallCount; i++) {

                    const x = Math.floor(Math.random() * (this.gridSize - 4)) + 2;

                    const y = Math.floor(Math.random() * (this.gridSize - 4)) + 2;

                    

                    // 检查是否可以放置墙壁

                    if (this.gameBoard[y][x] === CONFIG.elements.floor) {

                        // 检查是否会影响通路

                        if (!this.wouldBlockPath(x, y, boxes, targets)) {

                            this.gameBoard[y][x] = CONFIG.elements.wall;

                            placedWalls++;

                        }

                    }

                }

            }

 

            // 5. 检查是否会阻断通路

            wouldBlockPath(x, y, boxes, targets) {

                // 简单检查:确保不会完全包围箱子或目标

                for (let box of boxes) {

                    const dist = Math.abs(box.x - x) + Math.abs(box.y - y);

                    if (dist <= 1) return true; // 太近了

                }

                for (let target of targets) {

                    const dist = Math.abs(target.x - x) + Math.abs(target.y - y);

                    if (dist <= 1) return true; // 太近了

                }

                return false;

            }

 

            // 6. 使用预设关卡

            usePresetLevel() {

                this.gridSize = 8;

                this.cellSize = Math.floor(Math.min(CONFIG.canvas.maxWidth, CONFIG.canvas.maxHeight) / this.gridSize);

                this.canvas.width = this.gridSize * this.cellSize;

                this.canvas.height = this.gridSize * this.cellSize;

                

                this.gameBoard = [

                    [0,0,0,0,0,0,0,0],

                    [0,2,3,0,4,0,0,0],

                    [0,0,0,0,0,0,0,0],

                    [0,3,0,0,4,0,0,0],

                    [0,0,0,0,0,0,0,0],

                    [0,0,4,0,3,0,0,0],

                    [0,0,0,0,0,0,0,0],

                    [0,0,0,0,0,0,0,0]

                ];

                

                // 找到玩家位置

                for (let y = 0; y < this.gameBoard.length; y++) {

                    for (let x = 0; x < this.gameBoard[y].length; x++) {

                        if (this.gameBoard[y][x] === CONFIG.elements.player)

                            this.playerPos = { x, y };

                    }

                }

                

                // 计算总箱子数

                this.totalBoxes = 0;

                for (let y = 0; y < this.gameBoard.length; y++) {

                    for (let x = 0; x < this.gameBoard[y].length; x++) {

                        if (this.gameBoard[y][x] === CONFIG.elements.box || 

                            this.gameBoard[y][x] === CONFIG.elements.boxOnTarget) {

                            this.totalBoxes++;

                        }

                    }

                }

                

                this.minMoves = 8;

                this.moves = 0;

                this.history = [];

                this.isComplete = false;

            }

 

            // 7. 验证关卡可解性

            isSolvable() {

                return this.checkBasicSolvable();

            }

 

            // 8. 基本可解性检查

            checkBasicSolvable() {

                // 检查每个箱子是否至少有一个方向可以移动

                for (let y = 0; y < this.gameBoard.length; y++) {

                    for (let x = 0; x < this.gameBoard[y].length; x++) {

                        if (this.gameBoard[y][x] === CONFIG.elements.box) {

                            let canMove = false;

                            const directions = [

                                { dx: 0, dy: -1 },

                                { dx: 1, dy: 0 },

                                { dx: 0, dy: 1 },

                                { dx: -1, dy: 0 }

                            ];

                            for (let dir of directions) {

                                const boxNewX = x + dir.dx;

                                const boxNewY = y + dir.dy;

                                const playerNewX = x - dir.dx;

                                const playerNewY = y - dir.dy;

                                if (boxNewX >= 0 && boxNewX < this.gridSize &&

                                    boxNewY >= 0 && boxNewY < this.gridSize &&

                                    playerNewX >= 0 && playerNewX < this.gridSize &&

                                    playerNewY >= 0 && playerNewY < this.gridSize) {

                                    if (

                                        this.gameBoard[boxNewY][boxNewX] !== CONFIG.elements.wall &&

                                        this.gameBoard[boxNewY][boxNewX] !== CONFIG.elements.box &&

                                        this.gameBoard[playerNewY][playerNewX] !== CONFIG.elements.wall) {

                                        canMove = true;

                                        break;

                                    }

                                }

                            }

                            if (!canMove) return false;

                        }

                    }

                }

                return true;

            }

 

            // 9. 洗牌算法

            shuffle(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]];

                }

            }

 

            // 10. 计算最少步数

            calculateMinMoves() {

                let boxCount = 0;

                for (let y = 0; y < this.gameBoard.length; y++) {

                    for (let x = 0; x < this.gameBoard[y].length; x++) {

                        if (this.gameBoard[y][x] === CONFIG.elements.box ||

                            this.gameBoard[y][x] === CONFIG.elements.boxOnTarget) {

                            boxCount++;

                        }

                    }

                }

                return boxCount * 2;

            }

 

            // 11. 绘制游戏

            draw() {

                // 1. 清空画布

                this.ctx.fillStyle = CONFIG.colors.floor;

                this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);

                

                // 2. 计算箱子内边距

                const boxPadding = Math.max(2, this.cellSize * 0.1);

                

                // 3. 绘制游戏元素

                for (let y = 0; y < this.gameBoard.length; y++) {

                    for (let x = 0; x < this.gameBoard[y].length; x++) {

                        const element = this.gameBoard[y][x];

                        const px = x * this.cellSize;

                        const py = y * this.cellSize;

                        

                        switch(element) {

                            case CONFIG.elements.wall:

                                this.ctx.fillStyle = CONFIG.colors.wall;

                                this.ctx.fillRect(px, py, this.cellSize, this.cellSize);

                                this.ctx.strokeStyle = '#1a252f';

                                this.ctx.strokeRect(px, py, this.cellSize, this.cellSize);

                                break;

                            case CONFIG.elements.floor:

                                this.ctx.fillStyle = CONFIG.colors.floor;

                                this.ctx.fillRect(px, py, this.cellSize, this.cellSize);

                                break;

                            case CONFIG.elements.target:

                                this.ctx.fillStyle = CONFIG.colors.floor;

                                this.ctx.fillRect(px, py, this.cellSize, this.cellSize);

                                this.ctx.fillStyle = CONFIG.colors.target;

                                this.ctx.beginPath();

                                this.ctx.arc(px + this.cellSize/2, py + this.cellSize/2, this.cellSize/3, 0, Math.PI * 2);

                                this.ctx.fill();

                                break;

                            case CONFIG.elements.box:

                                this.ctx.fillStyle = CONFIG.colors.floor;

                                this.ctx.fillRect(px, py, this.cellSize, this.cellSize);

                                this.ctx.fillStyle = CONFIG.colors.box;

                                this.ctx.fillRect(px + boxPadding, py + boxPadding,

                                    this.cellSize - boxPadding * 2,

                                    this.cellSize - boxPadding * 2);

                                this.ctx.strokeStyle = '#d35400';

                                this.ctx.strokeRect(px + boxPadding, py + boxPadding,

                                    this.cellSize - boxPadding * 2,

                                    this.cellSize - boxPadding * 2);

                                break;

                            case CONFIG.elements.boxOnTarget:

                                this.ctx.fillStyle = CONFIG.colors.floor;

                                this.ctx.fillRect(px, py, this.cellSize, this.cellSize);

                                this.ctx.fillStyle = CONFIG.colors.target;

                                this.ctx.beginPath();

                                this.ctx.arc(px + this.cellSize/2, py + this.cellSize/2, this.cellSize/3, 0, Math.PI * 2);

                                this.ctx.fill();

                                this.ctx.fillStyle = CONFIG.colors.boxOnTarget;

                                this.ctx.fillRect(px + boxPadding, py + boxPadding,

                                    this.cellSize - boxPadding * 2,

                                    this.cellSize - boxPadding * 2);

                                break;

                            case CONFIG.elements.player:

                                this.ctx.fillStyle = CONFIG.colors.floor;

                                this.ctx.fillRect(px, py, this.cellSize, this.cellSize);

                                this.ctx.fillStyle = CONFIG.colors.player;

                                this.ctx.beginPath();

                                this.ctx.arc(px + this.cellSize/2, py + this.cellSize/2, this.cellSize/3, 0, Math.PI * 2);

                                this.ctx.fill();

                                break;

                            case CONFIG.elements.playerOnTarget:

                                this.ctx.fillStyle = CONFIG.colors.floor;

                                this.ctx.fillRect(px, py, this.cellSize, this.cellSize);

                                this.ctx.fillStyle = CONFIG.colors.target;

                                this.ctx.beginPath();

                                this.ctx.arc(px + this.cellSize/2, py + this.cellSize/2, this.cellSize/3, 0, Math.PI * 2);

                                this.ctx.fill();

                                this.ctx.fillStyle = CONFIG.colors.playerOnTarget;

                                this.ctx.beginPath();

                                this.ctx.arc(px + this.cellSize/2, py + this.cellSize/2, this.cellSize/4, 0, Math.PI * 2);

                                this.ctx.fill();

                                break;

                        }

                        // 绘制网格线

                        this.ctx.strokeStyle = 'rgba(255, 255, 255, 0.1)';

                        this.ctx.strokeRect(px, py, this.cellSize, this.cellSize);

                    }

                }

            }

 

            // 12. 移动玩家

            movePlayer(dx, dy) {

                // 1. 检查游戏是否完成

                if (this.isComplete) return;

                

                // 2. 计算新位置

                const newX = this.playerPos.x + dx;

                const newY = this.playerPos.y + dy;

                

                // 3. 检查边界

                if (newX < 0 || newX >= this.gridSize || newY < 0 || newY >= this.gridSize) {

                    return;

                }

                

                // 4. 获取目标位置元素

                const targetCell = this.gameBoard[newY][newX];

                

                // 5. 检查是否是墙

                if (targetCell === CONFIG.elements.wall) {

                    return;

                }

                

                // 6. 保存历史状态

                this.saveHistory();

                

                // 7. 检查是否推箱子

                if (targetCell === CONFIG.elements.box || targetCell === CONFIG.elements.boxOnTarget) {

                    const boxNewX = newX + dx;

                    const boxNewY = newY + dy;

                    

                    // 检查箱子能否移动

                    if (boxNewX < 0 || boxNewX >= this.gridSize ||

                        boxNewY < 0 || boxNewY >= this.gridSize || 

                        this.gameBoard[boxNewY][boxNewX] === CONFIG.elements.wall ||

                        this.gameBoard[boxNewY][boxNewX] === CONFIG.elements.box ||

                        this.gameBoard[boxNewY][boxNewX] === CONFIG.elements.boxOnTarget) {

                        this.history.pop(); // 撤销历史记录

                        return;

                    }

                    

                    // 移动箱子

                    const boxOnTarget = this.gameBoard[boxNewY][boxNewX] === CONFIG.elements.target;

                    this.gameBoard[boxNewY][boxNewX] = boxOnTarget ? 

                        CONFIG.elements.boxOnTarget : CONFIG.elements.box;

                    

                    // 更新箱子原位置

                    const wasOnTarget = targetCell === CONFIG.elements.boxOnTarget;

                    this.gameBoard[newY][newX] = wasOnTarget ? 

                        CONFIG.elements.target : CONFIG.elements.floor;

                }

                

                // 8. 移动玩家

                const playerOnTarget = this.gameBoard[newY][newX] === CONFIG.elements.target;

                this.gameBoard[this.playerPos.y][this.playerPos.x] =

                    (this.gameBoard[this.playerPos.y][this.playerPos.x] === CONFIG.elements.playerOnTarget) ? 

                    CONFIG.elements.target : CONFIG.elements.floor;

                this.gameBoard[newY][newX] = playerOnTarget ? 

                    CONFIG.elements.playerOnTarget : CONFIG.elements.player;

                this.playerPos = { x: newX, y: newY };

                

                // 9. 更新步数

                this.moves++;

                this.updateUI();

                

                // 10. 检查是否完成

                if (this.checkWin()) {

                    setTimeout(() => {

                        if (this.checkWin()) {

                            this.onLevelComplete();

                        }

                    }, 100);

                }

                

                // 11. 重绘

                this.draw();

            }

 

            // 13. 保存历史状态

            saveHistory() {

                const state = {

                    board: this.gameBoard.map(row => [...row]),

                    playerPos: { ...this.playerPos },

                    moves: this.moves

                };

                this.history.push(state);

                // 限制历史记录长度

                if (this.history.length > 50) {

                    this.history.shift();

                }

            }

 

            // 14. 撤销操作

            undo() {

                if (this.history.length === 0 || this.isComplete) return;

                const state = this.history.pop();

                this.gameBoard = state.board;

                this.playerPos = state.playerPos;

                this.moves = state.moves;

                this.updateUI();

                this.draw();

            }

 

            // 15. 检查胜利条件

            checkWin() {

                let boxesOnTarget = 0;

                for (let y = 0; y < this.gameBoard.length; y++) {

                    for (let x = 0; x < this.gameBoard[y].length; x++) {

                        if (this.gameBoard[y][x] === CONFIG.elements.boxOnTarget) {

                            boxesOnTarget++;

                        }

                    }

                }

                return boxesOnTarget === this.totalBoxes;

            }

 

            // 16. 关卡完成处理

            onLevelComplete() {

                if (this.isComplete) return;

                this.isComplete = true;

                document.getElementById('completeMoves').textContent = this.moves;

                document.getElementById('completeMinMoves').textContent = this.minMoves;

                document.getElementById('levelComplete').style.display = 'block';

            }

 

            // 17. 下一关

            nextLevel() {

                this.level++;

                document.getElementById('levelComplete').style.display = 'none';

                this.generateValidLevel();

            }

 

            // 18. 重新开始当前关卡

            restart() {

                this.generateValidLevel();

            }

 

            // 19. 更新UI

            updateUI() {

                document.getElementById('level').textContent = this.level;

                document.getElementById('moves').textContent = this.moves;

                document.getElementById('minMoves').textContent = this.minMoves;

            }

 

            // 20. 绑定事件

            bindEvents() {

                // 1. 键盘控制

                document.addEventListener('keydown', (e) => {

                    switch(e.key) {

                        case 'ArrowUp':

                        case 'w':

                        case 'W':

                            e.preventDefault();

                            this.movePlayer(0, -1);

                            break;

                        case 'ArrowDown':

                        case 's':

                        case 'S':

                            e.preventDefault();

                            this.movePlayer(0, 1);

                            break;

                        case 'ArrowLeft':

                        case 'a':

                        case 'A':

                            e.preventDefault();

                            this.movePlayer(-1, 0);

                            break;

                        case 'ArrowRight':

                        case 'd':

                        case 'D':

                            e.preventDefault();

                            this.movePlayer(1, 0);

                            break;

                        case 'z':

                        case 'Z':

                            this.undo();

                            break;

                        case 'r':

                        case 'R':

                            this.restart();

                            break;

                    }

                });

 

                // 2. 按钮控制

                document.getElementById('upBtn').addEventListener('click', () => {

                    this.movePlayer(0, -1);

                });

                document.getElementById('downBtn').addEventListener('click', () => {

                    this.movePlayer(0, 1);

                });

                document.getElementById('leftBtn').addEventListener('click', () => {

                    this.movePlayer(-1, 0);

                });

                document.getElementById('rightBtn').addEventListener('click', () => {

                    this.movePlayer(1, 0);

                });

                document.getElementById('undoBtn').addEventListener('click', () => {

                    this.undo();

                });

                document.getElementById('restartBtn').addEventListener('click', () => {

                    this.restart();

                });

                document.getElementById('newLevelBtn').addEventListener('click', () => {

                    this.level++;

                    this.generateValidLevel();

                });

                document.getElementById('nextLevelBtn').addEventListener('click', () => {

                    this.nextLevel();

                });

 

                // 3. 触摸控制

                let touchStartX = 0;

                let touchStartY = 0;

                this.canvas.addEventListener('touchstart', (e) => {

                    e.preventDefault();

                    touchStartX = e.touches[0].clientX;

                    touchStartY = e.touches[0].clientY;

                });

                this.canvas.addEventListener('touchend', (e) => {

                    e.preventDefault();

                    const touchEndX = e.changedTouches[0].clientX;

                    const touchEndY = e.changedTouches[0].clientY;

                    const dx = touchEndX - touchStartX;

                    const dy = touchEndY - touchStartY;

                    

                    if (Math.abs(dx) > Math.abs(dy)) {

                        // 水平移动

                        if (dx > 30) {

                            this.movePlayer(1, 0);

                        } else if (dx < -30) {

                            this.movePlayer(-1, 0);

                        }

                    } else {

                        // 垂直移动

                        if (dy > 30) {

                            this.movePlayer(0, 1);

                        } else if (dy < -30) {

                            this.movePlayer(0, -1);

                        }

                    }

                });

            }

        }

 

        // 三、初始化游戏

        window.addEventListener('DOMContentLoaded', () => {

            new SokobanGame();

        });

    </script>

</body>

</html>

 

http://www.dtcms.com/a/585741.html

相关文章:

  • 网站建设要钱么深圳一百讯网站建设
  • [Java算法] 双指针(1)
  • 江苏省建设厅网站官网湖南做网站最厉害的公司
  • 杭州家具网站建设方案郑州app开发价格
  • gdb调试命令和GDB 到 LLDB 命令映射
  • 【CUDA笔记】02 CUDA GPU 架构与一般的程序优化思路(上)
  • 198种组合算法+优化XGBoost+SHAP分析+新数据预测!机器学习可解释分析,强烈安利,粉丝必备!
  • 东莞做网站要多少钱安顺建设局网站官网
  • 在线做h5 的网站网站服务器怎么查询
  • Vue 项目实战《尚医通》,展示已有医院的数据并分页展示,笔记11
  • Modbus RTU 转 Modbus TCP:借助数据采集提升三菱PLC冷库温度反馈实时性案例
  • DeepSeek-OCR实战(01):基础运行环境搭建-Ubuntu
  • SQLite 索引:优化数据库查询的关键
  • 可拖拽网站三星官网商城
  • MySQL 8.x 的 my.ini配置设置
  • 周志华《机器学习导论》第 15 章 规则学习(符号主义学习)
  • 使用pycharm自带debug模式运行flask时报错
  • 福州做网站需要多少钱懒设计app
  • Dify 安全架构设计
  • 网站推广国外网站建设素材库
  • Rust 练习册 :Pythagorean Triplet与数学算法
  • 构建一个短链接生成器服务(FastAPI + SQLite)
  • 基于SpringBoot智慧社区系统/乡村振兴系统/大数据与人工智能平台
  • 做网站的公司跑了wordpress 首页显示产品
  • BLDCPMSM电机控制器硬件设计工程(八)72V 10kW电机控制器原理图工程及库文件
  • 西宁的网站建设公司怎样建立网站的快捷方式
  • MATLAB基于IOWGA算子的最优组合预测模型及应用
  • HarmonyOS Web组件深度解析:构建高性能JavaScript交互的实践与创新
  • 华为OD机试双机位A卷 - 竖直四子棋 (JAVA Python C++ JS GO)
  • Qt C++:跨平台开发利器