Snake Battle: Human vs AI on Drawaria

Play a Snake Battle game (Human vs AI) on drawaria.online

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Snake Battle: Human vs AI on Drawaria
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Play a Snake Battle game (Human vs AI) on drawaria.online
// @author       YouTubeDrawaria
// @match        https://drawaria.online/*
// @grant        none
// @license      MIT
// @icon         https://drawaria.online/avatar/cache/86e33830-86ea-11ec-8553-bff27824cf71.jpg
// ==/UserScript==

(function () {
    'use strict';

    // Create game container
    const gameContainer = document.createElement('div');
    gameContainer.style.position = 'fixed';
    gameContainer.style.top = '20px';
    gameContainer.style.right = '20px';
    gameContainer.style.zIndex = '10000';
    gameContainer.style.backgroundColor = 'rgba(0, 0, 0, 0.8)';
    gameContainer.style.padding = '10px';
    gameContainer.style.borderRadius = '10px';
    gameContainer.style.boxShadow = '0 0 20px rgba(0, 255, 255, 0.5)';

    // Add game title
    const title = document.createElement('h3');
    title.textContent = 'Snake Battle 🐍';
    title.style.color = '#00ffff';
    title.style.textAlign = 'center';
    title.style.margin = '0 0 10px 0';
    gameContainer.appendChild(title);

    // Add score display
    const score = document.createElement('div');
    score.id = 'score';
    score.textContent = '🚀 PLAYER: 0 | 🤖 AI: 0';
    score.style.color = '#ffffff';
    score.style.fontSize = '16px';
    score.style.marginBottom = '10px';


    // Add canvas for the game
    const canvas = document.createElement('canvas');
    canvas.id = 'gameBoard';
    canvas.width = 400;
    canvas.height = 300;
    canvas.style.border = '2px solid #00ffff';
    canvas.style.borderRadius = '10px';
    canvas.style.backgroundColor = '#000000';
    gameContainer.appendChild(canvas);

    // Add restart button
    const restartBtn = document.createElement('button');
    restartBtn.textContent = '🔄 NEW BATTLE';
    restartBtn.style.display = 'block';
    restartBtn.style.margin = '10px auto 0';
    restartBtn.style.padding = '8px 16px';
    restartBtn.style.backgroundColor = '#00ffff';
    restartBtn.style.color = '#000000';
    restartBtn.style.border = 'none';
    restartBtn.style.borderRadius = '20px';
    restartBtn.style.cursor = 'pointer';
    restartBtn.style.fontWeight = 'bold';
    restartBtn.onclick = resetGame;
    gameContainer.appendChild(restartBtn);

    // Append game container to the body
    document.body.appendChild(gameContainer);

    // Game logic
    const ctx = canvas.getContext('2d');
    const GRID_SIZE = 20;
    const CELL_SIZE = canvas.width / GRID_SIZE;

    let particles = [];
    let playerScore = 0;
    let aiScore = 0;
    let gameSpeed = 100;
    let gameLoop;

    let obstacles = [];
    let playerSnake = [{ x: 5, y: 5 }];
    let aiSnake = [{ x: GRID_SIZE - 5, y: GRID_SIZE - 5 }];
    let playerDirection = 'right';
    let aiDirection = 'left';
    let food = generateFood();

    function generateObstacles() {
        const obstacles = [];
        for (let i = 2; i < GRID_SIZE - 2; i += 4) {
            for (let j = 2; j < GRID_SIZE - 2; j += 4) {
                if (Math.random() > 0.5) {
                    obstacles.push({ x: i, y: j });
                    obstacles.push({ x: i + 1, y: j });
                    obstacles.push({ x: i, y: j + 1 });
                }
            }
        }
        return obstacles;
    }

    function generateFood() {
        let position;
        do {
            position = {
                x: Math.floor(Math.random() * (GRID_SIZE - 4)) + 2,
                y: Math.floor(Math.random() * (GRID_SIZE - 4)) + 2
            };
        } while ([...playerSnake, ...aiSnake, ...obstacles].some(item =>
            item.x === position.x && item.y === position.y));
        return position;
    }

    function aiPathfinding() {
        const head = aiSnake[0];
        const queue = [{ pos: head, path: [] }];
        const visited = new Set();

        while (queue.length > 0) {
            const current = queue.shift();
            if (current.pos.x === food.x && current.pos.y === food.y) {
                return current.path[0] || aiDirection;
            }

            const directions = [
                { dir: 'up', x: 0, y: -1 },
                { dir: 'down', x: 0, y: 1 },
                { dir: 'left', x: -1, y: 0 },
                { dir: 'right', x: 1, y: 0 }
            ];

            for (const d of directions) {
                const newPos = {
                    x: current.pos.x + d.x,
                    y: current.pos.y + d.y
                };
                const posKey = `${newPos.x},${newPos.y}`;

                if (!visited.has(posKey) &&
                    newPos.x >= 0 && newPos.x < GRID_SIZE &&
                    newPos.y >= 0 && newPos.y < GRID_SIZE &&
                    !obstacles.some(o => o.x === newPos.x && o.y === newPos.y) &&
                    !aiSnake.some(s => s.x === newPos.x && s.y === newPos.y)) {
                    visited.add(posKey);
                    queue.push({
                        pos: newPos,
                        path: [...current.path, d.dir]
                    });
                }
            }
        }
        return aiDirection;
    }

    function updateParticles() {
        particles = particles.filter(p => {
            p.x += p.velocity.x;
            p.y += p.velocity.y;
            p.life -= 0.02;
            return p.life > 0;
        });
    }

    function draw() {
        ctx.fillStyle = '#000';
        ctx.fillRect(0, 0, canvas.width, canvas.height);

        ctx.strokeStyle = 'rgba(0, 255, 255, 0.05)';
        for (let i = 0; i < GRID_SIZE; i++) {
            ctx.beginPath();
            ctx.moveTo(i * CELL_SIZE, 0);
            ctx.lineTo(i * CELL_SIZE, canvas.height);
            ctx.stroke();
            ctx.beginPath();
            ctx.moveTo(0, i * CELL_SIZE);
            ctx.lineTo(canvas.width, i * CELL_SIZE);
            ctx.stroke();
        }

        ctx.fillStyle = '#2a2a2a';
        obstacles.forEach(obs => {
            ctx.beginPath();
            ctx.roundRect(obs.x * CELL_SIZE, obs.y * CELL_SIZE, CELL_SIZE, CELL_SIZE, 5);
            ctx.fill();
        });

        ctx.fillStyle = '#ff006e';
        ctx.shadowColor = '#ff006e';
        ctx.shadowBlur = 20;
        ctx.beginPath();
        ctx.arc(
            (food.x + 0.5) * CELL_SIZE,
            (food.y + 0.5) * CELL_SIZE,
            CELL_SIZE / 2 - 2,
            0,
            Math.PI * 2
        );
        ctx.fill();
        ctx.shadowBlur = 0;

        playerSnake.forEach((segment, index) => {
            const gradient = ctx.createLinearGradient(
                segment.x * CELL_SIZE,
                segment.y * CELL_SIZE,
                (segment.x + 1) * CELL_SIZE,
                (segment.y + 1) * CELL_SIZE
            );
            gradient.addColorStop(0, '#00ff88');
            gradient.addColorStop(1, '#00ff00');
            ctx.fillStyle = gradient;
            ctx.beginPath();
            ctx.roundRect(
                segment.x * CELL_SIZE + 2,
                segment.y * CELL_SIZE + 2,
                CELL_SIZE - 4,
                CELL_SIZE - 4,
                8
            );
            ctx.fill();
        });

        aiSnake.forEach((segment, index) => {
            const gradient = ctx.createLinearGradient(
                segment.x * CELL_SIZE,
                segment.y * CELL_SIZE,
                (segment.x + 1) * CELL_SIZE,
                (segment.y + 1) * CELL_SIZE
            );
            gradient.addColorStop(0, '#ff6600');
            gradient.addColorStop(1, '#ff0000');
            ctx.fillStyle = gradient;
            ctx.beginPath();
            ctx.roundRect(
                segment.x * CELL_SIZE + 2,
                segment.y * CELL_SIZE + 2,
                CELL_SIZE - 4,
                CELL_SIZE - 4,
                8
            );
            ctx.fill();
        });

        particles.forEach(p => {
            ctx.fillStyle = p.color;
            ctx.globalAlpha = p.life;
            ctx.beginPath();
            ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
            ctx.fill();
        });
        ctx.globalAlpha = 1;
    }

    function gameOver() {
        clearInterval(gameLoop);
        setTimeout(() => {
            alert(`GAME OVER\nPlayer: ${playerScore}\nAI: ${aiScore}`);
            resetGame();
        }, 1000);
    }

    function resetGame() {
        playerSnake = [{ x: 5, y: 5 }];
        aiSnake = [{ x: GRID_SIZE - 5, y: GRID_SIZE - 5 }];
        playerDirection = 'right';
        aiDirection = 'left';
        playerScore = aiScore = 0;
        food = generateFood();
        obstacles = generateObstacles();
        particles = [];
        score.textContent = '🚀 PLAYER: 0 | 🤖 AI: 0';
        if (gameLoop) clearInterval(gameLoop);
        gameLoop = setInterval(update, gameSpeed);
    }

    document.addEventListener('keydown', (e) => {
        switch (e.key) {
            case 'ArrowUp': if (playerDirection !== 'down') playerDirection = 'up'; break;
            case 'ArrowDown': if (playerDirection !== 'up') playerDirection = 'down'; break;
            case 'ArrowLeft': if (playerDirection !== 'right') playerDirection = 'left'; break;
            case 'ArrowRight': if (playerDirection !== 'left') playerDirection = 'right'; break;
        }
    });

    function update() {
        aiDirection = aiPathfinding();

        const playerHead = { ...playerSnake[0] };
        const aiHead = { ...aiSnake[0] };

        switch (playerDirection) {
            case 'up': playerHead.y--; break;
            case 'down': playerHead.y++; break;
            case 'left': playerHead.x--; break;
            case 'right': playerHead.x++; break;
        }

        switch (aiDirection) {
            case 'up': aiHead.y--; break;
            case 'down': aiHead.y++; break;
            case 'left': aiHead.x--; break;
            case 'right': aiHead.x++; break;
        }

        const playerCollision = checkCollision(playerHead, playerSnake);
        const aiCollision = checkCollision(aiHead, aiSnake);

        if (playerCollision || aiCollision) {
            gameOver();
            return;
        }

        playerSnake.unshift(playerHead);
        aiSnake.unshift(aiHead);

        if (playerHead.x === food.x && playerHead.y === food.y) {
            playerScore++;
            food = generateFood();
        } else {
            playerSnake.pop();
        }

        if (aiHead.x === food.x && aiHead.y === food.y) {
            aiScore++;
            food = generateFood();
        } else {
            aiSnake.pop();
        }

        score.textContent = `🚀 PLAYER: ${playerScore} | 🤖 AI: ${aiScore}`;
        updateParticles();
        draw();
    }

    function checkCollision(head, snake) {
        return head.x < 0 || head.x >= GRID_SIZE ||
            head.y < 0 || head.y >= GRID_SIZE ||
            obstacles.some(o => o.x === head.x && o.y === head.y) ||
            snake.slice(1).some(s => s.x === head.x && s.y === head.y) ||
            (snake === playerSnake ? aiSnake : playerSnake).some(s =>
                s.x === head.x && s.y === head.y);
    }

    resetGame();
})();