AI贪吃蛇大战设置

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AI贪吃蛇大战</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: "Microsoft Yahei", sans-serif;
        }

        body {
            background-color: #f0f0f0;
            display: flex;
            flex-direction: column;
            align-items: center;
            padding: 20px;
        }

        .game-container {
            position: relative;
            border: 2px solid #333;
            background-color: #fff;
        }

        .start-panel {
            background-color: white;
            padding: 30px;
            border-radius: 10px;
            box-shadow: 0 0 10px rgba(0,0,0,0.2);
            margin-bottom: 20px;
        }

        .start-panel h2 {
            text-align: center;
            margin-bottom: 20px;
            color: #333;
        }

        .form-group {
            margin-bottom: 15px;
        }

        .form-group label {
            display: block;
            margin-bottom: 5px;
            font-weight: bold;
        }

        .form-group input, .form-group select {
            width: 100%;
            padding: 8px;
            border: 1px solid #ddd;
            border-radius: 4px;
        }

        .ai-type-group {
            display: flex;
            flex-wrap: wrap;
            gap: 10px;
            margin-bottom: 15px;
        }

        .ai-type-item {
            flex: 1;
            min-width: 120px;
        }

        button {
            background-color: #4CAF50;
            color: white;
            border: none;
            padding: 10px 20px;
            border-radius: 4px;
            cursor: pointer;
            font-size: 16px;
            width: 100%;
        }

        button:hover {
            background-color: #45a049;
        }

        .rank-panel {
            position: absolute;
            top: 10px;
            right: 10px;
            background-color: rgba(0,0,0,0.7);
            color: white;
            padding: 10px;
            border-radius: 5px;
            font-size: 12px;
            min-width: 150px;
        }

        .rank-panel h3 {
            text-align: center;
            margin-bottom: 5px;
        }

        .rank-item {
            margin: 3px 0;
        }

        .game-info {
            margin-top: 15px;
            font-size: 18px;
            color: #333;
        }
    </style>
</head>
<body>
    <div class="start-panel" id="startPanel">
        <h2>AI贪吃蛇大战设置</h2>
        
        <div class="form-group">
            <label>游戏模式</label>
            <select id="gameMode">
                <option value="player">玩家游玩模式(有主角蛇)</option>
                <option value="aiOnly">纯AI搏杀模式(无主角蛇)</option>
            </select>
        </div>

        <div class="form-group">
            <label>地图尺寸</label>
            <select id="mapSize">
                <option value="50">50x50</option>
                <option value="100">100x100</option>
                <option value="150">150x150</option>
                <option value="200">200x200</option>
            </select>
        </div>

        <div class="form-group">
            <label>障碍比例</label>
            <select id="obstacleRate">
                <option value="0">无障碍</option>
                <option value="1">障碍1%</option>
                <option value="5">障碍5%</option>
                <option value="10">障碍10%</option>
                <option value="20">障碍20%</option>
                <option value="50">障碍50%</option>
            </select>
        </div>

        <div class="form-group">
            <label>AI蛇总数</label>
            <input type="number" id="totalAiSnakes" min="0" max="20" value="4" placeholder="输入AI蛇数量(0-20)">
        </div>

        <div class="ai-type-group">
            <div class="ai-type-item">
                <label>天才AI数量</label>
                <input type="number" id="geniusAi" min="0" value="1" placeholder="天才AI数量">
            </div>
            <div class="ai-type-item">
                <label>聪明AI数量</label>
                <input type="number" id="smartAi" min="0" value="1" placeholder="聪明AI数量">
            </div>
            <div class="ai-type-item">
                <label>平凡AI数量</label>
                <input type="number" id="ordinaryAi" min="0" value="1" placeholder="平凡AI数量">
            </div>
            <div class="ai-type-item">
                <label>愚蠢AI数量</label>
                <input type="number" id="stupidAi" min="0" value="1" placeholder="愚蠢AI数量">
            </div>
        </div>

        <button onclick="startGame()">开始游戏</button>
    </div>

    <div class="game-container" id="gameContainer"></div>
    <div class="rank-panel" id="rankPanel" style="display: none;">
        <h3>AI蛇排行</h3>
        <div id="rankList"></div>
    </div>
    <div class="game-info" id="gameInfo"></div>

    <script>
        // 游戏全局变量
        let canvas, ctx;
        let mapSize, cellSize;
        let obstacleRate;
        let gameMode;
        let obstacles = [];
        let foods = [];
        let snakes = [];
        let playerSnake = null;
        let gameInterval;
        let aiSnakeConfigs = {
            genius: 0,
            smart: 0,
            ordinary: 0,
            stupid: 0
        };

        // 方向常量
        const directions = {
            UP: { x: 0, y: -1 },
            DOWN: { x: 0, y: 1 },
            LEFT: { x: -1, y: 0 },
            RIGHT: { x: 1, y: 0 }
        };

        // 颜色配置
        const snakeColors = [
            '#FF0000', '#00FF00', '#0000FF', '#FFFF00', '#FF00FF', '#00FFFF',
            '#800000', '#008000', '#000080', '#808000', '#800080', '#008080'
        ];

        // 初始化游戏
        function startGame() {
            // 获取配置参数
            gameMode = document.getElementById('gameMode').value;
            mapSize = parseInt(document.getElementById('mapSize').value);
            obstacleRate = parseInt(document.getElementById('obstacleRate').value) / 100;
            aiSnakeConfigs.genius = parseInt(document.getElementById('geniusAi').value) || 0;
            aiSnakeConfigs.smart = parseInt(document.getElementById('smartAi').value) || 0;
            aiSnakeConfigs.ordinary = parseInt(document.getElementById('ordinaryAi').value) || 0;
            aiSnakeConfigs.stupid = parseInt(document.getElementById('stupidAi').value) || 0;

            // 验证AI数量总和
            const totalAi = aiSnakeConfigs.genius + aiSnakeConfigs.smart + 
                           aiSnakeConfigs.ordinary + aiSnakeConfigs.stupid;
            const inputTotal = parseInt(document.getElementById('totalAiSnakes').value) || 0;
            
            if (totalAi !== inputTotal) {
                alert(`AI蛇数量不匹配!各类型总和(${totalAi})与输入总数(${inputTotal})不一致`);
                return;
            }

            // 隐藏开始面板,显示游戏容器
            document.getElementById('startPanel').style.display = 'none';
            const gameContainer = document.getElementById('gameContainer');
            gameContainer.style.display = 'block';
            
            // 显示排行面板(纯AI模式)
            if (gameMode === 'aiOnly') {
                document.getElementById('rankPanel').style.display = 'block';
            }

            // 创建画布
            cellSize = Math.floor(800 / mapSize); // 适配800px宽度
            canvas = document.createElement('canvas');
            canvas.width = mapSize * cellSize;
            canvas.height = mapSize * cellSize;
            gameContainer.innerHTML = '';
            gameContainer.appendChild(canvas);
            ctx = canvas.getContext('2d');

            // 初始化游戏元素
            initObstacles();
            initFoods(10); // 初始10个食物
            initSnakes();

            // 绑定玩家控制
            if (gameMode === 'player') {
                bindPlayerControls();
            }

            // 启动游戏循环
            gameInterval = setInterval(gameLoop, 100);
        }

        // 初始化障碍
        function initObstacles() {
            obstacles = [];
            const totalCells = mapSize * mapSize;
            const obstacleCount = Math.floor(totalCells * obstacleRate);

            for (let i = 0; i < obstacleCount; i++) {
                let x, y;
                // 确保障碍不重叠且不在边缘
                do {
                    x = Math.floor(Math.random() * (mapSize - 2)) + 1;
                    y = Math.floor(Math.random() * (mapSize - 2)) + 1;
                } while (obstacles.some(obs => obs.x === x && obs.y === y));

                obstacles.push({ x, y });
            }
        }

        // 初始化食物
        function initFoods(count) {
            foods = [];
            for (let i = 0; i < count; i++) {
                spawnFood();
            }
        }

        // 生成单个食物
        function spawnFood(excludePositions = []) {
            let x, y;
            // 确保食物不与蛇、障碍、其他食物重叠
            do {
                x = Math.floor(Math.random() * mapSize);
                y = Math.floor(Math.random() * mapSize);
            } while (
                obstacles.some(obs => obs.x === x && obs.y === y) ||
                foods.some(food => food.x === x && food.y === y) ||
                snakes.some(snake => snake.body.some(part => part.x === x && part.y === y)) ||
                excludePositions.some(pos => pos.x === x && pos.y === y)
            );

            foods.push({ x, y });
        }

        // 初始化蛇
        function initSnakes() {
            snakes = [];
            let snakeId = 0;

            // 创建玩家蛇(如果是玩家模式)
            if (gameMode === 'player') {
                playerSnake = createSnake(snakeId++, 'player', '#000000', mapSize / 2, mapSize / 2);
                snakes.push(playerSnake);
            }

            // 创建AI蛇
            let colorIndex = 0;
            // 天才AI
            for (let i = 0; i < aiSnakeConfigs.genius; i++) {
                const [x, y] = getRandomSafePosition();
                snakes.push(createSnake(
                    snakeId++, 
                    'genius', 
                    snakeColors[colorIndex++ % snakeColors.length],
                    x, y,
                    `天才-${i+1}`
                ));
            }
            // 聪明AI
            for (let i = 0; i < aiSnakeConfigs.smart; i++) {
                const [x, y] = getRandomSafePosition();
                snakes.push(createSnake(
                    snakeId++, 
                    'smart', 
                    snakeColors[colorIndex++ % snakeColors.length],
                    x, y,
                    `聪明-${i+1}`
                ));
            }
            // 平凡AI
            for (let i = 0; i < aiSnakeConfigs.ordinary; i++) {
                const [x, y] = getRandomSafePosition();
                snakes.push(createSnake(
                    snakeId++, 
                    'ordinary', 
                    snakeColors[colorIndex++ % snakeColors.length],
                    x, y,
                    `平凡-${i+1}`
                ));
            }
            // 愚蠢AI
            for (let i = 0; i < aiSnakeConfigs.stupid; i++) {
                const [x, y] = getRandomSafePosition();
                snakes.push(createSnake(
                    snakeId++, 
                    'stupid', 
                    snakeColors[colorIndex++ % snakeColors.length],
                    x, y,
                    `愚蠢-${i+1}`
                ));
            }
        }

        // 创建单个蛇
        function createSnake(id, type, color, x, y, name = 'AI蛇') {
            const initialDirection = Object.values(directions)[Math.floor(Math.random() * 4)];
            return {
                id,
                type, // player/genius/smart/ordinary/stupid
                name,
                color,
                body: [
                    { x, y },
                    { x: x - initialDirection.x, y: y - initialDirection.y },
                    { x: x - 2 * initialDirection.x, y: y - 2 * initialDirection.y }
                ],
                direction: initialDirection,
                nextDirection: initialDirection,
                alive: true,
                score: 0
            };
        }

        // 获取随机安全位置(不与障碍/其他蛇重叠)
        function getRandomSafePosition() {
            let x, y;
            do {
                x = Math.floor(Math.random() * (mapSize - 10)) + 5;
                y = Math.floor(Math.random() * (mapSize - 10)) + 5;
            } while (
                obstacles.some(obs => obs.x === x && obs.y === y) ||
                snakes.some(snake => snake.body.some(part => part.x === x && part.y === y))
            );
            return [x, y];
        }

        // 绑定玩家控制
        function bindPlayerControls() {
            document.addEventListener('keydown', (e) => {
                if (!playerSnake || !playerSnake.alive) return;

                switch (e.key) {
                    case 'ArrowUp':
                        if (playerSnake.direction !== directions.DOWN) {
                            playerSnake.nextDirection = directions.UP;
                        }
                        break;
                    case 'ArrowDown':
                        if (playerSnake.direction !== directions.UP) {
                            playerSnake.nextDirection = directions.DOWN;
                        }
                        break;
                    case 'ArrowLeft':
                        if (playerSnake.direction !== directions.RIGHT) {
                            playerSnake.nextDirection = directions.LEFT;
                        }
                        break;
                    case 'ArrowRight':
                        if (playerSnake.direction !== directions.LEFT) {
                            playerSnake.nextDirection = directions.RIGHT;
                        }
                        break;
                }
            });
        }

        // AI蛇决策逻辑
        function aiDecision(snake) {
            if (!snake.alive) return;

            const head = snake.body[0];
            const possibleDirections = getSafeDirections(snake);

            // 如果没有安全方向,随机选一个(必死)
            if (possibleDirections.length === 0) {
                snake.nextDirection = Object.values(directions)[Math.floor(Math.random() * 4)];
                return;
            }

            switch (snake.type) {
                case 'genius':
                    geniusAiDecision(snake, possibleDirections);
                    break;
                case 'smart':
                    smartAiDecision(snake, possibleDirections);
                    break;
                case 'ordinary':
                    ordinaryAiDecision(snake, possibleDirections);
                    break;
                case 'stupid':
                    stupidAiDecision(snake, possibleDirections);
                    break;
            }
        }

        // 获取安全方向(不撞墙、障碍、自己身体)
        function getSafeDirections(snake) {
            const head = snake.body[0];
            const safeDirs = [];

            Object.values(directions).forEach(dir => {
                const nextX = head.x + dir.x;
                const nextY = head.y + dir.y;

                // 检查是否撞墙
                if (nextX < 0 || nextX >= mapSize || nextY < 0 || nextY >= mapSize) return;
                
                // 检查是否撞障碍
                if (obstacles.some(obs => obs.x === nextX && obs.y === nextY)) return;
                
                // 检查是否撞自己身体
                if (snake.body.some((part, index) => index > 0 && part.x === nextX && part.y === nextY)) return;

                safeDirs.push(dir);
            });

            return safeDirs;
        }

        // 天才AI决策:长时杀蛇,短时吃食物,遇长蛇优先吃食物
        function geniusAiDecision(snake, possibleDirections) {
            const head = snake.body[0];
            const snakeLength = snake.body.length;
            let targetDir = null;

            // 找到最长的其他蛇
            let longestSnake = null;
            let maxLength = 0;
            snakes.forEach(s => {
                if (s.alive && s.id !== snake.id && s.body.length > maxLength) {
                    maxLength = s.body.length;
                    longestSnake = s;
                }
            });

            // 判断策略:如果自己长度 < 15 或 最长蛇比自己长很多(>1.5倍),优先吃食物
            const prioritizeFood = snakeLength < 15 || (longestSnake && longestSnake.body.length > snakeLength * 1.5);

            if (prioritizeFood) {
                // 优先找最近的食物
                targetDir = findDirectionToNearestFood(snake, possibleDirections);
            } else {
                // 优先找其他蛇(攻击模式)
                targetDir = findDirectionToNearestSnake(snake, possibleDirections);
            }

            // 如果找不到目标,选安全方向
            snake.nextDirection = targetDir || possibleDirections[Math.floor(Math.random() * possibleDirections.length)];
        }

        // 聪明AI决策:优先吃食物
        function smartAiDecision(snake, possibleDirections) {
            const targetDir = findDirectionToNearestFood(snake, possibleDirections);
            snake.nextDirection = targetDir || possibleDirections[Math.floor(Math.random() * possibleDirections.length)];
        }

        // 平凡AI决策:优先存活(选最开阔的方向)
        function ordinaryAiDecision(snake, possibleDirections) {
            // 计算每个方向的安全空间
            let bestDir = possibleDirections[0];
            let maxSpace = 0;

            possibleDirections.forEach(dir => {
                const space = calculateSafeSpace(snake.body[0], dir, snake);
                if (space > maxSpace) {
                    maxSpace = space;
                    bestDir = dir;
                }
            });

            snake.nextDirection = bestDir;
        }

        // 愚蠢AI决策:随机游走
        function stupidAiDecision(snake, possibleDirections) {
            snake.nextDirection = possibleDirections[Math.floor(Math.random() * possibleDirections.length)];
        }

        // 计算某个方向的安全空间
        function calculateSafeSpace(startPos, dir, snake) {
            let x = startPos.x + dir.x;
            let y = startPos.y + dir.y;
            let space = 0;

            // 一直往前走,直到遇到危险
            while (
                x >= 0 && x < mapSize && y >= 0 && y < mapSize &&
                !obstacles.some(obs => obs.x === x && obs.y === y) &&
                !snakes.some(s => s.alive && s.body.some(part => part.x === x && part.y === y))
            ) {
                space++;
                x += dir.x;
                y += dir.y;
            }

            return space;
        }

        // 找到最近食物的方向
        function findDirectionToNearestFood(snake, possibleDirections) {
            const head = snake.body[0];
            let nearestFood = null;
            let minDistance = Infinity;

            // 找到最近的食物
            foods.forEach(food => {
                const distance = Math.hypot(head.x - food.x, head.y - food.y);
                if (distance < minDistance) {
                    minDistance = distance;
                    nearestFood = food;
                }
            });

            if (!nearestFood) return null;

            // 找到朝向食物的安全方向
            let bestDir = null;
            let bestDistance = Infinity;

            possibleDirections.forEach(dir => {
                const nextX = head.x + dir.x;
                const nextY = head.y + dir.y;
                const distance = Math.hypot(nextX - nearestFood.x, nextY - nearestFood.y);
                
                if (distance < bestDistance) {
                    bestDistance = distance;
                    bestDir = dir;
                }
            });

            return bestDir;
        }

        // 找到最近其他蛇的方向
        function findDirectionToNearestSnake(snake, possibleDirections) {
            const head = snake.body[0];
            let nearestSnakePart = null;
            let minDistance = Infinity;

            // 找到最近的其他蛇身体
            snakes.forEach(s => {
                if (s.alive && s.id !== snake.id) {
                    s.body.forEach(part => {
                        const distance = Math.hypot(head.x - part.x, head.y - part.y);
                        if (distance < minDistance && distance > 0) {
                            minDistance = distance;
                            nearestSnakePart = part;
                        }
                    });
                }
            });

            if (!nearestSnakePart) return null;

            // 找到朝向其他蛇的安全方向
            let bestDir = null;
            let bestDistance = Infinity;

            possibleDirections.forEach(dir => {
                const nextX = head.x + dir.x;
                const nextY = head.y + dir.y;
                const distance = Math.hypot(nextX - nearestSnakePart.x, nextY - nearestSnakePart.y);
                
                if (distance < bestDistance) {
                    bestDistance = distance;
                    bestDir = dir;
                }
            });

            return bestDir;
        }

        // 移动蛇
        function moveSnake(snake) {
            if (!snake.alive) return;

            // 更新方向
            snake.direction = snake.nextDirection;

            // 创建新头部
            const head = { ...snake.body[0] };
            head.x += snake.direction.x;
            head.y += snake.direction.y;

            // 检查碰撞
            if (checkCollision(head, snake)) {
                snake.alive = false;
                handleSnakeDeath(snake);
                return;
            }

            // 添加新头部
            snake.body.unshift(head);

            // 检查是否吃到食物
            const foodIndex = foods.findIndex(food => food.x === head.x && food.y === head.y);
            if (foodIndex !== -1) {
                // 吃到食物,加分且不删除尾部
                foods.splice(foodIndex, 1);
                snake.score += 10;
                spawnFood(); // 生成新食物
            } else {
                // 没吃到食物,删除尾部
                snake.body.pop();
            }
        }

        // 检查碰撞(墙、障碍、其他蛇)
        function checkCollision(head, snake) {
            // 撞墙
            if (head.x < 0 || head.x >= mapSize || head.y < 0 || head.y >= mapSize) {
                return true;
            }

            // 撞障碍
            if (obstacles.some(obs => obs.x === head.x && obs.y === head.y)) {
                return true;
            }

            // 撞其他蛇或自己身体
            for (const s of snakes) {
                if (s.alive) {
                    for (let i = 0; i < s.body.length; i++) {
                        // 跳过自己的尾部(移动时尾部会消失)
                        if (s.id === snake.id && i === s.body.length - 1) continue;
                        
                        if (s.body[i].x === head.x && s.body[i].y === head.y) {
                            return true;
                        }
                    }
                }
            }

            return false;
        }

        // 处理蛇死亡
        function handleSnakeDeath(snake) {
            // 身体前50%化为食物
            const foodCount = Math.floor(snake.body.length * 0.5);
            const bodyPartsToFood = snake.body.slice(0, foodCount);
            
            bodyPartsToFood.forEach(part => {
                spawnFood([...foods, ...obstacles]); // 避免食物重叠
            });

            // 玩家蛇死亡提示
            if (snake.type === 'player') {
                document.getElementById('gameInfo').textContent = '游戏结束!你的蛇死亡了';
                clearInterval(gameInterval);
            }
        }

        // 更新排行榜
        function updateRankings() {
            const rankList = document.getElementById('rankList');
            // 按长度(score)排序
            const sortedSnakes = snakes
                .filter(s => s.alive && s.type !== 'player')
                .sort((a, b) => b.body.length - a.body.length);

            let html = '';
            sortedSnakes.forEach((snake, index) => {
                html += `<div class="rank-item">${index+1}. ${snake.name} - 长度: ${snake.body.length}</div>`;
            });

            rankList.innerHTML = html;
        }

        // 绘制游戏
        function drawGame() {
            // 清空画布
            ctx.fillStyle = '#f8f8f8';
            ctx.fillRect(0, 0, canvas.width, canvas.height);

            // 绘制障碍
            ctx.fillStyle = '#888888';
            obstacles.forEach(obs => {
                ctx.fillRect(obs.x * cellSize, obs.y * cellSize, cellSize - 1, cellSize - 1);
            });

            // 绘制食物
            ctx.fillStyle = '#FF4444';
            foods.forEach(food => {
                ctx.fillRect(food.x * cellSize, food.y * cellSize, cellSize - 1, cellSize - 1);
            });

            // 绘制蛇
            snakes.forEach(snake => {
                if (snake.alive) {
                    // 绘制头部
                    ctx.fillStyle = snake.color;
                    ctx.fillRect(snake.body[0].x * cellSize, snake.body[0].y * cellSize, cellSize - 1, cellSize - 1);
                    
                    // 绘制身体
                    ctx.fillStyle = adjustColor(snake.color, -30);
                    for (let i = 1; i < snake.body.length; i++) {
                        ctx.fillRect(snake.body[i].x * cellSize, snake.body[i].y * cellSize, cellSize - 1, cellSize - 1);
                    }
                }
            });

            // 更新信息
            if (playerSnake && playerSnake.alive) {
                document.getElementById('gameInfo').textContent = `你的蛇长度: ${playerSnake.body.length} | 剩余AI蛇: ${snakes.filter(s => s.alive && s.type !== 'player').length}`;
            } else if (gameMode === 'aiOnly') {
                document.getElementById('gameInfo').textContent = `剩余AI蛇: ${snakes.filter(s => s.alive).length}`;
            }
        }

        // 调整颜色亮度
        function adjustColor(color, amount) {
            let usePound = false;
            if (color[0] === '#') {
                color = color.slice(1);
                usePound = true;
            }

            let num = parseInt(color, 16);
            let r = (num >> 16) + amount;
            let g = (num >> 8 & 0x00FF) + amount;
            let b = (num & 0x0000FF) + amount;

            r = Math.min(255, Math.max(0, r));
            g = Math.min(255, Math.max(0, g));
            b = Math.min(255, Math.max(0, b));

            return (usePound ? '#' : '') + (g | (b << 8) | (r << 16)).toString(16).padStart(6, '0');
        }

        // 游戏主循环
        function gameLoop() {
            // AI决策
            snakes.forEach(snake => {
                if (snake.alive && snake.type !== 'player') {
                    aiDecision(snake);
                }
            });

            // 移动所有蛇
            snakes.forEach(snake => {
                if (snake.alive) {
                    moveSnake(snake);
                }
            });

            // 检查游戏结束(纯AI模式)
            if (gameMode === 'aiOnly') {
                const aliveAis = snakes.filter(s => s.alive);
                if (aliveAis.length <= 1) {
                    clearInterval(gameInterval);
                    document.getElementById('gameInfo').textContent = `游戏结束!胜利者: ${aliveAis[0]?.name || '无'}`;
                }
            }

            // 更新排行榜
            if (gameMode === 'aiOnly') {
                updateRankings();
            }

            // 绘制游戏
            drawGame();
        }
    </script>
</body>
</html>
posted @ 2026-02-25 13:17  bz02_2023f2  阅读(2)  评论(0)    收藏  举报  来源